-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathminibus.html
More file actions
178 lines (160 loc) · 8.1 KB
/
minibus.html
File metadata and controls
178 lines (160 loc) · 8.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MiniBus - Hierarchical Scoped Ownership</title>
<link href="https://cdn.jsdelivr.net/npm/daisyui@2.51.5/dist/full.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<script>
const MiniBus = (() => {
let state = {};
const watchers = {};
const permissions = {};
return {
init() {
document.querySelectorAll('[id]').forEach(element => {
[...element.attributes]
.filter(attr => attr.name.startsWith('data-'))
.forEach(attr => {
const stateKey = `${element.id}.${attr.name}`;
state[stateKey] = attr.value;
});
});
console.log('Initial State:', JSON.stringify(state, null, 2));
},
// Grant permission for a component to modify another component's state if it's within the hierarchy
grantPermission(ownerId, stateKey) {
const ownerElement = document.getElementById(ownerId);
const [targetElementId] = stateKey.split('.');
const targetElement = document.getElementById(targetElementId);
if (this._isChildOf(targetElement, ownerElement)) {
if (!permissions[stateKey]) {
permissions[stateKey] = [];
}
permissions[stateKey].push(ownerId);
} else {
console.error(`Permission denied: ${ownerId} cannot modify ${stateKey} as it is not within its hierarchy.`);
}
},
// Request a state change - only permitted components can modify
requestStateChange(ownerId, stateKey, newValue) {
if (permissions[stateKey]?.includes(ownerId) || stateKey.startsWith(ownerId)) {
const [elementId, attribute] = stateKey.split('.');
if (state[stateKey] !== newValue) {
state = { ...state, [stateKey]: newValue }; // Immutable state change
console.log('State Change:', stateKey, '->', newValue);
console.log('Updated State:', JSON.stringify(state, null, 2));
this._applyStateChange(elementId, attribute, newValue);
if (watchers[stateKey]) {
watchers[stateKey].forEach(callback => callback(newValue));
}
}
} else {
console.error(`Component ${ownerId} is not allowed to modify ${stateKey}`);
}
},
// Apply state change to the DOM
_applyStateChange(elementId, attribute, newValue) {
const element = document.getElementById(elementId);
if (!element) return;
switch (attribute) {
case 'data-text':
element.textContent = newValue;
break;
case 'data-class':
element.className = '';
newValue.split(' ').forEach(cls => {
if (cls.trim()) element.classList.add(cls);
});
break;
default:
element.setAttribute(attribute, newValue);
}
},
// Watch for changes to a specific state key
watch(stateKey, callback) {
if (!watchers[stateKey]) watchers[stateKey] = [];
watchers[stateKey].push(callback);
},
// Register event to modify state declaratively
registerEvent(componentId, eventType, stateKeyToChange) {
const element = document.getElementById(componentId);
if (!element) {
console.error(`Element with ID "${componentId}" not found.`);
return;
}
element.addEventListener(eventType, () => {
const currentValue = state[stateKeyToChange] || 'false';
const newValue = currentValue === 'true' ? 'false' : 'true';
this.requestStateChange(componentId, stateKeyToChange, newValue);
});
},
// Utility function to check if an element is a descendant of another element
_isChildOf(child, parent) {
let node = child;
while (node !== null) {
if (node === parent) return true;
node = node.parentNode;
}
return false;
}
};
})();
// Function to generate a nonce
function generateNonce() {
return btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
}
// Apply nonce to scripts and styles
document.addEventListener('DOMContentLoaded', () => {
const nonce = generateNonce();
const metaTag = document.createElement('meta');
metaTag.setAttribute('http-equiv', 'Content-Security-Policy');
metaTag.setAttribute('content', `default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdn.tailwindcss.com 'nonce-${nonce}'; style-src 'self' https://cdn.jsdelivr.net 'nonce-${nonce}';`);
document.head.appendChild(metaTag);
const scripts = document.querySelectorAll('script[nonce-placeholder]');
scripts.forEach(script => script.setAttribute('nonce', nonce));
});
// Logging function
function logEvent(event) {
console.log(`[${new Date().toISOString()}] ${event}`);
}
</script>
</head>
<body>
<!-- Sidebar Component -->
<div id="sidebarComponent" class="w-64 p-4 mt-6 bg-base-200 hidden" data-class="hidden">
<p>Sidebar Content</p>
<script nonce-placeholder>
// Corrected usage of watch with logging
MiniBus.watch('buttonComponent.data-click', (value) => {
logEvent(`Sidebar visibility toggled: ${value}`);
MiniBus.requestStateChange('sidebarComponent', 'sidebarComponent.data-class', value === 'true' ? '' : 'hidden');
});
</script>
</div>
<!-- Button Component -->
<div id="buttonComponent" data-click="false">
<button id="toggleButton" class="btn btn-primary" data-text="Toggle Sidebar">Toggle Sidebar</button>
<script nonce-placeholder>
// Register the button click event via MiniBus with logging
MiniBus.registerEvent('buttonComponent', 'click', 'buttonComponent.data-click');
logEvent('Button component click event registered');
// Grant permission for buttonComponent to modify the button text (only allowed if within hierarchy)
MiniBus.grantPermission('buttonComponent', 'toggleButton.data-text');
logEvent('Permission granted for buttonComponent to modify toggleButton');
// Corrected usage of watch with logging
MiniBus.watch('buttonComponent.data-click', (value) => {
logEvent(`Button click state changed: ${value}`);
MiniBus.requestStateChange('buttonComponent', 'toggleButton.data-text', value === 'true' ? 'Hide Sidebar' : 'Show Sidebar');
});
</script>
</div>
<!-- Initialize MiniBus -->
<script nonce-placeholder>
document.addEventListener('DOMContentLoaded', () => {
logEvent('Document loaded, initializing MiniBus');
MiniBus.init();
});
</script>
</body>
</html>