@mozert-studio/mozert-slider 0.0.0-stage → 1.1.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.
@@ -0,0 +1,2615 @@
1
+ /*!
2
+ * Mozert 1.1.0
3
+ * A dependency-free, responsive, touch-friendly carousel / slider.
4
+ * MIT License
5
+ */
6
+ const Mozert = (function () {
7
+ const VERSION = '1.1.0';
8
+
9
+ const DEFAULTS = {
10
+ accessibility: true,
11
+ adaptiveHeight: false,
12
+ appendArrows: null,
13
+ appendDots: null,
14
+ arrows: true,
15
+ asNavFor: null,
16
+ autoplay: false,
17
+ disableOnInteraction: false,
18
+ autoplayReverse: false,
19
+ autoplaySpeed: 3000,
20
+ centerMode: false,
21
+ centerPadding: '50px',
22
+ cssEase: 'ease',
23
+ customPaging: null,
24
+ dots: false,
25
+ dotsClass: 'mozert-dots',
26
+ dragThreshold: 6,
27
+ draggable: true,
28
+ easing: 'linear',
29
+ edgeFriction: 0.15,
30
+ edgePadding: 0,
31
+ effect: 'slide',
32
+ fade: false,
33
+ focusOnSelect: false,
34
+ freeMode: false,
35
+ infinite: true,
36
+ initialSlide: 0,
37
+ labels: null,
38
+ lazyLoad: 'ondemand',
39
+ mobileFirst: false,
40
+ nextArrow: null,
41
+ pauseOnDotsHover: false,
42
+ pauseOnFocus: true,
43
+ pauseOnHover: true,
44
+ prevArrow: null,
45
+ respectReducedMotion: true,
46
+ respondTo: 'window',
47
+ responsive: null,
48
+ rows: 1,
49
+ rtl: false,
50
+ slide: '',
51
+ slideWidth: 0,
52
+ slidesPerRow: 1,
53
+ slidesToScroll: 1,
54
+ slidesToShow: 1,
55
+ spaceBetween: 0,
56
+ speed: 300,
57
+ stopOnLastSlide: false,
58
+ swipe: true,
59
+ swipeToSlide: false,
60
+ touchMove: true,
61
+ touchRatio: 1,
62
+ touchThreshold: 5,
63
+ useCSS: true,
64
+ useTransform: true,
65
+ variableWidth: false,
66
+ vertical: false,
67
+ verticalSwiping: false,
68
+ waitForAnimate: true,
69
+ zIndex: 1000,
70
+ on: null,
71
+ };
72
+
73
+ const LABELS = { slide: 'Slide {n} of {total}', prev: 'Previous slide', next: 'Next slide', goTo: 'Go to slide {n}' };
74
+ const DEFAULT_PREV = '<button type="button" class="mozert-prev" aria-label="Previous slide">Previous</button>';
75
+ const DEFAULT_NEXT = '<button type="button" class="mozert-next" aria-label="Next slide">Next</button>';
76
+ const FOCUSABLE = 'a[href],button,input,select,textarea,[tabindex],[contenteditable="true"]';
77
+ const STASH_ATTRS = ['class', 'style', 'role', 'aria-hidden', 'aria-label', 'aria-roledescription', 'data-mozert-index'];
78
+
79
+ const STACKED = ['fade', 'flip', 'cube', 'cards', 'creative'];
80
+ const EASINGS = {
81
+ linear: (t) => t,
82
+ swing: (t) => 0.5 - Math.cos(t * Math.PI) / 2,
83
+ easeInQuad: (t) => t * t,
84
+ easeOutQuad: (t) => t * (2 - t),
85
+ easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
86
+ easeInCubic: (t) => t * t * t,
87
+ easeOutCubic: (t) => --t * t * t + 1,
88
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
89
+ easeInOutQuart: (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t),
90
+ };
91
+
92
+ const registry = {
93
+ get: (e) => e._mozert || undefined,
94
+ set: (e, v) => { e._mozert = v; },
95
+ delete: (e) => { delete e._mozert; },
96
+ };
97
+ const isEl = (v) => typeof Element !== 'undefined' && v instanceof Element;
98
+ const clamp = (v, a, b) => Math.min(b, Math.max(a, v));
99
+
100
+ function htmlToEl(html) {
101
+ const t = document.createElement('template');
102
+ t.innerHTML = String(html).trim();
103
+ return t.content.firstElementChild;
104
+ }
105
+
106
+ function resolveNode(v) {
107
+ if (!v) return null;
108
+ if (isEl(v)) return v;
109
+ if (typeof v === 'string') {
110
+ const t = v.trim();
111
+ return t.charAt(0) === '<' ? htmlToEl(t) : document.querySelector(t);
112
+ }
113
+ if (v[0] && isEl(v[0])) return v[0];
114
+ return null;
115
+ }
116
+
117
+ function parseData(el) {
118
+ const raw = el.getAttribute('data-mozert');
119
+ if (!raw) return {};
120
+ try {
121
+ return JSON.parse(raw);
122
+ } catch (e) {
123
+ console.warn('Mozert: could not parse data-mozert JSON', e);
124
+ return {};
125
+ }
126
+ }
127
+
128
+ class Mozert {
129
+ constructor(el, options = {}) {
130
+ if (typeof el === 'string') el = document.querySelector(el);
131
+ if (!isEl(el)) throw new TypeError('Mozert: a valid element or selector is required');
132
+ const existing = registry.get(el);
133
+ if (existing) return existing;
134
+
135
+ this.el = el;
136
+ const _o = Object.assign({}, options, parseData(el));
137
+ Mozert._boot.forEach((f) => f(el, _o));
138
+ this._base = Object.assign({}, DEFAULTS, options, parseData(el));
139
+ this.options = this._base;
140
+ this._dormant = false;
141
+ this.active = false;
142
+ this.canNav = false;
143
+ this.current = 0;
144
+ this.animating = false;
145
+ this.n = 0;
146
+
147
+ this._handlers = {};
148
+ this._sources = [];
149
+ this._filter = null;
150
+ this._stash = new WeakMap();
151
+ this._logical = 0;
152
+ this._t = 0;
153
+ this._cur = 0;
154
+ this._bp = null;
155
+ this._off = [];
156
+ this._life = [];
157
+ this._pause = new Set();
158
+ this._fxHooks = [];
159
+ this._mi = [];
160
+ this._release = null;
161
+ this._lockDrag = false;
162
+ this._autoOn = false;
163
+ this._dir = 1;
164
+ this._built = false;
165
+ this._initDone = false;
166
+ this._origDir = el.getAttribute('dir');
167
+ this._lastW = el.clientWidth;
168
+
169
+ registry.set(el, this);
170
+ this._collect();
171
+ this._bindLife();
172
+ this._apply(true);
173
+ }
174
+
175
+ /* ------------------------------------------------------------------ */
176
+ /* Public API */
177
+ /* ------------------------------------------------------------------ */
178
+
179
+ /** Read-only snapshot of the slider's current state. */
180
+ getState() {
181
+ return {
182
+ active: this.active,
183
+ current: this.current,
184
+ count: this.n,
185
+ total: this.trackLen || 0,
186
+ inner: this.Lc || 0,
187
+ clones: this.cloneCount || 0,
188
+ offset: this._cur || 0,
189
+ animating: this.animating,
190
+ canNavigate: this.canNav,
191
+ playing: this.isPlaying(),
192
+ breakpoint: this._bp,
193
+ };
194
+ }
195
+
196
+ slideCount() {
197
+ return this.n;
198
+ }
199
+
200
+ currentSlide() {
201
+ return this.current;
202
+ }
203
+
204
+ isPlaying() {
205
+ return !!(this.active && this._autoOn && !this._pause.has('user'));
206
+ }
207
+
208
+ get _c() {
209
+ return this.cloneCount;
210
+ }
211
+
212
+ next() {
213
+ if (!this.active || !this.canNav) return this;
214
+ this._slideTo(this.current + this.s.slidesToScroll);
215
+ return this;
216
+ }
217
+
218
+ prev() {
219
+ if (!this.active || !this.canNav) return this;
220
+ this._slideTo(this.current - this.s.slidesToScroll);
221
+ return this;
222
+ }
223
+
224
+ goTo(index, dontAnimate = false) {
225
+ if (!this.active || !this.canNav) return this;
226
+ index = parseInt(index, 10);
227
+ if (isNaN(index)) return this;
228
+ this._slideTo(this.s.infinite ? this._norm(index) : clamp(index, 0, Math.max(0, this.n - 1)), { animate: !dontAnimate });
229
+ return this;
230
+ }
231
+
232
+ play() {
233
+ const was = this.isPlaying();
234
+ this._autoOn = true;
235
+ this._pause.delete('user');
236
+ this._schedule();
237
+ this._live();
238
+ if (!was) this.emit('autoplayStart');
239
+ return this;
240
+ }
241
+
242
+ pause() {
243
+ const was = this.isPlaying();
244
+ this._autoOn = false;
245
+ this._pause.add('user');
246
+ clearTimeout(this._apTimer);
247
+ cancelAnimationFrame(this._apRaf);
248
+ this._live();
249
+ if (was) this.emit('autoplayStop');
250
+ return this;
251
+ }
252
+
253
+ getSlides() {
254
+ return this._units ? this._units.slice() : this._activeItems();
255
+ }
256
+
257
+ getOption(name) {
258
+ return this.options[name];
259
+ }
260
+
261
+ setOption(name, value, refresh) {
262
+ if (name && typeof name === 'object') {
263
+ refresh = value;
264
+ Object.assign(this._base, name);
265
+ } else {
266
+ this._base[name] = value;
267
+ }
268
+ if (refresh === false) {
269
+ // Only re-resolve settings that are read live (autoplay, pauseOn*, ...).
270
+ this._applyLive();
271
+ } else {
272
+ this._apply(false);
273
+ }
274
+ return this;
275
+ }
276
+
277
+ _applyLive() {
278
+ const r = this._resolve();
279
+ this.options = r.s;
280
+ if (this.active) {
281
+ this.s = this._prepare(r.s);
282
+ this._autoOn = !!this.s.autoplay && this.canNav;
283
+ if (this._autoOn) this._pause.delete('user');
284
+ this._live();
285
+ this._schedule();
286
+ }
287
+ }
288
+
289
+ add(markup, index, addBefore) {
290
+ const node = resolveNode(typeof markup === 'string' && markup.trim().charAt(0) !== '<' ? `<div>${markup}</div>` : markup);
291
+ if (!node) return false;
292
+ if (typeof index === 'boolean') {
293
+ addBefore = index;
294
+ index = null;
295
+ }
296
+ const items = this._activeItems();
297
+ if (index != null && (index < 0 || index >= items.length)) return false;
298
+ if (index == null || !items.length) {
299
+ if (addBefore) this._sources.unshift(node);
300
+ else this._sources.push(node);
301
+ } else {
302
+ const at = this._sources.indexOf(items[index]);
303
+ this._sources.splice(at + (addBefore ? 0 : 1), 0, node);
304
+ }
305
+ this._refreshKeep();
306
+ return this;
307
+ }
308
+
309
+ remove(index, removeBefore) {
310
+ const items = this._activeItems();
311
+ if (typeof index === 'boolean') {
312
+ removeBefore = index;
313
+ index = removeBefore ? 0 : items.length - 1;
314
+ } else {
315
+ index = removeBefore ? index - 1 : index;
316
+ }
317
+ if (index == null || index < 0 || index >= items.length) return false;
318
+ const node = items[index];
319
+ this._sources.splice(this._sources.indexOf(node), 1);
320
+ this._restoreItem(node);
321
+ if (node.parentNode) node.parentNode.removeChild(node);
322
+ if (index < this.current) this.current -= 1;
323
+ this._refreshKeep();
324
+ return this;
325
+ }
326
+
327
+ removeAll() {
328
+ this._activeItems().forEach((node) => {
329
+ this._sources.splice(this._sources.indexOf(node), 1);
330
+ this._restoreItem(node);
331
+ if (node.parentNode) node.parentNode.removeChild(node);
332
+ });
333
+ this.current = 0;
334
+ this._refreshKeep();
335
+ return this;
336
+ }
337
+
338
+ filter(selectorOrFn) {
339
+ this._filter = selectorOrFn || null;
340
+ this.current = 0;
341
+ this._refreshKeep();
342
+ return this;
343
+ }
344
+
345
+ unfilter() {
346
+ return this.filter(null);
347
+ }
348
+
349
+ /** Re-read options and rebuild everything, keeping the current slide. */
350
+ refresh() {
351
+ this._refreshKeep();
352
+ this.emit('reInit');
353
+ return this;
354
+ }
355
+
356
+ /** Re-measure and reposition, e.g. after the container was resized by script. */
357
+ setPosition() {
358
+ if (!this.active) return this;
359
+ this._layout();
360
+ this._paintCurrent(false);
361
+ this.emit('setPosition');
362
+ return this;
363
+ }
364
+
365
+ destroy() {
366
+ this.emit('destroy');
367
+ if (this.active) this._teardown();
368
+ this._sources.forEach((n) => this.el.appendChild(n));
369
+ this.active = false;
370
+ this._life.forEach((fn) => fn());
371
+ this._life = [];
372
+ if (this._ro) this._ro.disconnect();
373
+ registry.delete(this.el);
374
+ this._dormant = false;
375
+ this._handlers = {};
376
+ return this;
377
+ }
378
+
379
+ on(name, fn) {
380
+ (this._handlers[name] = this._handlers[name] || []).push(fn);
381
+ return this;
382
+ }
383
+
384
+ once(name, fn) {
385
+ const wrap = (...a) => {
386
+ this.off(name, wrap);
387
+ fn.apply(this, a);
388
+ };
389
+ return this.on(name, wrap);
390
+ }
391
+
392
+ one(name, fn) {
393
+ return this.once(name, fn);
394
+ }
395
+
396
+ off(name, fn) {
397
+ if (!name) this._handlers = {};
398
+ else if (!fn) delete this._handlers[name];
399
+ else this._handlers[name] = (this._handlers[name] || []).filter((f) => f !== fn);
400
+ return this;
401
+ }
402
+
403
+ emit(name, ...args) {
404
+ (this._handlers[name] || []).slice().forEach((fn) => fn.call(this, this, ...args));
405
+ const cb = this._base.on && this._base.on[name];
406
+ if (typeof cb === 'function') cb.call(this, this, ...args);
407
+ this.el.dispatchEvent(new CustomEvent('mozert:' + name, { bubbles: true, detail: { slider: this, args } }));
408
+ if (Mozert._bridge) Mozert._bridge(this, name, args);
409
+ }
410
+
411
+ static get(el) {
412
+ if (typeof el === 'string') el = document.querySelector(el);
413
+ return (isEl(el) && registry.get(el)) || null;
414
+ }
415
+
416
+ static initAll(root, options) {
417
+ const scope = root && root.querySelectorAll ? root : document;
418
+ const found = Array.from(scope.querySelectorAll('[data-mozert]'));
419
+ if (scope !== document && scope.matches && scope.matches('[data-mozert]')) found.unshift(scope);
420
+ return found.map((n) => Mozert.get(n) || new Mozert(n, options));
421
+ }
422
+
423
+ /* ------------------------------------------------------------------ */
424
+ /* Settings and responsive handling */
425
+ /* ------------------------------------------------------------------ */
426
+
427
+ _respondWidth() {
428
+ const mode = this._base.respondTo;
429
+ const w = window.innerWidth;
430
+ if (mode === 'slider') return this.el.clientWidth;
431
+ if (mode === 'min') return Math.min(w, this.el.clientWidth);
432
+ return w;
433
+ }
434
+
435
+ _resolve() {
436
+ const base = this._base;
437
+ const list = Array.isArray(base.responsive) ? base.responsive.filter((r) => r && typeof r.breakpoint === 'number') : [];
438
+ const w = this._respondWidth();
439
+ let hit = null;
440
+ if (base.mobileFirst) {
441
+ list.sort((a, b) => a.breakpoint - b.breakpoint).forEach((r) => {
442
+ if (w >= r.breakpoint) hit = r;
443
+ });
444
+ } else {
445
+ list.sort((a, b) => b.breakpoint - a.breakpoint).forEach((r) => {
446
+ if (w <= r.breakpoint) hit = r;
447
+ });
448
+ }
449
+ if (hit && hit.settings === 'destroy') return { bp: hit.breakpoint, dormant: true, s: Object.assign({}, base) };
450
+ return { bp: hit ? hit.breakpoint : null, dormant: false, s: Object.assign({}, base, hit ? hit.settings : {}) };
451
+ }
452
+
453
+ _prepare(raw) {
454
+ const s = Object.assign({}, raw);
455
+ s.slidesToShow = Math.max(1, parseFloat(s.slidesToShow) || 1);
456
+ s.slidesToScroll = Math.max(1, parseInt(s.slidesToScroll, 10) || 1);
457
+ s.rows = Math.max(1, parseInt(s.rows, 10) || 1);
458
+ s.slidesPerRow = Math.max(1, parseInt(s.slidesPerRow, 10) || 1);
459
+ s.effect = s.fade ? 'fade' : s.effect || 'slide';
460
+ s.fade = s.effect === 'fade';
461
+ s.stacked = STACKED.indexOf(s.effect) > -1;
462
+ if (s.stacked) {
463
+ s.slidesToShow = 1;
464
+ s.slidesToScroll = 1;
465
+ }
466
+ if (s.effect === 'coverflow') s.centerMode = true;
467
+ if (s.freeMode || s.virtual || s.bleed) s.infinite = false;
468
+ if (s.marquee) {
469
+ s.infinite = true;
470
+ s.arrows = false;
471
+ s.dots = false;
472
+ s.autoplay = false;
473
+ }
474
+ s.spaceBetween = parseFloat(s.spaceBetween) || 0;
475
+ if (s.respectReducedMotion && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
476
+ s.speed = 0;
477
+ s.autoplay = false;
478
+ }
479
+ return s;
480
+ }
481
+
482
+ _apply(first) {
483
+ const r = this._resolve();
484
+ const changed = r.bp !== this._bp;
485
+ this._bp = r.bp;
486
+ this._raw = r.s;
487
+ this.options = r.s;
488
+ this._dormant = !!r.dormant;
489
+ if (r.dormant) {
490
+ if (this.active) {
491
+ this._teardown();
492
+ this.active = false;
493
+ this.emit('destroy');
494
+ }
495
+ if (changed && !first) this.emit('breakpoint', r.bp);
496
+ return;
497
+ }
498
+ this.s = this._prepare(r.s);
499
+ if (this.active) this._teardown();
500
+ this._build(!first);
501
+ if (changed && !first) this.emit('breakpoint', r.bp);
502
+ }
503
+
504
+ _refreshKeep() {
505
+ if (!this.active) {
506
+ this._apply(false);
507
+ return;
508
+ }
509
+ this._teardown();
510
+ this._build(true);
511
+ }
512
+
513
+ _bindLife() {
514
+ let timer;
515
+ const onResize = () => {
516
+ clearTimeout(timer);
517
+ timer = setTimeout(() => this._onResize(), 40);
518
+ };
519
+ window.addEventListener('resize', onResize);
520
+ window.addEventListener('orientationchange', onResize);
521
+ const onVis = () => {
522
+ if (document.hidden) this._holdOn('hidden');
523
+ else this._holdOff('hidden');
524
+ };
525
+ document.addEventListener('visibilitychange', onVis);
526
+ this._life.push(() => {
527
+ clearTimeout(timer);
528
+ window.removeEventListener('resize', onResize);
529
+ window.removeEventListener('orientationchange', onResize);
530
+ document.removeEventListener('visibilitychange', onVis);
531
+ });
532
+ if (typeof ResizeObserver !== 'undefined') {
533
+ this._ro = new ResizeObserver(() => {
534
+ const w = this.el.clientWidth;
535
+ if (w !== this._lastW) {
536
+ this._lastW = w;
537
+ onResize();
538
+ }
539
+ });
540
+ this._ro.observe(this.el);
541
+ }
542
+ }
543
+
544
+ _onResize() {
545
+ const r = this._resolve();
546
+ const must = r.bp !== this._bp || (r.dormant && this.active) || (!r.dormant && !this.active);
547
+ if (must) {
548
+ this._apply(false);
549
+ return;
550
+ }
551
+ if (!this.active) return;
552
+ this._layout();
553
+ if (this._computeCan() !== this.canNav) {
554
+ this._refreshKeep();
555
+ return;
556
+ }
557
+ this._paintCurrent(false);
558
+ this.emit('setPosition');
559
+ }
560
+
561
+ /* ------------------------------------------------------------------ */
562
+ /* DOM build / teardown */
563
+ /* ------------------------------------------------------------------ */
564
+
565
+ _collect() {
566
+ const kids = Array.from(this.el.children);
567
+ const sel = this._base.slide;
568
+ this._sources = sel ? kids.filter((k) => k.matches(sel)) : kids;
569
+ }
570
+
571
+ _activeItems() {
572
+ const f = this._filter;
573
+ if (!f) return this._sources.slice();
574
+ return this._sources.filter((el, i) => (typeof f === 'function' ? f.call(el, el, i) : el.matches(f)));
575
+ }
576
+
577
+ _remember(el) {
578
+ if (this._stash.has(el)) return;
579
+ const o = {};
580
+ STASH_ATTRS.forEach((a) => (o[a] = el.getAttribute(a)));
581
+ this._stash.set(el, o);
582
+ }
583
+
584
+ _restoreItem(el) {
585
+ const o = this._stash.get(el);
586
+ if (!o) return;
587
+ STASH_ATTRS.forEach((a) => (o[a] === null ? el.removeAttribute(a) : el.setAttribute(a, o[a])));
588
+ el.querySelectorAll('[data-mozert-tabindex]').forEach((f) => {
589
+ const v = f.getAttribute('data-mozert-tabindex');
590
+ if (v === '') f.removeAttribute('tabindex');
591
+ else f.setAttribute('tabindex', v);
592
+ f.removeAttribute('data-mozert-tabindex');
593
+ });
594
+ el.querySelectorAll('[data-mozert-bg]').forEach((b) => b.removeAttribute('data-mozert-bg'));
595
+ this._stash.delete(el);
596
+ }
597
+
598
+ _label(key, vars) {
599
+ const l = Object.assign({}, LABELS, this.s && this.s.labels);
600
+ return String(l[key]).replace(/\{(\w+)\}/g, (m, k) => (vars && vars[k] !== undefined ? vars[k] : m));
601
+ }
602
+
603
+ _px(v) {
604
+ const p = String(v || '0');
605
+ if (p.indexOf('%') > -1) return (parseFloat(p) / 100) * this.el.clientWidth;
606
+ return parseFloat(p) || 0;
607
+ }
608
+
609
+ _padPx() {
610
+ return this._px(this.s.centerPadding);
611
+ }
612
+
613
+ _edgePad() {
614
+ const s = this.s;
615
+ return s.centerMode ? this._padPx() : this._px(s.edgePadding);
616
+ }
617
+
618
+ /** Works out how many slides fit (can be fractional) before the first layout. */
619
+ _calcShow() {
620
+ const s = this.s;
621
+ let show = s.slidesToShow;
622
+ if (s.slideWidth && !s.vertical && !s.variableWidth) {
623
+ const inner = Math.max(1, (this.list ? this.list.clientWidth : this.el.clientWidth) - 2 * this._edgePad());
624
+ show = Math.max(1, (inner + s.spaceBetween) / (parseFloat(s.slideWidth) + s.spaceBetween));
625
+ }
626
+ this._show = show;
627
+ this._showC = Math.ceil(show - 1e-6);
628
+ }
629
+
630
+ _build(keep) {
631
+ const s = this.s;
632
+ const el = this.el;
633
+ const items = this._activeItems();
634
+ // Slides hidden by filter() leave the DOM until unfilter() (or destroy) puts them back.
635
+ this._sources.forEach((n) => {
636
+ if (items.indexOf(n) === -1 && n.parentNode === el) el.removeChild(n);
637
+ });
638
+
639
+ el.classList.add('mozert-slider', 'mozert-initialized');
640
+ el.classList.toggle('mozert-vertical', !!s.vertical);
641
+ el.classList.toggle('mozert-rtl', !!s.rtl);
642
+ el.classList.toggle('mozert-fade', !!s.fade);
643
+ el.classList.toggle('mozert-fx', s.effect !== 'slide' && !s.fade);
644
+ if (s.effect !== 'slide') el.classList.add('mozert-effect-' + s.effect);
645
+ el.classList.toggle('mozert-center', !!s.centerMode);
646
+ el.classList.toggle('mozert-adaptive', !!s.adaptiveHeight);
647
+ el.classList.toggle('mozert-draggable', !!(s.draggable || s.swipe));
648
+ if (s.rtl) el.setAttribute('dir', 'rtl');
649
+ el.setAttribute('role', 'region');
650
+ el.setAttribute('aria-roledescription', 'carousel');
651
+
652
+ this.list = document.createElement('div');
653
+ this.list.className = 'mozert-list';
654
+ this.track = document.createElement('div');
655
+ this.track.className = 'mozert-track';
656
+ this.list.appendChild(this.track);
657
+ el.appendChild(this.list);
658
+
659
+ // Group into pages when grid mode is on.
660
+ let units = items;
661
+ if (s.rows > 1 || s.slidesPerRow > 1) {
662
+ units = [];
663
+ const per = s.rows * s.slidesPerRow;
664
+ for (let i = 0; i < items.length; i += per) {
665
+ const page = document.createElement('div');
666
+ page.className = 'mozert-page';
667
+ for (let r = 0; r < s.rows; r++) {
668
+ const row = document.createElement('div');
669
+ row.className = 'mozert-row';
670
+ for (let c = 0; c < s.slidesPerRow; c++) {
671
+ const it = items[i + r * s.slidesPerRow + c];
672
+ if (!it) break;
673
+ this._remember(it);
674
+ it.classList.add('mozert-cell');
675
+ it.style.width = 100 / s.slidesPerRow + '%';
676
+ row.appendChild(it);
677
+ }
678
+ if (row.children.length) page.appendChild(row);
679
+ }
680
+ units.push(page);
681
+ }
682
+ }
683
+ this._items = items;
684
+ this._units = units;
685
+ this.n = units.length;
686
+ this._calcShow();
687
+
688
+ units.forEach((u, i) => {
689
+ this._remember(u);
690
+ u.classList.add('mozert-slide');
691
+ u.setAttribute('data-mozert-index', i);
692
+ u.setAttribute('role', 'group');
693
+ u.setAttribute('aria-roledescription', 'slide');
694
+ u.setAttribute('aria-label', this._label('slide', { n: i + 1, total: units.length }));
695
+ u.querySelectorAll('[data-lazy-bg]').forEach((b, k) => b.setAttribute('data-mozert-bg', String(k)));
696
+ this.track.appendChild(u);
697
+ });
698
+ this.dom = units.slice();
699
+ this.cloneCount = 0;
700
+
701
+ // First pass (only for variable widths) so we know whether navigation is needed.
702
+ if (s.variableWidth) this._layout();
703
+ this.canNav = this._computeCan();
704
+
705
+ if (s.infinite && this.canNav && !s.stacked) this._addClones();
706
+
707
+ this._layout();
708
+ this.maxIndex = this._maxIndex();
709
+ this.current = clamp(keep ? this.current : parseInt(s.initialSlide, 10) || 0, 0, s.infinite ? Math.max(0, this.n - 1) : this.maxIndex);
710
+ if (!this.canNav) this.current = 0;
711
+ this._logical = this.current;
712
+
713
+ if (!this.canNav) el.classList.add('mozert-static');
714
+ else el.classList.remove('mozert-static');
715
+
716
+ this._buildArrows();
717
+ this._buildDots();
718
+ this._bindEvents();
719
+ this.active = true;
720
+ this._built = true;
721
+ this._paintCurrent(false);
722
+ if (s.fade) {
723
+ // Show the first slide immediately, then switch the cross-fade on.
724
+ this.dom.forEach((e) => (e.style.transition = 'none'));
725
+ void this.track.offsetWidth;
726
+ this.dom.forEach((e) => (e.style.transition = s.speed > 0 ? 'opacity ' + s.speed + 'ms ' + s.cssEase : 'none'));
727
+ }
728
+ this._updateNav();
729
+ this._lazy();
730
+ this._mi = Mozert._mods.map((m) => (m.create ? m.create(this) : null)).filter(Boolean);
731
+
732
+ this._autoOn = !!s.autoplay && this.canNav;
733
+ if (!this._initDone) {
734
+ this._initDone = true;
735
+ this._autoOn = !!s.autoplay && this.canNav;
736
+ this.emit('init');
737
+ } else if (keep) {
738
+ this.emit('reInit');
739
+ }
740
+ this._live();
741
+ this._schedule();
742
+ this.emit('setPosition');
743
+ }
744
+
745
+ _addClones() {
746
+ const s = this.s;
747
+ const n = this.n;
748
+ const count = s.variableWidth ? n * 2 : this._showC + s.slidesToScroll + (s.centerMode ? 1 : 0);
749
+ this.cloneCount = count;
750
+ const mk = (idx) => {
751
+ const base = this._units[((idx % n) + n) % n];
752
+ const c = base.cloneNode(true);
753
+ c.removeAttribute('id');
754
+ c.querySelectorAll('[id]').forEach((x) => x.removeAttribute('id'));
755
+ c.classList.remove('mozert-active', 'mozert-center');
756
+ c.classList.add('mozert-cloned');
757
+ c.setAttribute('aria-hidden', 'true');
758
+ c.removeAttribute('role');
759
+ c.removeAttribute('aria-label');
760
+ c.removeAttribute('aria-roledescription');
761
+ c.querySelectorAll(FOCUSABLE).forEach((f) => {
762
+ f.setAttribute('data-mozert-tabindex', f.getAttribute('tabindex') || '');
763
+ f.setAttribute('tabindex', '-1');
764
+ });
765
+ return c;
766
+ };
767
+ const before = [];
768
+ const after = [];
769
+ for (let i = -count; i < 0; i++) before.push(mk(i));
770
+ for (let i = 0; i < count; i++) after.push(mk(n + i));
771
+ const first = this._units[0];
772
+ before.forEach((c) => this.track.insertBefore(c, first));
773
+ after.forEach((c) => this.track.appendChild(c));
774
+ this.dom = before.concat(this._units, after);
775
+ }
776
+
777
+ _teardown() {
778
+ this._mi.forEach((m) => m.destroy && m.destroy());
779
+ this._mi = [];
780
+ this._fxHooks = [];
781
+ this._release = null;
782
+ this._lockDrag = false;
783
+ this._off.forEach((fn) => fn());
784
+ this._off = [];
785
+ clearTimeout(this._animTimer);
786
+ clearTimeout(this._apTimer);
787
+ if (this._raf) cancelAnimationFrame(this._raf);
788
+ this._raf = 0;
789
+ this.animating = false;
790
+ const el = this.el;
791
+
792
+ (this._items || []).forEach((it) => {
793
+ this._restoreItem(it);
794
+ // Slides removed through remove()/removeAll() must not come back.
795
+ if (this._sources.indexOf(it) > -1) el.appendChild(it);
796
+ });
797
+ (this._units || []).forEach((u) => {
798
+ if (this._items.indexOf(u) === -1) this._stash.delete(u);
799
+ });
800
+ if (this.list && this.list.parentNode) this.list.parentNode.removeChild(this.list);
801
+ (this._navNodes || []).forEach((fn) => fn());
802
+ this._navNodes = [];
803
+
804
+ Array.from(el.classList)
805
+ .filter((c) => c.indexOf('mozert') === 0)
806
+ .forEach((c) => el.classList.remove(c));
807
+ el.removeAttribute('role');
808
+ el.removeAttribute('aria-roledescription');
809
+ if (this._origDir === null) el.removeAttribute('dir');
810
+ else el.setAttribute('dir', this._origDir);
811
+ this.dom = [];
812
+ this.active = false;
813
+ }
814
+
815
+ /* ------------------------------------------------------------------ */
816
+ /* Measuring */
817
+ /* ------------------------------------------------------------------ */
818
+
819
+ _layout() {
820
+ const s = this.s;
821
+ const v = !!s.vertical;
822
+ const list = this.list;
823
+ const track = this.track;
824
+ const dom = this.dom;
825
+ this._calcShow();
826
+ const show = this._show;
827
+ const gap = s.spaceBetween;
828
+ const pad = this._edgePad();
829
+ const mProp = v ? 'marginBottom' : s.rtl ? 'marginLeft' : 'marginRight';
830
+ list.style.padding = v ? pad + 'px 0' : '0 ' + pad + 'px';
831
+
832
+ if (v) {
833
+ dom.forEach((e) => (e.style.height = ''));
834
+ track.style.height = '';
835
+ let h = 0;
836
+ this._units.forEach((u) => (h = Math.max(h, u.offsetHeight)));
837
+ this.slideSize = h;
838
+ dom.forEach((e) => {
839
+ e.style.height = h + 'px';
840
+ e.style[mProp] = gap ? gap + 'px' : '';
841
+ });
842
+ this.Lc = h * show + gap * (show - 1);
843
+ list.style.height = this.Lc + 2 * pad + 'px';
844
+ this.trackLen = (h + gap) * dom.length - gap;
845
+ track.style.height = this.trackLen + gap + 'px';
846
+ } else {
847
+ this.Lc = Math.max(0, list.clientWidth - 2 * pad);
848
+ if (!s.variableWidth) {
849
+ const w = s.slideWidth ? parseFloat(s.slideWidth) : (this.Lc - gap * (show - 1)) / show;
850
+ this.slideSize = w;
851
+ dom.forEach((e) => {
852
+ e.style.width = w + 'px';
853
+ e.style[mProp] = gap ? gap + 'px' : '';
854
+ });
855
+ this.trackLen = (w + gap) * dom.length - gap;
856
+ track.style.width = this.trackLen + gap + 'px';
857
+ } else {
858
+ dom.forEach((e) => (e.style[mProp] = gap ? gap + 'px' : ''));
859
+ track.style.width = '100000px';
860
+ }
861
+ }
862
+
863
+ const tr = track.getBoundingClientRect();
864
+ this._mags = [];
865
+ this._sizes = [];
866
+ dom.forEach((e) => {
867
+ const r = e.getBoundingClientRect();
868
+ if (v) {
869
+ this._mags.push(r.top - tr.top);
870
+ this._sizes.push(r.height);
871
+ } else if (s.rtl) {
872
+ this._mags.push(tr.right - r.right);
873
+ this._sizes.push(r.width);
874
+ } else {
875
+ this._mags.push(r.left - tr.left);
876
+ this._sizes.push(r.width);
877
+ }
878
+ });
879
+ if (!v && s.variableWidth) {
880
+ let end = 0;
881
+ this._mags.forEach((m, i) => (end = Math.max(end, m + this._sizes[i])));
882
+ this.trackLen = Math.ceil(end);
883
+ track.style.width = this.trackLen + 'px';
884
+ }
885
+ const cc = this.cloneCount;
886
+ const last = cc + this.n - 1;
887
+ this._realLen = this.n ? this._mags[last] + this._sizes[last] - this._mags[cc] : 0;
888
+
889
+ if (s.stacked) {
890
+ const sz = this.slideSize;
891
+ dom.forEach((e, i) => {
892
+ e.style.position = 'relative';
893
+ if (v) e.style.top = -i * sz + 'px';
894
+ else e.style.left = (s.rtl ? i * sz : -i * sz) + 'px';
895
+ });
896
+ }
897
+ this._adaptHeight();
898
+ this._arrowY();
899
+ this.maxIndex = this._maxIndex();
900
+ }
901
+
902
+ _computeCan() {
903
+ const s = this.s;
904
+ if (s.variableWidth) return this.n > 1 && this._realLen > this.Lc + 1;
905
+ return this.n > this._show + 1e-6;
906
+ }
907
+
908
+ _maxIndex() {
909
+ const s = this.s;
910
+ if (s.infinite || s.stacked) return Math.max(0, this.n - 1);
911
+ if (s.centerMode) return Math.max(0, this.n - 1);
912
+ if (s.variableWidth) {
913
+ const need = this._realLen - this.Lc - 0.5;
914
+ if (need <= 0) return 0;
915
+ for (let i = 0; i < this.n; i++) if (this._mags[this.cloneCount + i] - this._mags[this.cloneCount] >= need) return i;
916
+ return this.n - 1;
917
+ }
918
+ return Math.max(0, Math.ceil(this.n - this._show - 1e-6));
919
+ }
920
+
921
+ _adaptHeight() {
922
+ const s = this.s;
923
+ if (!s.adaptiveHeight || s.vertical || this._show !== 1) return;
924
+ const e = this.dom[this.cloneCount + this._logical] || this.dom[0];
925
+ if (!e) return;
926
+ this.list.style.transition = s.speed > 0 ? 'height ' + s.speed + 'ms ' + s.cssEase : 'none';
927
+ this.list.style.height = e.offsetHeight + 'px';
928
+ this._arrowY(e.offsetHeight);
929
+ }
930
+
931
+ /** Keeps arrows centred on the slides, not on the whole slider (dots/pagination sit below the list). */
932
+ _arrowY(h) {
933
+ const l = this.list;
934
+ if (!l || this.s.vertical) return;
935
+ const hh = h === undefined ? l.offsetHeight : h;
936
+ this.el.style.setProperty('--mozert-arrow-y', l.offsetTop + hh / 2 + 'px');
937
+ }
938
+
939
+ _norm(i) {
940
+ const n = this.n;
941
+ if (this.s.infinite || this.s.stacked) return ((i % n) + n) % n;
942
+ return clamp(i, 0, this.maxIndex);
943
+ }
944
+
945
+ _pos(L) {
946
+ const s = this.s;
947
+ const k = this.cloneCount + L;
948
+ if (this._mags[k] === undefined) return this._t;
949
+ let mag = this._mags[k];
950
+ let c = 0;
951
+ if (s.centerMode) c = (this.Lc - this._sizes[k]) / 2;
952
+ else if (!s.infinite) mag = Math.min(mag, Math.max(0, this.trackLen - this.Lc));
953
+ const dir = s.rtl && !s.vertical ? -1 : 1;
954
+ return dir * (c - mag);
955
+ }
956
+
957
+ /* ------------------------------------------------------------------ */
958
+ /* Movement */
959
+ /* ------------------------------------------------------------------ */
960
+
961
+ _paint(val, animate) {
962
+ const s = this.s;
963
+ const track = this.track;
964
+ this._cur = val;
965
+ if (s.useTransform) track.style.transform = s.vertical ? 'translate3d(0,' + val + 'px,0)' : 'translate3d(' + val + 'px,0,0)';
966
+ else track.style[s.vertical ? 'top' : 'left'] = val + 'px';
967
+ if (this._fxHooks.length) this._fx(this._pvFromT(val), !!animate);
968
+ }
969
+
970
+ /** Visual position in slide units (0 = first slide at the anchor), used by effects, scrollbar and parallax. */
971
+ _pvFromT(t) {
972
+ const s = this.s;
973
+ if (s.variableWidth || !this._mags || !this._mags.length) return this._logical;
974
+ const dir = s.rtl && !s.vertical ? -1 : 1;
975
+ const c = s.centerMode ? (this.Lc - this.slideSize) / 2 : 0;
976
+ const pitch = this.slideSize + s.spaceBetween;
977
+ return (c - dir * t - this._mags[this.cloneCount]) / (pitch || 1);
978
+ }
979
+
980
+ _fx(pv, animate) {
981
+ for (let i = 0; i < this._fxHooks.length; i++) this._fxHooks[i](pv, animate, this.s.speed);
982
+ }
983
+
984
+ /** For modules: run fn(pv, animate, speed) whenever the visual position changes. */
985
+ _onFx(fn) {
986
+ this._fxHooks.push(fn);
987
+ }
988
+
989
+ /** For modules: subscribe to a slider event for the life of this build. */
990
+ _sub(name, fn) {
991
+ this.on(name, fn);
992
+ this._off.push(() => this.off(name, fn));
993
+ }
994
+
995
+ _user() {
996
+ if (this.s.disableOnInteraction && this._autoOn) this.pause();
997
+ }
998
+
999
+ _setPos(t, animate, opts) {
1000
+ const s = this.s;
1001
+ const track = this.track;
1002
+ if (this._raf) cancelAnimationFrame(this._raf);
1003
+ this._raf = 0;
1004
+ if (animate && s.speed > 0) {
1005
+ const dur = opts && opts.duration ? opts.duration : s.speed;
1006
+ if (s.useCSS) {
1007
+ track.style.transition = (s.useTransform ? 'transform' : s.vertical ? 'top' : 'left') + ' ' + dur + 'ms ' + (opts && opts.ease ? opts.ease : s.cssEase);
1008
+ this._paint(t, true);
1009
+ } else {
1010
+ track.style.transition = 'none';
1011
+ const from = this._cur;
1012
+ const ease = typeof s.easing === 'function' ? s.easing : EASINGS[s.easing] || EASINGS.linear;
1013
+ const t0 = performance.now();
1014
+ const step = (now) => {
1015
+ const p = Math.min(1, (now - t0) / s.speed);
1016
+ this._paint(from + (t - from) * ease(p));
1017
+ this._raf = p < 1 ? requestAnimationFrame(step) : 0;
1018
+ };
1019
+ this._raf = requestAnimationFrame(step);
1020
+ }
1021
+ } else {
1022
+ track.style.transition = 'none';
1023
+ this._paint(t);
1024
+ void track.offsetWidth;
1025
+ }
1026
+ this._t = t;
1027
+ }
1028
+
1029
+ _paintCurrent(animate) {
1030
+ if (this.s.stacked) this._fx(this._logical, false);
1031
+ else this._setPos(this._pos(this._logical), animate);
1032
+ this._updateSlideClasses(this._logical);
1033
+ this._adaptHeight();
1034
+ }
1035
+
1036
+ _snapBack() {
1037
+ if (this.s.stacked) this._fx(this._logical, true);
1038
+ else this._setPos(this._pos(this._logical), true);
1039
+ }
1040
+
1041
+ _slideTo(target, opts = {}) {
1042
+ const s = this.s;
1043
+ if (!this.active || !this.canNav) return false;
1044
+ if (this.animating && !opts.fromSync && s.waitForAnimate) return false;
1045
+ let next = target;
1046
+ if (!s.infinite || s.stacked) next = s.stacked ? this._norm(next) : clamp(next, 0, this.maxIndex);
1047
+ const real = this._norm(next);
1048
+ // A synced partner follows the requested slide even when this slider cannot scroll that far
1049
+ // (a thumbnail strip stops at its last full page, but the main slider should still go to the clicked one).
1050
+ const want = s.infinite || s.stacked ? real : clamp(target, 0, this.n - 1);
1051
+ if (next === this.current && !this.animating && !opts.force) {
1052
+ if (!opts.fromSync && want !== next) {
1053
+ this._sel = want;
1054
+ this._updateSlideClasses(this._logical);
1055
+ this._syncNav(want);
1056
+ }
1057
+ return false;
1058
+ }
1059
+
1060
+ const old = this.current;
1061
+ this.emit('beforeChange', old, real);
1062
+ this._sel = opts.fromSync ? (opts.sel !== undefined && opts.sel !== next ? opts.sel : null) : want !== next ? want : null;
1063
+ if (!opts.fromSync) this._syncNav(want);
1064
+ clearTimeout(this._animTimer);
1065
+ this.current = real;
1066
+ this._logical = next;
1067
+ this._updateNav();
1068
+ this._updateSlideClasses(next);
1069
+ this._lazy();
1070
+
1071
+ const animate = opts.animate !== false;
1072
+ if (s.stacked) this._fx(next, animate && s.speed > 0);
1073
+ else this._setPos(this._pos(next), animate);
1074
+ if (!animate || s.speed <= 0) {
1075
+ this._finish();
1076
+ } else {
1077
+ this.animating = true;
1078
+ this._animTimer = setTimeout(() => this._finish(), s.speed);
1079
+ }
1080
+ return true;
1081
+ }
1082
+
1083
+ _finish() {
1084
+ this.animating = false;
1085
+ if (this._logical !== this.current) {
1086
+ this._logical = this.current;
1087
+ if (!this.s.stacked) this._setPos(this._pos(this.current), false);
1088
+ this._updateSlideClasses(this._logical);
1089
+ }
1090
+ this._adaptHeight();
1091
+ this.emit('afterChange', this.current);
1092
+ this._schedule();
1093
+ }
1094
+
1095
+ _updateSlideClasses(L) {
1096
+ const s = this.s;
1097
+ const show = this._show;
1098
+ const showC = this._showC;
1099
+ const half = Math.floor(showC / 2);
1100
+ const t = s.stacked ? 0 : this._pos(L);
1101
+ this.dom.forEach((e, k) => {
1102
+ const l = k - this.cloneCount;
1103
+ let active;
1104
+ let centered = false;
1105
+ if (s.stacked) {
1106
+ active = l === L;
1107
+ } else if (s.variableWidth) {
1108
+ const d = s.rtl ? this._mags[k] - t : this._mags[k] + t;
1109
+ active = d + this._sizes[k] > 0.5 && d < this.Lc - 0.5;
1110
+ centered = s.centerMode && l === L;
1111
+ } else if (s.centerMode) {
1112
+ active = l >= L - half && l <= L + half - (showC % 2 === 0 ? 1 : 0);
1113
+ centered = l === L;
1114
+ } else {
1115
+ active = l >= L && l <= L + showC - 1;
1116
+ }
1117
+ e.classList.toggle('mozert-active', active);
1118
+ e.classList.toggle('mozert-center', centered);
1119
+ e.classList.toggle('mozert-current', l === (this._sel != null ? this._sel : L));
1120
+ e.setAttribute('aria-hidden', active ? 'false' : 'true');
1121
+ if (s.stacked) e.style.zIndex = l === L ? s.zIndex : s.zIndex - 2;
1122
+ if (!e.classList.contains('mozert-cloned')) {
1123
+ e.querySelectorAll(FOCUSABLE).forEach((f) => {
1124
+ if (active) {
1125
+ if (f.hasAttribute('data-mozert-tabindex')) {
1126
+ const v = f.getAttribute('data-mozert-tabindex');
1127
+ if (v === '') f.removeAttribute('tabindex');
1128
+ else f.setAttribute('tabindex', v);
1129
+ f.removeAttribute('data-mozert-tabindex');
1130
+ }
1131
+ } else if (!f.hasAttribute('data-mozert-tabindex')) {
1132
+ f.setAttribute('data-mozert-tabindex', f.getAttribute('tabindex') || '');
1133
+ f.setAttribute('tabindex', '-1');
1134
+ }
1135
+ });
1136
+ }
1137
+ });
1138
+ }
1139
+
1140
+ /* ------------------------------------------------------------------ */
1141
+ /* Arrows and dots */
1142
+ /* ------------------------------------------------------------------ */
1143
+
1144
+ _buildArrows() {
1145
+ this._navNodes = this._navNodes || [];
1146
+ this._arrows = { prev: null, next: null };
1147
+ this._prev = this._next = null;
1148
+ const s = this.s;
1149
+ if (!s.arrows || !this.canNav) return;
1150
+ const mount = resolveNode(s.appendArrows) || this.el;
1151
+ const make = (val, html, cls, handler) => {
1152
+ const node = resolveNode(val) || htmlToEl(html);
1153
+ const created = !(val && !(typeof val === 'string' && val.trim().charAt(0) === '<'));
1154
+ node.classList.add('mozert-arrow', cls);
1155
+ if (!node.parentNode || created) mount.appendChild(node);
1156
+ const lab = this._label(cls === 'mozert-prev' ? 'prev' : 'next');
1157
+ if (!val || (!node.getAttribute('aria-label') && !node.textContent.trim())) node.setAttribute('aria-label', lab);
1158
+ node.addEventListener('click', handler);
1159
+ this._navNodes.push(() => {
1160
+ node.removeEventListener('click', handler);
1161
+ node.classList.remove('mozert-arrow', cls, 'mozert-disabled');
1162
+ node.removeAttribute('aria-disabled');
1163
+ if (created && node.parentNode) node.parentNode.removeChild(node);
1164
+ });
1165
+ return node;
1166
+ };
1167
+ this._arrows.prev = make(s.prevArrow, DEFAULT_PREV, 'mozert-prev', (e) => {
1168
+ e.preventDefault();
1169
+ this._user();
1170
+ this.prev();
1171
+ });
1172
+ this._arrows.next = make(s.nextArrow, DEFAULT_NEXT, 'mozert-next', (e) => {
1173
+ e.preventDefault();
1174
+ this._user();
1175
+ this.next();
1176
+ });
1177
+ this._prev = this._arrows.prev;
1178
+ this._next = this._arrows.next;
1179
+ }
1180
+
1181
+ _dotCount() {
1182
+ const s = this.s;
1183
+ if (!this.canNav) return 0;
1184
+ if (s.infinite) return Math.ceil(this.n / s.slidesToScroll);
1185
+ return Math.ceil(this.maxIndex / s.slidesToScroll) + 1;
1186
+ }
1187
+
1188
+ _buildDots() {
1189
+ this._dots = null;
1190
+ const s = this.s;
1191
+ if (!s.dots || !this.canNav) return;
1192
+ const count = this._dotCount();
1193
+ const ul = document.createElement('ul');
1194
+ ul.className = s.dotsClass;
1195
+ ul.setAttribute('role', 'tablist');
1196
+ for (let i = 0; i < count; i++) {
1197
+ const li = document.createElement('li');
1198
+ li.setAttribute('role', 'presentation');
1199
+ let inner = typeof s.customPaging === 'function' ? s.customPaging(this, i) : null;
1200
+ if (typeof inner === 'string') inner = htmlToEl(inner.trim().charAt(0) === '<' ? inner : '<button type="button">' + inner + '</button>');
1201
+ if (!isEl(inner)) {
1202
+ inner = document.createElement('button');
1203
+ inner.type = 'button';
1204
+ inner.setAttribute('aria-label', this._label('goTo', { n: i + 1 }));
1205
+ }
1206
+ inner.setAttribute('role', 'tab');
1207
+ li.appendChild(inner);
1208
+ li.addEventListener('click', (e) => {
1209
+ e.preventDefault();
1210
+ this._user();
1211
+ this._slideTo(this._dotTarget(i));
1212
+ });
1213
+ ul.appendChild(li);
1214
+ }
1215
+ (resolveNode(s.appendDots) || this.el).appendChild(ul);
1216
+ this._dots = ul;
1217
+ this._navNodes.push(() => ul.parentNode && ul.parentNode.removeChild(ul));
1218
+ if (s.pauseOnDotsHover) {
1219
+ const a = () => this._holdOn('dots');
1220
+ const b = () => this._holdOff('dots');
1221
+ ul.addEventListener('mouseenter', a);
1222
+ ul.addEventListener('mouseleave', b);
1223
+ }
1224
+ }
1225
+
1226
+ _dotTarget(i) {
1227
+ const s = this.s;
1228
+ const t = i * s.slidesToScroll;
1229
+ return s.infinite ? t : Math.min(t, this.maxIndex);
1230
+ }
1231
+
1232
+ _updateNav() {
1233
+ const s = this.s;
1234
+ if (this._dots) {
1235
+ const total = this._dots.children.length;
1236
+ const idx = s.infinite ? Math.floor(this.current / s.slidesToScroll) : Math.ceil(this.current / s.slidesToScroll);
1237
+ Array.from(this._dots.children).forEach((li, i) => {
1238
+ const on = i === Math.min(total - 1, idx);
1239
+ li.classList.toggle('mozert-active', on);
1240
+ if (li.firstElementChild) li.firstElementChild.setAttribute('aria-selected', on ? 'true' : 'false');
1241
+ });
1242
+ }
1243
+ const { prev, next } = this._arrows || {};
1244
+ if (!s.infinite && prev && next) {
1245
+ const setD = (node, d) => {
1246
+ node.classList.toggle('mozert-disabled', d);
1247
+ node.setAttribute('aria-disabled', d ? 'true' : 'false');
1248
+ if ('disabled' in node) node.disabled = d;
1249
+ };
1250
+ setD(prev, this.current <= 0);
1251
+ setD(next, this.current >= this.maxIndex);
1252
+ }
1253
+ }
1254
+
1255
+ /* ------------------------------------------------------------------ */
1256
+ /* Events: drag, keyboard, hover, focus */
1257
+ /* ------------------------------------------------------------------ */
1258
+
1259
+ _axis() {
1260
+ const s = this.s;
1261
+ if (s.vertical) return s.verticalSwiping ? 'y' : null;
1262
+ return 'x';
1263
+ }
1264
+
1265
+ _on(target, type, fn, opts) {
1266
+ target.addEventListener(type, fn, opts);
1267
+ this._off.push(() => target.removeEventListener(type, fn, opts));
1268
+ }
1269
+
1270
+ _bindEvents() {
1271
+ const s = this.s;
1272
+ const el = this.el;
1273
+ const list = this.list;
1274
+ const axis = this._axis();
1275
+ list.style.touchAction = axis === 'x' ? 'pan-y' : axis === 'y' ? 'pan-x' : '';
1276
+ if (s.accessibility) list.setAttribute('tabindex', '0');
1277
+
1278
+ // Keyboard
1279
+ this._on(el, 'keydown', (e) => {
1280
+ if (!s.accessibility || !this.canNav) return;
1281
+ const t = e.target;
1282
+ if (t && (/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName) || t.isContentEditable)) return;
1283
+ const rtl = s.rtl && !s.vertical;
1284
+ let handled = true;
1285
+ if (e.key === 'ArrowLeft' || (s.vertical && e.key === 'ArrowUp')) rtl ? this.next() : this.prev();
1286
+ else if (e.key === 'ArrowRight' || (s.vertical && e.key === 'ArrowDown')) rtl ? this.prev() : this.next();
1287
+ else if (e.key === 'Home') this.goTo(0);
1288
+ else if (e.key === 'End') this.goTo(this.n - 1);
1289
+ else handled = false;
1290
+ if (handled) e.preventDefault();
1291
+ });
1292
+
1293
+ // Pause on hover/focus
1294
+ if (s.pauseOnHover) {
1295
+ this._on(el, 'mouseenter', () => this._holdOn('hover'));
1296
+ this._on(el, 'mouseleave', () => this._holdOff('hover'));
1297
+ }
1298
+ if (s.pauseOnFocus) {
1299
+ this._on(el, 'focusin', () => this._holdOn('focus'));
1300
+ this._on(el, 'focusout', (e) => {
1301
+ if (!el.contains(e.relatedTarget)) this._holdOff('focus');
1302
+ });
1303
+ }
1304
+
1305
+ // Click handling (select-to-focus, suppress after drag)
1306
+ this._on(
1307
+ list,
1308
+ 'click',
1309
+ (e) => {
1310
+ if (this._dragged) {
1311
+ e.preventDefault();
1312
+ e.stopPropagation();
1313
+ this._dragged = false;
1314
+ return;
1315
+ }
1316
+ if (s.focusOnSelect && this.canNav) {
1317
+ const slide = e.target.closest ? e.target.closest('.mozert-slide') : null;
1318
+ if (slide && this.track.contains(slide)) {
1319
+ const i = parseInt(slide.getAttribute('data-mozert-index'), 10);
1320
+ if (!isNaN(i)) this.goTo(i);
1321
+ }
1322
+ }
1323
+ },
1324
+ true
1325
+ );
1326
+ this._on(list, 'dragstart', (e) => e.preventDefault());
1327
+ this._on(list, 'selectstart', (e) => {
1328
+ if (this._pointerDown) e.preventDefault();
1329
+ });
1330
+
1331
+ // Images that load late change heights
1332
+ this._on(
1333
+ this.track,
1334
+ 'load',
1335
+ () => {
1336
+ if (!this.active) return;
1337
+ clearTimeout(this._imgTimer);
1338
+ this._imgTimer = setTimeout(() => this.setPosition(), 30);
1339
+ },
1340
+ true
1341
+ );
1342
+
1343
+ if (axis && this.canNav) this._bindDrag(axis);
1344
+ }
1345
+
1346
+ _bindDrag(axis) {
1347
+ const s = this.s;
1348
+ const list = this.list;
1349
+ let d = null;
1350
+
1351
+ const onDown = (e) => {
1352
+ if (this._lockDrag) return;
1353
+ if (this.animating && s.waitForAnimate) return;
1354
+ if (e.pointerType === 'mouse') {
1355
+ if (e.button !== 0 || !s.draggable) return;
1356
+ } else if (!s.swipe) {
1357
+ return;
1358
+ }
1359
+ this._dragged = false;
1360
+ this._pointerDown = true;
1361
+ d = { id: e.pointerId, x: e.clientX, y: e.clientY, started: false, base: this._t, delta: 0, samples: [] };
1362
+ window.addEventListener('pointermove', onMove);
1363
+ window.addEventListener('pointerup', onUp);
1364
+ window.addEventListener('pointercancel', onCancel);
1365
+ };
1366
+
1367
+ const limits = () => {
1368
+ const a = this._pos(0);
1369
+ const b = this._pos(this.maxIndex);
1370
+ return [Math.min(a, b), Math.max(a, b)];
1371
+ };
1372
+
1373
+ const onMove = (e) => {
1374
+ if (!d || e.pointerId !== d.id) return;
1375
+ if (this._lockDrag && !d.started) return;
1376
+ const dx = e.clientX - d.x;
1377
+ const dy = e.clientY - d.y;
1378
+ const main = (axis === 'x' ? dx : dy) * s.touchRatio;
1379
+ const cross = axis === 'x' ? dy : dx;
1380
+ if (!d.started) {
1381
+ if (Math.abs(main) < s.dragThreshold || Math.abs(main) < Math.abs(cross)) return;
1382
+ d.started = true;
1383
+ this.el.classList.add('mozert-dragging');
1384
+ this._holdOn('drag');
1385
+ try {
1386
+ list.setPointerCapture(e.pointerId);
1387
+ } catch (err) {
1388
+ /* ignore */
1389
+ }
1390
+ this.track.style.transition = 'none';
1391
+ }
1392
+ d.delta = main;
1393
+ d.samples.push({ t: performance.now(), v: main });
1394
+ if (d.samples.length > 6) d.samples.shift();
1395
+ if (!s.touchMove) return;
1396
+ if (s.stacked) {
1397
+ const sgn = s.rtl && !s.vertical ? 1 : -1;
1398
+ let pv = this._logical + (sgn * main) / (axis === 'x' ? this.list.clientWidth : this.list.clientHeight);
1399
+ if (!s.infinite) {
1400
+ if (pv < 0) pv *= s.edgeFriction;
1401
+ else if (pv > this.maxIndex) pv = this.maxIndex + (pv - this.maxIndex) * s.edgeFriction;
1402
+ }
1403
+ this._fx(pv, false);
1404
+ return;
1405
+ }
1406
+ let raw = d.base + main;
1407
+ if (!s.infinite) {
1408
+ const [lo, hi] = limits();
1409
+ if (raw > hi) raw = hi + (raw - hi) * s.edgeFriction;
1410
+ else if (raw < lo) raw = lo - (lo - raw) * s.edgeFriction;
1411
+ }
1412
+ this._paint(raw);
1413
+ };
1414
+
1415
+ const end = () => {
1416
+ window.removeEventListener('pointermove', onMove);
1417
+ window.removeEventListener('pointerup', onUp);
1418
+ window.removeEventListener('pointercancel', onCancel);
1419
+ this._pointerDown = false;
1420
+ this.el.classList.remove('mozert-dragging');
1421
+ this._holdOff('drag');
1422
+ };
1423
+
1424
+ const onCancel = () => {
1425
+ if (!d) return;
1426
+ const started = d.started;
1427
+ d = null;
1428
+ end();
1429
+ if (started) this._snapBack();
1430
+ };
1431
+
1432
+ const onUp = (e) => {
1433
+ if (!d || e.pointerId !== d.id) return;
1434
+ const st = d;
1435
+ d = null;
1436
+ end();
1437
+ if (!st.started) return;
1438
+ this._dragged = true;
1439
+ const delta = st.delta;
1440
+ const size = axis === 'x' ? this.list.clientWidth : this.list.clientHeight;
1441
+ this._user();
1442
+ if (this._release && this._release(st, size, axis)) return;
1443
+ const fingerDir = axis === 'x' ? (delta < 0 ? 'left' : 'right') : delta < 0 ? 'up' : 'down';
1444
+ const rtl = s.rtl && !s.vertical;
1445
+ const forward = rtl ? delta > 0 : delta < 0;
1446
+ if (Math.abs(delta) < size / s.touchThreshold) {
1447
+ this._snapBack();
1448
+ return;
1449
+ }
1450
+ this.emit('swipe', fingerDir);
1451
+ let target;
1452
+ if (s.swipeToSlide) {
1453
+ const raw = st.base + delta;
1454
+ let best = this.current;
1455
+ let bestD = Infinity;
1456
+ const lo = s.infinite ? -this.cloneCount : 0;
1457
+ const hi = s.infinite ? this.n + this.cloneCount - 1 : this.maxIndex;
1458
+ for (let i = lo; i <= hi; i++) {
1459
+ const dist = Math.abs(this._pos(i) - raw);
1460
+ if (dist < bestD) {
1461
+ bestD = dist;
1462
+ best = i;
1463
+ }
1464
+ }
1465
+ target = best;
1466
+ } else {
1467
+ target = this.current + (forward ? 1 : -1) * s.slidesToScroll;
1468
+ }
1469
+ if (!s.infinite) {
1470
+ const clamped = clamp(target, 0, this.maxIndex);
1471
+ if (clamped === this.current) {
1472
+ this.emit('edge', fingerDir);
1473
+ this._snapBack();
1474
+ return;
1475
+ }
1476
+ target = clamped;
1477
+ }
1478
+ if (target === this.current) {
1479
+ this._snapBack();
1480
+ return;
1481
+ }
1482
+ this._slideTo(target);
1483
+ };
1484
+
1485
+ this._on(list, 'pointerdown', onDown);
1486
+ this._off.push(() => {
1487
+ window.removeEventListener('pointermove', onMove);
1488
+ window.removeEventListener('pointerup', onUp);
1489
+ window.removeEventListener('pointercancel', onCancel);
1490
+ });
1491
+ }
1492
+
1493
+ /* ------------------------------------------------------------------ */
1494
+ /* Autoplay */
1495
+ /* ------------------------------------------------------------------ */
1496
+
1497
+ _holdOn(reason) {
1498
+ this._pause.add(reason);
1499
+ clearTimeout(this._apTimer);
1500
+ }
1501
+
1502
+ _holdOff(reason) {
1503
+ this._pause.delete(reason);
1504
+ this._schedule();
1505
+ }
1506
+
1507
+ _live() {
1508
+ if (this.track) this.track.setAttribute('aria-live', this._autoOn && !this._pause.has('user') ? 'off' : 'polite');
1509
+ }
1510
+
1511
+ _schedule() {
1512
+ clearTimeout(this._apTimer);
1513
+ cancelAnimationFrame(this._apRaf);
1514
+ if (!this.active || !this.canNav || !this._autoOn) return;
1515
+ if (this._pause.size) return;
1516
+ const delay = this.s.autoplaySpeed;
1517
+ this._apTimer = setTimeout(() => this._tick(), delay);
1518
+ const cb = this.options.on && this.options.on.autoplayTimeLeft;
1519
+ if ((this._handlers.autoplayTimeLeft && this._handlers.autoplayTimeLeft.length) || cb) {
1520
+ const t0 = performance.now();
1521
+ const loop = () => {
1522
+ const left = Math.max(0, delay - (performance.now() - t0));
1523
+ this.emit('autoplayTimeLeft', left, left / delay);
1524
+ if (left > 0) this._apRaf = requestAnimationFrame(loop);
1525
+ };
1526
+ loop();
1527
+ }
1528
+ }
1529
+
1530
+ _tick() {
1531
+ const s = this.s;
1532
+ if (this._pause.size || !this.active) return;
1533
+ if (this.animating) {
1534
+ this._schedule();
1535
+ return;
1536
+ }
1537
+ const rev = s.autoplayReverse;
1538
+ if (s.infinite) {
1539
+ if (rev) this.prev();
1540
+ else this.next();
1541
+ return;
1542
+ }
1543
+ if (s.stopOnLastSlide && (rev ? this.current <= 0 : this.current >= this.maxIndex)) {
1544
+ this.pause();
1545
+ return;
1546
+ }
1547
+ if (this.current >= this.maxIndex) this._dir = -1;
1548
+ else if (this.current <= 0) this._dir = 1;
1549
+ if ((this._dir === 1) !== !!rev) this.next();
1550
+ else this.prev();
1551
+ }
1552
+
1553
+ /* ------------------------------------------------------------------ */
1554
+ /* Syncing and lazy loading */
1555
+ /* ------------------------------------------------------------------ */
1556
+
1557
+ _navTargets() {
1558
+ const v = this.s.asNavFor;
1559
+ if (!v) return [];
1560
+ let els = [];
1561
+ if (v instanceof Mozert) return [v];
1562
+ if (typeof v === 'string') els = Array.from(document.querySelectorAll(v));
1563
+ else if (isEl(v)) els = [v];
1564
+ else if (v.length) els = Array.from(v);
1565
+ return els.map((e) => registry.get(e)).filter((i) => i && i !== this && i.active && i.canNav);
1566
+ }
1567
+
1568
+ _syncNav(index) {
1569
+ this._navTargets().forEach((t) => t._slideTo(t.s.infinite ? index : clamp(index, 0, t.maxIndex), { fromSync: true, sel: index }));
1570
+ }
1571
+
1572
+ _lazy() {
1573
+ const s = this.s;
1574
+ if (!this.track || !this.n) return;
1575
+ const mode = s.lazyLoad;
1576
+ if (mode !== 'ondemand' && mode !== 'progressive') return;
1577
+ const from = this.current - (s.centerMode ? Math.floor(this._show / 2) : 0);
1578
+ const to = this.current + this._show - 1 + (s.centerMode ? Math.floor(this._show / 2) : 1);
1579
+ const seen = new Set();
1580
+ for (let i = from; i <= to; i++) {
1581
+ const r = ((i % this.n) + this.n) % this.n;
1582
+ if (!seen.has(r)) {
1583
+ seen.add(r);
1584
+ this._loadSlide(r);
1585
+ }
1586
+ }
1587
+ if (mode === 'progressive' && !this._progressing) this._progressive();
1588
+ }
1589
+
1590
+ _progressive() {
1591
+ this._progressing = true;
1592
+ const step = () => {
1593
+ if (!this.active) {
1594
+ this._progressing = false;
1595
+ return;
1596
+ }
1597
+ const next = this._units.findIndex((u) => u.querySelector('img[data-lazy]:not([data-mozert-loading]), [data-lazy-bg]:not([data-mozert-loading])'));
1598
+ if (next === -1) {
1599
+ this._progressing = false;
1600
+ return;
1601
+ }
1602
+ this._loadSlide(next, step);
1603
+ };
1604
+ step();
1605
+ }
1606
+
1607
+ _loadSlide(index, done) {
1608
+ const unit = this._units[index];
1609
+ if (!unit) return;
1610
+ const imgs = unit.querySelectorAll('img[data-lazy]');
1611
+ const bgs = unit.querySelectorAll('[data-lazy-bg]');
1612
+ if (!imgs.length && !bgs.length) {
1613
+ if (done) done();
1614
+ return;
1615
+ }
1616
+ let left = imgs.length + bgs.length;
1617
+ const fin = () => {
1618
+ if (--left === 0 && done) done();
1619
+ };
1620
+ bgs.forEach((el) => {
1621
+ if (el.hasAttribute('data-mozert-loading')) {
1622
+ fin();
1623
+ return;
1624
+ }
1625
+ el.setAttribute('data-mozert-loading', '');
1626
+ const src = el.getAttribute('data-lazy-bg');
1627
+ const k = el.getAttribute('data-mozert-bg');
1628
+ const apply = (n) => {
1629
+ n.style.backgroundImage = 'url("' + src.replace(/"/g, '%22') + '")';
1630
+ n.removeAttribute('data-lazy-bg');
1631
+ n.removeAttribute('data-mozert-loading');
1632
+ };
1633
+ const probe = new Image();
1634
+ probe.onload = () => {
1635
+ apply(el);
1636
+ this.dom.forEach((e) => {
1637
+ if (!e.classList.contains('mozert-cloned') || parseInt(e.getAttribute('data-mozert-index'), 10) !== index) return;
1638
+ const c = e.querySelector('[data-mozert-bg="' + k + '"]');
1639
+ if (c) apply(c);
1640
+ });
1641
+ this.emit('lazyLoaded', el, src);
1642
+ fin();
1643
+ };
1644
+ probe.onerror = () => {
1645
+ el.removeAttribute('data-lazy-bg');
1646
+ el.removeAttribute('data-mozert-loading');
1647
+ el.classList.add('mozert-lazy-error');
1648
+ this.emit('lazyLoadError', el, src);
1649
+ fin();
1650
+ };
1651
+ probe.src = src;
1652
+ });
1653
+ imgs.forEach((img) => {
1654
+ if (img.hasAttribute('data-mozert-loading')) {
1655
+ fin();
1656
+ return;
1657
+ }
1658
+ img.setAttribute('data-mozert-loading', '');
1659
+ img.classList.add('mozert-loading');
1660
+ const src = img.getAttribute('data-lazy');
1661
+ const srcset = img.getAttribute('data-lazy-srcset');
1662
+ const sizes = img.getAttribute('data-lazy-sizes');
1663
+ const probe = new Image();
1664
+ probe.onload = () => {
1665
+ if (srcset) img.srcset = srcset;
1666
+ if (sizes) img.sizes = sizes;
1667
+ img.src = src;
1668
+ img.removeAttribute('data-lazy');
1669
+ img.removeAttribute('data-mozert-loading');
1670
+ img.classList.remove('mozert-loading');
1671
+ img.classList.add('mozert-loaded');
1672
+ this._lazyClones(index, src, srcset, sizes, img);
1673
+ this.emit('lazyLoaded', img, src);
1674
+ fin();
1675
+ };
1676
+ probe.onerror = () => {
1677
+ img.removeAttribute('data-lazy');
1678
+ img.removeAttribute('data-mozert-loading');
1679
+ img.classList.remove('mozert-loading');
1680
+ img.classList.add('mozert-lazy-error');
1681
+ this.emit('lazyLoadError', img, src);
1682
+ fin();
1683
+ };
1684
+ if (srcset) probe.srcset = srcset;
1685
+ probe.src = src;
1686
+ });
1687
+ }
1688
+
1689
+ _lazyClones(index, src, srcset, sizes, realImg) {
1690
+ const imgsInUnit = Array.from(this._units[index].querySelectorAll('img'));
1691
+ const pos = imgsInUnit.indexOf(realImg);
1692
+ this.dom.forEach((e) => {
1693
+ if (!e.classList.contains('mozert-cloned') || parseInt(e.getAttribute('data-mozert-index'), 10) !== index) return;
1694
+ const c = e.querySelectorAll('img')[pos];
1695
+ if (!c) return;
1696
+ if (srcset) c.srcset = srcset;
1697
+ if (sizes) c.sizes = sizes;
1698
+ c.src = src;
1699
+ c.removeAttribute('data-lazy');
1700
+ c.classList.add('mozert-loaded');
1701
+ });
1702
+ }
1703
+ }
1704
+
1705
+ Mozert._mods = [];
1706
+ Mozert._boot = [];
1707
+ /** Register a module: { name, defaults?, create(slider) => { destroy() } | null }. */
1708
+ Mozert.use = function (mod) {
1709
+ if (mod && Mozert._mods.indexOf(mod) < 0) {
1710
+ Mozert._mods.push(mod);
1711
+ Object.keys(mod.defaults || {}).forEach((k) => {
1712
+ if (!(k in DEFAULTS)) DEFAULTS[k] = mod.defaults[k];
1713
+ });
1714
+ }
1715
+ return Mozert;
1716
+ };
1717
+ Mozert.version = VERSION;
1718
+ Mozert.defaults = DEFAULTS;
1719
+ Mozert._bridge = null;
1720
+
1721
+ /* ---- Mozert modules: effects, parallax, zoom, virtual, hash, scrollbar, pagination, free mode, mousewheel, marquee, video, fullscreen.
1722
+ * This file is a fragment: it runs inside the Mozert closure (see scripts/build.mjs) and uses `Mozert`. */
1723
+ (function () {
1724
+ const clampN = (v, a, b) => Math.min(b, Math.max(a, v));
1725
+ const num = (v, d) => (v === undefined || v === null || isNaN(parseFloat(v)) ? d : parseFloat(v));
1726
+ const objOpt = (v, d) => Object.assign({}, d, v && typeof v === 'object' ? v : {});
1727
+ const mk = (tag, cls, html) => {
1728
+ const e = document.createElement(tag);
1729
+ if (cls) e.className = cls;
1730
+ if (html != null) e.innerHTML = html;
1731
+ return e;
1732
+ };
1733
+ /** Signed distance (in slides) of slide i from visual position pv; wraps for loops. */
1734
+ const relOf = (i, pv, n, loop) => {
1735
+ let r = i - pv;
1736
+ if (loop) r = ((((r + n / 2) % n) + n) % n) - n / 2;
1737
+ return r;
1738
+ };
1739
+ const pct = (v, size) => (typeof v === 'string' && v.indexOf('%') > -1 ? (parseFloat(v) / 100) * size : parseFloat(v) || 0);
1740
+
1741
+ /* ------------------------------------------------------------ effects */
1742
+ const FX = ['coverflow', 'cube', 'flip', 'cards', 'creative'];
1743
+ Mozert.use({
1744
+ name: 'effects',
1745
+ defaults: {
1746
+ coverflowEffect: { rotate: 45, depth: 120, scale: 1, stretch: 0, modifier: 1, shadow: true },
1747
+ cubeEffect: { shadow: true, slideShadows: true, perspective: 1200 },
1748
+ flipEffect: { perspective: 1400, limitRotation: true },
1749
+ cardsEffect: { perSlideOffset: 10, perSlideRotate: 2, rotate: true, visible: 4 },
1750
+ creativeEffect: {
1751
+ limitProgress: 1,
1752
+ prev: { translate: ['-120%', 0, -300], rotate: [0, 0, 0], opacity: 0, scale: 1 },
1753
+ next: { translate: ['120%', 0, 0], rotate: [0, 0, 0], opacity: 0, scale: 1 },
1754
+ },
1755
+ },
1756
+ create(sl) {
1757
+ const s = sl.s;
1758
+ const fx = s.effect;
1759
+ if (FX.indexOf(fx) < 0) return null;
1760
+ const o = objOpt(s[fx + 'Effect'], Mozert.defaults[fx + 'Effect']);
1761
+ const stacked = s.stacked;
1762
+ const n = sl.n;
1763
+ const list = sl.list;
1764
+ const prevR = [];
1765
+ const persp = fx === 'coverflow' ? 1200 : o.perspective || 1200;
1766
+ list.style.perspective = persp + 'px';
1767
+ sl.track.style.transformStyle = 'preserve-3d';
1768
+
1769
+ const slides = () => (stacked ? sl._units : sl.dom);
1770
+ const place = (e, tr, op, z, animate, speed, jump) => {
1771
+ e.style.transition = animate && !jump ? 'transform ' + speed + 'ms ' + s.cssEase + ', opacity ' + speed + 'ms ' + s.cssEase : 'none';
1772
+ e.style.transform = tr;
1773
+ e.style.opacity = op;
1774
+ e.style.zIndex = z;
1775
+ e.style.pointerEvents = op > 0.5 && z >= 0 ? '' : 'none';
1776
+ e.style.backfaceVisibility = fx === 'flip' || fx === 'cube' ? 'hidden' : '';
1777
+ };
1778
+
1779
+ const apply = (pv, animate, speed) => {
1780
+ const W = sl.slideSize || sl.list.clientWidth;
1781
+ const items = slides();
1782
+ const loop = stacked ? true : false;
1783
+ for (let k = 0; k < items.length; k++) {
1784
+ const i = stacked ? k : k - sl.cloneCount;
1785
+ const r = relOf(i, pv, n, loop);
1786
+ const jump = prevR[k] !== undefined && Math.abs(r - prevR[k]) > 1.5;
1787
+ prevR[k] = r;
1788
+ const e = items[k];
1789
+ const a = Math.abs(r);
1790
+ let tr = '';
1791
+ let op = 1;
1792
+ let z = Math.round(1000 - a * 10);
1793
+ if (fx === 'coverflow') {
1794
+ const rot = clampN(-r, -3, 3) * o.rotate * o.modifier;
1795
+ const tz = -a * o.depth;
1796
+ const sc = 1 - Math.min(a, 3) * (1 - o.scale) * 0.5;
1797
+ tr = 'translate3d(' + -r * o.stretch + 'px,0,' + tz + 'px) rotateY(' + rot + 'deg) scale(' + sc + ')';
1798
+ op = a > 4 ? 0 : 1;
1799
+ } else if (fx === 'cube') {
1800
+ tr = 'translate3d(0,0,' + -W / 2 + 'px) rotateY(' + r * 90 + 'deg) translate3d(0,0,' + W / 2 + 'px)';
1801
+ op = a <= 1.01 ? 1 : 0;
1802
+ } else if (fx === 'flip') {
1803
+ const rr = o.limitRotation ? clampN(r, -1, 1) : r;
1804
+ tr = 'rotateY(' + rr * -180 + 'deg)';
1805
+ op = a < 1 ? 1 : 0;
1806
+ z = Math.round(1000 - a * 100);
1807
+ } else if (fx === 'cards') {
1808
+ const vis = o.visible;
1809
+ if (r < 0) {
1810
+ const t = Math.max(r, -1);
1811
+ tr = 'translate3d(' + t * 105 + '%,0,0)' + (o.rotate ? ' rotate(' + t * 12 + 'deg)' : '');
1812
+ op = 1 + t * 0.3;
1813
+ z = 2000;
1814
+ } else {
1815
+ tr = 'translate3d(0,' + r * -o.perSlideOffset + 'px,' + -r * 40 + 'px) scale(' + (1 - r * 0.05) + ')' + (o.rotate ? ' rotate(' + r * o.perSlideRotate * (i % 2 ? 1 : -1) + 'deg)' : '');
1816
+ op = r > vis ? 0 : 1;
1817
+ z = Math.round(1000 - r * 10);
1818
+ }
1819
+ } else {
1820
+ const lim = o.limitProgress;
1821
+ const p = clampN(r, -lim, lim);
1822
+ const d = p < 0 ? o.prev : o.next;
1823
+ const m = Math.abs(p);
1824
+ const t = d.translate || [0, 0, 0];
1825
+ const rt = d.rotate || [0, 0, 0];
1826
+ tr =
1827
+ 'translate3d(' + pct(t[0], W) * m + 'px,' + pct(t[1], sl.list.clientHeight) * m + 'px,' + (parseFloat(t[2]) || 0) * m + 'px) rotateX(' + (rt[0] || 0) * m + 'deg) rotateY(' + (rt[1] || 0) * m + 'deg) rotateZ(' + (rt[2] || 0) * m + 'deg) scale(' + (1 + ((d.scale === undefined ? 1 : d.scale) - 1) * m) + ')';
1828
+ op = 1 + ((d.opacity === undefined ? 1 : d.opacity) - 1) * m;
1829
+ if (a > lim + 0.01) op = d.opacity === undefined ? 1 : d.opacity;
1830
+ z = d.zIndex !== undefined && p !== 0 ? d.zIndex : Math.round(1000 - a * 10);
1831
+ }
1832
+ place(e, tr, op, z, animate, speed, jump);
1833
+ }
1834
+ };
1835
+ sl._onFx(apply);
1836
+ sl.el.classList.add('mozert-fx-' + fx);
1837
+ sl._paintCurrent(false);
1838
+ return {
1839
+ destroy() {
1840
+ list.style.perspective = '';
1841
+ sl.track.style.transformStyle = '';
1842
+ list.style.overflow = '';
1843
+ sl.el.classList.remove('mozert-fx-' + fx);
1844
+ slides().forEach((e) => {
1845
+ e.style.transform = e.style.opacity = e.style.zIndex = e.style.transition = e.style.pointerEvents = e.style.backfaceVisibility = '';
1846
+ });
1847
+ },
1848
+ };
1849
+ },
1850
+ });
1851
+
1852
+ /* ----------------------------------------------------------- parallax */
1853
+ Mozert.use({
1854
+ name: 'parallax',
1855
+ defaults: { parallax: false },
1856
+ create(sl) {
1857
+ if (!sl.s.parallax) return null;
1858
+ const items = [];
1859
+ const stacked = sl.s.stacked;
1860
+ const slides = stacked ? sl._units : sl.dom;
1861
+ slides.forEach((slide, k) => {
1862
+ const els = [];
1863
+ if (slide.hasAttribute('data-parallax-x') || slide.hasAttribute('data-parallax-y')) els.push(slide);
1864
+ slide.querySelectorAll('[data-parallax-x],[data-parallax-y],[data-parallax-scale],[data-parallax-opacity]').forEach((e) => els.push(e));
1865
+ items.push(els);
1866
+ });
1867
+ const bg = sl.el.querySelector('[data-parallax-bg]');
1868
+ const apply = (pv, animate, speed) => {
1869
+ const W = sl.slideSize || sl.list.clientWidth;
1870
+ slides.forEach((slide, k) => {
1871
+ const i = stacked ? k : k - sl.cloneCount;
1872
+ const r = clampN(relOf(i, pv, sl.n, stacked), -2, 2);
1873
+ items[k].forEach((e) => {
1874
+ const x = e.getAttribute('data-parallax-x');
1875
+ const y = e.getAttribute('data-parallax-y');
1876
+ const sc = e.getAttribute('data-parallax-scale');
1877
+ const op = e.getAttribute('data-parallax-opacity');
1878
+ const tx = x ? pct(x, e === slide ? W : e.offsetWidth || W) * r : 0;
1879
+ const ty = y ? pct(y, e.offsetHeight || 100) * r : 0;
1880
+ e.style.transition = animate ? 'transform ' + speed + 'ms ' + sl.s.cssEase + ', opacity ' + speed + 'ms ' + sl.s.cssEase : 'none';
1881
+ e.style.transform = 'translate3d(' + tx + 'px,' + ty + 'px,0)' + (sc ? ' scale(' + (1 + (parseFloat(sc) - 1) * Math.abs(r)) + ')' : '');
1882
+ if (op !== null) e.style.opacity = 1 + (parseFloat(op) - 1) * Math.abs(r);
1883
+ });
1884
+ });
1885
+ if (bg) {
1886
+ const amt = parseFloat(bg.getAttribute('data-parallax-bg')) || 0.5;
1887
+ bg.style.transition = animate ? 'transform ' + speed + 'ms ' + sl.s.cssEase : 'none';
1888
+ bg.style.transform = 'translate3d(' + -pv * W * amt + 'px,0,0)';
1889
+ }
1890
+ };
1891
+ sl._onFx(apply);
1892
+ sl._paintCurrent(false);
1893
+ return {
1894
+ destroy() {
1895
+ items.forEach((l) => l.forEach((e) => (e.style.transform = e.style.opacity = e.style.transition = '')));
1896
+ if (bg) bg.style.transform = '';
1897
+ },
1898
+ };
1899
+ },
1900
+ });
1901
+
1902
+ /* --------------------------------------------------------------- zoom */
1903
+ Mozert.use({
1904
+ name: 'zoom',
1905
+ defaults: { zoom: false },
1906
+ create(sl) {
1907
+ if (!sl.s.zoom) return null;
1908
+ const o = objOpt(sl.s.zoom, { max: 3, min: 1, toggle: true, step: 0.5 });
1909
+ let scale = 1;
1910
+ let tx = 0;
1911
+ let ty = 0;
1912
+ let target = null;
1913
+ const ptrs = new Map();
1914
+ let startDist = 0;
1915
+ let startScale = 1;
1916
+ let pan = null;
1917
+ const cur = () => sl._units[sl.current];
1918
+ const tgt = () => {
1919
+ const u = cur();
1920
+ return u ? u.querySelector('.mozert-zoom-target') || u.querySelector('img,picture,canvas,video') : null;
1921
+ };
1922
+ const paint = (animate) => {
1923
+ if (!target) return;
1924
+ target.style.transition = animate ? 'transform .25s ease' : 'none';
1925
+ target.style.transform = scale === 1 && !tx && !ty ? '' : 'translate3d(' + tx + 'px,' + ty + 'px,0) scale(' + scale + ')';
1926
+ target.style.transformOrigin = '50% 50%';
1927
+ target.style.cursor = scale > 1 ? 'grab' : '';
1928
+ sl._lockDrag = scale > 1;
1929
+ sl.el.classList.toggle('mozert-zoomed', scale > 1);
1930
+ };
1931
+ const bound = () => {
1932
+ if (!target) return;
1933
+ const mx = (target.offsetWidth * (scale - 1)) / 2;
1934
+ const my = (target.offsetHeight * (scale - 1)) / 2;
1935
+ tx = clampN(tx, -mx, mx);
1936
+ ty = clampN(ty, -my, my);
1937
+ };
1938
+ const reset = (animate) => {
1939
+ if (scale === 1 && !tx && !ty) {
1940
+ target = tgt();
1941
+ return;
1942
+ }
1943
+ scale = 1;
1944
+ tx = ty = 0;
1945
+ paint(animate);
1946
+ target = tgt();
1947
+ };
1948
+ const setScale = (v, animate) => {
1949
+ target = tgt();
1950
+ if (!target) return;
1951
+ scale = clampN(v, o.min, o.max);
1952
+ if (scale === 1) tx = ty = 0;
1953
+ bound();
1954
+ paint(animate);
1955
+ sl.emit('zoomChange', scale);
1956
+ };
1957
+ sl.zoom = {
1958
+ in: () => setScale(scale + o.step, true),
1959
+ out: () => setScale(scale - o.step, true),
1960
+ toggle: () => setScale(scale > 1 ? 1 : o.max, true),
1961
+ reset: () => reset(true),
1962
+ get scale() {
1963
+ return scale;
1964
+ },
1965
+ };
1966
+ const onDbl = (e) => {
1967
+ if (!o.toggle || !e.target.closest || !cur() || !cur().contains(e.target)) return;
1968
+ e.preventDefault();
1969
+ sl.zoom.toggle();
1970
+ };
1971
+ const onDown = (e) => {
1972
+ if (!cur() || !cur().contains(e.target)) return;
1973
+ ptrs.set(e.pointerId, { x: e.clientX, y: e.clientY });
1974
+ if (ptrs.size === 2) {
1975
+ const [a, b] = Array.from(ptrs.values());
1976
+ startDist = Math.hypot(a.x - b.x, a.y - b.y);
1977
+ startScale = scale;
1978
+ sl._lockDrag = true;
1979
+ } else if (scale > 1) pan = { x: e.clientX, y: e.clientY, tx, ty };
1980
+ };
1981
+ const onMove = (e) => {
1982
+ if (!ptrs.has(e.pointerId)) return;
1983
+ ptrs.set(e.pointerId, { x: e.clientX, y: e.clientY });
1984
+ if (ptrs.size === 2 && startDist) {
1985
+ const [a, b] = Array.from(ptrs.values());
1986
+ setScale((startScale * Math.hypot(a.x - b.x, a.y - b.y)) / startDist, false);
1987
+ } else if (pan && scale > 1) {
1988
+ tx = pan.tx + (e.clientX - pan.x);
1989
+ ty = pan.ty + (e.clientY - pan.y);
1990
+ bound();
1991
+ paint(false);
1992
+ }
1993
+ };
1994
+ const onUp = (e) => {
1995
+ ptrs.delete(e.pointerId);
1996
+ if (ptrs.size < 2) startDist = 0;
1997
+ if (!ptrs.size) pan = null;
1998
+ if (scale <= 1.001) {
1999
+ scale = 1;
2000
+ tx = ty = 0;
2001
+ paint(true);
2002
+ }
2003
+ };
2004
+ const onWheel = (e) => {
2005
+ if (!e.ctrlKey || !cur() || !cur().contains(e.target)) return;
2006
+ e.preventDefault();
2007
+ setScale(scale - e.deltaY * 0.01, false);
2008
+ };
2009
+ sl._on(sl.list, 'dblclick', onDbl);
2010
+ sl._on(sl.list, 'pointerdown', onDown);
2011
+ sl._on(window, 'pointermove', onMove);
2012
+ sl._on(window, 'pointerup', onUp);
2013
+ sl._on(window, 'pointercancel', onUp);
2014
+ sl._on(sl.list, 'wheel', onWheel, { passive: false });
2015
+ sl._sub('beforeChange', () => reset(false));
2016
+ target = tgt();
2017
+ return {
2018
+ destroy() {
2019
+ reset(false);
2020
+ sl._lockDrag = false;
2021
+ delete sl.zoom;
2022
+ },
2023
+ };
2024
+ },
2025
+ });
2026
+
2027
+ /* ------------------------------------------------------------ virtual */
2028
+ Mozert._boot.push((el, o) => {
2029
+ const v = o.virtual;
2030
+ if (!v || !v.slides || el.children.length) return;
2031
+ v.slides.forEach((d, i) => {
2032
+ const sh = mk('div', 'mozert-v');
2033
+ sh.setAttribute('data-vi', i);
2034
+ el.appendChild(sh);
2035
+ });
2036
+ });
2037
+ Mozert.use({
2038
+ name: 'virtual',
2039
+ defaults: { virtual: false },
2040
+ create(sl) {
2041
+ const v = sl.s.virtual;
2042
+ if (!v || !v.slides) return null;
2043
+ const before = num(v.addSlidesBefore, 2);
2044
+ const after = num(v.addSlidesAfter, 2);
2045
+ const store = v.slides;
2046
+ const hostOf = (u) => (u.classList.contains('mozert-v') ? u : u.querySelector('.mozert-v') || u);
2047
+ const make = (i) => {
2048
+ const d = store[i];
2049
+ const html = typeof v.renderSlide === 'function' ? v.renderSlide(d, i) : d;
2050
+ return html;
2051
+ };
2052
+ const render = () => {
2053
+ const a = Math.max(0, sl.current - before);
2054
+ const b = Math.min(sl.n - 1, sl.current + sl._showC - 1 + after);
2055
+ const added = [];
2056
+ sl._units.forEach((u, i) => {
2057
+ const h = hostOf(u);
2058
+ const inWin = i >= a && i <= b;
2059
+ if (inWin && !h.hasAttribute('data-vr')) {
2060
+ const c = make(i);
2061
+ if (typeof c === 'string') h.innerHTML = c;
2062
+ else if (c && c.nodeType) {
2063
+ h.innerHTML = '';
2064
+ h.appendChild(c);
2065
+ }
2066
+ h.setAttribute('data-vr', '1');
2067
+ added.push(i);
2068
+ } else if (!inWin && h.hasAttribute('data-vr') && v.cache === false) {
2069
+ h.innerHTML = '';
2070
+ h.removeAttribute('data-vr');
2071
+ }
2072
+ });
2073
+ if (added.length) {
2074
+ sl._arrowY();
2075
+ sl._lazy();
2076
+ sl.emit('virtualRender', added);
2077
+ }
2078
+ };
2079
+ sl._sub('beforeChange', (_s, old, real) => {
2080
+ sl.current = real;
2081
+ render();
2082
+ });
2083
+ render();
2084
+ sl.virtual = {
2085
+ slides: store,
2086
+ appendSlide(d) {
2087
+ store.push(d);
2088
+ sl.add(mk('div', 'mozert-v'));
2089
+ return sl;
2090
+ },
2091
+ prependSlide(d) {
2092
+ store.unshift(d);
2093
+ sl.add(mk('div', 'mozert-v'), true);
2094
+ return sl;
2095
+ },
2096
+ removeSlide(i) {
2097
+ store.splice(i, 1);
2098
+ sl.remove(i);
2099
+ return sl;
2100
+ },
2101
+ update() {
2102
+ sl._units.forEach((u) => {
2103
+ const h = hostOf(u);
2104
+ h.innerHTML = '';
2105
+ h.removeAttribute('data-vr');
2106
+ });
2107
+ render();
2108
+ },
2109
+ };
2110
+ return {
2111
+ destroy() {
2112
+ delete sl.virtual;
2113
+ },
2114
+ };
2115
+ },
2116
+ });
2117
+
2118
+ /* ----------------------------------------------------- hash navigation */
2119
+ Mozert.use({
2120
+ name: 'hash',
2121
+ defaults: { hashNavigation: false },
2122
+ create(sl) {
2123
+ const h = sl.s.hashNavigation;
2124
+ if (!h || typeof window === 'undefined') return null;
2125
+ const o = objOpt(h, { watchState: true, replaceState: true, prefix: '' });
2126
+ const hashes = sl._units.map((u, i) => (u.getAttribute('data-hash') || (u.firstElementChild && u.firstElementChild.getAttribute('data-hash')) || String(i + 1)));
2127
+ const read = () => decodeURIComponent((window.location.hash || '').replace(/^#/, '').replace(o.prefix, ''));
2128
+ const go = (anim) => {
2129
+ const i = hashes.indexOf(read());
2130
+ if (i > -1 && i !== sl.current) sl.goTo(i, !anim);
2131
+ };
2132
+ go(false);
2133
+ sl._sub('afterChange', () => {
2134
+ const val = o.prefix + hashes[sl.current];
2135
+ try {
2136
+ if (o.replaceState && window.history && window.history.replaceState) window.history.replaceState(null, '', '#' + val);
2137
+ else window.location.hash = val;
2138
+ } catch (e) {}
2139
+ });
2140
+ if (o.watchState) sl._on(window, 'hashchange', () => go(true));
2141
+ return null;
2142
+ },
2143
+ });
2144
+
2145
+ /* ---------------------------------------------------------- scrollbar */
2146
+ Mozert.use({
2147
+ name: 'scrollbar',
2148
+ defaults: { scrollbar: false },
2149
+ create(sl) {
2150
+ const c = sl.s.scrollbar;
2151
+ if (!c || !sl.canNav) return null;
2152
+ const o = objOpt(c, { draggable: true, hide: false, el: null });
2153
+ const host = o.el ? (typeof o.el === 'string' ? document.querySelector(o.el) : o.el) : null;
2154
+ const bar = host || mk('div', 'mozert-scrollbar');
2155
+ const thumb = mk('div', 'mozert-scrollbar-drag');
2156
+ bar.classList.add('mozert-scrollbar');
2157
+ bar.appendChild(thumb);
2158
+ if (!host) sl.el.appendChild(bar);
2159
+ const show = () => Math.min(sl.n, sl._show);
2160
+ const apply = (pv) => {
2161
+ const max = Math.max(0.0001, sl.n - show());
2162
+ const frac = sl.s.stacked ? (pv - 0) / Math.max(1, sl.n - 1) : clampN(pv / max, 0, 1);
2163
+ const tw = sl.s.stacked ? 100 / sl.n : (show() / sl.n) * 100;
2164
+ thumb.style.width = tw + '%';
2165
+ thumb.style.left = clampN(frac, 0, 1) * (100 - tw) + '%';
2166
+ };
2167
+ sl._onFx(apply);
2168
+ sl._paintCurrent(false);
2169
+ let hideT;
2170
+ if (o.hide) {
2171
+ bar.classList.add('mozert-scrollbar-hide');
2172
+ sl._sub('beforeChange', () => {
2173
+ bar.classList.add('mozert-scrollbar-show');
2174
+ clearTimeout(hideT);
2175
+ hideT = setTimeout(() => bar.classList.remove('mozert-scrollbar-show'), 1200);
2176
+ });
2177
+ }
2178
+ if (o.draggable) {
2179
+ let drag = false;
2180
+ const move = (e) => {
2181
+ const r = bar.getBoundingClientRect();
2182
+ const f = clampN((e.clientX - r.left) / (r.width || 1), 0, 1);
2183
+ const idx = Math.round(f * (sl.s.stacked ? sl.n - 1 : sl.maxIndex));
2184
+ sl.goTo(idx);
2185
+ };
2186
+ const down = (e) => {
2187
+ drag = true;
2188
+ move(e);
2189
+ e.preventDefault();
2190
+ };
2191
+ sl._on(bar, 'pointerdown', down);
2192
+ sl._on(window, 'pointermove', (e) => drag && move(e));
2193
+ sl._on(window, 'pointerup', () => (drag = false));
2194
+ }
2195
+ return {
2196
+ destroy() {
2197
+ clearTimeout(hideT);
2198
+ if (host) {
2199
+ if (thumb.parentNode) thumb.parentNode.removeChild(thumb);
2200
+ } else if (bar.parentNode) bar.parentNode.removeChild(bar);
2201
+ },
2202
+ };
2203
+ },
2204
+ });
2205
+
2206
+ /* --------------------------------------------------------- pagination */
2207
+ Mozert.use({
2208
+ name: 'pagination',
2209
+ defaults: { pagination: false },
2210
+ create(sl) {
2211
+ const c = sl.s.pagination;
2212
+ if (!c || !sl.canNav) return null;
2213
+ const o = objOpt(c, { type: 'bullets', el: null, clickable: true, dynamicMainBullets: 1, progressbarOpposite: false, hideOnClick: false });
2214
+ const host = o.el ? (typeof o.el === 'string' ? document.querySelector(o.el) : o.el) : null;
2215
+ const root = host || mk('div');
2216
+ root.classList.add('mozert-pagination', 'mozert-pagination-' + o.type);
2217
+ if (!host) sl.el.appendChild(root);
2218
+ const total = sl.n;
2219
+ const cur = () => sl.current;
2220
+ let bullets = [];
2221
+ let fill = null;
2222
+ const buildBullets = () => {
2223
+ root.innerHTML = '';
2224
+ bullets = [];
2225
+ for (let i = 0; i < total; i++) {
2226
+ let b;
2227
+ if (typeof o.renderBullet === 'function') {
2228
+ const html = o.renderBullet(i, 'mozert-bullet');
2229
+ const t = mk('div', null, String(html).trim());
2230
+ b = t.firstElementChild || mk('span', 'mozert-bullet');
2231
+ } else b = mk('button', 'mozert-bullet');
2232
+ if (b.tagName === 'BUTTON') {
2233
+ b.type = 'button';
2234
+ b.setAttribute('aria-label', sl._label('goTo', { n: i + 1 }));
2235
+ }
2236
+ if (o.clickable) {
2237
+ b.addEventListener('click', (e) => {
2238
+ e.preventDefault();
2239
+ sl._user();
2240
+ sl.goTo(i);
2241
+ });
2242
+ }
2243
+ root.appendChild(b);
2244
+ bullets.push(b);
2245
+ }
2246
+ };
2247
+ const update = () => {
2248
+ const i = cur();
2249
+ if (o.type === 'bullets' || o.type === 'dynamic') {
2250
+ const m = o.dynamicMainBullets;
2251
+ bullets.forEach((b, k) => {
2252
+ b.classList.toggle('mozert-bullet-active', k === i);
2253
+ if (o.type === 'dynamic') {
2254
+ const d = Math.abs(k - i);
2255
+ b.classList.toggle('mozert-bullet-active-main', d <= (m - 1) / 2 || k === i);
2256
+ b.classList.toggle('mozert-bullet-near', d === 1 || d === 2);
2257
+ b.classList.toggle('mozert-bullet-far', d === 3 || d === 4);
2258
+ b.classList.toggle('mozert-bullet-hidden', d > 4 + (m - 1));
2259
+ }
2260
+ });
2261
+ } else if (o.type === 'fraction') {
2262
+ const cs = '<span class="mozert-pg-current">' + (i + 1) + '</span>';
2263
+ const ts = '<span class="mozert-pg-total">' + total + '</span>';
2264
+ root.innerHTML = typeof o.renderFraction === 'function' ? o.renderFraction('mozert-pg-current', 'mozert-pg-total', i + 1, total) : cs + ' / ' + ts;
2265
+ } else if (o.type === 'custom' && typeof o.renderCustom === 'function') {
2266
+ root.innerHTML = o.renderCustom(sl, i + 1, total);
2267
+ }
2268
+ };
2269
+ if (o.type === 'bullets' || o.type === 'dynamic') buildBullets();
2270
+ else if (o.type === 'progressbar') {
2271
+ fill = mk('span', 'mozert-pg-fill');
2272
+ root.appendChild(fill);
2273
+ if (o.progressbarOpposite) root.classList.add('mozert-pg-opposite');
2274
+ sl._onFx((pv) => {
2275
+ const p = sl.s.stacked ? (relOf(0, -pv, total, false) + 1) / total : clampN((pv + Math.min(sl._show, total)) / total, 0, 1);
2276
+ fill.style.transform = 'scaleX(' + clampN(sl.s.stacked ? (pv + 1) / total : p, 0, 1) + ')';
2277
+ });
2278
+ sl._paintCurrent(false);
2279
+ }
2280
+ update();
2281
+ sl._sub('beforeChange', (_s, old, real) => {
2282
+ sl.current = real;
2283
+ update();
2284
+ });
2285
+ sl._sub('afterChange', update);
2286
+ return {
2287
+ destroy() {
2288
+ if (host) root.innerHTML = '';
2289
+ else if (root.parentNode) root.parentNode.removeChild(root);
2290
+ root.classList.remove('mozert-pagination', 'mozert-pagination-' + o.type);
2291
+ },
2292
+ };
2293
+ },
2294
+ });
2295
+
2296
+ /* ---------------------------------------------------- free mode + wheel */
2297
+ const bounds = (sl) => [sl._pos(sl.maxIndex), sl._pos(0)]; // [min, max]
2298
+ const syncCurrent = (sl) => {
2299
+ const s = sl.s;
2300
+ if (s.stacked) return;
2301
+ const pv = sl._pvFromT(sl._cur);
2302
+ const idx = clampN(Math.round(pv), 0, sl.maxIndex);
2303
+ if (idx !== sl.current) {
2304
+ const old = sl.current;
2305
+ sl.emit('beforeChange', old, idx);
2306
+ sl.current = idx;
2307
+ sl._logical = idx;
2308
+ sl._updateNav();
2309
+ sl._updateSlideClasses(idx);
2310
+ sl._lazy();
2311
+ sl.emit('afterChange', idx);
2312
+ }
2313
+ };
2314
+ Mozert.use({
2315
+ name: 'freeMode',
2316
+ defaults: { freeMode: false },
2317
+ create(sl) {
2318
+ const s = sl.s;
2319
+ if (!s.freeMode || s.stacked) return null;
2320
+ const o = objOpt(s.freeMode, { momentum: true, momentumRatio: 1, momentumBounce: true, sticky: false, minimumVelocity: 0.02 });
2321
+ let timer;
2322
+ sl.el.classList.add('mozert-free');
2323
+ sl._release = (st, size, axis) => {
2324
+ const sm = st.samples;
2325
+ let v = 0;
2326
+ if (sm.length > 1) {
2327
+ const a = sm[0];
2328
+ const b = sm[sm.length - 1];
2329
+ v = (b.v - a.v) / Math.max(1, b.t - a.t);
2330
+ }
2331
+ if (performance.now() - (sm.length ? sm[sm.length - 1].t : 0) > 120) v = 0;
2332
+ const [mn, mx] = bounds(sl);
2333
+ const cur = sl._cur;
2334
+ let dist = o.momentum && Math.abs(v) > o.minimumVelocity ? v * 350 * o.momentumRatio : 0;
2335
+ let target = clampN(cur + dist, mn, mx);
2336
+ const dur = clampN(Math.abs(target - cur) * 2.2 + 250, 250, 1400);
2337
+ sl._setPos(target, true, { duration: dur, ease: 'cubic-bezier(.17,.67,.35,1)' });
2338
+ clearTimeout(timer);
2339
+ timer = setTimeout(() => {
2340
+ if (o.sticky) {
2341
+ const idx = clampN(Math.round(sl._pvFromT(sl._cur)), 0, sl.maxIndex);
2342
+ sl._setPos(sl._pos(idx), true);
2343
+ }
2344
+ syncCurrent(sl);
2345
+ }, dur + 20);
2346
+ sl.emit('freeModeEnd', target);
2347
+ return true;
2348
+ };
2349
+ return {
2350
+ destroy() {
2351
+ clearTimeout(timer);
2352
+ sl.el.classList.remove('mozert-free');
2353
+ },
2354
+ };
2355
+ },
2356
+ });
2357
+
2358
+ Mozert.use({
2359
+ name: 'mousewheel',
2360
+ defaults: { mousewheel: false },
2361
+ create(sl) {
2362
+ const c = sl.s.mousewheel;
2363
+ if (!c) return null;
2364
+ const o = objOpt(c, { sensitivity: 1, forceToAxis: false, releaseOnEdges: true, thresholdDelta: 4, thresholdTime: 450 });
2365
+ let last = 0;
2366
+ let timer;
2367
+ const free = !!sl.s.freeMode && !sl.s.stacked;
2368
+ const on = (e) => {
2369
+ if (!sl.canNav || !sl.active) return;
2370
+ let d = sl.s.vertical ? e.deltaY : o.forceToAxis ? e.deltaX : Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.deltaY;
2371
+ if (e.shiftKey && !sl.s.vertical) d = e.deltaY || e.deltaX;
2372
+ if (!d) return;
2373
+ if (d > 0 === (sl.s.rtl && !sl.s.vertical)) d = d; // direction handled below
2374
+ const edgeFwd = d > 0 && sl.current >= (sl.s.infinite || sl.s.stacked ? Infinity : sl.maxIndex) && (!free || sl._cur <= bounds(sl)[0] + 1);
2375
+ const edgeBack = d < 0 && sl.current <= (sl.s.infinite || sl.s.stacked ? -Infinity : 0) && (!free || sl._cur >= bounds(sl)[1] - 1);
2376
+ if (o.releaseOnEdges && (edgeFwd || edgeBack)) return;
2377
+ e.preventDefault();
2378
+ sl._user();
2379
+ if (free) {
2380
+ const [mn, mx] = bounds(sl);
2381
+ const dir = sl.s.rtl && !sl.s.vertical ? 1 : -1;
2382
+ sl._setPos(clampN(sl._cur + dir * d * o.sensitivity, mn, mx), false);
2383
+ clearTimeout(timer);
2384
+ timer = setTimeout(() => syncCurrent(sl), 120);
2385
+ return;
2386
+ }
2387
+ const now = performance.now();
2388
+ if (Math.abs(d) < o.thresholdDelta && now - last < o.thresholdTime) return;
2389
+ if (now - last < o.thresholdTime) return;
2390
+ last = now;
2391
+ if (d > 0) sl.next();
2392
+ else sl.prev();
2393
+ };
2394
+ sl._on(sl.el, 'wheel', on, { passive: false });
2395
+ return {
2396
+ destroy() {
2397
+ clearTimeout(timer);
2398
+ },
2399
+ };
2400
+ },
2401
+ });
2402
+
2403
+ /* ---------------------------------------------------------- keyboard */
2404
+ Mozert.use({
2405
+ name: 'globalKeyboard',
2406
+ defaults: { globalKeyboard: false },
2407
+ create(sl) {
2408
+ const c = sl.s.globalKeyboard;
2409
+ if (!c) return null;
2410
+ const o = objOpt(c, { onlyInViewport: true });
2411
+ sl._on(document, 'keydown', (e) => {
2412
+ if (e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey) return;
2413
+ const t = e.target;
2414
+ if (t && (/^(input|textarea|select)$/i.test(t.tagName) || t.isContentEditable)) return;
2415
+ if (o.onlyInViewport) {
2416
+ const r = sl.el.getBoundingClientRect();
2417
+ const vh = window.innerHeight || document.documentElement.clientHeight;
2418
+ if (r.bottom < 0 || r.top > vh) return;
2419
+ }
2420
+ const v = sl.s.vertical;
2421
+ const k = e.key;
2422
+ const rtl = sl.s.rtl && !v;
2423
+ if ((!v && (k === 'ArrowLeft' || k === 'ArrowRight')) || (v && (k === 'ArrowUp' || k === 'ArrowDown'))) {
2424
+ const fwd = k === 'ArrowRight' || k === 'ArrowDown';
2425
+ sl._user();
2426
+ if (fwd !== rtl) sl.next();
2427
+ else sl.prev();
2428
+ e.preventDefault();
2429
+ }
2430
+ });
2431
+ return null;
2432
+ },
2433
+ });
2434
+
2435
+ /* ------------------------------------------------------------ marquee */
2436
+ Mozert.use({
2437
+ name: 'marquee',
2438
+ defaults: { marquee: false },
2439
+ create(sl) {
2440
+ const c = sl.s.marquee;
2441
+ if (!c || !sl.canNav) return null;
2442
+ const o = objOpt(c, { speed: 60, pauseOnHover: true, direction: 'left' });
2443
+ if (typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches && sl.s.respectReducedMotion) return null;
2444
+ const pitch = sl.slideSize + sl.s.spaceBetween;
2445
+ const setLen = pitch * sl.n;
2446
+ const base = sl._pos(0);
2447
+ let off = 0;
2448
+ let last = 0;
2449
+ let raf = 0;
2450
+ let hold = false;
2451
+ sl._lockDrag = true;
2452
+ sl.track.style.transition = 'none';
2453
+ const dir = (o.direction === 'right' ? -1 : 1) * (sl.s.rtl ? -1 : 1);
2454
+ const loop = (t) => {
2455
+ if (!last) last = t;
2456
+ const dt = Math.min(64, t - last);
2457
+ last = t;
2458
+ if (!hold) {
2459
+ off += (o.speed * dt) / 1000;
2460
+ const m = ((off % setLen) + setLen) % setLen;
2461
+ const v = o.direction === 'right' ? base - setLen + m : base - m;
2462
+ sl._paint(sl.s.rtl ? -v : v);
2463
+ sl._t = sl._cur;
2464
+ }
2465
+ raf = requestAnimationFrame(loop);
2466
+ };
2467
+ raf = requestAnimationFrame(loop);
2468
+ if (o.pauseOnHover) {
2469
+ sl._on(sl.el, 'mouseenter', () => (hold = true));
2470
+ sl._on(sl.el, 'mouseleave', () => {
2471
+ hold = false;
2472
+ last = 0;
2473
+ });
2474
+ }
2475
+ sl.marquee = {
2476
+ pause: () => (hold = true),
2477
+ resume: () => {
2478
+ hold = false;
2479
+ last = 0;
2480
+ },
2481
+ };
2482
+ return {
2483
+ destroy() {
2484
+ cancelAnimationFrame(raf);
2485
+ sl._lockDrag = false;
2486
+ delete sl.marquee;
2487
+ },
2488
+ };
2489
+ },
2490
+ });
2491
+
2492
+ /* -------------------------------------------------------------- video */
2493
+ Mozert.use({
2494
+ name: 'video',
2495
+ defaults: { videoAutoplay: false },
2496
+ create(sl) {
2497
+ const c = sl.s.videoAutoplay;
2498
+ if (!c) return null;
2499
+ const o = objOpt(c, { wait: false, pauseOthers: true, rewind: true });
2500
+ const vids = (u) => Array.from(u.querySelectorAll('video'));
2501
+ const ended = () => sl._holdOff('video');
2502
+ const sync = () => {
2503
+ sl._units.forEach((u, i) => {
2504
+ vids(u).forEach((v) => {
2505
+ if (i === sl.current) {
2506
+ try {
2507
+ const p = v.play();
2508
+ if (p && p.catch) p.catch(() => {});
2509
+ } catch (e) {}
2510
+ if (o.wait) {
2511
+ sl._holdOn('video');
2512
+ v.addEventListener('ended', ended, { once: true });
2513
+ }
2514
+ } else if (o.pauseOthers) {
2515
+ try {
2516
+ v.pause();
2517
+ if (o.rewind) v.currentTime = 0;
2518
+ } catch (e) {}
2519
+ }
2520
+ });
2521
+ });
2522
+ if (o.wait && !vids(sl._units[sl.current] || document.createElement('i')).length) sl._holdOff('video');
2523
+ };
2524
+ sl._sub('afterChange', sync);
2525
+ sync();
2526
+ return {
2527
+ destroy() {
2528
+ sl._holdOff('video');
2529
+ },
2530
+ };
2531
+ },
2532
+ });
2533
+
2534
+ /* --------------------------------------------------------- fullscreen */
2535
+ Mozert.use({
2536
+ name: 'fullscreen',
2537
+ defaults: { fullscreen: false },
2538
+ create(sl) {
2539
+ if (!sl.s.fullscreen) return null;
2540
+ const btn = mk('button', 'mozert-fullscreen-btn');
2541
+ btn.type = 'button';
2542
+ btn.setAttribute('aria-label', sl._label('fullscreen') || 'Toggle fullscreen');
2543
+ btn.innerHTML = '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></svg>';
2544
+ sl.el.appendChild(btn);
2545
+ const toggle = () => {
2546
+ const on = !sl.el.classList.contains('mozert-fullscreen');
2547
+ if (on && sl.el.requestFullscreen) sl.el.requestFullscreen().catch(() => sl.el.classList.add('mozert-fullscreen'));
2548
+ else if (!on && document.fullscreenElement && document.exitFullscreen) document.exitFullscreen();
2549
+ else sl.el.classList.toggle('mozert-fullscreen', on);
2550
+ setTimeout(() => window.dispatchEvent(new Event('resize')), 60);
2551
+ };
2552
+ btn.addEventListener('click', toggle);
2553
+ sl._on(document, 'fullscreenchange', () => {
2554
+ sl.el.classList.toggle('mozert-fullscreen', document.fullscreenElement === sl.el);
2555
+ window.dispatchEvent(new Event('resize'));
2556
+ });
2557
+ sl.fullscreen = { toggle };
2558
+ return {
2559
+ destroy() {
2560
+ if (btn.parentNode) btn.parentNode.removeChild(btn);
2561
+ delete sl.fullscreen;
2562
+ },
2563
+ };
2564
+ },
2565
+ });
2566
+
2567
+ /* -------------------------------------------------------------- bleed */
2568
+ // Slides start aligned with the container, overflow towards the viewport edge (right, left or both) and the last slide ends aligned with the container.
2569
+ Mozert.use({
2570
+ name: 'bleed',
2571
+ defaults: { bleed: false },
2572
+ create(sl) {
2573
+ const c = sl.s.bleed;
2574
+ if (!c || sl.s.vertical) return null;
2575
+ const side = c === 'left' || c === 'right' ? c : 'both';
2576
+ const list = sl.list;
2577
+ list.style.overflow = 'visible';
2578
+ sl.el.classList.add('mozert-bleed');
2579
+ // Overflowing slides must not create a horizontal page scrollbar: clip the page on the x axis (restored on destroy).
2580
+ const body = document.body;
2581
+ const prevClip = body.style.overflowX;
2582
+ let clipped = false;
2583
+ if (getComputedStyle(body).overflowX === 'visible') {
2584
+ body.style.overflowX = 'clip';
2585
+ clipped = true;
2586
+ }
2587
+ const update = () => {
2588
+ const r = sl.el.getBoundingClientRect();
2589
+ const vw = document.documentElement.clientWidth || window.innerWidth;
2590
+ const L = side === 'right' ? 0 : Math.max(0, r.left);
2591
+ const R = side === 'left' ? 0 : Math.max(0, vw - r.right);
2592
+ list.style.clipPath = 'inset(-48px ' + -R + 'px -48px ' + -L + 'px)';
2593
+ };
2594
+ update();
2595
+ sl._on(window, 'resize', update);
2596
+ sl._sub('setPosition', update);
2597
+ const t = setTimeout(update, 120);
2598
+ return {
2599
+ destroy() {
2600
+ clearTimeout(t);
2601
+ list.style.overflow = '';
2602
+ list.style.clipPath = '';
2603
+ if (clipped) body.style.overflowX = prevClip;
2604
+ sl.el.classList.remove('mozert-bleed');
2605
+ },
2606
+ };
2607
+ },
2608
+ });
2609
+ })();
2610
+
2611
+
2612
+ return Mozert;
2613
+ })();
2614
+ export { Mozert };
2615
+ export default Mozert;