@hortonstudio/main 1.2.35 → 1.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.
@@ -1,17 +1,15 @@
1
1
  export function init() {
2
2
  const config = {
3
- defaultMode: 'split',
3
+ defaultMode: "split",
4
4
  sliderPosition: 50,
5
5
  touchSensitivity: 1,
6
6
  keyboardStep: 5,
7
7
  autoPlay: false,
8
- autoPlayInterval: 5000
8
+ autoPlayInterval: 5000,
9
9
  };
10
10
 
11
11
  let instances = [];
12
12
  let currentSlideIndex = {};
13
- let isDragging = false;
14
- let dragInstance = null;
15
13
 
16
14
  function updateConfig(newConfig) {
17
15
  Object.assign(config, newConfig);
@@ -20,7 +18,7 @@ export function init() {
20
18
  function initInstance(wrapper) {
21
19
  const instanceId = instances.length;
22
20
  const items = Array.from(wrapper.children);
23
-
21
+
24
22
  if (items.length === 0) return;
25
23
 
26
24
  const instance = {
@@ -30,7 +28,11 @@ export function init() {
30
28
  currentIndex: 0,
31
29
  mode: config.defaultMode,
32
30
  sliderPosition: config.sliderPosition,
33
- previousSliderPosition: config.sliderPosition
31
+ previousSliderPosition: config.sliderPosition,
32
+ isDragging: false,
33
+ dragInstance: null,
34
+ // Cache DOM queries
35
+ cachedElements: new Map(),
34
36
  };
35
37
 
36
38
  instances.push(instance);
@@ -38,7 +40,7 @@ export function init() {
38
40
 
39
41
  setupInstance(instance);
40
42
  showSlide(instance, 0);
41
-
43
+
42
44
  return instance;
43
45
  }
44
46
 
@@ -46,14 +48,14 @@ export function init() {
46
48
  const { wrapper, items } = instance;
47
49
 
48
50
  items.forEach((item, index) => {
49
- item.style.display = index === 0 ? 'block' : 'none';
50
-
51
+ item.style.display = index === 0 ? "block" : "none";
52
+
51
53
  // Set default clip path for after image
52
54
  const afterImage = item.querySelector('[data-hs-ba="image-after"]');
53
55
  if (afterImage) {
54
56
  afterImage.style.clipPath = `polygon(${config.sliderPosition}% 0%, 100% 0%, 100% 100%, ${config.sliderPosition}% 100%)`;
55
57
  }
56
-
58
+
57
59
  setupItemInteractions(instance, item, index);
58
60
  });
59
61
 
@@ -65,75 +67,80 @@ export function init() {
65
67
  const leftArrow = item.querySelector('[data-hs-ba="left"]');
66
68
  const rightArrow = item.querySelector('[data-hs-ba="right"]');
67
69
  const slider = item.querySelector('[data-hs-ba="slider"]');
68
- const pagination = item.querySelector('[data-hs-ba="pagination"]');
70
+ const paginationContainer = item.querySelector('[data-hs-ba="pagination"]');
69
71
 
70
- modeButtons.forEach(button => {
71
- const mode = button.getAttribute('data-hs-ba').replace('mode-', '');
72
- button.addEventListener('click', (e) => {
72
+ modeButtons.forEach((button) => {
73
+ const mode = button.getAttribute("data-hs-ba").replace("mode-", "");
74
+ button.addEventListener("click", (e) => {
73
75
  e.preventDefault();
74
-
76
+
75
77
  // If clicking split mode when already in split mode, reset to default position
76
- if (mode === 'split' && instance.mode === 'split') {
78
+ if (mode === "split" && instance.mode === "split") {
77
79
  instance.sliderPosition = config.sliderPosition;
78
80
  }
79
-
81
+
80
82
  setMode(instance, itemIndex, mode);
81
83
  });
82
84
  });
83
85
 
84
86
  if (leftArrow) {
85
- leftArrow.addEventListener('click', (e) => {
87
+ leftArrow.addEventListener("click", (e) => {
86
88
  e.preventDefault();
87
89
  navigateSlide(instance, -1);
88
90
  });
89
91
  }
90
92
 
91
93
  if (rightArrow) {
92
- rightArrow.addEventListener('click', (e) => {
94
+ rightArrow.addEventListener("click", (e) => {
93
95
  e.preventDefault();
94
96
  navigateSlide(instance, 1);
95
97
  });
96
98
  }
97
99
 
98
100
  if (slider) {
99
- slider.style.cursor = 'grab';
101
+ slider.style.cursor = "grab";
100
102
  setupSliderDragging(instance, slider, itemIndex);
101
103
  }
102
104
 
103
- if (pagination) {
104
- setupPagination(instance, pagination);
105
+ if (paginationContainer) {
106
+ setupPagination(instance, paginationContainer);
105
107
  }
106
108
  }
107
109
 
108
110
  function setMode(instance, itemIndex, mode) {
109
111
  const item = instance.items[itemIndex];
110
112
  const afterImage = item.querySelector('[data-hs-ba="image-after"]');
111
- const slider = item.querySelector('.ba-slider');
112
113
  const sliderHandle = item.querySelector('[data-hs-ba="slider"]');
113
114
  const modeButtons = item.querySelectorAll('[data-hs-ba^="mode-"]');
114
115
 
115
- modeButtons.forEach(btn => {
116
- const btnMode = btn.getAttribute('data-hs-ba').replace('mode-', '');
116
+ modeButtons.forEach((btn) => {
117
+ const btnMode = btn.getAttribute("data-hs-ba").replace("mode-", "");
117
118
  if (btnMode === mode) {
118
- btn.classList.remove('light');
119
+ btn.classList.add("is-active");
119
120
  } else {
120
- btn.classList.add('light');
121
+ btn.classList.remove("is-active");
121
122
  }
122
123
  });
123
124
 
124
125
  switch (mode) {
125
- case 'before':
126
- if (afterImage) afterImage.style.clipPath = 'polygon(100% 0%, 100% 0%, 100% 100%, 100% 100%)';
127
- if (slider) slider.style.display = 'none';
126
+ case "before":
127
+ if (afterImage)
128
+ afterImage.style.clipPath =
129
+ "polygon(100% 0%, 100% 0%, 100% 100%, 100% 100%)";
130
+ if (sliderHandle) sliderHandle.style.display = "none";
128
131
  break;
129
- case 'after':
130
- if (afterImage) afterImage.style.clipPath = 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)';
131
- if (slider) slider.style.display = 'none';
132
+ case "after":
133
+ if (afterImage)
134
+ afterImage.style.clipPath =
135
+ "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)";
136
+ if (sliderHandle) sliderHandle.style.display = "none";
132
137
  break;
133
- case 'split':
134
- if (afterImage) afterImage.style.clipPath = `polygon(${instance.sliderPosition}% 0%, 100% 0%, 100% 100%, ${instance.sliderPosition}% 100%)`;
135
- if (sliderHandle) sliderHandle.style.left = `${instance.sliderPosition}%`;
136
- if (slider) slider.style.display = 'flex';
138
+ case "split":
139
+ if (afterImage)
140
+ afterImage.style.clipPath = `polygon(${instance.sliderPosition}% 0%, 100% 0%, 100% 100%, ${instance.sliderPosition}% 100%)`;
141
+ if (sliderHandle)
142
+ sliderHandle.style.left = `${instance.sliderPosition}%`;
143
+ if (sliderHandle) sliderHandle.style.display = "flex";
137
144
  break;
138
145
  }
139
146
 
@@ -142,65 +149,74 @@ export function init() {
142
149
 
143
150
  function setupSliderDragging(instance, slider, itemIndex) {
144
151
  const item = instance.items[itemIndex];
145
- const imageWrap = item.querySelector('.ba-image_wrap');
146
-
152
+ // Find the image wrapper using the data attribute
153
+ const imageWrap = item.querySelector('[data-hs-ba="image-wrapper"]');
154
+
147
155
  if (!imageWrap) return;
148
156
 
149
157
  function startDrag(e) {
150
- isDragging = true;
151
- dragInstance = { instance, itemIndex, imageWrap, slider };
152
- document.body.style.userSelect = 'none';
153
- document.body.style.cursor = 'grabbing';
154
- slider.style.cursor = 'grabbing';
158
+ instance.isDragging = true;
159
+ instance.dragInstance = { instance, itemIndex, imageWrap, slider };
160
+ document.body.style.userSelect = "none";
161
+ document.body.style.cursor = "grabbing";
162
+ slider.style.cursor = "grabbing";
155
163
  e.preventDefault();
156
164
  }
157
165
 
158
166
  function handleDrag(clientX) {
159
- if (!isDragging || !dragInstance) return;
167
+ if (!instance.isDragging || !instance.dragInstance) return;
160
168
 
161
- const rect = dragInstance.imageWrap.getBoundingClientRect();
169
+ const rect = instance.dragInstance.imageWrap.getBoundingClientRect();
162
170
  const x = clientX - rect.left;
163
171
  const percentage = Math.max(0, Math.min(100, (x / rect.width) * 100));
164
-
172
+
165
173
  // Move the slider element directly
166
- dragInstance.slider.style.left = `${percentage}%`;
167
-
174
+ instance.dragInstance.slider.style.left = `${percentage}%`;
175
+
168
176
  // Update the clip path
169
- updateSliderPosition(dragInstance.instance, dragInstance.itemIndex, percentage);
170
- dragInstance.instance.sliderPosition = percentage;
177
+ updateSliderPosition(
178
+ instance.dragInstance.instance,
179
+ instance.dragInstance.itemIndex,
180
+ percentage,
181
+ );
182
+ instance.dragInstance.instance.sliderPosition = percentage;
171
183
  }
172
184
 
173
185
  function endDrag() {
174
- isDragging = false;
175
- if (dragInstance && dragInstance.slider) {
176
- dragInstance.slider.style.cursor = 'grab';
186
+ instance.isDragging = false;
187
+ if (instance.dragInstance && instance.dragInstance.slider) {
188
+ instance.dragInstance.slider.style.cursor = "grab";
177
189
  }
178
- dragInstance = null;
179
- document.body.style.userSelect = '';
180
- document.body.style.cursor = '';
190
+ instance.dragInstance = null;
191
+ document.body.style.userSelect = "";
192
+ document.body.style.cursor = "";
181
193
  }
182
194
 
183
- slider.addEventListener('mousedown', startDrag);
184
- slider.addEventListener('touchstart', startDrag, { passive: false });
195
+ slider.addEventListener("mousedown", startDrag);
196
+ slider.addEventListener("touchstart", startDrag, { passive: false });
185
197
 
186
- document.addEventListener('mousemove', (e) => handleDrag(e.clientX));
187
- document.addEventListener('touchmove', (e) => {
188
- if (isDragging) {
189
- e.preventDefault();
190
- handleDrag(e.touches[0].clientX);
191
- }
192
- }, { passive: false });
198
+ document.addEventListener("mousemove", (e) => handleDrag(e.clientX));
199
+ document.addEventListener(
200
+ "touchmove",
201
+ (e) => {
202
+ if (instance.isDragging) {
203
+ e.preventDefault();
204
+ handleDrag(e.touches[0].clientX);
205
+ }
206
+ },
207
+ { passive: false },
208
+ );
209
+
210
+ document.addEventListener("mouseup", endDrag);
211
+ document.addEventListener("touchend", endDrag);
193
212
 
194
- document.addEventListener('mouseup', endDrag);
195
- document.addEventListener('touchend', endDrag);
213
+ imageWrap.addEventListener("click", (e) => {
214
+ if (instance.mode !== "split") return;
196
215
 
197
- imageWrap.addEventListener('click', (e) => {
198
- if (instance.mode !== 'split') return;
199
-
200
216
  const rect = imageWrap.getBoundingClientRect();
201
217
  const x = e.clientX - rect.left;
202
218
  const percentage = (x / rect.width) * 100;
203
-
219
+
204
220
  slider.style.left = `${percentage}%`;
205
221
  updateSliderPosition(instance, itemIndex, percentage);
206
222
  instance.sliderPosition = percentage;
@@ -208,8 +224,28 @@ export function init() {
208
224
  }
209
225
 
210
226
  function updateSliderPosition(instance, itemIndex, percentage) {
227
+ // Validate input
228
+ if (typeof percentage !== "number" || isNaN(percentage)) {
229
+ console.error(
230
+ "Invalid percentage value for slider position:",
231
+ percentage,
232
+ );
233
+ return;
234
+ }
235
+
236
+ percentage = Math.max(0, Math.min(100, percentage));
237
+
211
238
  const item = instance.items[itemIndex];
212
- const afterImage = item.querySelector('[data-hs-ba="image-after"]');
239
+ const cacheKey = `afterImage-${itemIndex}`;
240
+
241
+ // Use cached element if available
242
+ let afterImage = instance.cachedElements.get(cacheKey);
243
+ if (!afterImage) {
244
+ afterImage = item.querySelector('[data-hs-ba="image-after"]');
245
+ if (afterImage) {
246
+ instance.cachedElements.set(cacheKey, afterImage);
247
+ }
248
+ }
213
249
 
214
250
  if (afterImage) {
215
251
  afterImage.style.clipPath = `polygon(${percentage}% 0%, 100% 0%, 100% 100%, ${percentage}% 100%)`;
@@ -235,26 +271,25 @@ export function init() {
235
271
  function showSlide(instance, index) {
236
272
  if (index === instance.currentIndex) return;
237
273
 
238
- // Reset split position to default when switching items
239
- instance.sliderPosition = config.sliderPosition;
274
+ // Keep current slider position when switching items (don't reset)
240
275
 
241
276
  instance.items.forEach((item, i) => {
242
- item.style.display = i === index ? 'block' : 'none';
243
-
277
+ item.style.display = i === index ? "block" : "none";
278
+
244
279
  // Update clip path for the new active item
245
280
  if (i === index) {
246
281
  const afterImage = item.querySelector('[data-hs-ba="image-after"]');
247
282
  const sliderHandle = item.querySelector('[data-hs-ba="slider"]');
248
-
283
+
249
284
  if (afterImage) {
250
- // Apply default slider position to new item
251
- if (instance.mode === 'split') {
285
+ // Apply current slider position to new item
286
+ if (instance.mode === "split") {
252
287
  afterImage.style.clipPath = `polygon(${instance.sliderPosition}% 0%, 100% 0%, 100% 100%, ${instance.sliderPosition}% 100%)`;
253
288
  }
254
289
  }
255
-
256
- if (sliderHandle && instance.mode === 'split') {
257
- // Position slider at default position
290
+
291
+ if (sliderHandle && instance.mode === "split") {
292
+ // Position slider at current position
258
293
  sliderHandle.style.left = `${instance.sliderPosition}%`;
259
294
  }
260
295
  }
@@ -267,105 +302,156 @@ export function init() {
267
302
  setMode(instance, index, instance.mode);
268
303
  }
269
304
 
270
- function setupPagination(instance, pagination) {
271
- const dots = Array.from(pagination.querySelectorAll('.ba-pag_dot'));
272
-
273
- dots.forEach((dot, index) => {
274
- dot.addEventListener('click', () => {
305
+ function setupPagination(instance, paginationContainer) {
306
+ // Find existing dots as templates
307
+ const templateDots = Array.from(paginationContainer.children);
308
+ const templateDot = templateDots[0]; // Use first child as template
309
+
310
+ if (!templateDot) return; // No template found
311
+
312
+ // Clear existing dots
313
+ paginationContainer.innerHTML = "";
314
+
315
+ // Create dots for each item
316
+ instance.items.forEach((item, index) => {
317
+ const dot = templateDot.cloneNode(true);
318
+
319
+ // Add accessibility attributes
320
+ dot.setAttribute("role", "button");
321
+ dot.setAttribute("tabindex", "0");
322
+ dot.setAttribute("aria-label", `Go to slide ${index + 1}`);
323
+ dot.setAttribute("aria-current", index === 0 ? "true" : "false");
324
+
325
+ // Set initial active state
326
+ if (index === 0) {
327
+ dot.classList.add("is-active");
328
+ } else {
329
+ dot.classList.remove("is-active");
330
+ }
331
+
332
+ // Add interaction styles
333
+ dot.style.cursor = "pointer";
334
+
335
+ // Add click handler
336
+ dot.addEventListener("click", () => {
275
337
  showSlide(instance, index);
276
338
  });
277
-
278
- dot.style.cursor = 'pointer';
279
- dot.setAttribute('role', 'button');
280
- dot.setAttribute('tabindex', '0');
281
- dot.setAttribute('aria-label', `Go to slide ${index + 1}`);
282
-
283
- dot.addEventListener('keydown', (e) => {
284
- if (e.key === 'Enter' || e.key === ' ') {
339
+
340
+ // Add keyboard handler
341
+ dot.addEventListener("keydown", (e) => {
342
+ if (e.key === "Enter" || e.key === " ") {
285
343
  e.preventDefault();
286
344
  showSlide(instance, index);
287
345
  }
288
346
  });
347
+
348
+ // Append to container
349
+ paginationContainer.appendChild(dot);
289
350
  });
290
351
  }
291
352
 
292
353
  function updatePagination(instance) {
293
- instance.items.forEach((item, itemIndex) => {
294
- const pagination = item.querySelector('[data-hs-ba="pagination"]');
295
- if (!pagination) return;
354
+ instance.items.forEach((item) => {
355
+ const paginationContainer = item.querySelector(
356
+ '[data-hs-ba="pagination"]',
357
+ );
358
+ if (!paginationContainer) return;
296
359
 
297
- const dots = pagination.querySelectorAll('.ba-pag_dot');
360
+ const dots = Array.from(paginationContainer.children);
298
361
  dots.forEach((dot, dotIndex) => {
299
362
  if (dotIndex === instance.currentIndex) {
300
- dot.classList.add('active');
301
- dot.setAttribute('aria-current', 'true');
363
+ dot.classList.add("is-active");
364
+ dot.setAttribute("aria-current", "true");
302
365
  } else {
303
- dot.classList.remove('active');
304
- dot.removeAttribute('aria-current');
366
+ dot.classList.remove("is-active");
367
+ dot.setAttribute("aria-current", "false");
305
368
  }
306
369
  });
307
370
  });
308
371
  }
309
372
 
310
373
  function setupKeyboardNavigation(instance) {
311
- instance.wrapper.addEventListener('keydown', (e) => {
374
+ // Main wrapper: Left/Right arrows switch between items
375
+ instance.wrapper.addEventListener("keydown", (e) => {
376
+ // Only respond when the main wrapper itself is focused
377
+ if (e.target !== instance.wrapper) return;
378
+
312
379
  switch (e.key) {
313
- case 'ArrowLeft':
380
+ case "ArrowLeft":
314
381
  e.preventDefault();
315
- if (instance.mode === 'split') {
316
- const newPos = Math.max(0, instance.sliderPosition - config.keyboardStep);
317
- updateSliderPosition(instance, instance.currentIndex, newPos);
318
- instance.sliderPosition = newPos;
319
- } else {
320
- navigateSlide(instance, -1);
321
- }
382
+ navigateSlide(instance, -1);
322
383
  break;
323
- case 'ArrowRight':
384
+ case "ArrowRight":
324
385
  e.preventDefault();
325
- if (instance.mode === 'split') {
326
- const newPos = Math.min(100, instance.sliderPosition + config.keyboardStep);
327
- updateSliderPosition(instance, instance.currentIndex, newPos);
328
- instance.sliderPosition = newPos;
329
- } else {
330
- navigateSlide(instance, 1);
331
- }
386
+ navigateSlide(instance, 1);
332
387
  break;
333
- case 'ArrowUp':
388
+ case "ArrowUp":
334
389
  e.preventDefault();
335
390
  navigateSlide(instance, -1);
336
391
  break;
337
- case 'ArrowDown':
392
+ case "ArrowDown":
338
393
  e.preventDefault();
339
394
  navigateSlide(instance, 1);
340
395
  break;
341
- case '1':
342
- setMode(instance, instance.currentIndex, 'before');
343
- break;
344
- case '2':
345
- setMode(instance, instance.currentIndex, 'split');
346
- break;
347
- case '3':
348
- setMode(instance, instance.currentIndex, 'after');
349
- break;
350
396
  }
351
397
  });
352
398
 
353
- instance.wrapper.setAttribute('tabindex', '0');
354
- instance.wrapper.setAttribute('role', 'application');
355
- instance.wrapper.setAttribute('aria-label', 'Before and after image comparison');
356
- }
399
+ // Individual items: Left/Right arrows move slider
400
+ instance.items.forEach((item, itemIndex) => {
401
+ const imageWrapper = item.querySelector('[data-hs-ba="image-wrapper"]');
402
+ if (!imageWrapper) return;
403
+
404
+ imageWrapper.addEventListener("keydown", (e) => {
405
+ // Only respond when the image wrapper itself is focused
406
+ if (e.target !== imageWrapper) return;
407
+ // Only work in split mode
408
+ if (instance.mode !== "split") return;
409
+
410
+ switch (e.key) {
411
+ case "ArrowLeft":
412
+ e.preventDefault();
413
+ const newPosLeft = Math.max(
414
+ 0,
415
+ instance.sliderPosition - config.keyboardStep,
416
+ );
417
+ updateSliderPosition(instance, instance.currentIndex, newPosLeft);
418
+ instance.sliderPosition = newPosLeft;
419
+ const sliderLeft = item.querySelector('[data-hs-ba="slider"]');
420
+ if (sliderLeft) sliderLeft.style.left = `${newPosLeft}%`;
421
+ break;
422
+ case "ArrowRight":
423
+ e.preventDefault();
424
+ const newPosRight = Math.min(
425
+ 100,
426
+ instance.sliderPosition + config.keyboardStep,
427
+ );
428
+ updateSliderPosition(instance, instance.currentIndex, newPosRight);
429
+ instance.sliderPosition = newPosRight;
430
+ const sliderRight = item.querySelector('[data-hs-ba="slider"]');
431
+ if (sliderRight) sliderRight.style.left = `${newPosRight}%`;
432
+ break;
433
+ }
434
+ });
357
435
 
358
- function init() {
359
- const wrappers = document.querySelectorAll('[data-hs-ba="wrapper"]');
360
-
361
- wrappers.forEach(wrapper => {
362
- initInstance(wrapper);
436
+ // Make image wrapper focusable and accessible
437
+ imageWrapper.setAttribute("tabindex", "0");
438
+ imageWrapper.setAttribute("role", "img");
439
+ imageWrapper.setAttribute(
440
+ "aria-label",
441
+ `Before and after comparison image, use arrow keys to adjust slider`,
442
+ );
363
443
  });
364
444
 
365
- return { result: 'before-after initialized' };
445
+ // Set up main wrapper accessibility
446
+ instance.wrapper.setAttribute("tabindex", "0");
447
+ instance.wrapper.setAttribute("role", "application");
448
+ instance.wrapper.setAttribute(
449
+ "aria-label",
450
+ "Before and after image comparison, use arrow keys to navigate items",
451
+ );
366
452
  }
367
453
 
368
- if (typeof window !== 'undefined') {
454
+ if (typeof window !== "undefined") {
369
455
  window.hsmain = window.hsmain || {};
370
456
  window.hsmain.utilBeforeAfter = {
371
457
  init,
@@ -379,9 +465,56 @@ export function init() {
379
465
  setMode: (instanceId, mode) => {
380
466
  const instance = instances[instanceId];
381
467
  if (instance) setMode(instance, instance.currentIndex, mode);
382
- }
468
+ },
469
+ };
470
+ }
471
+
472
+ // Initialize - prevent duplicate initialization
473
+ const wrappers = document.querySelectorAll(
474
+ '[data-hs-ba="wrapper"]:not([data-initialized])',
475
+ );
476
+
477
+ if (wrappers.length === 0) {
478
+ return {
479
+ result: "before-after already initialized",
480
+ destroy: () => {},
383
481
  };
384
482
  }
385
483
 
386
- return init();
387
- }
484
+ wrappers.forEach((wrapper) => {
485
+ wrapper.setAttribute("data-initialized", "true");
486
+ initInstance(wrapper);
487
+ });
488
+
489
+ // Return destroy function and result
490
+ return {
491
+ result: "before-after initialized",
492
+ destroy: () => {
493
+ // Clean up all instances
494
+ instances.forEach((instance) => {
495
+ // Remove event listeners from wrapper
496
+ const newWrapper = instance.wrapper.cloneNode(true);
497
+ instance.wrapper.parentNode.replaceChild(newWrapper, instance.wrapper);
498
+
499
+ // Clear cached elements
500
+ instance.cachedElements.clear();
501
+ });
502
+
503
+ // Remove initialized markers
504
+ document
505
+ .querySelectorAll('[data-hs-ba="wrapper"][data-initialized]')
506
+ .forEach((wrapper) => {
507
+ wrapper.removeAttribute("data-initialized");
508
+ });
509
+
510
+ // Reset module state
511
+ instances = [];
512
+ currentSlideIndex = {};
513
+
514
+ // Clean up window object
515
+ if (window.hsmain && window.hsmain.utilBeforeAfter) {
516
+ delete window.hsmain.utilBeforeAfter;
517
+ }
518
+ },
519
+ };
520
+ }