@gemboss/gem-framework 0.4.0
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.
- package/README.md +42 -0
- package/dist/SOURCE.json +7 -0
- package/dist/gem-framework.css +2493 -0
- package/dist/gem-runtime.js +880 -0
- package/dist/index.js +6061 -0
- package/dist/section-gate.js +1199 -0
- package/dist/sections.js +112 -0
- package/dist/settings-schema.js +254 -0
- package/package.json +34 -0
|
@@ -0,0 +1,880 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const config = {"planId":"gem-framework-package","base":"gem-native","dawnAdapter":false,"plugins":[{"key":"cart-drawer","loadStrategy":"interaction","dependsOn":[]},{"key":"mobile-menu","loadStrategy":"interaction","dependsOn":[]},{"key":"quantity-input","loadStrategy":"interaction","dependsOn":[]},{"key":"variant-selects","loadStrategy":"interaction","dependsOn":[]},{"key":"predictive-search","loadStrategy":"critical","dependsOn":[]},{"key":"localization","loadStrategy":"interaction","dependsOn":[]},{"key":"disclosure","loadStrategy":"interaction","dependsOn":[]},{"key":"tabs","loadStrategy":"interaction","dependsOn":[]},{"key":"media-gallery","loadStrategy":"viewport","dependsOn":[]},{"key":"carousel","loadStrategy":"viewport","dependsOn":[]},{"key":"hero-slideshow","loadStrategy":"idle","dependsOn":[]},{"key":"quick-view","loadStrategy":"interaction","dependsOn":[]},{"key":"promo-popup","loadStrategy":"idle","dependsOn":[]},{"key":"before_after","loadStrategy":"viewport","dependsOn":[]},{"key":"runtime-slot-loader","loadStrategy":"idle","dependsOn":[]},{"key":"sticky-atc","loadStrategy":"viewport","dependsOn":[]},{"key":"recently-viewed","loadStrategy":"idle","dependsOn":[]},{"key":"countdown","loadStrategy":"viewport","dependsOn":[]},{"key":"reveal-on-scroll","loadStrategy":"viewport","dependsOn":[]},{"key":"seasonal-effects","loadStrategy":"idle","dependsOn":[]},{"key":"context-adapter","loadStrategy":"idle","dependsOn":[]},{"key":"offer-engine","loadStrategy":"interaction","dependsOn":[]}]};
|
|
3
|
+
const target = window;
|
|
4
|
+
const existing = target.GemTheme || {};
|
|
5
|
+
const listeners = existing.listeners || {};
|
|
6
|
+
const api = {
|
|
7
|
+
...existing,
|
|
8
|
+
config,
|
|
9
|
+
listeners,
|
|
10
|
+
on(event, callback) {
|
|
11
|
+
(listeners[event] ||= []).push(callback);
|
|
12
|
+
return () => { listeners[event] = (listeners[event] || []).filter((item) => item !== callback); };
|
|
13
|
+
},
|
|
14
|
+
emit(event, detail = {}) {
|
|
15
|
+
document.dispatchEvent(new CustomEvent(event, { detail }));
|
|
16
|
+
for (const callback of listeners[event] || []) callback(detail);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
target.GemTheme = api;
|
|
20
|
+
const pluginRegistry = {};
|
|
21
|
+
const loadedPlugins = {};
|
|
22
|
+
const hasPlugin = (key) => config.plugins.some((plugin) => plugin.key === key);
|
|
23
|
+
const pluginConfig = (key) => config.plugins.find((plugin) => plugin.key === key);
|
|
24
|
+
const registerPlugin = (key, setup) => { pluginRegistry[key] = setup; };
|
|
25
|
+
const loadPlugin = (key, reason = 'direct') => {
|
|
26
|
+
const spec = pluginConfig(key);
|
|
27
|
+
if (!spec || loadedPlugins[key]) return false;
|
|
28
|
+
for (const dependency of spec.dependsOn || []) loadPlugin(dependency, 'dependency');
|
|
29
|
+
const setup = pluginRegistry[key];
|
|
30
|
+
if (typeof setup === 'function') setup({ api, config, reason });
|
|
31
|
+
loadedPlugins[key] = true;
|
|
32
|
+
api.emit('gem:plugin:loaded', { key, reason, loadStrategy: spec.loadStrategy });
|
|
33
|
+
return true;
|
|
34
|
+
};
|
|
35
|
+
const onInteraction = (callback) => {
|
|
36
|
+
const events = ['click', 'focusin', 'submit', 'input', 'change'];
|
|
37
|
+
const handler = (event) => {
|
|
38
|
+
events.forEach((name) => document.removeEventListener && document.removeEventListener(name, handler, true));
|
|
39
|
+
callback(event);
|
|
40
|
+
};
|
|
41
|
+
events.forEach((name) => canListen(document) && document.addEventListener(name, handler, true));
|
|
42
|
+
};
|
|
43
|
+
const onIdle = (callback) => {
|
|
44
|
+
if (target.requestIdleCallback) target.requestIdleCallback(callback);
|
|
45
|
+
else setTimeout(callback, 0);
|
|
46
|
+
};
|
|
47
|
+
const onViewport = (callback) => {
|
|
48
|
+
if (!target.IntersectionObserver) { setTimeout(callback, 0); return; }
|
|
49
|
+
const sentinel = document.querySelector('[data-gem-plugin], [data-gem-component], .gem-section') || document.documentElement;
|
|
50
|
+
const observer = new target.IntersectionObserver((entries) => {
|
|
51
|
+
if (!entries.some((entry) => entry.isIntersecting)) return;
|
|
52
|
+
observer.disconnect();
|
|
53
|
+
callback();
|
|
54
|
+
}, { rootMargin: '160px' });
|
|
55
|
+
observer.observe(sentinel);
|
|
56
|
+
};
|
|
57
|
+
const schedulePlugin = (plugin) => {
|
|
58
|
+
if (!plugin || loadedPlugins[plugin.key]) return;
|
|
59
|
+
api.emit('gem:plugin:scheduled', { key: plugin.key, loadStrategy: plugin.loadStrategy });
|
|
60
|
+
if (plugin.loadStrategy === 'critical') loadPlugin(plugin.key, 'critical');
|
|
61
|
+
else if (plugin.loadStrategy === 'idle') onIdle(() => loadPlugin(plugin.key, 'idle'));
|
|
62
|
+
else if (plugin.loadStrategy === 'viewport') onViewport(() => loadPlugin(plugin.key, 'viewport'));
|
|
63
|
+
else onInteraction(() => loadPlugin(plugin.key, 'interaction'));
|
|
64
|
+
};
|
|
65
|
+
const canListen = (element) => Boolean(element && typeof element.addEventListener === 'function');
|
|
66
|
+
const registerNoopPlugin = (key, readyEvent) => {
|
|
67
|
+
registerPlugin(key, ({ api }) => {
|
|
68
|
+
if (readyEvent) api.emit(readyEvent, { source: 'gem-runtime' });
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
registerPlugin('cart-drawer', ({ api }) => {
|
|
72
|
+
const drawers = document.querySelectorAll('[data-gem-cart-drawer], [data-gem-cart-canvas], .gem-cart-drawer');
|
|
73
|
+
const cartLinks = document.querySelectorAll('[data-gem-cart-drawer-trigger], [data-gem-cart-toggle], a[href*="/cart"]');
|
|
74
|
+
const cartForms = document.querySelectorAll('form[action*="/cart/add"], form[action*="/cart"]');
|
|
75
|
+
const drawer = drawers[0];
|
|
76
|
+
const setDrawerA11y = (isOpen) => {
|
|
77
|
+
if (!drawer) return;
|
|
78
|
+
drawer.setAttribute && drawer.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
|
79
|
+
const panel = drawer.querySelector && drawer.querySelector('[data-gem-cart-canvas-panel], .gem-cart-drawer__panel');
|
|
80
|
+
if (panel && panel.setAttribute) panel.setAttribute('aria-hidden', isOpen ? 'false' : 'true');
|
|
81
|
+
};
|
|
82
|
+
const openDrawer = (reason, href) => {
|
|
83
|
+
if (drawer && drawer.tagName === 'DETAILS') drawer.open = true;
|
|
84
|
+
if (drawer && drawer.dataset) drawer.dataset.gemCartDrawerState = 'open';
|
|
85
|
+
setDrawerA11y(true);
|
|
86
|
+
api.emit('gem:cart:open', { source: 'gem-runtime', reason, href });
|
|
87
|
+
};
|
|
88
|
+
const closeDrawer = (reason) => {
|
|
89
|
+
if (drawer && drawer.tagName === 'DETAILS') drawer.open = false;
|
|
90
|
+
if (drawer && drawer.dataset) drawer.dataset.gemCartDrawerState = 'closed';
|
|
91
|
+
setDrawerA11y(false);
|
|
92
|
+
api.emit('gem:cart:close', { source: 'gem-runtime', reason });
|
|
93
|
+
};
|
|
94
|
+
let bound = 0;
|
|
95
|
+
drawers.forEach((drawer) => {
|
|
96
|
+
if (!mark(drawer, 'cart-drawer')) return;
|
|
97
|
+
drawer.dataset.gemCartDrawerState = drawer.hasAttribute('open') ? 'open' : 'ready';
|
|
98
|
+
setDrawerA11y(drawer.hasAttribute('open'));
|
|
99
|
+
drawer.querySelectorAll('[data-gem-cart-drawer-close], [data-gem-cart-canvas-close]').forEach((control) => { if (!canListen(control)) return; control.addEventListener('click', (event) => {
|
|
100
|
+
event.preventDefault && event.preventDefault();
|
|
101
|
+
closeDrawer('close-control');
|
|
102
|
+
}); });
|
|
103
|
+
if (canListen(drawer)) drawer.addEventListener('toggle', () => { drawer.dataset.gemCartDrawerState = drawer.hasAttribute('open') ? 'open' : 'closed'; });
|
|
104
|
+
bound += 1;
|
|
105
|
+
});
|
|
106
|
+
cartLinks.forEach((link) => {
|
|
107
|
+
if (!mark(link, 'cart-link')) return;
|
|
108
|
+
if (!canListen(link)) return;
|
|
109
|
+
link.addEventListener('click', (event) => {
|
|
110
|
+
if (drawer && (link.hasAttribute('data-gem-cart-drawer-trigger') || link.hasAttribute('data-gem-cart-toggle'))) event.preventDefault && event.preventDefault();
|
|
111
|
+
openDrawer('cart-link', link.href || undefined);
|
|
112
|
+
});
|
|
113
|
+
bound += 1;
|
|
114
|
+
});
|
|
115
|
+
if (canListen(document)) document.addEventListener('keydown', (event) => { if (event.key === 'Escape' && drawer && drawer.hasAttribute('open')) closeDrawer('escape'); });
|
|
116
|
+
cartForms.forEach((form) => {
|
|
117
|
+
if (!mark(form, 'cart-form')) return;
|
|
118
|
+
if (!canListen(form)) return;
|
|
119
|
+
form.addEventListener('submit', () => api.emit('gem:cart:submit', { source: 'gem-runtime', action: form.getAttribute('action') || undefined }));
|
|
120
|
+
bound += 1;
|
|
121
|
+
});
|
|
122
|
+
api.emit('gem:cart:ready', { source: 'gem-runtime', count: bound });
|
|
123
|
+
});
|
|
124
|
+
registerPlugin('mobile-menu', ({ api }) => {
|
|
125
|
+
const menus = document.querySelectorAll('[data-gem-mobile-menu], details.gem-shell-header__drawer');
|
|
126
|
+
let bound = 0;
|
|
127
|
+
menus.forEach((menu) => {
|
|
128
|
+
if (!mark(menu, 'mobile-menu')) return;
|
|
129
|
+
const setMenuA11y = () => {
|
|
130
|
+
menu.setAttribute && menu.setAttribute('aria-expanded', menu.open ? 'true' : 'false');
|
|
131
|
+
const panel = menu.querySelector && menu.querySelector('[data-gem-mobile-menu-panel], .gem-shell-header__drawer-panel');
|
|
132
|
+
if (panel && panel.setAttribute) panel.setAttribute('aria-hidden', menu.open ? 'false' : 'true');
|
|
133
|
+
};
|
|
134
|
+
menu.dataset.gemMenuState = menu.open ? 'open' : 'closed';
|
|
135
|
+
setMenuA11y();
|
|
136
|
+
if (!canListen(menu)) return;
|
|
137
|
+
menu.addEventListener('toggle', () => {
|
|
138
|
+
menu.dataset.gemMenuState = menu.open ? 'open' : 'closed';
|
|
139
|
+
setMenuA11y();
|
|
140
|
+
if (menu.open) {
|
|
141
|
+
const panel = menu.querySelector && menu.querySelector('[data-gem-mobile-menu-panel], .gem-shell-header__drawer-panel');
|
|
142
|
+
const firstFocusable = panel && panel.querySelector && panel.querySelector('a[href], button, select, input, textarea, summary, [tabindex]:not([tabindex="-1"])');
|
|
143
|
+
if (firstFocusable && firstFocusable.focus) firstFocusable.focus();
|
|
144
|
+
}
|
|
145
|
+
api.emit(menu.open ? 'gem:menu:open' : 'gem:menu:close', { source: 'gem-runtime', id: menu.id || undefined });
|
|
146
|
+
});
|
|
147
|
+
menu.addEventListener('keydown', (event) => {
|
|
148
|
+
if (event.key !== 'Escape' || !menu.open) return;
|
|
149
|
+
menu.open = false;
|
|
150
|
+
menu.dataset.gemMenuState = 'closed';
|
|
151
|
+
setMenuA11y();
|
|
152
|
+
const summary = menu.querySelector && menu.querySelector('summary');
|
|
153
|
+
if (summary && summary.focus) summary.focus();
|
|
154
|
+
});
|
|
155
|
+
bound += 1;
|
|
156
|
+
});
|
|
157
|
+
api.emit('gem:menu:ready', { source: 'gem-runtime', count: bound });
|
|
158
|
+
});
|
|
159
|
+
registerPlugin('quantity-input', ({ api }) => {
|
|
160
|
+
const controls = document.querySelectorAll('[data-gem-quantity-input], .gem-quantity-input');
|
|
161
|
+
let bound = 0;
|
|
162
|
+
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
163
|
+
controls.forEach((control) => {
|
|
164
|
+
if (!mark(control, 'quantity-input')) return;
|
|
165
|
+
const input = control.querySelector && control.querySelector('input[type="number"]');
|
|
166
|
+
if (!input) return;
|
|
167
|
+
const setValue = (nextValue, reason) => {
|
|
168
|
+
const min = Number.isFinite(Number(input.min)) ? Number(input.min) : 0;
|
|
169
|
+
const max = Number.isFinite(Number(input.max)) && input.max !== '' ? Number(input.max) : Number.MAX_SAFE_INTEGER;
|
|
170
|
+
const value = clamp(Number(nextValue) || min, min, max);
|
|
171
|
+
input.value = String(value);
|
|
172
|
+
api.emit('gem:quantity:change', { source: 'gem-runtime', value, reason, name: input.name || undefined });
|
|
173
|
+
};
|
|
174
|
+
if (!canListen(control) || !canListen(input)) return;
|
|
175
|
+
control.addEventListener('click', (event) => {
|
|
176
|
+
const button = event.target && event.target.closest ? event.target.closest('[data-gem-quantity-change]') : null;
|
|
177
|
+
if (!button) return;
|
|
178
|
+
event.preventDefault && event.preventDefault();
|
|
179
|
+
const delta = Number(button.getAttribute('data-gem-quantity-change') || '0');
|
|
180
|
+
setValue(Number(input.value || 0) + delta, delta > 0 ? 'increase' : 'decrease');
|
|
181
|
+
});
|
|
182
|
+
input.addEventListener('change', () => setValue(input.value, 'input'));
|
|
183
|
+
bound += 1;
|
|
184
|
+
});
|
|
185
|
+
api.emit('gem:quantity:ready', { source: 'gem-runtime', count: bound });
|
|
186
|
+
});
|
|
187
|
+
registerPlugin('variant-selects', ({ api }) => {
|
|
188
|
+
const selects = document.querySelectorAll('[data-gem-variant-select], select[name="id"]');
|
|
189
|
+
let bound = 0;
|
|
190
|
+
selects.forEach((select) => {
|
|
191
|
+
if (!mark(select, 'variant-selects')) return;
|
|
192
|
+
let hasInitialized = false;
|
|
193
|
+
const update = () => {
|
|
194
|
+
const option = select.options && select.options[select.selectedIndex];
|
|
195
|
+
const available = !option || option.getAttribute('data-gem-variant-available') !== 'false';
|
|
196
|
+
const price = option && option.getAttribute('data-gem-variant-price');
|
|
197
|
+
const mediaId = option && option.getAttribute('data-gem-variant-media-id');
|
|
198
|
+
const form = select.closest && select.closest('form');
|
|
199
|
+
const scope = (form && form.closest && form.closest('[data-gem-section-id], .gem-section, [data-qa-surface]')) || (select.closest && select.closest('[data-gem-section-id], .gem-section, [data-qa-surface]')) || document;
|
|
200
|
+
const submit = form && form.querySelector && form.querySelector('[type="submit"]');
|
|
201
|
+
const status = form && form.querySelector && form.querySelector('[data-gem-variant-status]');
|
|
202
|
+
const priceOutput = scope && scope.querySelector && scope.querySelector('[data-gem-variant-price-output]');
|
|
203
|
+
const availabilityOutput = scope && scope.querySelector && scope.querySelector('[data-gem-variant-availability]');
|
|
204
|
+
if (form && form.dataset) form.dataset.gemSelectedVariantId = String(select.value || '');
|
|
205
|
+
if (form && form.dataset && price) form.dataset.gemSelectedVariantPrice = price;
|
|
206
|
+
if (form && form.dataset && mediaId) form.dataset.gemSelectedMediaId = mediaId;
|
|
207
|
+
if (submit) submit.disabled = !available;
|
|
208
|
+
if (submit) submit.setAttribute('aria-disabled', available ? 'false' : 'true');
|
|
209
|
+
if (status) status.textContent = available ? 'Selected variant available' : 'Selected variant unavailable';
|
|
210
|
+
if (hasInitialized && price && priceOutput) priceOutput.textContent = price;
|
|
211
|
+
if (availabilityOutput) {
|
|
212
|
+
availabilityOutput.textContent = available ? 'In stock' : 'Sold out';
|
|
213
|
+
availabilityOutput.classList && availabilityOutput.classList.toggle('gem-product-main__availability--in-stock', available);
|
|
214
|
+
availabilityOutput.classList && availabilityOutput.classList.toggle('gem-product-main__availability--sold-out', !available);
|
|
215
|
+
}
|
|
216
|
+
if (mediaId) {
|
|
217
|
+
const escapedMediaId = String(mediaId).replace(/["\\]/g, '\\$&');
|
|
218
|
+
const gallery = (scope && scope.querySelector && scope.querySelector('[data-gem-media-gallery], .gem-product-media-gallery')) || document.querySelector('[data-gem-media-gallery], .gem-product-media-gallery');
|
|
219
|
+
const thumb = gallery && gallery.querySelector && gallery.querySelector('[data-gem-media-id="' + escapedMediaId + '"]');
|
|
220
|
+
const mediaIndex = thumb && thumb.getAttribute && thumb.getAttribute('data-gem-media-index');
|
|
221
|
+
if (thumb && typeof thumb.click === 'function') thumb.click();
|
|
222
|
+
if (gallery && mediaIndex !== null && mediaIndex !== undefined) {
|
|
223
|
+
gallery.dataset && (gallery.dataset.gemMediaActiveIndex = String(mediaIndex));
|
|
224
|
+
gallery.querySelectorAll && gallery.querySelectorAll('[data-gem-media-thumb]').forEach((control) => {
|
|
225
|
+
const isActive = control.getAttribute && control.getAttribute('data-gem-media-index') === String(mediaIndex);
|
|
226
|
+
if (isActive) control.setAttribute('aria-current', 'true'); else control.removeAttribute('aria-current');
|
|
227
|
+
if (control.dataset) control.dataset.gemMediaActive = isActive ? 'true' : 'false';
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
api.emit('gem:variant:change', { source: 'gem-runtime', variantId: select.value || undefined, available, price, mediaId });
|
|
232
|
+
hasInitialized = true;
|
|
233
|
+
};
|
|
234
|
+
if (!canListen(select)) return;
|
|
235
|
+
select.addEventListener('change', update);
|
|
236
|
+
update();
|
|
237
|
+
bound += 1;
|
|
238
|
+
});
|
|
239
|
+
api.emit('gem:variant:ready', { source: 'gem-runtime', count: bound });
|
|
240
|
+
});
|
|
241
|
+
registerPlugin('predictive-search', ({ api }) => {
|
|
242
|
+
const forms = document.querySelectorAll('[data-gem-predictive-search], [data-gem-search-canvas], form[action*="/search"], form[role="search"]');
|
|
243
|
+
const toggles = document.querySelectorAll('[data-gem-search-toggle]');
|
|
244
|
+
const canvas = document.querySelector('[data-gem-search-canvas]');
|
|
245
|
+
let bound = 0;
|
|
246
|
+
const setCanvasState = (state, reason = 'api') => {
|
|
247
|
+
if (!canvas || !canvas.dataset) return false;
|
|
248
|
+
canvas.dataset.gemSearchCanvasState = state;
|
|
249
|
+
canvas.setAttribute && canvas.setAttribute('aria-hidden', state === 'open' ? 'false' : 'true');
|
|
250
|
+
if (state === 'open') {
|
|
251
|
+
const input = canvas.querySelector && canvas.querySelector('input[type="search"], input[name="q"]');
|
|
252
|
+
if (input && input.focus) input.focus();
|
|
253
|
+
}
|
|
254
|
+
api.emit(state === 'open' ? 'gem:search:open' : 'gem:search:close', { source: 'gem-runtime', reason });
|
|
255
|
+
return true;
|
|
256
|
+
};
|
|
257
|
+
forms.forEach((form) => {
|
|
258
|
+
if (!mark(form, 'predictive-search')) return;
|
|
259
|
+
const input = form.querySelector && form.querySelector('input[type="search"], input[name="q"]');
|
|
260
|
+
const results = form.querySelector && form.querySelector('[data-gem-predictive-search-results]');
|
|
261
|
+
const setState = (state, value = '') => {
|
|
262
|
+
if (form.dataset) form.dataset.gemPredictiveSearchState = state;
|
|
263
|
+
if (results) {
|
|
264
|
+
results.hidden = state === 'closed';
|
|
265
|
+
if (results.dataset) results.dataset.gemPredictiveSearchState = state;
|
|
266
|
+
results.setAttribute && results.setAttribute('aria-hidden', state === 'closed' ? 'true' : 'false');
|
|
267
|
+
if (state === 'typing') results.textContent = value ? 'Search suggestions for ' + value : 'Start typing to search';
|
|
268
|
+
if (state === 'empty') results.textContent = 'Start typing to search';
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
if (results) {
|
|
272
|
+
results.hidden = true;
|
|
273
|
+
results.setAttribute && results.setAttribute('role', 'status');
|
|
274
|
+
results.setAttribute && results.setAttribute('aria-live', 'polite');
|
|
275
|
+
}
|
|
276
|
+
setState('closed');
|
|
277
|
+
if (canListen(input)) input.addEventListener('focus', () => { setState(input.value ? 'typing' : 'empty', input.value || ''); api.emit('gem:search:open', { source: 'gem-runtime', value: input.value || '' }); });
|
|
278
|
+
const closeOnEscape = (event) => { if (event.key === 'Escape') { event.preventDefault && event.preventDefault(); event.stopPropagation && event.stopPropagation(); setState('closed'); api.emit('gem:search:close', { source: 'gem-runtime', reason: 'escape' }); } };
|
|
279
|
+
if (canListen(input)) input.addEventListener('input', () => { setState(input.value ? 'typing' : 'empty', input.value || ''); api.emit('gem:search:input', { source: 'gem-runtime', value: input.value || '' }); });
|
|
280
|
+
if (canListen(input)) input.addEventListener('keydown', closeOnEscape);
|
|
281
|
+
if (canListen(form)) form.addEventListener('keydown', closeOnEscape);
|
|
282
|
+
if (canListen(form)) form.addEventListener('submit', () => { setState('submit', input && input.value || ''); api.emit('gem:search:submit', { source: 'gem-runtime', value: input && input.value || '' }); });
|
|
283
|
+
bound += 1;
|
|
284
|
+
});
|
|
285
|
+
if (canvas) {
|
|
286
|
+
canvas.dataset && (canvas.dataset.gemSearchCanvasState = 'closed');
|
|
287
|
+
canvas.setAttribute && canvas.setAttribute('aria-hidden', 'true');
|
|
288
|
+
if (mark(canvas, 'search-canvas')) bound += 1;
|
|
289
|
+
if (canListen(canvas)) canvas.addEventListener('keydown', (event) => { if (event.key === 'Escape') setCanvasState('closed', 'escape'); });
|
|
290
|
+
}
|
|
291
|
+
toggles.forEach((toggle) => {
|
|
292
|
+
if (!mark(toggle, 'search-toggle')) return;
|
|
293
|
+
if (!canListen(toggle)) return;
|
|
294
|
+
toggle.addEventListener('click', (event) => {
|
|
295
|
+
if (canvas) event.preventDefault && event.preventDefault();
|
|
296
|
+
setCanvasState('open', 'toggle');
|
|
297
|
+
});
|
|
298
|
+
bound += 1;
|
|
299
|
+
});
|
|
300
|
+
api.emit('gem:search:ready', { source: 'gem-runtime', count: bound });
|
|
301
|
+
});
|
|
302
|
+
registerPlugin('localization', ({ api }) => {
|
|
303
|
+
const forms = document.querySelectorAll('form[action*="/localization"], form[action*="localization"], [data-gem-footer-localization], [data-gem-header-localization], .gem-navigation-shell__localization, .gem-shell-footer__localization, .gem-shell-header__localization-form');
|
|
304
|
+
let bound = 0;
|
|
305
|
+
forms.forEach((form) => {
|
|
306
|
+
if (!mark(form, 'localization')) return;
|
|
307
|
+
form.dataset && (form.dataset.gemLocalizationState = 'ready');
|
|
308
|
+
form.querySelectorAll && form.querySelectorAll('select[name="country_code"], select[name="locale_code"]').forEach((select) => {
|
|
309
|
+
if (!canListen(select)) return;
|
|
310
|
+
select.addEventListener('change', () => {
|
|
311
|
+
form.dataset && (form.dataset.gemLocalizationState = 'changed');
|
|
312
|
+
api.emit('gem:localization:change', { source: 'gem-runtime', name: select.name || undefined, value: select.value || undefined });
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
canListen(form) && form.addEventListener('submit', () => {
|
|
316
|
+
form.dataset && (form.dataset.gemLocalizationState = 'submit');
|
|
317
|
+
api.emit('gem:localization:submit', { source: 'gem-runtime' });
|
|
318
|
+
});
|
|
319
|
+
bound += 1;
|
|
320
|
+
});
|
|
321
|
+
api.emit('gem:localization:ready', { source: 'gem-runtime', count: bound });
|
|
322
|
+
});
|
|
323
|
+
registerPlugin('disclosure', ({ api }) => {
|
|
324
|
+
const items = document.querySelectorAll('details[data-gem-disclosure], .gem-accordion details, .gem-tabs details, .gem-footer-shell details, [data-gem-footer-shell] details');
|
|
325
|
+
let bound = 0;
|
|
326
|
+
items.forEach((item) => {
|
|
327
|
+
if (!mark(item, 'disclosure')) return;
|
|
328
|
+
item.dataset.gemDisclosureState = item.open ? 'open' : 'closed';
|
|
329
|
+
if (!canListen(item)) return;
|
|
330
|
+
item.addEventListener('toggle', () => {
|
|
331
|
+
item.dataset.gemDisclosureState = item.open ? 'open' : 'closed';
|
|
332
|
+
api.emit(item.open ? 'gem:disclosure:open' : 'gem:disclosure:close', { source: 'gem-runtime', id: item.id || undefined });
|
|
333
|
+
});
|
|
334
|
+
item.addEventListener('keydown', (event) => {
|
|
335
|
+
if (event.key !== 'Escape' || !item.open) return;
|
|
336
|
+
event.preventDefault && event.preventDefault();
|
|
337
|
+
item.open = false;
|
|
338
|
+
item.dataset.gemDisclosureState = 'closed';
|
|
339
|
+
const summary = item.querySelector && item.querySelector('summary');
|
|
340
|
+
if (summary && summary.focus) summary.focus();
|
|
341
|
+
api.emit('gem:disclosure:close', { source: 'gem-runtime', id: item.id || undefined, reason: 'escape' });
|
|
342
|
+
});
|
|
343
|
+
bound += 1;
|
|
344
|
+
});
|
|
345
|
+
api.emit('gem:disclosure:ready', { source: 'gem-runtime', count: bound });
|
|
346
|
+
});
|
|
347
|
+
registerPlugin('tabs', ({ api }) => {
|
|
348
|
+
const roots = document.querySelectorAll('[data-gem-tabs]');
|
|
349
|
+
let bound = 0;
|
|
350
|
+
roots.forEach((root) => {
|
|
351
|
+
if (!mark(root, 'tabs')) return;
|
|
352
|
+
const tabs = Array.prototype.slice.call(root.querySelectorAll('[data-gem-tab]'));
|
|
353
|
+
const panels = Array.prototype.slice.call(root.querySelectorAll('[data-gem-panel]'));
|
|
354
|
+
if (!tabs.length || !panels.length) return;
|
|
355
|
+
const select = (key, userInitiated) => {
|
|
356
|
+
tabs.forEach((t) => t.setAttribute('aria-selected', String(t.getAttribute('data-gem-tab') === key)));
|
|
357
|
+
let active = null;
|
|
358
|
+
panels.forEach((p) => {
|
|
359
|
+
if (p.getAttribute('data-gem-panel') === key) { p.removeAttribute('hidden'); active = p; }
|
|
360
|
+
else p.setAttribute('hidden', '');
|
|
361
|
+
});
|
|
362
|
+
if (userInitiated && active && active.getBoundingClientRect) {
|
|
363
|
+
const r = active.getBoundingClientRect();
|
|
364
|
+
const vh = window.innerHeight || document.documentElement.clientHeight;
|
|
365
|
+
if (!(r.top < vh * 0.85 && r.bottom > vh * 0.15) && active.scrollIntoView) {
|
|
366
|
+
active.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
api.emit('gem:tabs:change', { source: 'gem-runtime', key: key });
|
|
370
|
+
};
|
|
371
|
+
tabs.forEach((t) => {
|
|
372
|
+
if (!canListen(t)) return;
|
|
373
|
+
t.addEventListener('click', () => select(t.getAttribute('data-gem-tab'), true));
|
|
374
|
+
});
|
|
375
|
+
const initial = tabs.filter((t) => t.getAttribute('aria-selected') === 'true')[0] || tabs[0];
|
|
376
|
+
select(initial.getAttribute('data-gem-tab'), false);
|
|
377
|
+
bound += 1;
|
|
378
|
+
});
|
|
379
|
+
api.emit('gem:tabs:ready', { source: 'gem-runtime', count: bound });
|
|
380
|
+
});
|
|
381
|
+
registerPlugin('media-gallery', ({ api }) => {
|
|
382
|
+
const galleries = document.querySelectorAll('[data-gem-media-gallery], .gem-media-gallery, .gem-product-media-gallery');
|
|
383
|
+
let bound = 0;
|
|
384
|
+
galleries.forEach((gallery) => {
|
|
385
|
+
if (!mark(gallery, 'media-gallery')) return;
|
|
386
|
+
const items = gallery.querySelectorAll('[data-gem-media-item], img, video, model-viewer');
|
|
387
|
+
const thumbControls = gallery.querySelectorAll('[data-gem-media-thumb]');
|
|
388
|
+
const controls = thumbControls.length ? thumbControls : items;
|
|
389
|
+
gallery.dataset.gemMediaState = 'ready';
|
|
390
|
+
items.forEach((item, index) => {
|
|
391
|
+
if (!item.dataset) return;
|
|
392
|
+
item.dataset.gemMediaIndex = String(index);
|
|
393
|
+
});
|
|
394
|
+
const activate = (target, reason = 'click') => {
|
|
395
|
+
if (!target) return;
|
|
396
|
+
const index = target.dataset && target.dataset.gemMediaIndex ? Number(target.dataset.gemMediaIndex) : undefined;
|
|
397
|
+
if (!Number.isFinite(index)) return;
|
|
398
|
+
controls.forEach((control) => {
|
|
399
|
+
const isActive = control.dataset && Number(control.dataset.gemMediaIndex) === index;
|
|
400
|
+
if (isActive) control.setAttribute('aria-current', 'true'); else control.removeAttribute('aria-current');
|
|
401
|
+
if (control.dataset) control.dataset.gemMediaActive = isActive ? 'true' : 'false';
|
|
402
|
+
});
|
|
403
|
+
gallery.dataset.gemMediaActiveIndex = String(index);
|
|
404
|
+
api.emit('gem:media:change', { source: 'gem-runtime', index, galleryId: gallery.id || undefined, reason });
|
|
405
|
+
};
|
|
406
|
+
const firstActive = gallery.querySelector('[aria-current="true"], [data-gem-media-thumb]');
|
|
407
|
+
if (firstActive) activate(firstActive, 'init');
|
|
408
|
+
if (canListen(gallery)) gallery.addEventListener('click', (event) => {
|
|
409
|
+
const target = event.target && event.target.closest ? event.target.closest('[data-gem-media-item], img, video, model-viewer') : null;
|
|
410
|
+
if (!target) return;
|
|
411
|
+
activate(target, 'click');
|
|
412
|
+
});
|
|
413
|
+
controls.forEach((control) => { if (!canListen(control)) return; control.addEventListener('keydown', (event) => {
|
|
414
|
+
if (!['Enter', ' '].includes(event.key)) return;
|
|
415
|
+
event.preventDefault && event.preventDefault();
|
|
416
|
+
activate(control, 'keyboard');
|
|
417
|
+
}); });
|
|
418
|
+
bound += 1;
|
|
419
|
+
});
|
|
420
|
+
api.emit('gem:media:ready', { source: 'gem-runtime', count: bound });
|
|
421
|
+
});
|
|
422
|
+
registerPlugin('carousel', ({ api }) => {
|
|
423
|
+
const carousels = document.querySelectorAll('[data-gem-carousel], .gem-carousel');
|
|
424
|
+
let bound = 0;
|
|
425
|
+
carousels.forEach((carousel) => {
|
|
426
|
+
if (!mark(carousel, 'carousel')) return;
|
|
427
|
+
const viewport = carousel.querySelector && (carousel.querySelector('[data-gem-carousel-viewport]') || carousel.querySelector('.gem-carousel__viewport'));
|
|
428
|
+
const slides = carousel.querySelectorAll('[data-gem-carousel-slide], .gem-carousel__slide');
|
|
429
|
+
const previous = carousel.querySelector && carousel.querySelector('[data-gem-carousel-prev]');
|
|
430
|
+
const next = carousel.querySelector && carousel.querySelector('[data-gem-carousel-next]');
|
|
431
|
+
const slideCount = slides.length || 0;
|
|
432
|
+
const currentIndex = () => {
|
|
433
|
+
if (!viewport || !slideCount) return 0;
|
|
434
|
+
const width = Number(viewport.clientWidth || viewport.offsetWidth || 1);
|
|
435
|
+
return Math.max(0, Math.min(slideCount - 1, Math.round(Number(viewport.scrollLeft || 0) / width)));
|
|
436
|
+
};
|
|
437
|
+
const setIndex = (index, reason = 'api') => {
|
|
438
|
+
const nextIndex = Math.max(0, Math.min(slideCount - 1, Number.isFinite(index) ? index : 0));
|
|
439
|
+
carousel.dataset.gemCarouselIndex = String(nextIndex);
|
|
440
|
+
carousel.dataset.gemCarouselCount = String(slideCount);
|
|
441
|
+
if (viewport && viewport.scrollTo) viewport.scrollTo({ left: Number(viewport.clientWidth || viewport.offsetWidth || 0) * nextIndex, behavior: 'smooth' });
|
|
442
|
+
api.emit('gem:carousel:change', { source: 'gem-runtime', id: carousel.id || undefined, index: nextIndex, count: slideCount, reason });
|
|
443
|
+
};
|
|
444
|
+
slides.forEach((slide, index) => { if (slide.dataset) slide.dataset.gemCarouselIndex = String(index); });
|
|
445
|
+
carousel.dataset.gemCarouselState = slideCount > 1 ? 'ready' : 'single';
|
|
446
|
+
carousel.dataset.gemCarouselCount = String(slideCount);
|
|
447
|
+
carousel.dataset.gemCarouselIndex = String(currentIndex());
|
|
448
|
+
if (canListen(previous)) previous.addEventListener('click', (event) => { event.preventDefault && event.preventDefault(); setIndex(currentIndex() - 1, 'previous'); });
|
|
449
|
+
if (canListen(next)) next.addEventListener('click', (event) => { event.preventDefault && event.preventDefault(); setIndex(currentIndex() + 1, 'next'); });
|
|
450
|
+
if (canListen(viewport)) viewport.addEventListener('scroll', () => { carousel.dataset.gemCarouselIndex = String(currentIndex()); }, { passive: true });
|
|
451
|
+
bound += 1;
|
|
452
|
+
});
|
|
453
|
+
api.emit('gem:carousel:ready', { source: 'gem-runtime', count: bound });
|
|
454
|
+
});
|
|
455
|
+
registerPlugin('hero-slideshow', ({ api }) => {
|
|
456
|
+
const roots = document.querySelectorAll('[data-gem-hero-slideshow]');
|
|
457
|
+
let bound = 0;
|
|
458
|
+
roots.forEach((root) => {
|
|
459
|
+
if (!mark(root, 'hero-slideshow')) return;
|
|
460
|
+
const slides = Array.prototype.slice.call(root.querySelectorAll('[data-gem-hero-slide]'));
|
|
461
|
+
if (slides.length < 2) return;
|
|
462
|
+
const dots = Array.prototype.slice.call(root.querySelectorAll('[data-gem-hero-dot]'));
|
|
463
|
+
const previous = root.querySelector && root.querySelector('[data-gem-hero-prev]');
|
|
464
|
+
const next = root.querySelector && root.querySelector('[data-gem-hero-next]');
|
|
465
|
+
const pause = root.querySelector && root.querySelector('[data-gem-hero-pause]');
|
|
466
|
+
const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
467
|
+
const autoplay = !!(root.dataset && root.dataset.autoplay === 'true') && !reduce;
|
|
468
|
+
const interval = Math.max(Number((root.dataset && root.dataset.autoplayInterval) || 6000), 4000);
|
|
469
|
+
let active = 0;
|
|
470
|
+
for (let i = 0; i < slides.length; i += 1) { if ((slides[i].className || '').indexOf('gem-hero-slideshow__slide--active') > -1) { active = i; break; } }
|
|
471
|
+
let timer = null; let paused = false;
|
|
472
|
+
const show = (index) => {
|
|
473
|
+
active = (index + slides.length) % slides.length;
|
|
474
|
+
slides.forEach((s, i) => { const on = i === active; if (s.classList) s.classList.toggle('gem-hero-slideshow__slide--active', on); s.hidden = !on; });
|
|
475
|
+
dots.forEach((d, i) => { const on = i === active; if (d.classList) d.classList.toggle('gem-hero-slideshow__dot--active', on); if (d.setAttribute) { d.setAttribute('aria-selected', String(on)); d.setAttribute('tabindex', on ? '0' : '-1'); } });
|
|
476
|
+
api.emit('gem:hero-slideshow:change', { source: 'gem-runtime', id: root.id || undefined, index: active, count: slides.length });
|
|
477
|
+
};
|
|
478
|
+
const stop = () => { if (timer) { window.clearInterval(timer); timer = null; } };
|
|
479
|
+
const start = () => { stop(); if (autoplay && !paused) timer = window.setInterval(() => { show(active + 1); }, interval); };
|
|
480
|
+
if (canListen(previous)) previous.addEventListener('click', (e) => { if (e.preventDefault) e.preventDefault(); show(active - 1); start(); });
|
|
481
|
+
if (canListen(next)) next.addEventListener('click', (e) => { if (e.preventDefault) e.preventDefault(); show(active + 1); start(); });
|
|
482
|
+
dots.forEach((d, i) => { if (canListen(d)) d.addEventListener('click', () => { show(Number((d.dataset && d.dataset.slideIndex) || i)); start(); }); });
|
|
483
|
+
const focusDot = (i) => { const n = dots.length; if (!n) return; const k = ((i % n) + n) % n; show(k); start(); if (dots[k].focus) dots[k].focus(); };
|
|
484
|
+
dots.forEach((d, i) => { if (canListen(d)) d.addEventListener('keydown', (e) => {
|
|
485
|
+
const k = e.key;
|
|
486
|
+
if (k === 'ArrowRight' || k === 'ArrowDown') { if (e.preventDefault) e.preventDefault(); focusDot(i + 1); }
|
|
487
|
+
else if (k === 'ArrowLeft' || k === 'ArrowUp') { if (e.preventDefault) e.preventDefault(); focusDot(i - 1); }
|
|
488
|
+
else if (k === 'Home') { if (e.preventDefault) e.preventDefault(); focusDot(0); }
|
|
489
|
+
else if (k === 'End') { if (e.preventDefault) e.preventDefault(); focusDot(dots.length - 1); }
|
|
490
|
+
}); });
|
|
491
|
+
if (canListen(pause)) pause.addEventListener('click', () => { paused = !paused; if (pause.setAttribute) pause.setAttribute('aria-pressed', String(paused)); start(); });
|
|
492
|
+
show(active); start(); bound += 1;
|
|
493
|
+
});
|
|
494
|
+
api.emit('gem:hero-slideshow:ready', { source: 'gem-runtime', count: bound });
|
|
495
|
+
});
|
|
496
|
+
registerPlugin('quick-view', ({ api }) => {
|
|
497
|
+
const triggers = document.querySelectorAll('[data-gem-quick-view], [data-gem-action="quick-view"]');
|
|
498
|
+
const surface = document.querySelector('[data-gem-quick-view-surface], .gem-quick-view, .gem-modal[data-gem-component="quick-view"]');
|
|
499
|
+
let bound = 0;
|
|
500
|
+
const close = (reason = 'close-control') => {
|
|
501
|
+
if (surface) {
|
|
502
|
+
surface.dataset.gemQuickViewState = 'closed';
|
|
503
|
+
surface.removeAttribute && surface.removeAttribute('open');
|
|
504
|
+
surface.setAttribute && surface.setAttribute('aria-hidden', 'true');
|
|
505
|
+
}
|
|
506
|
+
api.emit('gem:quick-view:close', { source: 'gem-runtime', reason });
|
|
507
|
+
};
|
|
508
|
+
const openSurface = (detail) => {
|
|
509
|
+
if (surface) {
|
|
510
|
+
surface.dataset.gemQuickViewState = 'open';
|
|
511
|
+
surface.dataset.gemProductHandle = detail.productHandle || '';
|
|
512
|
+
surface.dataset.gemProductUrl = detail.productUrl || '';
|
|
513
|
+
surface.setAttribute && surface.setAttribute('open', '');
|
|
514
|
+
surface.setAttribute && surface.setAttribute('aria-hidden', 'false');
|
|
515
|
+
surface.setAttribute && surface.setAttribute('role', 'dialog');
|
|
516
|
+
surface.setAttribute && surface.setAttribute('aria-modal', 'true');
|
|
517
|
+
const content = surface.querySelector && surface.querySelector('[data-gem-quick-view-content]');
|
|
518
|
+
if (content && content.textContent !== undefined) content.textContent = 'Quick view for ' + (detail.productHandle || 'product');
|
|
519
|
+
else if (surface.textContent !== undefined) surface.textContent = 'Quick view for ' + (detail.productHandle || 'product');
|
|
520
|
+
const closeButton = surface.querySelector && surface.querySelector('[data-gem-quick-view-close]');
|
|
521
|
+
if (closeButton && closeButton.focus) closeButton.focus();
|
|
522
|
+
}
|
|
523
|
+
api.emit('gem:quick-view:open', { source: 'gem-runtime', ...detail, hasSurface: Boolean(surface) });
|
|
524
|
+
};
|
|
525
|
+
triggers.forEach((trigger) => {
|
|
526
|
+
if (!mark(trigger, 'quick-view')) return;
|
|
527
|
+
if (!canListen(trigger)) return;
|
|
528
|
+
trigger.addEventListener('click', (event) => {
|
|
529
|
+
const productUrl = trigger.getAttribute && (trigger.getAttribute('data-gem-product-url') || trigger.getAttribute('href'));
|
|
530
|
+
const productHandle = trigger.getAttribute && trigger.getAttribute('data-gem-product-handle');
|
|
531
|
+
if (!productUrl && !productHandle) return;
|
|
532
|
+
event.preventDefault && event.preventDefault();
|
|
533
|
+
openSurface({ productUrl, productHandle });
|
|
534
|
+
});
|
|
535
|
+
bound += 1;
|
|
536
|
+
});
|
|
537
|
+
if (surface && mark(surface, 'quick-view-surface')) {
|
|
538
|
+
surface.dataset.gemQuickViewState = 'closed';
|
|
539
|
+
surface.setAttribute && surface.setAttribute('aria-hidden', 'true');
|
|
540
|
+
if (canListen(surface)) surface.addEventListener('click', (event) => {
|
|
541
|
+
const target = event.target;
|
|
542
|
+
if (target && target.closest && target.closest('[data-gem-quick-view-close]')) close('close-control');
|
|
543
|
+
});
|
|
544
|
+
if (canListen(surface)) surface.addEventListener('keydown', (event) => { if (event.key === 'Escape') close('escape'); });
|
|
545
|
+
}
|
|
546
|
+
api.quickView = { open: (detail = {}) => openSurface({ ...detail }), close };
|
|
547
|
+
api.emit('gem:quick-view:ready', { source: 'gem-runtime', count: bound, hasSurface: Boolean(surface) });
|
|
548
|
+
});
|
|
549
|
+
registerPlugin('promo-popup', ({ api }) => {
|
|
550
|
+
const popups = document.querySelectorAll('[data-gem-promo-popup], .gem-promo-popup');
|
|
551
|
+
let bound = 0;
|
|
552
|
+
const storagePrefix = 'gem:promo-popup-dismissed:';
|
|
553
|
+
const now = () => Date.now ? Date.now() : new Date().getTime();
|
|
554
|
+
const readDismissedUntil = (key) => {
|
|
555
|
+
try { return Number(target.localStorage && target.localStorage.getItem(storagePrefix + key) || '0'); }
|
|
556
|
+
catch { return 0; }
|
|
557
|
+
};
|
|
558
|
+
const writeDismissedUntil = (key, timestamp) => {
|
|
559
|
+
try { if (target.localStorage) target.localStorage.setItem(storagePrefix + key, String(timestamp)); }
|
|
560
|
+
catch { /* storage can be unavailable in private contexts */ }
|
|
561
|
+
};
|
|
562
|
+
popups.forEach((popup, index) => {
|
|
563
|
+
if (!mark(popup, 'promo-popup')) return;
|
|
564
|
+
const key = popup.id || popup.getAttribute && popup.getAttribute('data-gem-section-library-id') || String(index);
|
|
565
|
+
const delaySeconds = Number(popup.getAttribute && (popup.getAttribute('data-gem-promo-delay') || '0'));
|
|
566
|
+
const frequencyDays = Number(popup.getAttribute && (popup.getAttribute('data-gem-promo-frequency-days') || '7'));
|
|
567
|
+
const dialog = popup.querySelector && popup.querySelector('[data-gem-promo-popup-dialog]');
|
|
568
|
+
const overlay = popup.querySelector && popup.querySelector('[data-gem-promo-popup-overlay]');
|
|
569
|
+
const open = (reason = 'timer') => {
|
|
570
|
+
if (readDismissedUntil(key) > now()) { popup.dataset.gemPromoPopupState = 'dismissed'; return false; }
|
|
571
|
+
popup.hidden = false;
|
|
572
|
+
if (dialog) dialog.hidden = false;
|
|
573
|
+
if (overlay) overlay.hidden = false;
|
|
574
|
+
popup.dataset.gemPromoPopupState = 'open';
|
|
575
|
+
api.emit('gem:promo-popup:open', { source: 'gem-runtime', id: popup.id || undefined, reason });
|
|
576
|
+
return true;
|
|
577
|
+
};
|
|
578
|
+
const close = (reason = 'dismiss') => {
|
|
579
|
+
const days = Number.isFinite(frequencyDays) ? Math.max(1, frequencyDays) : 7;
|
|
580
|
+
writeDismissedUntil(key, now() + days * 86400000);
|
|
581
|
+
if (dialog) dialog.hidden = true;
|
|
582
|
+
if (overlay) overlay.hidden = true;
|
|
583
|
+
popup.dataset.gemPromoPopupState = 'closed';
|
|
584
|
+
api.emit('gem:promo-popup:close', { source: 'gem-runtime', id: popup.id || undefined, reason });
|
|
585
|
+
};
|
|
586
|
+
popup.querySelectorAll && popup.querySelectorAll('[data-gem-promo-popup-dismiss]').forEach((button) => {
|
|
587
|
+
if (!canListen(button)) return;
|
|
588
|
+
button.addEventListener('click', (event) => { event.preventDefault && event.preventDefault(); close('dismiss'); });
|
|
589
|
+
});
|
|
590
|
+
if (canListen(overlay)) overlay.addEventListener('click', () => close('overlay'));
|
|
591
|
+
popup.dataset.gemPromoPopupState = 'ready';
|
|
592
|
+
const delay = Math.max(0, Number.isFinite(delaySeconds) ? delaySeconds : 0) * 1000;
|
|
593
|
+
target.setTimeout ? target.setTimeout(() => open('timer'), delay) : open('timer');
|
|
594
|
+
bound += 1;
|
|
595
|
+
});
|
|
596
|
+
api.promoPopup = { open: () => popups.forEach((popup) => { popup.hidden = false; popup.dataset.gemPromoPopupState = 'open'; }) };
|
|
597
|
+
api.emit('gem:promo-popup:ready', { source: 'gem-runtime', count: bound });
|
|
598
|
+
});
|
|
599
|
+
registerPlugin('sticky-atc', ({ api }) => {
|
|
600
|
+
const bars = document.querySelectorAll('[data-gem-sticky-atc], .gem-sticky-atc');
|
|
601
|
+
let bound = 0;
|
|
602
|
+
const setState = (bar, state) => {
|
|
603
|
+
if (!bar || !bar.dataset) return;
|
|
604
|
+
if (bar.dataset.gemStickyAtcState === state) return;
|
|
605
|
+
bar.dataset.gemStickyAtcState = state;
|
|
606
|
+
api.emit(state === 'visible' ? 'gem:sticky-atc:show' : 'gem:sticky-atc:hide', { source: 'gem-runtime', id: bar.id || undefined });
|
|
607
|
+
};
|
|
608
|
+
bars.forEach((bar) => {
|
|
609
|
+
if (!mark(bar, 'sticky-atc')) return;
|
|
610
|
+
const threshold = Number(bar.getAttribute && (bar.getAttribute('data-gem-sticky-threshold') || '240'));
|
|
611
|
+
const update = () => {
|
|
612
|
+
const scrollY = Number(target.scrollY || target.pageYOffset || 0);
|
|
613
|
+
setState(bar, scrollY >= threshold ? 'visible' : 'hidden');
|
|
614
|
+
};
|
|
615
|
+
update();
|
|
616
|
+
if (canListen(target)) target.addEventListener('scroll', update, { passive: true });
|
|
617
|
+
const form = bar.querySelector && bar.querySelector('form');
|
|
618
|
+
if (canListen(form)) form.addEventListener('submit', () => api.emit('gem:sticky-atc:submit', { source: 'gem-runtime', id: bar.id || undefined }));
|
|
619
|
+
bound += 1;
|
|
620
|
+
});
|
|
621
|
+
api.emit('gem:sticky-atc:ready', { source: 'gem-runtime', count: bound });
|
|
622
|
+
});
|
|
623
|
+
registerPlugin('before_after', ({ api }) => {
|
|
624
|
+
const frames = document.querySelectorAll('[data-gem-before-after-frame], .gem-before-after__frame');
|
|
625
|
+
let bound = 0;
|
|
626
|
+
const clamp = (value) => Math.max(0, Math.min(100, Number.isFinite(value) ? value : 50));
|
|
627
|
+
frames.forEach((frame) => {
|
|
628
|
+
if (!mark(frame, 'before_after')) return;
|
|
629
|
+
const range = frame.querySelector && frame.querySelector('[data-gem-before-after-range], input[type="range"]');
|
|
630
|
+
const setPosition = (value, reason = 'init') => {
|
|
631
|
+
const position = clamp(Number(value));
|
|
632
|
+
if (frame.style && frame.style.setProperty) frame.style.setProperty('--gem-before-after-position', position + '%');
|
|
633
|
+
frame.dataset.gemBeforeAfterPosition = String(position);
|
|
634
|
+
if (range && range.value !== undefined) range.value = String(position);
|
|
635
|
+
api.emit('gem:before-after:change', { source: 'gem-runtime', position, reason, id: frame.id || undefined });
|
|
636
|
+
};
|
|
637
|
+
const initial = range && range.value !== undefined ? range.value : frame.getAttribute && frame.getAttribute('data-gem-before-after-position');
|
|
638
|
+
setPosition(initial || 50);
|
|
639
|
+
if (canListen(range)) range.addEventListener('input', () => setPosition(range.value, 'input'));
|
|
640
|
+
bound += 1;
|
|
641
|
+
});
|
|
642
|
+
api.emit('gem:before-after:ready', { source: 'gem-runtime', count: bound });
|
|
643
|
+
});
|
|
644
|
+
registerPlugin('countdown', ({ api }) => {
|
|
645
|
+
const timers = document.querySelectorAll('[data-gem-countdown], .gem-countdown');
|
|
646
|
+
let bound = 0;
|
|
647
|
+
const pad = (value) => String(Math.max(0, value)).padStart(2, '0');
|
|
648
|
+
const write = (timer, selector, value) => {
|
|
649
|
+
const targetNode = timer.querySelector && timer.querySelector(selector);
|
|
650
|
+
if (targetNode) targetNode.textContent = pad(value);
|
|
651
|
+
};
|
|
652
|
+
timers.forEach((timer) => {
|
|
653
|
+
if (!mark(timer, 'countdown')) return;
|
|
654
|
+
const targetIso = timer.getAttribute && timer.getAttribute('data-gem-countdown-target');
|
|
655
|
+
const targetTime = targetIso ? Date.parse(targetIso) : NaN;
|
|
656
|
+
const update = () => {
|
|
657
|
+
if (!Number.isFinite(targetTime)) {
|
|
658
|
+
timer.dataset.gemCountdownState = 'invalid';
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const remainingSeconds = Math.max(0, Math.floor((targetTime - Date.now()) / 1000));
|
|
662
|
+
const days = Math.floor(remainingSeconds / 86400);
|
|
663
|
+
const hours = Math.floor((remainingSeconds % 86400) / 3600);
|
|
664
|
+
const minutes = Math.floor((remainingSeconds % 3600) / 60);
|
|
665
|
+
timer.dataset.gemCountdownState = remainingSeconds > 0 ? 'running' : 'complete';
|
|
666
|
+
timer.dataset.gemCountdownRemainingSeconds = String(remainingSeconds);
|
|
667
|
+
write(timer, '[data-gem-countdown-days]', days);
|
|
668
|
+
write(timer, '[data-gem-countdown-hours]', hours);
|
|
669
|
+
write(timer, '[data-gem-countdown-minutes]', minutes);
|
|
670
|
+
api.emit(remainingSeconds > 0 ? 'gem:countdown:start' : 'gem:countdown:complete', { source: 'gem-runtime', id: timer.id || undefined, remainingSeconds });
|
|
671
|
+
};
|
|
672
|
+
update();
|
|
673
|
+
if (target.setInterval && Number.isFinite(targetTime)) target.setInterval(update, 60000);
|
|
674
|
+
bound += 1;
|
|
675
|
+
});
|
|
676
|
+
api.emit('gem:countdown:ready', { source: 'gem-runtime', count: bound });
|
|
677
|
+
});
|
|
678
|
+
registerPlugin('reveal-on-scroll', ({ api }) => {
|
|
679
|
+
const nodes = document.querySelectorAll('[data-gem-reveal], .gem-reveal');
|
|
680
|
+
let bound = 0;
|
|
681
|
+
const reveal = (node) => { node.dataset.gemRevealState = 'in'; };
|
|
682
|
+
if (!target.IntersectionObserver) { nodes.forEach(reveal); api.emit('gem:reveal:ready', { source: 'gem-runtime', count: 0 }); return; }
|
|
683
|
+
const observer = new target.IntersectionObserver((entries, obs) => {
|
|
684
|
+
entries.forEach((entry) => {
|
|
685
|
+
if (!entry.isIntersecting) return;
|
|
686
|
+
reveal(entry.target);
|
|
687
|
+
obs.unobserve(entry.target);
|
|
688
|
+
api.emit('gem:reveal:in', { source: 'gem-runtime', id: entry.target.id || undefined });
|
|
689
|
+
});
|
|
690
|
+
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
|
|
691
|
+
nodes.forEach((node) => {
|
|
692
|
+
if (!mark(node, 'reveal-on-scroll')) return;
|
|
693
|
+
if (!node.dataset.gemRevealState) node.dataset.gemRevealState = 'ready';
|
|
694
|
+
observer.observe(node);
|
|
695
|
+
bound += 1;
|
|
696
|
+
});
|
|
697
|
+
api.emit('gem:reveal:ready', { source: 'gem-runtime', count: bound });
|
|
698
|
+
});
|
|
699
|
+
registerPlugin('recently-viewed', ({ api }) => {
|
|
700
|
+
const storageKey = 'gem:recently-viewed-products';
|
|
701
|
+
const readItems = () => {
|
|
702
|
+
try { return JSON.parse(target.localStorage && target.localStorage.getItem(storageKey) || '[]'); }
|
|
703
|
+
catch { return []; }
|
|
704
|
+
};
|
|
705
|
+
const writeItems = (items) => {
|
|
706
|
+
try { if (target.localStorage) target.localStorage.setItem(storageKey, JSON.stringify(items.slice(0, 12))); }
|
|
707
|
+
catch { /* storage can be unavailable in private contexts */ }
|
|
708
|
+
};
|
|
709
|
+
const escapeHtml = (value) => String(value || '').replace(/[&<>'"]/g, (char) => ({ '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[char]));
|
|
710
|
+
const renderItem = (item) => {
|
|
711
|
+
const url = item && item.url ? String(item.url) : '#';
|
|
712
|
+
const label = item && (item.title || item.handle || item.url) ? String(item.title || item.handle || item.url) : 'Recently viewed product';
|
|
713
|
+
return '<a class="gem-card gem-recently-viewed__item" href="' + escapeHtml(url) + '"><strong>' + escapeHtml(label) + '</strong></a>';
|
|
714
|
+
};
|
|
715
|
+
const current = document.querySelector('[data-gem-current-product], [data-gem-product-handle]');
|
|
716
|
+
if (current) {
|
|
717
|
+
const handle = current.getAttribute && current.getAttribute('data-gem-product-handle');
|
|
718
|
+
const url = current.getAttribute && (current.getAttribute('data-gem-product-url') || current.getAttribute('href'));
|
|
719
|
+
const title = current.getAttribute && current.getAttribute('data-gem-product-title');
|
|
720
|
+
if (handle || url) {
|
|
721
|
+
const nextItem = { handle, url, title, recordedAt: new Date().toISOString() };
|
|
722
|
+
const items = [nextItem, ...readItems().filter((item) => item && item.handle !== handle && item.url !== url)];
|
|
723
|
+
writeItems(items);
|
|
724
|
+
api.emit('gem:recently-viewed:record', { source: 'gem-runtime', handle, url });
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const rails = document.querySelectorAll('[data-gem-recently-viewed], .gem-recently-viewed');
|
|
728
|
+
let bound = 0;
|
|
729
|
+
rails.forEach((rail) => {
|
|
730
|
+
if (!mark(rail, 'recently-viewed')) return;
|
|
731
|
+
const limit = Number(rail.getAttribute && (rail.getAttribute('data-gem-recently-viewed-limit') || '4'));
|
|
732
|
+
const items = readItems().slice(0, Number.isFinite(limit) ? limit : 4);
|
|
733
|
+
const list = rail.querySelector && (rail.querySelector('[data-gem-recently-viewed-list]') || rail);
|
|
734
|
+
const empty = rail.querySelector && rail.querySelector('[data-gem-recently-viewed-empty]');
|
|
735
|
+
rail.dataset.gemRecentlyViewedCount = String(items.length);
|
|
736
|
+
if (items.length > 0) {
|
|
737
|
+
rail.dataset.gemRecentlyViewedState = 'ready';
|
|
738
|
+
if (list) {
|
|
739
|
+
list.hidden = false;
|
|
740
|
+
if (list.dataset) list.dataset.gemRecentlyViewedCount = String(items.length);
|
|
741
|
+
if ('innerHTML' in list) list.innerHTML = items.map(renderItem).join('');
|
|
742
|
+
else if ('textContent' in list) list.textContent = items.map((item) => item && (item.title || item.handle || item.url) || 'Recently viewed product').join(', ');
|
|
743
|
+
}
|
|
744
|
+
if (empty) empty.hidden = true;
|
|
745
|
+
} else {
|
|
746
|
+
rail.dataset.gemRecentlyViewedState = 'empty';
|
|
747
|
+
if (empty) empty.hidden = false;
|
|
748
|
+
}
|
|
749
|
+
bound += 1;
|
|
750
|
+
});
|
|
751
|
+
api.recentlyViewed = { list: readItems };
|
|
752
|
+
api.emit('gem:recently-viewed:ready', { source: 'gem-runtime', count: bound });
|
|
753
|
+
});
|
|
754
|
+
registerPlugin('runtime-slot-loader', ({ api }) => {
|
|
755
|
+
const slots = document.querySelectorAll('[data-gem-runtime-slot], [data-gem-footer-block-builder], .gem-navigation-shell__layout');
|
|
756
|
+
let bound = 0;
|
|
757
|
+
slots.forEach((slot, index) => {
|
|
758
|
+
if (!mark(slot, 'runtime-slot-loader')) return;
|
|
759
|
+
if (slot.dataset) {
|
|
760
|
+
slot.dataset.gemRuntimeSlotState = 'ready';
|
|
761
|
+
if (!slot.dataset.gemRuntimeSlotIndex) slot.dataset.gemRuntimeSlotIndex = String(index);
|
|
762
|
+
}
|
|
763
|
+
bound += 1;
|
|
764
|
+
});
|
|
765
|
+
api.emit('gem:slot:ready', { source: 'gem-runtime', count: bound });
|
|
766
|
+
});
|
|
767
|
+
registerPlugin('offer-engine', ({ api }) => {
|
|
768
|
+
(function(){
|
|
769
|
+
document.querySelectorAll('[data-gem-offer-engine]').forEach(function(root){
|
|
770
|
+
if(root.__gemOffer)return; root.__gemOffer=1;
|
|
771
|
+
var el=root.querySelector('[data-gem-offer-model]'); if(!el)return;
|
|
772
|
+
var model; try{model=JSON.parse(el.textContent);}catch(e){return;}
|
|
773
|
+
var dims=model.dimensions||{}, sel={};
|
|
774
|
+
Object.keys(dims).forEach(function(d){var o=(dims[d].options||[]),def=o.filter(function(x){return x.default;})[0]||o[0];sel[d]=(model.default_selection&&model.default_selection[d])||(def&&def.id);});
|
|
775
|
+
function m(w){return Object.keys(w).every(function(k){return sel[k]===w[k];});}
|
|
776
|
+
function sp(w){return Object.keys(w).length;}
|
|
777
|
+
function pick(list){return (list||[]).filter(function(r){return m(r.when);}).sort(function(a,b){return sp(b.when)-sp(a.when);})[0];}
|
|
778
|
+
function gift(g,t){if(!g||t==null)return null;var sym=g.currency_symbol||'$';var rem=Math.max(0,Math.round((g.threshold-t)*100)/100);var unl=rem<=0;var rs=sym+rem.toFixed(2);var pct=g.threshold>0?Math.max(0,Math.min(100,Math.round((t/g.threshold)*100))):100;var tpl=unl?(g.unlocked_text||'\uD83C\uDF81 {label} unlocked!'):(g.locked_text||'Add {remaining} more to unlock {label}');return {unlocked:unl,remaining:rs,remaining_amount:rem,percent:pct,text:tpl.replace('{remaining}',rs).replace('{label}',g.label)};}
|
|
779
|
+
function render(){
|
|
780
|
+
var row=pick(model.price_matrix)||{}, plan=pick(model.selling_plan_map);
|
|
781
|
+
var vis={}; Object.keys(dims).forEach(function(d){vis[d]=true;});
|
|
782
|
+
(model.conditions||[]).forEach(function(c){vis[c.show]=(sel[c.when_dim]===c.equals);});
|
|
783
|
+
(model.panels||[]).forEach(function(p){if(p.when)vis[p.id]=(sel[p.when.when_dim]===p.when.equals);});
|
|
784
|
+
var headline=row.total||row.price||'';
|
|
785
|
+
var gf=gift(model.gift,row.total_amount);
|
|
786
|
+
root.querySelectorAll('[data-gem-offer-out]').forEach(function(o){var k=o.getAttribute('data-gem-offer-out');
|
|
787
|
+
if(k==='price')o.textContent=row.price||'';
|
|
788
|
+
else if(k==='compare'){o.textContent=row.compare_at||'';o.style.display=row.compare_at?'':'none';}
|
|
789
|
+
else if(k==='save'){o.textContent=row.save_label||'';o.style.display=row.save_label?'':'none';}
|
|
790
|
+
else if(k==='gift'){o.textContent=gf?gf.text:'';if(o.classList)o.classList.toggle('unlocked',!!(gf&&gf.unlocked));}
|
|
791
|
+
else if(k==='cta')o.textContent=(model.cta_template||'Add to cart').replace('{price}',headline).replace('{total}',headline);});
|
|
792
|
+
root.querySelectorAll('[data-gem-offer-gift]').forEach(function(w){w.style.display=gf?'':'none';if(w.classList)w.classList.toggle('unlocked',!!(gf&&gf.unlocked));});
|
|
793
|
+
root.querySelectorAll('[data-gem-offer-gift-bar]').forEach(function(b){b.style.width=(gf?gf.percent:0)+'%';});
|
|
794
|
+
root.querySelectorAll('[data-gem-offer-group]').forEach(function(g){g.style.display=(vis[g.getAttribute('data-gem-offer-group')]===false)?'none':'';});
|
|
795
|
+
root.querySelectorAll('[data-gem-offer-dim]').forEach(function(b){var d=b.getAttribute('data-gem-offer-dim'),v=b.getAttribute('data-gem-offer-value');if(b.tagName==='OPTION'){b.selected=(sel[d]===v);}else{b.classList.toggle('sel',sel[d]===v);}});
|
|
796
|
+
var s=root.querySelector('[data-gem-offer-selling-plan]'); if(s)s.value=(plan&&plan.selling_plan_id)||'';
|
|
797
|
+
var ca=(plan&&plan.cart_attributes)||{};
|
|
798
|
+
root.querySelectorAll('[data-gem-offer-cart-field]').forEach(function(f){var n=f.getAttribute('data-gem-offer-cart-field');f.value=ca[n]||'';f.disabled=!(n in ca);});
|
|
799
|
+
root.dispatchEvent(new CustomEvent('gem:offer:change',{bubbles:true,detail:{selection:sel,row:row,selling_plan_id:(plan&&plan.selling_plan_id)||null,cart_attributes:ca,gift:gf}}));
|
|
800
|
+
}
|
|
801
|
+
root.addEventListener('click',function(e){var b=e.target.closest('[data-gem-offer-dim]');if(!b||b.tagName==='OPTION')return;sel[b.getAttribute('data-gem-offer-dim')]=b.getAttribute('data-gem-offer-value');render();});
|
|
802
|
+
root.addEventListener('change',function(e){var s=e.target.closest('select[data-gem-offer-select]');if(!s)return;var o=s.options[s.selectedIndex];sel[s.getAttribute('data-gem-offer-select')]=o.getAttribute('data-gem-offer-value');render();});
|
|
803
|
+
render();
|
|
804
|
+
});
|
|
805
|
+
})();
|
|
806
|
+
api.emit('gem:offer:ready', { source: 'gem-runtime' });
|
|
807
|
+
});
|
|
808
|
+
registerNoopPlugin("seasonal-effects", "gem:seasonal:ready");
|
|
809
|
+
registerNoopPlugin("context-adapter", "gem:context:ready");
|
|
810
|
+
const mark = (element, key) => {
|
|
811
|
+
const attr = 'data-gem-bound-' + key;
|
|
812
|
+
if (!element || element.hasAttribute(attr)) return false;
|
|
813
|
+
element.setAttribute(attr, 'true');
|
|
814
|
+
return true;
|
|
815
|
+
};
|
|
816
|
+
const wrapMethod = (element, method, event, detail = {}) => {
|
|
817
|
+
if (!element || typeof element[method] !== 'function') return false;
|
|
818
|
+
const attr = 'data-gem-wrap-' + method;
|
|
819
|
+
if (element.hasAttribute(attr)) return true;
|
|
820
|
+
const original = element[method].bind(element);
|
|
821
|
+
element[method] = (...args) => {
|
|
822
|
+
const result = original(...args);
|
|
823
|
+
api.emit(event, typeof detail === 'function' ? detail(...args) : detail);
|
|
824
|
+
return result;
|
|
825
|
+
};
|
|
826
|
+
element.setAttribute(attr, 'true');
|
|
827
|
+
return true;
|
|
828
|
+
};
|
|
829
|
+
const whenElementDefined = (name, callback) => {
|
|
830
|
+
if (!target.customElements || !target.customElements.whenDefined) { callback(); return; }
|
|
831
|
+
target.customElements.whenDefined(name).then(callback).catch(callback);
|
|
832
|
+
};
|
|
833
|
+
const wireDawnCartDrawer = () => {
|
|
834
|
+
if (!hasPlugin('cart-drawer')) return false;
|
|
835
|
+
const drawer = document.querySelector('cart-drawer');
|
|
836
|
+
if (!drawer || !mark(drawer, 'cart-drawer')) return false;
|
|
837
|
+
wrapMethod(drawer, 'open', 'gem:cart:open', { source: 'dawn' });
|
|
838
|
+
wrapMethod(drawer, 'close', 'gem:cart:close', { source: 'dawn' });
|
|
839
|
+
wrapMethod(drawer, 'renderContents', 'gem:cart:update', (state) => ({
|
|
840
|
+
source: 'dawn',
|
|
841
|
+
productId: state && state.id ? state.id : undefined,
|
|
842
|
+
}));
|
|
843
|
+
return true;
|
|
844
|
+
};
|
|
845
|
+
const wireDawnMenuDrawer = () => {
|
|
846
|
+
if (!hasPlugin('mobile-menu')) return false;
|
|
847
|
+
const drawers = document.querySelectorAll('menu-drawer, header-drawer');
|
|
848
|
+
let wired = false;
|
|
849
|
+
drawers.forEach((drawer) => {
|
|
850
|
+
if (!mark(drawer, 'mobile-menu')) return;
|
|
851
|
+
wrapMethod(drawer, 'openMenuDrawer', 'gem:menu:open', { source: 'dawn' });
|
|
852
|
+
wrapMethod(drawer, 'closeMenuDrawer', 'gem:menu:close', { source: 'dawn' });
|
|
853
|
+
wired = true;
|
|
854
|
+
});
|
|
855
|
+
return wired;
|
|
856
|
+
};
|
|
857
|
+
const wireDawnPredictiveSearch = () => {
|
|
858
|
+
if (!hasPlugin('predictive-search')) return false;
|
|
859
|
+
const search = document.querySelector('predictive-search');
|
|
860
|
+
if (!search || !mark(search, 'predictive-search')) return false;
|
|
861
|
+
const input = search.querySelector('input[type="search"], input[name="q"]');
|
|
862
|
+
const form = search.querySelector('form');
|
|
863
|
+
if (canListen(input)) input.addEventListener('focus', () => api.emit('gem:search:open', { source: 'dawn' }), { once: false });
|
|
864
|
+
if (canListen(form)) form.addEventListener('submit', () => api.emit('gem:search:submit', { source: 'dawn' }), { once: false });
|
|
865
|
+
return true;
|
|
866
|
+
};
|
|
867
|
+
const wireDawnAdapter = () => {
|
|
868
|
+
if (!config.dawnAdapter || config.base !== 'dawn') return;
|
|
869
|
+
const detected = [];
|
|
870
|
+
whenElementDefined('cart-drawer', () => { if (wireDawnCartDrawer()) detected.push('cart-drawer'); });
|
|
871
|
+
whenElementDefined('menu-drawer', () => { if (wireDawnMenuDrawer()) detected.push('mobile-menu'); });
|
|
872
|
+
whenElementDefined('header-drawer', () => { if (wireDawnMenuDrawer()) detected.push('mobile-menu'); });
|
|
873
|
+
whenElementDefined('predictive-search', () => { if (wireDawnPredictiveSearch()) detected.push('predictive-search'); });
|
|
874
|
+
setTimeout(() => api.emit('gem:dawn-adapter:ready', { detected: [...new Set(detected)] }), 0);
|
|
875
|
+
};
|
|
876
|
+
wireDawnAdapter();
|
|
877
|
+
config.plugins.forEach(schedulePlugin);
|
|
878
|
+
document.documentElement.dataset.gemRuntime = 'ready';
|
|
879
|
+
api.emit('gem:runtime:ready', { plugins: config.plugins.map((plugin) => plugin.key), loadedPlugins: Object.keys(loadedPlugins) });
|
|
880
|
+
})();
|