@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,25 @@
1
+ /**
2
+ * Prism.js language definition for zspark
3
+ * zSpark.*.zolo - Application entry point configuration
4
+ *
5
+ * Extends base 'zolo' language with file-type-specific patterns.
6
+ *
7
+ * Generated by zlsp/zlsp/generators/generate_prism_zolo.py
8
+ * DO NOT EDIT MANUALLY - regenerate with: python3 -m zlsp.generators.generate_prism_zolo
9
+ */
10
+
11
+ Prism.languages.zspark = Prism.languages.extend('zolo', {
12
+ });
13
+
14
+ // Insert value patterns BEFORE string-unquoted for priority
15
+ Prism.languages.insertBefore('zspark', 'string-unquoted', {
16
+ 'zspark-zpath-value': {
17
+ pattern: /(?<=(?:zScrapath|zVaFolder|zSpace):\s*)[@~](?:\.[a-zA-Z0-9_./]+)?/,
18
+ alias: 'keyword',
19
+ lookbehind: true,
20
+ greedy: true,
21
+ }
22
+ });
23
+
24
+ // Support both ```zspark and ```zSpark code fences
25
+ Prism.languages.zSpark = Prism.languages.zspark;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Prism.js language definition for zui
3
+ * zUI.*.zolo - User interface component files
4
+ *
5
+ * Extends base 'zolo' language with file-type-specific patterns.
6
+ *
7
+ * Generated by zlsp/zlsp/generators/generate_prism_zolo.py
8
+ * DO NOT EDIT MANUALLY - regenerate with: python3 -m zlsp.generators.generate_prism_zolo
9
+ */
10
+
11
+ Prism.languages.zui = Prism.languages.extend('zolo', {
12
+ 'zui-special-root': {
13
+ pattern: /((?:^|\n)[ \t]*)(?:zMeta|zVaF)(?=\s*:)/m,
14
+ alias: 'function',
15
+ lookbehind: true,
16
+ },
17
+ 'root-key': {
18
+ pattern: /(^|\n)([a-zA-Z][a-zA-Z0-9_]*)(?=\s*(?:\([^)]+\))?[*!^~]?:)/m,
19
+ alias: 'class-name',
20
+ lookbehind: true,
21
+ },
22
+ 'property': {
23
+ pattern: /\b[a-zA-Z][a-zA-Z0-9_]*(?=\s*(?:\([^)]+\))?[*!]?:)/,
24
+ },
25
+ 'prefix-modifier': {
26
+ pattern: /[\^~](?=[a-zA-Z][a-zA-Z0-9_]*\s*:)/,
27
+ alias: 'variable',
28
+ },
29
+ 'suffix-modifier': {
30
+ pattern: /[*!](?=:)/,
31
+ alias: 'operator',
32
+ }
33
+ });
34
+
35
+ // Insert key patterns BEFORE 'property' for correct priority
36
+ Prism.languages.insertBefore('zui', 'property', {
37
+ 'zrbac-key': {
38
+ pattern: /\bzRBAC(?=\s*:)/,
39
+ alias: 'constant',
40
+ },
41
+ 'control-flow-key': {
42
+ pattern: /\bzWizard(?=\s*:)/,
43
+ alias: 'function',
44
+ },
45
+ 'zsub-key': {
46
+ pattern: /\bzSub(?=\s*:)/,
47
+ alias: 'keyword',
48
+ },
49
+ 'zui-element': {
50
+ pattern: /\b(?:zError|zWizard|zImage|zWarning|zSuccess|zH3|zDL|zTable|zSignal|zH0|zRange|zCheckbox|zInput|zH4|zBtn|zNavBar|zMD|zOL|zH6|zH1|zCode|zLink|zFunc|zCrumbs|zUL|zInfo|zH5|zH2|zURL|zText|zTerminal|zSelect)(?=\s*(?:\([^)]+\))?[*!]?:)/,
51
+ alias: 'function',
52
+ },
53
+ 'metadata': {
54
+ pattern: /\b_z[A-Z][a-zA-Z]+(?=:)/,
55
+ alias: 'keyword',
56
+ },
57
+ 'zui-element-property': {
58
+ pattern: /\b(?:action|alt_text|autocomplete|break_message|burst|caption|checked|color|columns|content|count|delay|disabled|every|format|full_page|href|id|indent|interactive|items|label|limit|max|maxlength|min|minlength|multi|multiple|name|offset|open_prompt|options|overwrite|parent|pattern|pause|placeholder|prefix|prompt|quality|readonly|rel|required|resolution|rows|selector|semantic|show|show_header|size|src|step|style|suffix|target|title|type|value|window|zAnchor)(?=\s*(?:\([^)]+\))?:)/,
59
+ alias: 'variable',
60
+ },
61
+ 'zdispatch-event': {
62
+ pattern: /\b(?:zCRUD|zData|zDialog|zDispatch|zExport|zImport|zLogin|zMenu)(?=\s*(?:\([^)]+\))?[*!]?:)/,
63
+ alias: 'dispatch-event',
64
+ }
65
+ });
66
+
67
+ // Support both ```zui and ```zUI code fences
68
+ Prism.languages.zUI = Prism.languages.zui;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * zSys/accessibility - Accessibility Utilities Barrel
3
+ *
4
+ * Accessibility utilities (emoji, ARIA, screen readers).
5
+ *
6
+ * @module zSys/accessibility
7
+ * @layer zSys (System Utilities)
8
+ */
9
+
10
+ export * from './emoji_accessibility.js';
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Emoji Accessibility Module for Bifrost
3
+ *
4
+ * Provides ARIA labels for emojis to make them accessible to screen readers.
5
+ * Uses the CLDR-based emoji-a11y.en.json data file.
6
+ *
7
+ * Features:
8
+ * - Lazy loading of emoji descriptions
9
+ * - Auto-wrap emojis with aria-label spans
10
+ * - Graceful fallback if data unavailable
11
+ * - Singleton pattern for efficient loading
12
+ *
13
+ * Phase 5: Bifrost ARIA Integration
14
+ * Author: zOS Framework
15
+ * Version: 1.0.0
16
+ */
17
+
18
+ import { escapeHtml } from '../dom/encoding_utils.js';
19
+
20
+ class EmojiAccessibility {
21
+ constructor(logger = console) {
22
+ this.logger = logger;
23
+ this.descriptions = null;
24
+ this.loading = null;
25
+ this.enabled = true;
26
+ }
27
+
28
+ /**
29
+ * Lazy load emoji descriptions from JSON file.
30
+ *
31
+ * @returns {Promise<void>}
32
+ */
33
+ async load() {
34
+ // Already loaded
35
+ if (this.descriptions) {
36
+ return;
37
+ }
38
+
39
+ // Already loading (prevent duplicate requests)
40
+ if (this.loading) {
41
+ return this.loading;
42
+ }
43
+
44
+ // Start loading
45
+ this.loading = (async () => {
46
+ try {
47
+ const response = await fetch('/static/js/emoji-a11y.en.json');
48
+
49
+ if (!response.ok) {
50
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
51
+ }
52
+
53
+ this.descriptions = await response.json();
54
+ this.logger.log(`[EmojiA11y] Loaded ${Object.keys(this.descriptions).length} emoji descriptions`);
55
+ } catch (e) {
56
+ console.warn('[EmojiA11y] Failed to load descriptions:', e);
57
+ this.descriptions = {};
58
+ this.enabled = false;
59
+ } finally {
60
+ this.loading = null;
61
+ }
62
+ })();
63
+
64
+ return this.loading;
65
+ }
66
+
67
+ /**
68
+ * Get description for an emoji character.
69
+ *
70
+ * @param {string} emoji - Emoji character
71
+ * @returns {string|null} - Description or null if not found
72
+ */
73
+ getDescription(emoji) {
74
+ if (!this.descriptions) {
75
+ return null;
76
+ }
77
+
78
+ return this.descriptions[emoji] || null;
79
+ }
80
+
81
+ /**
82
+ * Wrap emoji with accessible ARIA span.
83
+ *
84
+ * @param {string} emoji - Emoji character
85
+ * @returns {string} - HTML string with aria-label or plain emoji
86
+ *
87
+ * @example
88
+ * wrapWithAria('') → '<span aria-label="mobile phone" role="img"></span>'
89
+ * wrapWithAria('A') → 'A' (not an emoji)
90
+ */
91
+ wrapWithAria(emoji) {
92
+ if (!this.enabled) {
93
+ return emoji;
94
+ }
95
+
96
+ const desc = this.getDescription(emoji);
97
+
98
+ if (desc) {
99
+ // Escape HTML in description to prevent XSS
100
+ const safeDesc = this._escapeHtml(desc);
101
+ return `<span aria-label="${safeDesc}" role="img">${emoji}</span>`;
102
+ }
103
+
104
+ return emoji;
105
+ }
106
+
107
+ /**
108
+ * Enhance text by wrapping all emojis with ARIA labels.
109
+ *
110
+ * @param {string} text - Plain or HTML text with emojis
111
+ * @returns {string} - Text with emojis wrapped in accessible spans
112
+ *
113
+ * @example
114
+ * enhanceText('Mobile: ') → 'Mobile: <span aria-label="mobile phone" role="img"></span>'
115
+ */
116
+ enhanceText(text) {
117
+ if (!this.enabled || !this.descriptions) {
118
+ return text;
119
+ }
120
+
121
+ // Emoji Unicode ranges (same as Python version)
122
+ // Matches: emoji characters only (excludes ASCII)
123
+ const emojiRegex = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F000}-\u{1F0FF}\u{1F200}-\u{1F2FF}\u{1F600}-\u{1F64F}\u{1F680}-\u{1F6FF}\u{1F900}-\u{1F9FF}\u{2300}-\u{23FF}\u{2B00}-\u{2BFF}\u{FE00}-\u{FE0F}]/gu;
124
+
125
+ return text.replace(emojiRegex, (emoji) => {
126
+ return this.wrapWithAria(emoji);
127
+ });
128
+ }
129
+
130
+ /**
131
+ * Escape HTML entities to prevent XSS.
132
+ *
133
+ * @private
134
+ * @param {string} text - Text to escape
135
+ * @returns {string} - Escaped text
136
+ */
137
+ _escapeHtml(text) {
138
+ // SSOT escape (also escapes " / ' — safe for the aria-label attribute)
139
+ return escapeHtml(text);
140
+ }
141
+
142
+ /**
143
+ * Check if emoji accessibility is enabled and loaded.
144
+ *
145
+ * @returns {boolean}
146
+ */
147
+ isReady() {
148
+ return this.enabled && this.descriptions !== null;
149
+ }
150
+
151
+ /**
152
+ * Get statistics about loaded descriptions.
153
+ *
154
+ * @returns {object} - Stats object
155
+ */
156
+ getStats() {
157
+ return {
158
+ enabled: this.enabled,
159
+ loaded: this.descriptions !== null,
160
+ count: this.descriptions ? Object.keys(this.descriptions).length : 0
161
+ };
162
+ }
163
+ }
164
+
165
+ // Global singleton instance
166
+ const emojiAccessibility = new EmojiAccessibility();
167
+
168
+ // Auto-load on module import (non-blocking)
169
+ emojiAccessibility.load().catch(console.error);
170
+
171
+ // Export for use in Bifrost
172
+ export default emojiAccessibility;
173
+ export { EmojiAccessibility };
@@ -0,0 +1,122 @@
1
+ /**
2
+ *
3
+ * Block Wrapper Utilities
4
+ *
5
+ *
6
+ * Helper functions for creating and managing zBlock wrappers with metadata.
7
+ * Supports both static and progressive rendering patterns.
8
+ *
9
+ * @module utils/block_utils
10
+ * @version 1.0.0
11
+ * @author Gal Nachshon
12
+ *
13
+ *
14
+ * Architecture:
15
+ * - Uses primitives for DOM creation (createDiv)
16
+ * - Handles _zClass and _zStyle metadata
17
+ * - Supports progressive rendering with data attributes
18
+ *
19
+ */
20
+
21
+ // ─────────────────────────────────────────────────────────────────
22
+ // Imports
23
+ // ─────────────────────────────────────────────────────────────────
24
+
25
+ // Layer 2: Utilities
26
+ import { createDiv } from './dom_utils.js';
27
+ import { convertStyleToString } from './style_utils.js';
28
+
29
+ /**
30
+ * Create a block wrapper with metadata
31
+ * @param {Object} blockData - Block data object (may contain _zClass, _zStyle)
32
+ * @param {string} blockName - Block name for data attribute (default: 'zBlock')
33
+ * @param {boolean} isProgressive - Whether this is for progressive rendering (default: false)
34
+ * @returns {HTMLElement|null} Block wrapper div, or null if no metadata
35
+ */
36
+ export function createBlockWrapper(blockData, blockName = 'zBlock', isProgressive = false) {
37
+ if (!blockData || typeof blockData !== 'object' || !blockData._zClass) {
38
+ return null;
39
+ }
40
+
41
+ const blockLevelDiv = createDiv();
42
+
43
+ // Apply block-level classes
44
+ const classes = Array.isArray(blockData._zClass)
45
+ ? blockData._zClass
46
+ : blockData._zClass.split(',').map(c => c.trim());
47
+ blockLevelDiv.className = classes.join(' ');
48
+
49
+ // Set data attribute
50
+ blockLevelDiv.setAttribute('data-zblock', isProgressive ? 'progressive' : blockName);
51
+
52
+ // Set id for named blocks (not progressive)
53
+ if (!isProgressive && blockName && blockName !== 'zBlock') {
54
+ blockLevelDiv.setAttribute('id', blockName);
55
+ }
56
+
57
+ // Apply inline styles if present
58
+ if (blockData._zStyle) {
59
+ const cssString = convertStyleToString(blockData._zStyle);
60
+ if (cssString) {
61
+ blockLevelDiv.setAttribute('style', cssString);
62
+ }
63
+ }
64
+
65
+ return blockLevelDiv;
66
+ }
67
+
68
+ /**
69
+ * Apply block metadata to an existing wrapper
70
+ * @param {HTMLElement} wrapper - The wrapper element
71
+ * @param {Object} metadata - Metadata object (may contain _zClass, _zStyle)
72
+ */
73
+ export function applyBlockMetadata(wrapper, metadata) {
74
+ if (!wrapper || !metadata) {
75
+ return;
76
+ }
77
+
78
+ if (metadata._zClass) {
79
+ wrapper.className = metadata._zClass;
80
+ }
81
+
82
+ if (metadata._zStyle) {
83
+ const cssString = convertStyleToString(metadata._zStyle);
84
+ if (cssString) {
85
+ wrapper.setAttribute('style', cssString);
86
+ }
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Find existing progressive block wrapper in container
92
+ * @param {HTMLElement} container - Container to search in
93
+ * @returns {HTMLElement|null} Existing wrapper, or null
94
+ */
95
+ export function findProgressiveBlockWrapper(container) {
96
+ if (!container) {
97
+ return null;
98
+ }
99
+ return container.querySelector('[data-zblock="progressive"]');
100
+ }
101
+
102
+ /**
103
+ * Check if block data has metadata (_zClass or _zStyle)
104
+ * @param {Object} blockData - Block data object
105
+ * @returns {boolean} True if has metadata
106
+ */
107
+ export function hasBlockMetadata(blockData) {
108
+ return blockData &&
109
+ typeof blockData === 'object' &&
110
+ (blockData._zClass || blockData._zStyle);
111
+ }
112
+
113
+ //
114
+ // Default Export (for convenience)
115
+ //
116
+ export default {
117
+ createBlockWrapper,
118
+ applyBlockMetadata,
119
+ findProgressiveBlockWrapper,
120
+ hasBlockMetadata
121
+ };
122
+