@zolomedia/bifrost-client 1.7.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/L1_Foundation/L1_Foundation.js +13 -0
  2. package/L1_Foundation/bootstrap/bootstrap.js +11 -0
  3. package/L1_Foundation/bootstrap/bootstrap_hooks.js +123 -0
  4. package/L1_Foundation/bootstrap/bootstrap_index.js +15 -0
  5. package/L1_Foundation/bootstrap/bootstrap_logger.js +135 -0
  6. package/L1_Foundation/bootstrap/cdn_loader.js +217 -0
  7. package/L1_Foundation/bootstrap/module_registry.js +102 -0
  8. package/L1_Foundation/bootstrap/prism_loader.js +164 -0
  9. package/L1_Foundation/config/client_config.js +110 -0
  10. package/L1_Foundation/config/config.js +7 -0
  11. package/L1_Foundation/connection/connection.js +8 -0
  12. package/L1_Foundation/connection/websocket_connection.js +122 -0
  13. package/L1_Foundation/constants/bifrost_constants.js +284 -0
  14. package/L1_Foundation/constants/constants.js +7 -0
  15. package/L1_Foundation/logger/logger.js +10 -0
  16. package/L2_Handling/L2_Handling.js +15 -0
  17. package/L2_Handling/cache/cache.js +22 -0
  18. package/L2_Handling/cache/cache_constants.js +69 -0
  19. package/L2_Handling/cache/orchestration/cache_manager.js +299 -0
  20. package/L2_Handling/cache/orchestration/cache_orchestrator.js +260 -0
  21. package/L2_Handling/cache/orchestration/orchestration.js +12 -0
  22. package/L2_Handling/cache/storage/session_manager.js +289 -0
  23. package/L2_Handling/cache/storage/storage.js +10 -0
  24. package/L2_Handling/cache/storage/storage_manager.js +590 -0
  25. package/L2_Handling/display/composite/composite.js +13 -0
  26. package/L2_Handling/display/composite/dashboard_renderer.js +221 -0
  27. package/L2_Handling/display/composite/swiper_renderer.js +564 -0
  28. package/L2_Handling/display/composite/terminal_renderer.js +922 -0
  29. package/L2_Handling/display/composite/wizard_conditional_renderer.js +274 -0
  30. package/L2_Handling/display/display.js +30 -0
  31. package/L2_Handling/display/feedback/feedback.js +11 -0
  32. package/L2_Handling/display/feedback/progressbar_renderer.js +418 -0
  33. package/L2_Handling/display/feedback/spinner_renderer.js +246 -0
  34. package/L2_Handling/display/inputs/button_renderer.js +634 -0
  35. package/L2_Handling/display/inputs/form_renderer.js +583 -0
  36. package/L2_Handling/display/inputs/input_renderer.js +658 -0
  37. package/L2_Handling/display/inputs/inputs.js +12 -0
  38. package/L2_Handling/display/navigation/menu_renderer.js +206 -0
  39. package/L2_Handling/display/navigation/navigation.js +11 -0
  40. package/L2_Handling/display/navigation/navigation_renderer.js +703 -0
  41. package/L2_Handling/display/orchestration/orchestration.js +11 -0
  42. package/L2_Handling/display/orchestration/renderer.js +430 -0
  43. package/L2_Handling/display/orchestration/zdisplay_orchestrator.js +1759 -0
  44. package/L2_Handling/display/outputs/alert_renderer.js +161 -0
  45. package/L2_Handling/display/outputs/audio_renderer.js +94 -0
  46. package/L2_Handling/display/outputs/card_renderer.js +229 -0
  47. package/L2_Handling/display/outputs/code_renderer.js +66 -0
  48. package/L2_Handling/display/outputs/dl_renderer.js +131 -0
  49. package/L2_Handling/display/outputs/header_renderer.js +162 -0
  50. package/L2_Handling/display/outputs/icon_renderer.js +107 -0
  51. package/L2_Handling/display/outputs/image_renderer.js +145 -0
  52. package/L2_Handling/display/outputs/list_renderer.js +190 -0
  53. package/L2_Handling/display/outputs/outputs.js +19 -0
  54. package/L2_Handling/display/outputs/table_renderer.js +765 -0
  55. package/L2_Handling/display/outputs/text_renderer.js +818 -0
  56. package/L2_Handling/display/outputs/typography_renderer.js +293 -0
  57. package/L2_Handling/display/outputs/video_renderer.js +116 -0
  58. package/L2_Handling/display/primitives/document_structure_primitives.js +319 -0
  59. package/L2_Handling/display/primitives/form_primitives.js +526 -0
  60. package/L2_Handling/display/primitives/generic_containers.js +109 -0
  61. package/L2_Handling/display/primitives/interactive_primitives.js +305 -0
  62. package/L2_Handling/display/primitives/link_primitives.js +552 -0
  63. package/L2_Handling/display/primitives/lists_primitives.js +262 -0
  64. package/L2_Handling/display/primitives/media_primitives.js +383 -0
  65. package/L2_Handling/display/primitives/primitives.js +19 -0
  66. package/L2_Handling/display/primitives/semantic_element_primitive.js +226 -0
  67. package/L2_Handling/display/primitives/table_primitives.js +528 -0
  68. package/L2_Handling/display/primitives/typography_primitives.js +175 -0
  69. package/L2_Handling/display/specialized/input_request_renderer.js +467 -0
  70. package/L2_Handling/display/specialized/specialized.js +10 -0
  71. package/L2_Handling/hooks/hooks.js +9 -0
  72. package/L2_Handling/hooks/menu_integration.js +57 -0
  73. package/L2_Handling/hooks/widget_hook_manager.js +292 -0
  74. package/L2_Handling/message/message.js +8 -0
  75. package/L2_Handling/message/message_handler.js +701 -0
  76. package/L2_Handling/navigation/navigation.js +8 -0
  77. package/L2_Handling/navigation/navigation_manager.js +403 -0
  78. package/L2_Handling/zhooks/features/cache_live.js +287 -0
  79. package/L2_Handling/zhooks/features/crumbs_live.js +292 -0
  80. package/L2_Handling/zhooks/zhooks_manager.js +65 -0
  81. package/L2_Handling/zvaf/zvaf.js +8 -0
  82. package/L2_Handling/zvaf/zvaf_manager.js +334 -0
  83. package/L3_Abstraction/L3_Abstraction.js +12 -0
  84. package/L3_Abstraction/orchestrator/container_unwrapper.js +101 -0
  85. package/L3_Abstraction/orchestrator/group_renderer.js +698 -0
  86. package/L3_Abstraction/orchestrator/input_event_handler.js +797 -0
  87. package/L3_Abstraction/orchestrator/metadata_processor.js +249 -0
  88. package/L3_Abstraction/orchestrator/navbar_builder.js +201 -0
  89. package/L3_Abstraction/orchestrator/orchestrator.js +13 -0
  90. package/L3_Abstraction/orchestrator/wizard_gate_handler.js +360 -0
  91. package/L3_Abstraction/renderer/renderer.js +1 -0
  92. package/L3_Abstraction/session/session.js +1 -0
  93. package/L4_Orchestration/L4_Orchestration.js +11 -0
  94. package/L4_Orchestration/client/client.js +1 -0
  95. package/L4_Orchestration/facade/facade.js +9 -0
  96. package/L4_Orchestration/facade/manager_registry.js +118 -0
  97. package/L4_Orchestration/facade/renderer_registry.js +274 -0
  98. package/L4_Orchestration/lifecycle/asset_loader.js +255 -0
  99. package/L4_Orchestration/lifecycle/initializer.js +135 -0
  100. package/L4_Orchestration/lifecycle/lifecycle.js +8 -0
  101. package/L4_Orchestration/rendering/facade.js +94 -0
  102. package/L4_Orchestration/rendering/rendering.js +7 -0
  103. package/LICENSE +21 -0
  104. package/README.md +82 -0
  105. package/bifrost_client.js +204 -0
  106. package/bifrost_core.js +1686 -0
  107. package/docs/ARCHITECTURE.md +111 -0
  108. package/docs/PROTOCOL.md +106 -0
  109. package/docs/RENDERERS.md +101 -0
  110. package/docs/SECURITY.md +92 -0
  111. package/package.json +24 -0
  112. package/syntax/prism-zconfig.js +41 -0
  113. package/syntax/prism-zenv.js +69 -0
  114. package/syntax/prism-zolo-theme.css +288 -0
  115. package/syntax/prism-zolo.js +380 -0
  116. package/syntax/prism-zschema.js +38 -0
  117. package/syntax/prism-zspark.js +25 -0
  118. package/syntax/prism-zui.js +68 -0
  119. package/zSys/accessibility/accessibility.js +10 -0
  120. package/zSys/accessibility/emoji_accessibility.js +173 -0
  121. package/zSys/dom/block_utils.js +122 -0
  122. package/zSys/dom/container_utils.js +370 -0
  123. package/zSys/dom/dom.js +13 -0
  124. package/zSys/dom/dom_utils.js +328 -0
  125. package/zSys/dom/encoding_utils.js +117 -0
  126. package/zSys/dom/style_utils.js +71 -0
  127. package/zSys/errors/error_display.js +299 -0
  128. package/zSys/errors/errors.js +10 -0
  129. package/zSys/theme/color_utils.js +274 -0
  130. package/zSys/theme/dark_mode_utils.js +272 -0
  131. package/zSys/theme/size_utils.js +256 -0
  132. package/zSys/theme/spacing_utils.js +405 -0
  133. package/zSys/theme/theme.js +14 -0
  134. package/zSys/theme/zbase.css +1735 -0
  135. package/zSys/theme/zbase_inject.js +161 -0
  136. package/zSys/theme/ztheme_utils.js +305 -0
  137. package/zSys/validation/error_boundary.js +201 -0
  138. package/zSys/validation/validation.js +11 -0
  139. package/zSys/validation/validation_utils.js +238 -0
  140. package/zSys/zSys.js +14 -0
@@ -0,0 +1,370 @@
1
+ /**
2
+ *
3
+ * Container Utilities - Layout Primitives
4
+ *
5
+ *
6
+ * Pure functions for creating and managing layout containers.
7
+ * These are the TRUE primitives - before text, before headers, before anything.
8
+ *
9
+ * @module rendering/container_utils
10
+ * @layer 0 (Foundation - below Layer 2 utilities)
11
+ * @pattern Pure Functions (no state, no side effects)
12
+ *
13
+ * Philosophy:
14
+ * - "Container first" - Everything needs a home
15
+ * - Pure functions (input → output, no side effects)
16
+ * - Uses zTheme exclusively (no custom CSS)
17
+ * - Composable (containers can nest)
18
+ *
19
+ * Dependencies:
20
+ * - Layer 2: dom_utils.js
21
+ *
22
+ * Exports:
23
+ * - createContainer()
24
+ * - createSection()
25
+ * - createWrapper()
26
+ * - applyContainerStyles()
27
+ *
28
+ * Example:
29
+ * ```javascript
30
+ * import { createContainer, createWrapper } from './container_utils.js';
31
+ *
32
+ * const container = createContainer(['zContainer', 'zMy-4']);
33
+ * const flexWrapper = createWrapper(['zD-flex', 'zGap-3']);
34
+ * ```
35
+ */
36
+
37
+ // ─────────────────────────────────────────────────────────────────
38
+ // Imports
39
+ // ─────────────────────────────────────────────────────────────────
40
+
41
+ // Layer 2: Utilities
42
+ import { createElement, setAttributes } from './dom_utils.js';
43
+
44
+ //
45
+ // Container Creation Functions
46
+ //
47
+
48
+ /**
49
+ * Create a main content container (zTheme: zContainer)
50
+ *
51
+ * A container is the primary layout element that provides:
52
+ * - Max width constraints
53
+ * - Horizontal centering
54
+ * - Responsive padding
55
+ *
56
+ * @param {Array<string>} [classes=[]] - Additional CSS classes to apply
57
+ * @param {Object} [attributes={}] - HTML attributes (id, data-*, etc.)
58
+ * @returns {HTMLElement} Div element with zContainer class
59
+ *
60
+ * @example
61
+ * // Basic centered container
62
+ * const container = createContainer();
63
+ *
64
+ * @example
65
+ * // Container with vertical margin
66
+ * const container = createContainer(['zMy-4']);
67
+ *
68
+ * @example
69
+ * // Fluid container (full width)
70
+ * const fluidContainer = createContainer(['zContainer-fluid']);
71
+ */
72
+ export function createContainer(classes = [], attributes = {}) {
73
+ const containerClasses = ['zContainer', ...classes];
74
+ const container = createElement('div', containerClasses);
75
+
76
+ if (Object.keys(attributes).length > 0) {
77
+ setAttributes(container, attributes);
78
+ }
79
+
80
+ return container;
81
+ }
82
+
83
+ /**
84
+ * Create a semantic section element
85
+ *
86
+ * Sections are semantic HTML5 elements for content grouping.
87
+ * Use for major page sections (hero, features, footer, etc.)
88
+ *
89
+ * @param {Array<string>} [classes=[]] - CSS classes to apply
90
+ * @param {Object} [attributes={}] - HTML attributes
91
+ * @returns {HTMLElement} Section element
92
+ *
93
+ * @example
94
+ * // Hero section with background
95
+ * const hero = createSection(['zBg-primary', 'zText-white', 'zPy-5']);
96
+ *
97
+ * @example
98
+ * // Feature section with ID
99
+ * const features = createSection(['zPy-4'], { id: 'features' });
100
+ */
101
+ export function createSection(classes = [], attributes = {}) {
102
+ const section = createElement('section', classes);
103
+
104
+ if (Object.keys(attributes).length > 0) {
105
+ setAttributes(section, attributes);
106
+ }
107
+
108
+ return section;
109
+ }
110
+
111
+ /**
112
+ * Create a generic wrapper div (for grouping/layout)
113
+ *
114
+ * Wrappers are flexible divs for:
115
+ * - Grouping related elements
116
+ * - Applying flex/grid layouts
117
+ * - Creating visual boundaries
118
+ *
119
+ * @param {Array<string>} [classes=[]] - CSS classes to apply
120
+ * @param {Object} [attributes={}] - HTML attributes
121
+ * @returns {HTMLElement} Div element (no default classes)
122
+ *
123
+ * @example
124
+ * // Flex row wrapper
125
+ * const row = createWrapper(['zD-flex', 'zGap-3', 'zFlex-items-center']);
126
+ *
127
+ * @example
128
+ * // Grid wrapper
129
+ * const grid = createWrapper(['zD-grid', 'zGap-4'], { style: 'grid-template-columns: repeat(3, 1fr);' });
130
+ *
131
+ * @example
132
+ * // Card body wrapper
133
+ * const cardBody = createWrapper(['zCard-body', 'zP-3']);
134
+ */
135
+ export function createWrapper(classes = [], attributes = {}) {
136
+ const wrapper = createElement('div', classes);
137
+
138
+ if (Object.keys(attributes).length > 0) {
139
+ setAttributes(wrapper, attributes);
140
+ }
141
+
142
+ return wrapper;
143
+ }
144
+
145
+ //
146
+ // Container Styling Functions
147
+ //
148
+
149
+ /**
150
+ * Apply container-specific styles via config object
151
+ *
152
+ * This is a higher-level function for applying multiple style concerns at once.
153
+ * Useful when you need to apply layout, display, and alignment in one call.
154
+ *
155
+ * @param {HTMLElement} element - Element to style
156
+ * @param {Object} config - Style configuration
157
+ * @param {string} [config.display] - Display mode (flex, grid, block, inline-block)
158
+ * @param {string} [config.direction] - Flex direction (row, column)
159
+ * @param {string} [config.align] - Alignment (start, center, end, stretch)
160
+ * @param {string} [config.justify] - Justification (start, center, end, between, around)
161
+ * @param {number} [config.gap] - Gap size (0-5)
162
+ * @param {boolean} [config.wrap] - Enable flex wrap
163
+ * @returns {HTMLElement} Element with styles applied
164
+ *
165
+ * @example
166
+ * // Create a flex container with centering
167
+ * const wrapper = createWrapper();
168
+ * applyContainerStyles(wrapper, {
169
+ * display: 'flex',
170
+ * direction: 'row',
171
+ * align: 'center',
172
+ * justify: 'between',
173
+ * gap: 3
174
+ * });
175
+ *
176
+ * @example
177
+ * // Create a grid container
178
+ * const grid = createWrapper();
179
+ * applyContainerStyles(grid, {
180
+ * display: 'grid',
181
+ * gap: 4
182
+ * });
183
+ */
184
+ export function applyContainerStyles(element, config = {}) {
185
+ const classes = [];
186
+
187
+ // Display mode
188
+ if (config.display) {
189
+ const displayMap = {
190
+ 'flex': 'zD-flex',
191
+ 'grid': 'zD-grid',
192
+ 'block': 'zD-block',
193
+ 'inline-block': 'zD-inline-block',
194
+ 'none': 'zD-none'
195
+ };
196
+ const displayClass = displayMap[config.display];
197
+ if (displayClass) {
198
+ classes.push(displayClass);
199
+ }
200
+ }
201
+
202
+ // Flex direction (only applies if display is flex)
203
+ if (config.direction && config.display === 'flex') {
204
+ const directionMap = {
205
+ 'row': 'zFlex-row',
206
+ 'column': 'zFlex-column',
207
+ 'row-reverse': 'zFlex-row-reverse',
208
+ 'column-reverse': 'zFlex-column-reverse'
209
+ };
210
+ const directionClass = directionMap[config.direction];
211
+ if (directionClass) {
212
+ classes.push(directionClass);
213
+ }
214
+ }
215
+
216
+ // Alignment (align-items - vertical alignment in row, horizontal in column)
217
+ if (config.align) {
218
+ const alignMap = {
219
+ 'start': 'zFlex-items-start',
220
+ 'center': 'zFlex-items-center',
221
+ 'end': 'zFlex-items-end',
222
+ 'stretch': 'zFlex-items-stretch',
223
+ 'baseline': 'zFlex-items-baseline'
224
+ };
225
+ const alignClass = alignMap[config.align];
226
+ if (alignClass) {
227
+ classes.push(alignClass);
228
+ }
229
+ }
230
+
231
+ // Justification (justify-content - horizontal alignment in row, vertical in column)
232
+ if (config.justify) {
233
+ const justifyMap = {
234
+ 'start': 'zFlex-start',
235
+ 'center': 'zFlex-center',
236
+ 'end': 'zFlex-end',
237
+ 'between': 'zFlex-between',
238
+ 'around': 'zFlex-around',
239
+ 'evenly': 'zFlex-evenly'
240
+ };
241
+ const justifyClass = justifyMap[config.justify];
242
+ if (justifyClass) {
243
+ classes.push(justifyClass);
244
+ }
245
+ }
246
+
247
+ // Gap
248
+ if (typeof config.gap === 'number' && config.gap >= 0 && config.gap <= 5) {
249
+ classes.push(`zGap-${config.gap}`);
250
+ }
251
+
252
+ // Flex wrap
253
+ if (config.wrap) {
254
+ classes.push('zFlex-wrap');
255
+ }
256
+
257
+ // Apply all classes at once
258
+ if (classes.length > 0) {
259
+ element.classList.add(...classes);
260
+ }
261
+
262
+ return element;
263
+ }
264
+
265
+ //
266
+ // Flex Utility Functions (for programmatic class generation)
267
+ //
268
+
269
+ /**
270
+ * Get zTheme flex justify-content class (horizontal alignment)
271
+ * @param {string} justify - Justify: 'start', 'center', 'end', 'between', 'around', 'evenly'
272
+ * @returns {string} zTheme justify class
273
+ */
274
+ export function getFlexJustifyClass(justify = 'start') {
275
+ const justifyMap = {
276
+ start: 'zFlex-start',
277
+ center: 'zFlex-center',
278
+ end: 'zFlex-end',
279
+ between: 'zFlex-between',
280
+ around: 'zFlex-around',
281
+ evenly: 'zFlex-evenly'
282
+ };
283
+
284
+ return justifyMap[justify] || 'zFlex-start';
285
+ }
286
+
287
+ /**
288
+ * Get zTheme flex align-items class (vertical alignment)
289
+ * @param {string} align - Align: 'start', 'center', 'end', 'stretch', 'baseline'
290
+ * @returns {string} zTheme align class
291
+ */
292
+ export function getFlexAlignClass(align = 'stretch') {
293
+ const alignMap = {
294
+ start: 'zFlex-items-start',
295
+ center: 'zFlex-items-center',
296
+ end: 'zFlex-items-end',
297
+ stretch: 'zFlex-items-stretch',
298
+ baseline: 'zFlex-items-baseline'
299
+ };
300
+
301
+ return alignMap[align] || 'zFlex-items-stretch';
302
+ }
303
+
304
+ /**
305
+ * Get zTheme flex direction class
306
+ * @param {string} direction - Direction: 'row', 'column', 'row-reverse', 'column-reverse'
307
+ * @returns {string} zTheme flex direction class
308
+ */
309
+ export function getFlexDirectionClass(direction = 'row') {
310
+ const directionMap = {
311
+ row: 'zFlex-row',
312
+ column: 'zFlex-column',
313
+ 'row-reverse': 'zFlex-row-reverse',
314
+ 'column-reverse': 'zFlex-column-reverse'
315
+ };
316
+
317
+ return directionMap[direction] || 'zFlex-row';
318
+ }
319
+
320
+ /**
321
+ * Create a container element for a zKey with metadata support
322
+ * @param {string} zKey - The zKey identifier
323
+ * @param {Object} metadata - Metadata object (may contain _zClass, _zHTML)
324
+ * @returns {Promise<HTMLElement>} Container element (div by default, or semantic element if _zHTML is specified)
325
+ */
326
+ export async function createZKeyContainer(zKey, metadata = {}) {
327
+ // Use centralized semantic element primitive (SSOT for _zHTML)
328
+ const { createSemanticElement } = await import('../../L2_Handling/display/primitives/semantic_element_primitive.js');
329
+ const elementType = metadata._zHTML || 'div';
330
+ const container = createSemanticElement(elementType);
331
+
332
+ // Check for custom classes in metadata
333
+ if (metadata._zClass !== undefined) {
334
+ if (metadata._zClass === '' || metadata._zClass === null) {
335
+ // Empty string or null = no container classes (opt-out)
336
+ container.className = '';
337
+ } else {
338
+ // Apply custom classes
339
+ const classes = Array.isArray(metadata._zClass)
340
+ ? metadata._zClass
341
+ : metadata._zClass.split(',').map(c => c.trim());
342
+ container.className = classes.join(' ');
343
+ }
344
+ } else {
345
+ // Default: responsive zTheme container
346
+ container.className = 'zContainer-fluid zp-2';
347
+ }
348
+
349
+ // Set data attribute for debugging
350
+ container.setAttribute('data-zkey', zKey);
351
+ // Set id for DevTools navigation and CSS targeting
352
+ container.setAttribute('id', zKey);
353
+
354
+ return container;
355
+ }
356
+
357
+ //
358
+ // Default Export (for convenience)
359
+ //
360
+ export default {
361
+ createContainer,
362
+ createSection,
363
+ createWrapper,
364
+ applyContainerStyles,
365
+ getFlexJustifyClass,
366
+ getFlexAlignClass,
367
+ getFlexDirectionClass,
368
+ createZKeyContainer
369
+ };
370
+
@@ -0,0 +1,13 @@
1
+ /**
2
+ * zSys/dom - DOM Utilities Barrel
3
+ *
4
+ * DOM manipulation utilities (element creation, containers, blocks, encoding).
5
+ *
6
+ * @module zSys/dom
7
+ * @layer zSys (System Utilities)
8
+ */
9
+
10
+ export * from './block_utils.js';
11
+ export * from './container_utils.js';
12
+ export * from './dom_utils.js';
13
+ export * from './encoding_utils.js';
@@ -0,0 +1,328 @@
1
+ /**
2
+ *
3
+ * DOM Utilities - Pure DOM Manipulation Functions
4
+ *
5
+ *
6
+ * Pure functions for DOM manipulation. Zero dependencies, zero state,
7
+ * zero side effects (except the DOM operations themselves).
8
+ *
9
+ * @module utils/dom_utils
10
+ * @layer 2
11
+ * @pattern Pure Functions
12
+ *
13
+ * Dependencies:
14
+ * - Layer 0: Browser DOM APIs only
15
+ *
16
+ * Exports:
17
+ * - createElement: Create element with classes and attributes
18
+ * - safeQuery: Safe querySelector (returns null instead of throwing)
19
+ * - safeQueryAll: Safe querySelectorAll (returns array instead of NodeList)
20
+ * - clearElement: Clear element content safely
21
+ * - appendChildren: Append multiple children at once
22
+ * - removeElement: Remove element safely (checks parent exists)
23
+ * - replaceElement: Replace element with another
24
+ * - setAttributes: Set multiple attributes at once
25
+ * - toggleClasses: Toggle multiple classes at once
26
+ * - createTextNode: Create text node (safe alternative to textContent)
27
+ *
28
+ * Example:
29
+ * ```javascript
30
+ * import { createElement, appendChildren } from './dom_utils.js';
31
+ *
32
+ * const button = createElement('button', ['zBtn', 'zBtnPrimary'], { type: 'button' });
33
+ * const icon = createElement('i', ['bi', 'bi-check']);
34
+ * appendChildren(button, [icon, createTextNode('Submit')]);
35
+ * ```
36
+ */
37
+
38
+ //
39
+ // DOM Creation & Manipulation
40
+ //
41
+
42
+ /**
43
+ * Create an HTML element with classes and attributes
44
+ *
45
+ * @param {string} tag - HTML tag name (e.g., 'div', 'button', 'input')
46
+ * @param {string[]} [classNames=[]] - Array of CSS class names to add
47
+ * @param {Object} [attributes={}] - HTML attributes to set {id, type, disabled, etc}
48
+ * @returns {HTMLElement} Created element
49
+ *
50
+ * @example
51
+ * const button = createElement('button', ['zBtn', 'zBtnPrimary'], {
52
+ * type: 'button',
53
+ * id: 'submit-btn',
54
+ * disabled: false
55
+ * });
56
+ *
57
+ * @throws {Error} If tag is empty or invalid
58
+ */
59
+ export function createElement(tag, classNames = [], attributes = {}) {
60
+ if (!tag || typeof tag !== 'string') {
61
+ throw new Error('[createElement] tag must be a non-empty string');
62
+ }
63
+
64
+ const element = document.createElement(tag);
65
+
66
+ // Add classes
67
+ if (classNames && classNames.length > 0) {
68
+ element.classList.add(...classNames);
69
+ }
70
+
71
+ // Set attributes
72
+ if (attributes && typeof attributes === 'object') {
73
+ setAttributes(element, attributes);
74
+ }
75
+
76
+ return element;
77
+ }
78
+
79
+ /**
80
+ * Create a text node (safe alternative to textContent)
81
+ *
82
+ * @param {string} text - Text content
83
+ * @returns {Text} Text node
84
+ *
85
+ * @example
86
+ * const textNode = createTextNode('Hello, World!');
87
+ * element.appendChild(textNode);
88
+ */
89
+ export function createTextNode(text) {
90
+ return document.createTextNode(text || '');
91
+ }
92
+
93
+ //
94
+ // DOM Querying
95
+ //
96
+
97
+ /**
98
+ * Safe querySelector (returns null instead of throwing)
99
+ *
100
+ * @param {string} selector - CSS selector
101
+ * @param {Element|Document} [context=document] - Search context
102
+ * @returns {Element|null} Found element or null
103
+ *
104
+ * @example
105
+ * const element = safeQuery('#my-element');
106
+ * if (element) {
107
+ * element.classList.add('active');
108
+ * }
109
+ */
110
+ export function safeQuery(selector, context = document) {
111
+ try {
112
+ return context.querySelector(selector);
113
+ } catch (error) {
114
+ this.logger.warn(`[safeQuery] Invalid selector: ${selector}`, error);
115
+ return null;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Safe querySelectorAll (returns array instead of NodeList)
121
+ *
122
+ * @param {string} selector - CSS selector
123
+ * @param {Element|Document} [context=document] - Search context
124
+ * @returns {Element[]} Array of found elements (empty if none found)
125
+ *
126
+ * @example
127
+ * const buttons = safeQueryAll('.zBtn');
128
+ * buttons.forEach(btn => btn.disabled = true);
129
+ */
130
+ export function safeQueryAll(selector, context = document) {
131
+ try {
132
+ return Array.from(context.querySelectorAll(selector));
133
+ } catch (error) {
134
+ this.logger.warn(`[safeQueryAll] Invalid selector: ${selector}`, error);
135
+ return [];
136
+ }
137
+ }
138
+
139
+ //
140
+ // DOM Modification
141
+ //
142
+
143
+ /**
144
+ * Clear element content safely
145
+ *
146
+ * @param {Element} element - Element to clear
147
+ *
148
+ * @example
149
+ * const container = document.getElementById('content');
150
+ * clearElement(container);
151
+ */
152
+ export function clearElement(element) {
153
+ if (!element) {
154
+ return;
155
+ }
156
+
157
+ // Remove all children
158
+ while (element.firstChild) {
159
+ element.removeChild(element.firstChild);
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Append multiple children to a parent element at once
165
+ *
166
+ * @param {Element} parent - Parent element
167
+ * @param {(Element|Text)[]} [children=[]] - Array of child elements or text nodes
168
+ *
169
+ * @example
170
+ * const container = createElement('div');
171
+ * const header = createElement('h1');
172
+ * const paragraph = createElement('p');
173
+ * appendChildren(container, [header, paragraph]);
174
+ */
175
+ export function appendChildren(parent, children = []) {
176
+ if (!parent || !Array.isArray(children)) {
177
+ return;
178
+ }
179
+
180
+ children.forEach(child => {
181
+ if (child) {
182
+ parent.appendChild(child);
183
+ }
184
+ });
185
+ }
186
+
187
+ /**
188
+ * Remove element from DOM safely (checks if parent exists)
189
+ *
190
+ * @param {Element} element - Element to remove
191
+ *
192
+ * @example
193
+ * const button = document.getElementById('old-button');
194
+ * removeElement(button);
195
+ */
196
+ export function removeElement(element) {
197
+ if (element && element.parentNode) {
198
+ element.parentNode.removeChild(element);
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Replace element with another element
204
+ *
205
+ * @param {Element} oldElement - Element to replace
206
+ * @param {Element} newElement - Replacement element
207
+ *
208
+ * @example
209
+ * const oldButton = document.getElementById('old-btn');
210
+ * const newButton = createElement('button', ['zBtn', 'zBtnSuccess']);
211
+ * replaceElement(oldButton, newButton);
212
+ */
213
+ export function replaceElement(oldElement, newElement) {
214
+ if (!oldElement || !newElement || !oldElement.parentNode) {
215
+ return;
216
+ }
217
+
218
+ oldElement.parentNode.replaceChild(newElement, oldElement);
219
+ }
220
+
221
+ //
222
+ // Attributes & Classes
223
+ //
224
+
225
+ /**
226
+ * Set multiple attributes on an element at once
227
+ *
228
+ * @param {Element} element - Target element
229
+ * @param {Object} attributes - Attributes to set {name: value}
230
+ *
231
+ * @example
232
+ * const input = createElement('input');
233
+ * setAttributes(input, {
234
+ * type: 'text',
235
+ * id: 'username',
236
+ * placeholder: 'Enter username',
237
+ * required: true
238
+ * });
239
+ */
240
+ export function setAttributes(element, attributes = {}) {
241
+ if (!element || !attributes) {
242
+ return;
243
+ }
244
+
245
+ Object.entries(attributes).forEach(([key, value]) => {
246
+ if (value === null || value === undefined) {
247
+ element.removeAttribute(key);
248
+ } else if (typeof value === 'boolean') {
249
+ // Handle boolean attributes (disabled, required, etc)
250
+ if (value) {
251
+ element.setAttribute(key, '');
252
+ } else {
253
+ element.removeAttribute(key);
254
+ }
255
+ } else {
256
+ element.setAttribute(key, value);
257
+ }
258
+ });
259
+ }
260
+
261
+ /**
262
+ * Toggle multiple classes on an element at once
263
+ *
264
+ * @param {Element} element - Target element
265
+ * @param {string[]} classes - Array of class names to toggle
266
+ * @param {boolean|null} [force=null] - Force add (true) or remove (false), or toggle (null)
267
+ *
268
+ * @example
269
+ * // Toggle classes
270
+ * toggleClasses(button, ['active', 'highlighted']);
271
+ *
272
+ * // Force add classes
273
+ * toggleClasses(button, ['disabled', 'inactive'], true);
274
+ *
275
+ * // Force remove classes
276
+ * toggleClasses(button, ['active', 'highlighted'], false);
277
+ */
278
+ export function toggleClasses(element, classes = [], force = null) {
279
+ if (!element || !Array.isArray(classes)) {
280
+ return;
281
+ }
282
+
283
+ classes.forEach(className => {
284
+ if (className) {
285
+ if (force === null) {
286
+ element.classList.toggle(className);
287
+ } else {
288
+ element.classList.toggle(className, force);
289
+ }
290
+ }
291
+ });
292
+ }
293
+
294
+ //
295
+ // Container Helpers (Layer 2 - Pure Utilities)
296
+ //
297
+
298
+ /**
299
+ * Create a div element with optional attributes
300
+ *
301
+ * Utility function to create a div with attributes in one call.
302
+ * This is a Layer 2 (Utilities) function - pure, no business logic.
303
+ *
304
+ * @param {Object} [attributes={}] - Attributes to set on the div
305
+ * @returns {HTMLDivElement} The created div element
306
+ *
307
+ * @example
308
+ * // Simple div
309
+ * const container = createDiv();
310
+ *
311
+ * // Div with classes
312
+ * const card = createDiv({ class: 'zCard zCard-primary' });
313
+ *
314
+ * // Div with multiple attributes
315
+ * const item = createDiv({ 'data-item-id': '123', 'data-type': 'product' });
316
+ *
317
+ * // Div with ARIA attributes for accessibility
318
+ * const alert = createDiv({ role: 'alert', 'aria-live': 'polite' });
319
+ */
320
+ export function createDiv(attributes = {}) {
321
+ const div = createElement('div');
322
+
323
+ if (Object.keys(attributes).length > 0) {
324
+ setAttributes(div, attributes);
325
+ }
326
+
327
+ return div;
328
+ }