@mozert-studio/mozert-slider 0.0.0-stage → 1.1.1

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,1768 @@
1
+ /*!
2
+ * Mozert 1.1.1
3
+ * A dependency-free, responsive, touch-friendly carousel / slider.
4
+ * MIT License
5
+ */
6
+ (function (root, factory) {
7
+ if (typeof define === 'function' && define.amd) define([], factory);
8
+ else if (typeof module === 'object' && module.exports) { module.exports = factory(); module.exports.default = module.exports; }
9
+ else root.Mozert = factory();
10
+ }(typeof self !== 'undefined' ? self : this, function () {
11
+ 'use strict';
12
+ //#region core
13
+ const VERSION = '1.1.1';
14
+
15
+ const DEFAULTS = {
16
+ accessibility: true,
17
+ adaptiveHeight: false,
18
+ appendArrows: null,
19
+ appendDots: null,
20
+ arrows: true,
21
+ asNavFor: null,
22
+ autoplay: false,
23
+ disableOnInteraction: false,
24
+ autoplayReverse: false,
25
+ autoplaySpeed: 3000,
26
+ centerMode: false,
27
+ centerPadding: '50px',
28
+ cssEase: 'ease',
29
+ customPaging: null,
30
+ dots: false,
31
+ dotsClass: 'mozert-dots',
32
+ dragThreshold: 6,
33
+ draggable: true,
34
+ easing: 'linear',
35
+ edgeFriction: 0.15,
36
+ edgePadding: 0,
37
+ effect: 'slide',
38
+ fade: false,
39
+ focusOnSelect: false,
40
+ freeMode: false,
41
+ infinite: true,
42
+ initialSlide: 0,
43
+ labels: null,
44
+ lazyLoad: 'ondemand',
45
+ mobileFirst: false,
46
+ nextArrow: null,
47
+ pauseOnDotsHover: false,
48
+ pauseOnFocus: true,
49
+ pauseOnHover: true,
50
+ prevArrow: null,
51
+ respectReducedMotion: true,
52
+ respondTo: 'window',
53
+ responsive: null,
54
+ rows: 1,
55
+ rtl: false,
56
+ slide: '',
57
+ slideWidth: 0,
58
+ slidesPerRow: 1,
59
+ slidesToScroll: 1,
60
+ slidesToShow: 1,
61
+ spaceBetween: 0,
62
+ speed: 300,
63
+ stopOnLastSlide: false,
64
+ swipe: true,
65
+ swipeToSlide: false,
66
+ touchMove: true,
67
+ touchRatio: 1,
68
+ touchThreshold: 5,
69
+ useCSS: true,
70
+ useTransform: true,
71
+ variableWidth: false,
72
+ vertical: false,
73
+ verticalSwiping: false,
74
+ waitForAnimate: true,
75
+ zIndex: 1000,
76
+ on: null,
77
+ };
78
+
79
+ const LABELS = { slide: 'Slide {n} of {total}', prev: 'Previous slide', next: 'Next slide', goTo: 'Go to slide {n}', fullscreen: 'Toggle fullscreen', close: 'Close', zoom: 'Zoom', lightbox: 'Image viewer', imageOf: 'Image {n} of {total}', magnifier: 'Magnifier' };
80
+ const DEFAULT_PREV = '<button type="button" class="mozert-prev" aria-label="Previous slide">Previous</button>';
81
+ const DEFAULT_NEXT = '<button type="button" class="mozert-next" aria-label="Next slide">Next</button>';
82
+ const FOCUSABLE = 'a[href],button,input,select,textarea,[tabindex],[contenteditable="true"]';
83
+ const STASH_ATTRS = ['class', 'style', 'role', 'aria-hidden', 'aria-label', 'aria-roledescription', 'data-mozert-index'];
84
+
85
+ const STACKED = ['fade', 'flip', 'cube', 'cards', 'creative'];
86
+ const EASINGS = {
87
+ linear: (t) => t,
88
+ swing: (t) => 0.5 - Math.cos(t * Math.PI) / 2,
89
+ easeInQuad: (t) => t * t,
90
+ easeOutQuad: (t) => t * (2 - t),
91
+ easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
92
+ easeInCubic: (t) => t * t * t,
93
+ easeOutCubic: (t) => --t * t * t + 1,
94
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
95
+ easeInOutQuart: (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t),
96
+ };
97
+
98
+ const registry = {
99
+ get: (e) => e._mozert || undefined,
100
+ set: (e, v) => { e._mozert = v; },
101
+ delete: (e) => { delete e._mozert; },
102
+ };
103
+ const isEl = (v) => typeof Element !== 'undefined' && v instanceof Element;
104
+ const clamp = (v, a, b) => Math.min(b, Math.max(a, v));
105
+
106
+ function htmlToEl(html) {
107
+ const t = document.createElement('template');
108
+ t.innerHTML = String(html).trim();
109
+ return t.content.firstElementChild;
110
+ }
111
+
112
+ function resolveNode(v) {
113
+ if (!v) return null;
114
+ if (isEl(v)) return v;
115
+ if (typeof v === 'string') {
116
+ const t = v.trim();
117
+ return t.charAt(0) === '<' ? htmlToEl(t) : document.querySelector(t);
118
+ }
119
+ if (v[0] && isEl(v[0])) return v[0];
120
+ return null;
121
+ }
122
+
123
+ function parseData(el) {
124
+ const raw = el.getAttribute('data-mozert');
125
+ if (!raw) return {};
126
+ try {
127
+ return JSON.parse(raw);
128
+ } catch (e) {
129
+ console.warn('Mozert: could not parse data-mozert JSON', e);
130
+ return {};
131
+ }
132
+ }
133
+
134
+ class Mozert {
135
+ constructor(el, options = {}) {
136
+ if (typeof el === 'string') el = document.querySelector(el);
137
+ if (!isEl(el)) throw new TypeError('Mozert: a valid element or selector is required');
138
+ const existing = registry.get(el);
139
+ if (existing) return existing;
140
+
141
+ this.el = el;
142
+ const _o = Object.assign({}, options, parseData(el));
143
+ Mozert._boot.forEach((f) => f(el, _o));
144
+ this._base = Object.assign({}, DEFAULTS, options, parseData(el));
145
+ this.options = this._base;
146
+ this._dormant = false;
147
+ this.active = false;
148
+ this.canNav = false;
149
+ this.current = 0;
150
+ this.animating = false;
151
+ this.n = 0;
152
+
153
+ this._handlers = {};
154
+ this._sources = [];
155
+ this._filter = null;
156
+ this._stash = new WeakMap();
157
+ this._logical = 0;
158
+ this._t = 0;
159
+ this._cur = 0;
160
+ this._bp = null;
161
+ this._off = [];
162
+ this._life = [];
163
+ this._pause = new Set();
164
+ this._fxHooks = [];
165
+ this._mi = [];
166
+ this._release = null;
167
+ this._lockDrag = false;
168
+ this._autoOn = false;
169
+ this._dir = 1;
170
+ this._built = false;
171
+ this._initDone = false;
172
+ this._origDir = el.getAttribute('dir');
173
+ this._lastW = el.clientWidth;
174
+
175
+ registry.set(el, this);
176
+ this._collect();
177
+ this._bindLife();
178
+ this._apply(true);
179
+ }
180
+
181
+ /* ------------------------------------------------------------------ */
182
+ /* Public API */
183
+ /* ------------------------------------------------------------------ */
184
+
185
+ /** Read-only snapshot of the slider's current state. */
186
+ getState() {
187
+ return {
188
+ active: this.active,
189
+ current: this.current,
190
+ count: this.n,
191
+ total: this.trackLen || 0,
192
+ inner: this.Lc || 0,
193
+ clones: this.cloneCount || 0,
194
+ offset: this._cur || 0,
195
+ animating: this.animating,
196
+ canNavigate: this.canNav,
197
+ playing: this.isPlaying(),
198
+ breakpoint: this._bp,
199
+ };
200
+ }
201
+
202
+ slideCount() {
203
+ return this.n;
204
+ }
205
+
206
+ currentSlide() {
207
+ return this.current;
208
+ }
209
+
210
+ isPlaying() {
211
+ return !!(this.active && this._autoOn && !this._pause.has('user'));
212
+ }
213
+
214
+ get _c() {
215
+ return this.cloneCount;
216
+ }
217
+
218
+ next() {
219
+ if (!this.active || !this.canNav) return this;
220
+ this._slideTo(this.current + this.s.slidesToScroll);
221
+ return this;
222
+ }
223
+
224
+ prev() {
225
+ if (!this.active || !this.canNav) return this;
226
+ this._slideTo(this.current - this.s.slidesToScroll);
227
+ return this;
228
+ }
229
+
230
+ goTo(index, dontAnimate = false) {
231
+ if (!this.active || !this.canNav) return this;
232
+ index = parseInt(index, 10);
233
+ if (isNaN(index)) return this;
234
+ this._slideTo(this.s.infinite ? this._norm(index) : clamp(index, 0, Math.max(0, this.n - 1)), { animate: !dontAnimate });
235
+ return this;
236
+ }
237
+
238
+ play() {
239
+ const was = this.isPlaying();
240
+ this._autoOn = true;
241
+ this._pause.delete('user');
242
+ this._schedule();
243
+ this._live();
244
+ if (!was) this.emit('autoplayStart');
245
+ return this;
246
+ }
247
+
248
+ pause() {
249
+ const was = this.isPlaying();
250
+ this._autoOn = false;
251
+ this._pause.add('user');
252
+ clearTimeout(this._apTimer);
253
+ cancelAnimationFrame(this._apRaf);
254
+ this._live();
255
+ if (was) this.emit('autoplayStop');
256
+ return this;
257
+ }
258
+
259
+ getSlides() {
260
+ return this._units ? this._units.slice() : this._activeItems();
261
+ }
262
+
263
+ getOption(name) {
264
+ return this.options[name];
265
+ }
266
+
267
+ setOption(name, value, refresh) {
268
+ if (name && typeof name === 'object') {
269
+ refresh = value;
270
+ Object.assign(this._base, name);
271
+ } else {
272
+ this._base[name] = value;
273
+ }
274
+ if (refresh === false) {
275
+ // Only re-resolve settings that are read live (autoplay, pauseOn*, ...).
276
+ this._applyLive();
277
+ } else {
278
+ this._apply(false);
279
+ }
280
+ return this;
281
+ }
282
+
283
+ _applyLive() {
284
+ const r = this._resolve();
285
+ this.options = r.s;
286
+ if (this.active) {
287
+ this.s = this._prepare(r.s);
288
+ this._autoOn = !!this.s.autoplay && this.canNav;
289
+ if (this._autoOn) this._pause.delete('user');
290
+ this._live();
291
+ this._schedule();
292
+ }
293
+ }
294
+
295
+ add(markup, index, addBefore) {
296
+ const node = resolveNode(typeof markup === 'string' && markup.trim().charAt(0) !== '<' ? `<div>${markup}</div>` : markup);
297
+ if (!node) return false;
298
+ if (typeof index === 'boolean') {
299
+ addBefore = index;
300
+ index = null;
301
+ }
302
+ const items = this._activeItems();
303
+ if (index != null && (index < 0 || index >= items.length)) return false;
304
+ if (index == null || !items.length) {
305
+ if (addBefore) this._sources.unshift(node);
306
+ else this._sources.push(node);
307
+ } else {
308
+ const at = this._sources.indexOf(items[index]);
309
+ this._sources.splice(at + (addBefore ? 0 : 1), 0, node);
310
+ }
311
+ this._refreshKeep();
312
+ return this;
313
+ }
314
+
315
+ remove(index, removeBefore) {
316
+ const items = this._activeItems();
317
+ if (typeof index === 'boolean') {
318
+ removeBefore = index;
319
+ index = removeBefore ? 0 : items.length - 1;
320
+ } else {
321
+ index = removeBefore ? index - 1 : index;
322
+ }
323
+ if (index == null || index < 0 || index >= items.length) return false;
324
+ const node = items[index];
325
+ this._sources.splice(this._sources.indexOf(node), 1);
326
+ this._restoreItem(node);
327
+ if (node.parentNode) node.parentNode.removeChild(node);
328
+ if (index < this.current) this.current -= 1;
329
+ this._refreshKeep();
330
+ return this;
331
+ }
332
+
333
+ removeAll() {
334
+ this._activeItems().forEach((node) => {
335
+ this._sources.splice(this._sources.indexOf(node), 1);
336
+ this._restoreItem(node);
337
+ if (node.parentNode) node.parentNode.removeChild(node);
338
+ });
339
+ this.current = 0;
340
+ this._refreshKeep();
341
+ return this;
342
+ }
343
+
344
+ filter(selectorOrFn) {
345
+ this._filter = selectorOrFn || null;
346
+ this.current = 0;
347
+ this._refreshKeep();
348
+ return this;
349
+ }
350
+
351
+ unfilter() {
352
+ return this.filter(null);
353
+ }
354
+
355
+ /** Re-read options and rebuild everything, keeping the current slide. */
356
+ refresh() {
357
+ this._refreshKeep();
358
+ this.emit('reInit');
359
+ return this;
360
+ }
361
+
362
+ /** Re-measure and reposition, e.g. after the container was resized by script. */
363
+ setPosition() {
364
+ if (!this.active) return this;
365
+ this._layout();
366
+ this._paintCurrent(false);
367
+ this.emit('setPosition');
368
+ return this;
369
+ }
370
+
371
+ destroy() {
372
+ this.emit('destroy');
373
+ if (this.active) this._teardown();
374
+ this._sources.forEach((n) => this.el.appendChild(n));
375
+ this.active = false;
376
+ this._life.forEach((fn) => fn());
377
+ this._life = [];
378
+ if (this._ro) this._ro.disconnect();
379
+ registry.delete(this.el);
380
+ this._dormant = false;
381
+ this._handlers = {};
382
+ return this;
383
+ }
384
+
385
+ on(name, fn) {
386
+ (this._handlers[name] = this._handlers[name] || []).push(fn);
387
+ return this;
388
+ }
389
+
390
+ once(name, fn) {
391
+ const wrap = (...a) => {
392
+ this.off(name, wrap);
393
+ fn.apply(this, a);
394
+ };
395
+ return this.on(name, wrap);
396
+ }
397
+
398
+ one(name, fn) {
399
+ return this.once(name, fn);
400
+ }
401
+
402
+ off(name, fn) {
403
+ if (!name) this._handlers = {};
404
+ else if (!fn) delete this._handlers[name];
405
+ else this._handlers[name] = (this._handlers[name] || []).filter((f) => f !== fn);
406
+ return this;
407
+ }
408
+
409
+ emit(name, ...args) {
410
+ (this._handlers[name] || []).slice().forEach((fn) => fn.call(this, this, ...args));
411
+ const cb = this._base.on && this._base.on[name];
412
+ if (typeof cb === 'function') cb.call(this, this, ...args);
413
+ this.el.dispatchEvent(new CustomEvent('mozert:' + name, { bubbles: true, detail: { slider: this, args } }));
414
+ if (Mozert._bridge) Mozert._bridge(this, name, args);
415
+ }
416
+
417
+ static get(el) {
418
+ if (typeof el === 'string') el = document.querySelector(el);
419
+ return (isEl(el) && registry.get(el)) || null;
420
+ }
421
+
422
+ static initAll(root, options) {
423
+ const scope = root && root.querySelectorAll ? root : document;
424
+ const found = Array.from(scope.querySelectorAll('[data-mozert]'));
425
+ if (scope !== document && scope.matches && scope.matches('[data-mozert]')) found.unshift(scope);
426
+ return found.map((n) => Mozert.get(n) || new Mozert(n, options));
427
+ }
428
+
429
+ /* ------------------------------------------------------------------ */
430
+ /* Settings and responsive handling */
431
+ /* ------------------------------------------------------------------ */
432
+
433
+ _respondWidth() {
434
+ const mode = this._base.respondTo;
435
+ const w = window.innerWidth;
436
+ if (mode === 'slider') return this.el.clientWidth;
437
+ if (mode === 'min') return Math.min(w, this.el.clientWidth);
438
+ return w;
439
+ }
440
+
441
+ _resolve() {
442
+ const base = this._base;
443
+ const list = Array.isArray(base.responsive) ? base.responsive.filter((r) => r && typeof r.breakpoint === 'number') : [];
444
+ const w = this._respondWidth();
445
+ let hit = null;
446
+ if (base.mobileFirst) {
447
+ list.sort((a, b) => a.breakpoint - b.breakpoint).forEach((r) => {
448
+ if (w >= r.breakpoint) hit = r;
449
+ });
450
+ } else {
451
+ list.sort((a, b) => b.breakpoint - a.breakpoint).forEach((r) => {
452
+ if (w <= r.breakpoint) hit = r;
453
+ });
454
+ }
455
+ if (hit && hit.settings === 'destroy') return { bp: hit.breakpoint, dormant: true, s: Object.assign({}, base) };
456
+ return { bp: hit ? hit.breakpoint : null, dormant: false, s: Object.assign({}, base, hit ? hit.settings : {}) };
457
+ }
458
+
459
+ _prepare(raw) {
460
+ const s = Object.assign({}, raw);
461
+ s.slidesToShow = Math.max(1, parseFloat(s.slidesToShow) || 1);
462
+ s.slidesToScroll = Math.max(1, parseInt(s.slidesToScroll, 10) || 1);
463
+ s.rows = Math.max(1, parseInt(s.rows, 10) || 1);
464
+ s.slidesPerRow = Math.max(1, parseInt(s.slidesPerRow, 10) || 1);
465
+ s.effect = s.fade ? 'fade' : s.effect || 'slide';
466
+ s.fade = s.effect === 'fade';
467
+ s.stacked = STACKED.indexOf(s.effect) > -1;
468
+ if (s.stacked) {
469
+ s.slidesToShow = 1;
470
+ s.slidesToScroll = 1;
471
+ }
472
+ if (s.effect === 'coverflow' || s.effect === 'expand') s.centerMode = true;
473
+ if (s.freeMode || s.virtual || s.bleed) s.infinite = false;
474
+ if (s.marquee) {
475
+ s.infinite = true;
476
+ s.arrows = false;
477
+ s.dots = false;
478
+ s.autoplay = false;
479
+ }
480
+ s.spaceBetween = parseFloat(s.spaceBetween) || 0;
481
+ if (s.respectReducedMotion && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
482
+ s.speed = 0;
483
+ s.autoplay = false;
484
+ }
485
+ return s;
486
+ }
487
+
488
+ _apply(first) {
489
+ const r = this._resolve();
490
+ const changed = r.bp !== this._bp;
491
+ this._bp = r.bp;
492
+ this._raw = r.s;
493
+ this.options = r.s;
494
+ this._dormant = !!r.dormant;
495
+ if (r.dormant) {
496
+ if (this.active) {
497
+ this._teardown();
498
+ this.active = false;
499
+ this.emit('destroy');
500
+ }
501
+ if (changed && !first) this.emit('breakpoint', r.bp);
502
+ return;
503
+ }
504
+ this.s = this._prepare(r.s);
505
+ if (this.active) this._teardown();
506
+ this._build(!first);
507
+ if (changed && !first) this.emit('breakpoint', r.bp);
508
+ }
509
+
510
+ _refreshKeep() {
511
+ if (!this.active) {
512
+ this._apply(false);
513
+ return;
514
+ }
515
+ this._teardown();
516
+ this._build(true);
517
+ }
518
+
519
+ _bindLife() {
520
+ let timer;
521
+ const onResize = () => {
522
+ clearTimeout(timer);
523
+ timer = setTimeout(() => this._onResize(), 40);
524
+ };
525
+ window.addEventListener('resize', onResize);
526
+ window.addEventListener('orientationchange', onResize);
527
+ const onVis = () => {
528
+ if (document.hidden) this._holdOn('hidden');
529
+ else this._holdOff('hidden');
530
+ };
531
+ document.addEventListener('visibilitychange', onVis);
532
+ this._life.push(() => {
533
+ clearTimeout(timer);
534
+ window.removeEventListener('resize', onResize);
535
+ window.removeEventListener('orientationchange', onResize);
536
+ document.removeEventListener('visibilitychange', onVis);
537
+ });
538
+ if (typeof ResizeObserver !== 'undefined') {
539
+ this._ro = new ResizeObserver(() => {
540
+ const w = this.el.clientWidth;
541
+ if (w !== this._lastW) {
542
+ this._lastW = w;
543
+ onResize();
544
+ }
545
+ });
546
+ this._ro.observe(this.el);
547
+ }
548
+ }
549
+
550
+ _onResize() {
551
+ const r = this._resolve();
552
+ const must = r.bp !== this._bp || (r.dormant && this.active) || (!r.dormant && !this.active);
553
+ if (must) {
554
+ this._apply(false);
555
+ return;
556
+ }
557
+ if (!this.active) return;
558
+ this._layout();
559
+ if (this._computeCan() !== this.canNav) {
560
+ this._refreshKeep();
561
+ return;
562
+ }
563
+ this._paintCurrent(false);
564
+ this.emit('setPosition');
565
+ }
566
+
567
+ /* ------------------------------------------------------------------ */
568
+ /* DOM build / teardown */
569
+ /* ------------------------------------------------------------------ */
570
+
571
+ _collect() {
572
+ const kids = Array.from(this.el.children);
573
+ const sel = this._base.slide;
574
+ this._sources = sel ? kids.filter((k) => k.matches(sel)) : kids;
575
+ }
576
+
577
+ _activeItems() {
578
+ const f = this._filter;
579
+ if (!f) return this._sources.slice();
580
+ return this._sources.filter((el, i) => (typeof f === 'function' ? f.call(el, el, i) : el.matches(f)));
581
+ }
582
+
583
+ _remember(el) {
584
+ if (this._stash.has(el)) return;
585
+ const o = {};
586
+ STASH_ATTRS.forEach((a) => (o[a] = el.getAttribute(a)));
587
+ this._stash.set(el, o);
588
+ }
589
+
590
+ _restoreItem(el) {
591
+ const o = this._stash.get(el);
592
+ if (!o) return;
593
+ STASH_ATTRS.forEach((a) => (o[a] === null ? el.removeAttribute(a) : el.setAttribute(a, o[a])));
594
+ el.querySelectorAll('[data-mozert-tabindex]').forEach((f) => {
595
+ const v = f.getAttribute('data-mozert-tabindex');
596
+ if (v === '') f.removeAttribute('tabindex');
597
+ else f.setAttribute('tabindex', v);
598
+ f.removeAttribute('data-mozert-tabindex');
599
+ });
600
+ el.querySelectorAll('[data-mozert-bg]').forEach((b) => b.removeAttribute('data-mozert-bg'));
601
+ this._stash.delete(el);
602
+ }
603
+
604
+ _label(key, vars) {
605
+ const l = Object.assign({}, LABELS, this.s && this.s.labels);
606
+ return String(l[key]).replace(/\{(\w+)\}/g, (m, k) => (vars && vars[k] !== undefined ? vars[k] : m));
607
+ }
608
+
609
+ _px(v) {
610
+ const p = String(v || '0');
611
+ if (p.indexOf('%') > -1) return (parseFloat(p) / 100) * this.el.clientWidth;
612
+ return parseFloat(p) || 0;
613
+ }
614
+
615
+ _padPx() {
616
+ return this._px(this.s.centerPadding);
617
+ }
618
+
619
+ _edgePad() {
620
+ const s = this.s;
621
+ return s.centerMode ? this._padPx() : this._px(s.edgePadding);
622
+ }
623
+
624
+ /** Works out how many slides fit (can be fractional) before the first layout. */
625
+ _calcShow() {
626
+ const s = this.s;
627
+ let show = s.slidesToShow;
628
+ if (s.slideWidth && !s.vertical && !s.variableWidth) {
629
+ const inner = Math.max(1, (this.list ? this.list.clientWidth : this.el.clientWidth) - 2 * this._edgePad());
630
+ show = Math.max(1, (inner + s.spaceBetween) / (parseFloat(s.slideWidth) + s.spaceBetween));
631
+ }
632
+ this._show = show;
633
+ this._showC = Math.ceil(show - 1e-6);
634
+ }
635
+
636
+ _build(keep) {
637
+ const s = this.s;
638
+ const el = this.el;
639
+ const items = this._activeItems();
640
+ // Slides hidden by filter() leave the DOM until unfilter() (or destroy) puts them back.
641
+ this._sources.forEach((n) => {
642
+ if (items.indexOf(n) === -1 && n.parentNode === el) el.removeChild(n);
643
+ });
644
+
645
+ el.classList.add('mozert-slider', 'mozert-initialized');
646
+ el.classList.toggle('mozert-vertical', !!s.vertical);
647
+ el.classList.toggle('mozert-rtl', !!s.rtl);
648
+ el.classList.toggle('mozert-fade', !!s.fade);
649
+ el.classList.toggle('mozert-fx', s.effect !== 'slide' && !s.fade);
650
+ if (s.effect !== 'slide') el.classList.add('mozert-effect-' + s.effect);
651
+ el.classList.toggle('mozert-center', !!s.centerMode);
652
+ el.classList.toggle('mozert-adaptive', !!s.adaptiveHeight);
653
+ el.classList.toggle('mozert-draggable', !!(s.draggable || s.swipe));
654
+ if (s.rtl) el.setAttribute('dir', 'rtl');
655
+ el.setAttribute('role', 'region');
656
+ el.setAttribute('aria-roledescription', 'carousel');
657
+
658
+ this.list = document.createElement('div');
659
+ this.list.className = 'mozert-list';
660
+ this.track = document.createElement('div');
661
+ this.track.className = 'mozert-track';
662
+ this.list.appendChild(this.track);
663
+ el.appendChild(this.list);
664
+
665
+ // Group into pages when grid mode is on.
666
+ let units = items;
667
+ if (s.rows > 1 || s.slidesPerRow > 1) {
668
+ units = [];
669
+ const per = s.rows * s.slidesPerRow;
670
+ for (let i = 0; i < items.length; i += per) {
671
+ const page = document.createElement('div');
672
+ page.className = 'mozert-page';
673
+ for (let r = 0; r < s.rows; r++) {
674
+ const row = document.createElement('div');
675
+ row.className = 'mozert-row';
676
+ for (let c = 0; c < s.slidesPerRow; c++) {
677
+ const it = items[i + r * s.slidesPerRow + c];
678
+ if (!it) break;
679
+ this._remember(it);
680
+ it.classList.add('mozert-cell');
681
+ it.style.width = 100 / s.slidesPerRow + '%';
682
+ row.appendChild(it);
683
+ }
684
+ if (row.children.length) page.appendChild(row);
685
+ }
686
+ units.push(page);
687
+ }
688
+ }
689
+ this._items = items;
690
+ this._units = units;
691
+ this.n = units.length;
692
+ this._calcShow();
693
+
694
+ units.forEach((u, i) => {
695
+ this._remember(u);
696
+ u.classList.add('mozert-slide');
697
+ u.setAttribute('data-mozert-index', i);
698
+ u.setAttribute('role', 'group');
699
+ u.setAttribute('aria-roledescription', 'slide');
700
+ u.setAttribute('aria-label', this._label('slide', { n: i + 1, total: units.length }));
701
+ u.querySelectorAll('[data-lazy-bg]').forEach((b, k) => b.setAttribute('data-mozert-bg', String(k)));
702
+ this.track.appendChild(u);
703
+ });
704
+ this.dom = units.slice();
705
+ this.cloneCount = 0;
706
+
707
+ // First pass (only for variable widths) so we know whether navigation is needed.
708
+ if (s.variableWidth) this._layout();
709
+ this.canNav = this._computeCan();
710
+
711
+ if (s.infinite && this.canNav && !s.stacked) this._addClones();
712
+
713
+ this._layout();
714
+ this.maxIndex = this._maxIndex();
715
+ this.current = clamp(keep ? this.current : parseInt(s.initialSlide, 10) || 0, 0, s.infinite ? Math.max(0, this.n - 1) : this.maxIndex);
716
+ if (!this.canNav) this.current = 0;
717
+ this._logical = this.current;
718
+
719
+ if (!this.canNav) el.classList.add('mozert-static');
720
+ else el.classList.remove('mozert-static');
721
+
722
+ this._buildArrows();
723
+ this._buildDots();
724
+ this._bindEvents();
725
+ this.active = true;
726
+ this._built = true;
727
+ this._paintCurrent(false);
728
+ if (s.fade) {
729
+ // Show the first slide immediately, then switch the cross-fade on.
730
+ this.dom.forEach((e) => (e.style.transition = 'none'));
731
+ void this.track.offsetWidth;
732
+ this.dom.forEach((e) => (e.style.transition = s.speed > 0 ? 'opacity ' + s.speed + 'ms ' + s.cssEase : 'none'));
733
+ }
734
+ this._updateNav();
735
+ this._lazy();
736
+ this._mi = Mozert._mods.map((m) => (m.create ? m.create(this) : null)).filter(Boolean);
737
+
738
+ this._autoOn = !!s.autoplay && this.canNav;
739
+ if (!this._initDone) {
740
+ this._initDone = true;
741
+ this._autoOn = !!s.autoplay && this.canNav;
742
+ this.emit('init');
743
+ } else if (keep) {
744
+ this.emit('reInit');
745
+ }
746
+ this._live();
747
+ this._schedule();
748
+ this.emit('setPosition');
749
+ }
750
+
751
+ _addClones() {
752
+ const s = this.s;
753
+ const n = this.n;
754
+ const count = s.variableWidth ? n * 2 : this._showC + s.slidesToScroll + (s.centerMode ? 1 : 0);
755
+ this.cloneCount = count;
756
+ const mk = (idx) => {
757
+ const base = this._units[((idx % n) + n) % n];
758
+ const c = base.cloneNode(true);
759
+ c.removeAttribute('id');
760
+ c.querySelectorAll('[id]').forEach((x) => x.removeAttribute('id'));
761
+ c.classList.remove('mozert-active', 'mozert-center');
762
+ c.classList.add('mozert-cloned');
763
+ c.setAttribute('aria-hidden', 'true');
764
+ c.removeAttribute('role');
765
+ c.removeAttribute('aria-label');
766
+ c.removeAttribute('aria-roledescription');
767
+ c.querySelectorAll(FOCUSABLE).forEach((f) => {
768
+ f.setAttribute('data-mozert-tabindex', f.getAttribute('tabindex') || '');
769
+ f.setAttribute('tabindex', '-1');
770
+ });
771
+ return c;
772
+ };
773
+ const before = [];
774
+ const after = [];
775
+ for (let i = -count; i < 0; i++) before.push(mk(i));
776
+ for (let i = 0; i < count; i++) after.push(mk(n + i));
777
+ const first = this._units[0];
778
+ before.forEach((c) => this.track.insertBefore(c, first));
779
+ after.forEach((c) => this.track.appendChild(c));
780
+ this.dom = before.concat(this._units, after);
781
+ }
782
+
783
+ _teardown() {
784
+ this._mi.forEach((m) => m.destroy && m.destroy());
785
+ this._mi = [];
786
+ this._fxHooks = [];
787
+ this._release = null;
788
+ this._lockDrag = false;
789
+ this._off.forEach((fn) => fn());
790
+ this._off = [];
791
+ clearTimeout(this._animTimer);
792
+ clearTimeout(this._apTimer);
793
+ if (this._raf) cancelAnimationFrame(this._raf);
794
+ this._raf = 0;
795
+ this.animating = false;
796
+ const el = this.el;
797
+
798
+ (this._items || []).forEach((it) => {
799
+ this._restoreItem(it);
800
+ // Slides removed through remove()/removeAll() must not come back.
801
+ if (this._sources.indexOf(it) > -1) el.appendChild(it);
802
+ });
803
+ (this._units || []).forEach((u) => {
804
+ if (this._items.indexOf(u) === -1) this._stash.delete(u);
805
+ });
806
+ if (this.list && this.list.parentNode) this.list.parentNode.removeChild(this.list);
807
+ (this._navNodes || []).forEach((fn) => fn());
808
+ this._navNodes = [];
809
+
810
+ Array.from(el.classList)
811
+ .filter((c) => c.indexOf('mozert') === 0)
812
+ .forEach((c) => el.classList.remove(c));
813
+ el.removeAttribute('role');
814
+ el.removeAttribute('aria-roledescription');
815
+ if (this._origDir === null) el.removeAttribute('dir');
816
+ else el.setAttribute('dir', this._origDir);
817
+ this.dom = [];
818
+ this.active = false;
819
+ }
820
+
821
+ /* ------------------------------------------------------------------ */
822
+ /* Measuring */
823
+ /* ------------------------------------------------------------------ */
824
+
825
+ _layout() {
826
+ const s = this.s;
827
+ const v = !!s.vertical;
828
+ const list = this.list;
829
+ const track = this.track;
830
+ const dom = this.dom;
831
+ this._calcShow();
832
+ const show = this._show;
833
+ const gap = s.spaceBetween;
834
+ const pad = this._edgePad();
835
+ const mProp = v ? 'marginBottom' : s.rtl ? 'marginLeft' : 'marginRight';
836
+ list.style.padding = v ? pad + 'px 0' : '0 ' + pad + 'px';
837
+
838
+ if (v) {
839
+ dom.forEach((e) => (e.style.height = ''));
840
+ track.style.height = '';
841
+ let h = 0;
842
+ this._units.forEach((u) => (h = Math.max(h, u.offsetHeight)));
843
+ this.slideSize = h;
844
+ dom.forEach((e) => {
845
+ e.style.height = h + 'px';
846
+ e.style[mProp] = gap ? gap + 'px' : '';
847
+ });
848
+ this.Lc = h * show + gap * (show - 1);
849
+ list.style.height = this.Lc + 2 * pad + 'px';
850
+ this.trackLen = (h + gap) * dom.length - gap;
851
+ track.style.height = this.trackLen + gap + 'px';
852
+ } else {
853
+ this.Lc = Math.max(0, list.clientWidth - 2 * pad);
854
+ if (!s.variableWidth) {
855
+ const w = s.slideWidth ? parseFloat(s.slideWidth) : (this.Lc - gap * (show - 1)) / show;
856
+ this.slideSize = w;
857
+ dom.forEach((e) => {
858
+ e.style.width = w + 'px';
859
+ e.style[mProp] = gap ? gap + 'px' : '';
860
+ });
861
+ this.trackLen = (w + gap) * dom.length - gap;
862
+ track.style.width = this.trackLen + gap + 'px';
863
+ } else {
864
+ dom.forEach((e) => (e.style[mProp] = gap ? gap + 'px' : ''));
865
+ track.style.width = '100000px';
866
+ }
867
+ }
868
+
869
+ const tr = track.getBoundingClientRect();
870
+ this._mags = [];
871
+ this._sizes = [];
872
+ dom.forEach((e) => {
873
+ const r = e.getBoundingClientRect();
874
+ if (v) {
875
+ this._mags.push(r.top - tr.top);
876
+ this._sizes.push(r.height);
877
+ } else if (s.rtl) {
878
+ this._mags.push(tr.right - r.right);
879
+ this._sizes.push(r.width);
880
+ } else {
881
+ this._mags.push(r.left - tr.left);
882
+ this._sizes.push(r.width);
883
+ }
884
+ });
885
+ if (!v && s.variableWidth) {
886
+ let end = 0;
887
+ this._mags.forEach((m, i) => (end = Math.max(end, m + this._sizes[i])));
888
+ this.trackLen = Math.ceil(end);
889
+ track.style.width = this.trackLen + 'px';
890
+ }
891
+ const cc = this.cloneCount;
892
+ const last = cc + this.n - 1;
893
+ this._realLen = this.n ? this._mags[last] + this._sizes[last] - this._mags[cc] : 0;
894
+
895
+ if (s.stacked) {
896
+ const sz = this.slideSize;
897
+ dom.forEach((e, i) => {
898
+ e.style.position = 'relative';
899
+ if (v) e.style.top = -i * sz + 'px';
900
+ else e.style.left = (s.rtl ? i * sz : -i * sz) + 'px';
901
+ });
902
+ }
903
+ this._adaptHeight();
904
+ this._arrowY();
905
+ this.maxIndex = this._maxIndex();
906
+ }
907
+
908
+ _computeCan() {
909
+ const s = this.s;
910
+ if (s.variableWidth) return this.n > 1 && this._realLen > this.Lc + 1;
911
+ return this.n > this._show + 1e-6;
912
+ }
913
+
914
+ _maxIndex() {
915
+ const s = this.s;
916
+ if (s.infinite || s.stacked) return Math.max(0, this.n - 1);
917
+ if (s.centerMode) return Math.max(0, this.n - 1);
918
+ if (s.variableWidth) {
919
+ const need = this._realLen - this.Lc - 0.5;
920
+ if (need <= 0) return 0;
921
+ for (let i = 0; i < this.n; i++) if (this._mags[this.cloneCount + i] - this._mags[this.cloneCount] >= need) return i;
922
+ return this.n - 1;
923
+ }
924
+ return Math.max(0, Math.ceil(this.n - this._show - 1e-6));
925
+ }
926
+
927
+ _adaptHeight() {
928
+ const s = this.s;
929
+ if (!s.adaptiveHeight || s.vertical || this._show !== 1) return;
930
+ const e = this.dom[this.cloneCount + this._logical] || this.dom[0];
931
+ if (!e) return;
932
+ this.list.style.transition = s.speed > 0 ? 'height ' + s.speed + 'ms ' + s.cssEase : 'none';
933
+ this.list.style.height = e.offsetHeight + 'px';
934
+ this._arrowY(e.offsetHeight);
935
+ }
936
+
937
+ /** Keeps arrows centred on the slides, not on the whole slider (dots/pagination sit below the list). */
938
+ _arrowY(h) {
939
+ const l = this.list;
940
+ if (!l || this.s.vertical) return;
941
+ const hh = h === undefined ? l.offsetHeight : h;
942
+ this.el.style.setProperty('--mozert-arrow-y', l.offsetTop + hh / 2 + 'px');
943
+ }
944
+
945
+ _norm(i) {
946
+ const n = this.n;
947
+ if (this.s.infinite || this.s.stacked) return ((i % n) + n) % n;
948
+ return clamp(i, 0, this.maxIndex);
949
+ }
950
+
951
+ _pos(L) {
952
+ const s = this.s;
953
+ const k = this.cloneCount + L;
954
+ if (this._mags[k] === undefined) return this._t;
955
+ let mag = this._mags[k];
956
+ let c = 0;
957
+ if (s.centerMode) c = (this.Lc - this._sizes[k]) / 2;
958
+ else if (!s.infinite) mag = Math.min(mag, Math.max(0, this.trackLen - this.Lc));
959
+ const dir = s.rtl && !s.vertical ? -1 : 1;
960
+ return dir * (c - mag);
961
+ }
962
+
963
+ /* ------------------------------------------------------------------ */
964
+ /* Movement */
965
+ /* ------------------------------------------------------------------ */
966
+
967
+ _paint(val, animate) {
968
+ const s = this.s;
969
+ const track = this.track;
970
+ this._cur = val;
971
+ if (s.useTransform) track.style.transform = s.vertical ? 'translate3d(0,' + val + 'px,0)' : 'translate3d(' + val + 'px,0,0)';
972
+ else track.style[s.vertical ? 'top' : 'left'] = val + 'px';
973
+ if (this._fxHooks.length) this._fx(this._pvFromT(val), !!animate);
974
+ }
975
+
976
+ /** Visual position in slide units (0 = first slide at the anchor), used by effects, scrollbar and parallax. */
977
+ _pvFromT(t) {
978
+ const s = this.s;
979
+ if (s.variableWidth || !this._mags || !this._mags.length) return this._logical;
980
+ const dir = s.rtl && !s.vertical ? -1 : 1;
981
+ const c = s.centerMode ? (this.Lc - this.slideSize) / 2 : 0;
982
+ const pitch = this.slideSize + s.spaceBetween;
983
+ return (c - dir * t - this._mags[this.cloneCount]) / (pitch || 1);
984
+ }
985
+
986
+ _fx(pv, animate) {
987
+ for (let i = 0; i < this._fxHooks.length; i++) this._fxHooks[i](pv, animate, this.s.speed);
988
+ }
989
+
990
+ /** For modules: run fn(pv, animate, speed) whenever the visual position changes. */
991
+ _onFx(fn) {
992
+ this._fxHooks.push(fn);
993
+ }
994
+
995
+ /** For modules: subscribe to a slider event for the life of this build. */
996
+ _sub(name, fn) {
997
+ this.on(name, fn);
998
+ this._off.push(() => this.off(name, fn));
999
+ }
1000
+
1001
+ _user() {
1002
+ if (this.s.disableOnInteraction && this._autoOn) this.pause();
1003
+ }
1004
+
1005
+ _setPos(t, animate, opts) {
1006
+ const s = this.s;
1007
+ const track = this.track;
1008
+ if (this._raf) cancelAnimationFrame(this._raf);
1009
+ this._raf = 0;
1010
+ if (animate && s.speed > 0) {
1011
+ const dur = opts && opts.duration ? opts.duration : s.speed;
1012
+ if (s.useCSS) {
1013
+ track.style.transition = (s.useTransform ? 'transform' : s.vertical ? 'top' : 'left') + ' ' + dur + 'ms ' + (opts && opts.ease ? opts.ease : s.cssEase);
1014
+ this._paint(t, true);
1015
+ } else {
1016
+ track.style.transition = 'none';
1017
+ const from = this._cur;
1018
+ const ease = typeof s.easing === 'function' ? s.easing : EASINGS[s.easing] || EASINGS.linear;
1019
+ const t0 = performance.now();
1020
+ const step = (now) => {
1021
+ const p = Math.min(1, (now - t0) / s.speed);
1022
+ this._paint(from + (t - from) * ease(p));
1023
+ this._raf = p < 1 ? requestAnimationFrame(step) : 0;
1024
+ };
1025
+ this._raf = requestAnimationFrame(step);
1026
+ }
1027
+ } else {
1028
+ track.style.transition = 'none';
1029
+ this._paint(t);
1030
+ void track.offsetWidth;
1031
+ }
1032
+ this._t = t;
1033
+ }
1034
+
1035
+ _paintCurrent(animate) {
1036
+ if (this.s.stacked) this._fx(this._logical, false);
1037
+ else this._setPos(this._pos(this._logical), animate);
1038
+ this._updateSlideClasses(this._logical);
1039
+ this._adaptHeight();
1040
+ }
1041
+
1042
+ _snapBack() {
1043
+ if (this.s.stacked) this._fx(this._logical, true);
1044
+ else this._setPos(this._pos(this._logical), true);
1045
+ }
1046
+
1047
+ _slideTo(target, opts = {}) {
1048
+ const s = this.s;
1049
+ if (!this.active) return false;
1050
+ if (!this.canNav) {
1051
+ // A strip that shows every slide cannot move, but a synced partner can still change which slide is highlighted.
1052
+ if (opts.fromSync) {
1053
+ const sel = opts.sel !== undefined && opts.sel !== 0 ? opts.sel : null;
1054
+ if (sel !== this._sel) {
1055
+ this._sel = sel;
1056
+ this._updateSlideClasses(this._logical);
1057
+ }
1058
+ }
1059
+ return false;
1060
+ }
1061
+ if (this.animating && !opts.fromSync && s.waitForAnimate) return false;
1062
+ let next = target;
1063
+ if (!s.infinite || s.stacked) next = s.stacked ? this._norm(next) : clamp(next, 0, this.maxIndex);
1064
+ const real = this._norm(next);
1065
+ // A synced partner follows the requested slide even when this slider cannot scroll that far
1066
+ // (a thumbnail strip stops at its last full page, but the main slider should still go to the clicked one).
1067
+ const want = s.infinite || s.stacked ? real : clamp(target, 0, this.n - 1);
1068
+ if (next === this.current && !this.animating && !opts.force) {
1069
+ if (opts.fromSync) {
1070
+ // Already at the clamped position: only move the highlight to the slide the partner asked for.
1071
+ const sel = opts.sel !== undefined && opts.sel !== next ? opts.sel : null;
1072
+ if (sel !== this._sel) {
1073
+ this._sel = sel;
1074
+ this._updateSlideClasses(this._logical);
1075
+ }
1076
+ } else {
1077
+ // The strip already sits on this index (its last full page), but the partner may still be elsewhere.
1078
+ if (want !== next) {
1079
+ this._sel = want;
1080
+ this._updateSlideClasses(this._logical);
1081
+ }
1082
+ this._syncNav(want);
1083
+ }
1084
+ return false;
1085
+ }
1086
+
1087
+ const old = this.current;
1088
+ this.emit('beforeChange', old, real);
1089
+ this._sel = opts.fromSync ? (opts.sel !== undefined && opts.sel !== next ? opts.sel : null) : want !== next ? want : null;
1090
+ if (!opts.fromSync) this._syncNav(want);
1091
+ clearTimeout(this._animTimer);
1092
+ this.current = real;
1093
+ this._logical = next;
1094
+ this._updateNav();
1095
+ this._updateSlideClasses(next);
1096
+ this._lazy();
1097
+
1098
+ const animate = opts.animate !== false;
1099
+ if (s.stacked) this._fx(next, animate && s.speed > 0);
1100
+ else this._setPos(this._pos(next), animate);
1101
+ if (!animate || s.speed <= 0) {
1102
+ this._finish();
1103
+ } else {
1104
+ this.animating = true;
1105
+ this._animTimer = setTimeout(() => this._finish(), s.speed);
1106
+ }
1107
+ return true;
1108
+ }
1109
+
1110
+ _finish() {
1111
+ this.animating = false;
1112
+ if (this._logical !== this.current) {
1113
+ this._logical = this.current;
1114
+ if (!this.s.stacked) this._setPos(this._pos(this.current), false);
1115
+ this._updateSlideClasses(this._logical);
1116
+ }
1117
+ this._adaptHeight();
1118
+ this.emit('afterChange', this.current);
1119
+ this._schedule();
1120
+ }
1121
+
1122
+ _updateSlideClasses(L) {
1123
+ const s = this.s;
1124
+ const show = this._show;
1125
+ const showC = this._showC;
1126
+ const half = Math.floor(showC / 2);
1127
+ const t = s.stacked ? 0 : this._pos(L);
1128
+ this.dom.forEach((e, k) => {
1129
+ const l = k - this.cloneCount;
1130
+ let active;
1131
+ let centered = false;
1132
+ if (s.stacked) {
1133
+ active = l === L;
1134
+ } else if (s.variableWidth) {
1135
+ const d = s.rtl ? this._mags[k] - t : this._mags[k] + t;
1136
+ active = d + this._sizes[k] > 0.5 && d < this.Lc - 0.5;
1137
+ centered = s.centerMode && l === L;
1138
+ } else if (s.centerMode) {
1139
+ active = l >= L - half && l <= L + half - (showC % 2 === 0 ? 1 : 0);
1140
+ centered = l === L;
1141
+ } else {
1142
+ active = l >= L && l <= L + showC - 1;
1143
+ }
1144
+ e.classList.toggle('mozert-active', active);
1145
+ e.classList.toggle('mozert-center', centered);
1146
+ e.classList.toggle('mozert-current', l === (this._sel != null ? this._sel : L));
1147
+ e.setAttribute('aria-hidden', active ? 'false' : 'true');
1148
+ if (s.stacked) e.style.zIndex = l === L ? s.zIndex : s.zIndex - 2;
1149
+ if (!e.classList.contains('mozert-cloned')) {
1150
+ e.querySelectorAll(FOCUSABLE).forEach((f) => {
1151
+ if (active) {
1152
+ if (f.hasAttribute('data-mozert-tabindex')) {
1153
+ const v = f.getAttribute('data-mozert-tabindex');
1154
+ if (v === '') f.removeAttribute('tabindex');
1155
+ else f.setAttribute('tabindex', v);
1156
+ f.removeAttribute('data-mozert-tabindex');
1157
+ }
1158
+ } else if (!f.hasAttribute('data-mozert-tabindex')) {
1159
+ f.setAttribute('data-mozert-tabindex', f.getAttribute('tabindex') || '');
1160
+ f.setAttribute('tabindex', '-1');
1161
+ }
1162
+ });
1163
+ }
1164
+ });
1165
+ }
1166
+
1167
+ /* ------------------------------------------------------------------ */
1168
+ /* Arrows and dots */
1169
+ /* ------------------------------------------------------------------ */
1170
+
1171
+ _buildArrows() {
1172
+ this._navNodes = this._navNodes || [];
1173
+ this._arrows = { prev: null, next: null };
1174
+ this._prev = this._next = null;
1175
+ const s = this.s;
1176
+ if (!s.arrows || !this.canNav) return;
1177
+ const mount = resolveNode(s.appendArrows) || this.el;
1178
+ const make = (val, html, cls, handler) => {
1179
+ const node = resolveNode(val) || htmlToEl(html);
1180
+ const created = !(val && !(typeof val === 'string' && val.trim().charAt(0) === '<'));
1181
+ node.classList.add('mozert-arrow', cls);
1182
+ if (!node.parentNode || created) mount.appendChild(node);
1183
+ const lab = this._label(cls === 'mozert-prev' ? 'prev' : 'next');
1184
+ if (!val || (!node.getAttribute('aria-label') && !node.textContent.trim())) node.setAttribute('aria-label', lab);
1185
+ node.addEventListener('click', handler);
1186
+ this._navNodes.push(() => {
1187
+ node.removeEventListener('click', handler);
1188
+ node.classList.remove('mozert-arrow', cls, 'mozert-disabled');
1189
+ node.removeAttribute('aria-disabled');
1190
+ if (created && node.parentNode) node.parentNode.removeChild(node);
1191
+ });
1192
+ return node;
1193
+ };
1194
+ this._arrows.prev = make(s.prevArrow, DEFAULT_PREV, 'mozert-prev', (e) => {
1195
+ e.preventDefault();
1196
+ this._user();
1197
+ this.prev();
1198
+ });
1199
+ this._arrows.next = make(s.nextArrow, DEFAULT_NEXT, 'mozert-next', (e) => {
1200
+ e.preventDefault();
1201
+ this._user();
1202
+ this.next();
1203
+ });
1204
+ this._prev = this._arrows.prev;
1205
+ this._next = this._arrows.next;
1206
+ }
1207
+
1208
+ _dotCount() {
1209
+ const s = this.s;
1210
+ if (!this.canNav) return 0;
1211
+ if (s.infinite) return Math.ceil(this.n / s.slidesToScroll);
1212
+ return Math.ceil(this.maxIndex / s.slidesToScroll) + 1;
1213
+ }
1214
+
1215
+ _buildDots() {
1216
+ this._dots = null;
1217
+ const s = this.s;
1218
+ if (!s.dots || !this.canNav) return;
1219
+ const count = this._dotCount();
1220
+ const ul = document.createElement('ul');
1221
+ ul.className = s.dotsClass;
1222
+ ul.setAttribute('role', 'tablist');
1223
+ for (let i = 0; i < count; i++) {
1224
+ const li = document.createElement('li');
1225
+ li.setAttribute('role', 'presentation');
1226
+ let inner = typeof s.customPaging === 'function' ? s.customPaging(this, i) : null;
1227
+ if (typeof inner === 'string') inner = htmlToEl(inner.trim().charAt(0) === '<' ? inner : '<button type="button">' + inner + '</button>');
1228
+ if (!isEl(inner)) {
1229
+ inner = document.createElement('button');
1230
+ inner.type = 'button';
1231
+ inner.setAttribute('aria-label', this._label('goTo', { n: i + 1 }));
1232
+ }
1233
+ inner.setAttribute('role', 'tab');
1234
+ li.appendChild(inner);
1235
+ li.addEventListener('click', (e) => {
1236
+ e.preventDefault();
1237
+ this._user();
1238
+ this._slideTo(this._dotTarget(i));
1239
+ });
1240
+ ul.appendChild(li);
1241
+ }
1242
+ (resolveNode(s.appendDots) || this.el).appendChild(ul);
1243
+ this._dots = ul;
1244
+ this._navNodes.push(() => ul.parentNode && ul.parentNode.removeChild(ul));
1245
+ if (s.pauseOnDotsHover) {
1246
+ const a = () => this._holdOn('dots');
1247
+ const b = () => this._holdOff('dots');
1248
+ ul.addEventListener('mouseenter', a);
1249
+ ul.addEventListener('mouseleave', b);
1250
+ }
1251
+ }
1252
+
1253
+ _dotTarget(i) {
1254
+ const s = this.s;
1255
+ const t = i * s.slidesToScroll;
1256
+ return s.infinite ? t : Math.min(t, this.maxIndex);
1257
+ }
1258
+
1259
+ _updateNav() {
1260
+ const s = this.s;
1261
+ if (this._dots) {
1262
+ const total = this._dots.children.length;
1263
+ const idx = s.infinite ? Math.floor(this.current / s.slidesToScroll) : Math.ceil(this.current / s.slidesToScroll);
1264
+ Array.from(this._dots.children).forEach((li, i) => {
1265
+ const on = i === Math.min(total - 1, idx);
1266
+ li.classList.toggle('mozert-active', on);
1267
+ if (li.firstElementChild) li.firstElementChild.setAttribute('aria-selected', on ? 'true' : 'false');
1268
+ });
1269
+ }
1270
+ const { prev, next } = this._arrows || {};
1271
+ if (!s.infinite && prev && next) {
1272
+ const setD = (node, d) => {
1273
+ node.classList.toggle('mozert-disabled', d);
1274
+ node.setAttribute('aria-disabled', d ? 'true' : 'false');
1275
+ if ('disabled' in node) node.disabled = d;
1276
+ };
1277
+ setD(prev, this.current <= 0);
1278
+ setD(next, this.current >= this.maxIndex);
1279
+ }
1280
+ }
1281
+
1282
+ /* ------------------------------------------------------------------ */
1283
+ /* Events: drag, keyboard, hover, focus */
1284
+ /* ------------------------------------------------------------------ */
1285
+
1286
+ _axis() {
1287
+ const s = this.s;
1288
+ if (s.vertical) return s.verticalSwiping ? 'y' : null;
1289
+ return 'x';
1290
+ }
1291
+
1292
+ _on(target, type, fn, opts) {
1293
+ target.addEventListener(type, fn, opts);
1294
+ this._off.push(() => target.removeEventListener(type, fn, opts));
1295
+ }
1296
+
1297
+ _bindEvents() {
1298
+ const s = this.s;
1299
+ const el = this.el;
1300
+ const list = this.list;
1301
+ const axis = this._axis();
1302
+ list.style.touchAction = axis === 'x' ? 'pan-y' : axis === 'y' ? 'pan-x' : '';
1303
+ if (s.accessibility) list.setAttribute('tabindex', '0');
1304
+
1305
+ // Keyboard
1306
+ this._on(el, 'keydown', (e) => {
1307
+ if (!s.accessibility || !this.canNav) return;
1308
+ const t = e.target;
1309
+ if (t && (/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName) || t.isContentEditable)) return;
1310
+ const rtl = s.rtl && !s.vertical;
1311
+ let handled = true;
1312
+ if (e.key === 'ArrowLeft' || (s.vertical && e.key === 'ArrowUp')) rtl ? this.next() : this.prev();
1313
+ else if (e.key === 'ArrowRight' || (s.vertical && e.key === 'ArrowDown')) rtl ? this.prev() : this.next();
1314
+ else if (e.key === 'Home') this.goTo(0);
1315
+ else if (e.key === 'End') this.goTo(this.n - 1);
1316
+ else handled = false;
1317
+ if (handled) e.preventDefault();
1318
+ });
1319
+
1320
+ // Pause on hover/focus
1321
+ if (s.pauseOnHover) {
1322
+ this._on(el, 'mouseenter', () => this._holdOn('hover'));
1323
+ this._on(el, 'mouseleave', () => this._holdOff('hover'));
1324
+ }
1325
+ if (s.pauseOnFocus) {
1326
+ this._on(el, 'focusin', () => this._holdOn('focus'));
1327
+ this._on(el, 'focusout', (e) => {
1328
+ if (!el.contains(e.relatedTarget)) this._holdOff('focus');
1329
+ });
1330
+ }
1331
+
1332
+ // Click handling (select-to-focus, suppress after drag)
1333
+ this._on(
1334
+ list,
1335
+ 'click',
1336
+ (e) => {
1337
+ if (this._dragged) {
1338
+ e.preventDefault();
1339
+ e.stopPropagation();
1340
+ this._dragged = false;
1341
+ return;
1342
+ }
1343
+ if (s.focusOnSelect && (this.canNav || s.asNavFor)) {
1344
+ const slide = e.target.closest ? e.target.closest('.mozert-slide') : null;
1345
+ if (slide && this.track.contains(slide)) {
1346
+ const i = parseInt(slide.getAttribute('data-mozert-index'), 10);
1347
+ if (!isNaN(i)) {
1348
+ if (this.canNav) this.goTo(i);
1349
+ else {
1350
+ // Nothing to scroll: just highlight the clicked slide and tell the partner.
1351
+ this._sel = i;
1352
+ this._updateSlideClasses(this._logical);
1353
+ this._syncNav(i);
1354
+ }
1355
+ }
1356
+ }
1357
+ }
1358
+ },
1359
+ true
1360
+ );
1361
+ this._on(list, 'dragstart', (e) => e.preventDefault());
1362
+ this._on(list, 'selectstart', (e) => {
1363
+ if (this._pointerDown) e.preventDefault();
1364
+ });
1365
+
1366
+ // Images that load late change heights
1367
+ this._on(
1368
+ this.track,
1369
+ 'load',
1370
+ () => {
1371
+ if (!this.active) return;
1372
+ clearTimeout(this._imgTimer);
1373
+ this._imgTimer = setTimeout(() => this.setPosition(), 30);
1374
+ },
1375
+ true
1376
+ );
1377
+
1378
+ if (axis && this.canNav) this._bindDrag(axis);
1379
+ }
1380
+
1381
+ _bindDrag(axis) {
1382
+ const s = this.s;
1383
+ const list = this.list;
1384
+ let d = null;
1385
+
1386
+ const onDown = (e) => {
1387
+ if (this._lockDrag) return;
1388
+ if (this.animating && s.waitForAnimate) return;
1389
+ if (e.pointerType === 'mouse') {
1390
+ if (e.button !== 0 || !s.draggable) return;
1391
+ } else if (!s.swipe) {
1392
+ return;
1393
+ }
1394
+ this._dragged = false;
1395
+ this._pointerDown = true;
1396
+ d = { id: e.pointerId, x: e.clientX, y: e.clientY, started: false, base: this._t, delta: 0, samples: [] };
1397
+ window.addEventListener('pointermove', onMove);
1398
+ window.addEventListener('pointerup', onUp);
1399
+ window.addEventListener('pointercancel', onCancel);
1400
+ };
1401
+
1402
+ const limits = () => {
1403
+ const a = this._pos(0);
1404
+ const b = this._pos(this.maxIndex);
1405
+ return [Math.min(a, b), Math.max(a, b)];
1406
+ };
1407
+
1408
+ const onMove = (e) => {
1409
+ if (!d || e.pointerId !== d.id) return;
1410
+ if (this._lockDrag && !d.started) return;
1411
+ const dx = e.clientX - d.x;
1412
+ const dy = e.clientY - d.y;
1413
+ const main = (axis === 'x' ? dx : dy) * s.touchRatio;
1414
+ const cross = axis === 'x' ? dy : dx;
1415
+ if (!d.started) {
1416
+ if (Math.abs(main) < s.dragThreshold || Math.abs(main) < Math.abs(cross)) return;
1417
+ d.started = true;
1418
+ this.el.classList.add('mozert-dragging');
1419
+ this._holdOn('drag');
1420
+ try {
1421
+ list.setPointerCapture(e.pointerId);
1422
+ } catch (err) {
1423
+ /* ignore */
1424
+ }
1425
+ this.track.style.transition = 'none';
1426
+ }
1427
+ d.delta = main;
1428
+ d.samples.push({ t: performance.now(), v: main });
1429
+ if (d.samples.length > 6) d.samples.shift();
1430
+ if (!s.touchMove) return;
1431
+ if (s.stacked) {
1432
+ const sgn = s.rtl && !s.vertical ? 1 : -1;
1433
+ let pv = this._logical + (sgn * main) / (axis === 'x' ? this.list.clientWidth : this.list.clientHeight);
1434
+ if (!s.infinite) {
1435
+ if (pv < 0) pv *= s.edgeFriction;
1436
+ else if (pv > this.maxIndex) pv = this.maxIndex + (pv - this.maxIndex) * s.edgeFriction;
1437
+ }
1438
+ this._fx(pv, false);
1439
+ return;
1440
+ }
1441
+ let raw = d.base + main;
1442
+ if (!s.infinite) {
1443
+ const [lo, hi] = limits();
1444
+ if (raw > hi) raw = hi + (raw - hi) * s.edgeFriction;
1445
+ else if (raw < lo) raw = lo - (lo - raw) * s.edgeFriction;
1446
+ }
1447
+ this._paint(raw);
1448
+ };
1449
+
1450
+ const end = () => {
1451
+ window.removeEventListener('pointermove', onMove);
1452
+ window.removeEventListener('pointerup', onUp);
1453
+ window.removeEventListener('pointercancel', onCancel);
1454
+ this._pointerDown = false;
1455
+ this.el.classList.remove('mozert-dragging');
1456
+ this._holdOff('drag');
1457
+ };
1458
+
1459
+ const onCancel = () => {
1460
+ if (!d) return;
1461
+ const started = d.started;
1462
+ d = null;
1463
+ end();
1464
+ if (started) this._snapBack();
1465
+ };
1466
+
1467
+ const onUp = (e) => {
1468
+ if (!d || e.pointerId !== d.id) return;
1469
+ const st = d;
1470
+ d = null;
1471
+ end();
1472
+ if (!st.started) return;
1473
+ this._dragged = true;
1474
+ const delta = st.delta;
1475
+ const size = axis === 'x' ? this.list.clientWidth : this.list.clientHeight;
1476
+ this._user();
1477
+ if (this._release && this._release(st, size, axis)) return;
1478
+ const fingerDir = axis === 'x' ? (delta < 0 ? 'left' : 'right') : delta < 0 ? 'up' : 'down';
1479
+ const rtl = s.rtl && !s.vertical;
1480
+ const forward = rtl ? delta > 0 : delta < 0;
1481
+ if (Math.abs(delta) < size / s.touchThreshold) {
1482
+ this._snapBack();
1483
+ return;
1484
+ }
1485
+ this.emit('swipe', fingerDir);
1486
+ let target;
1487
+ if (s.swipeToSlide) {
1488
+ const raw = st.base + delta;
1489
+ let best = this.current;
1490
+ let bestD = Infinity;
1491
+ const lo = s.infinite ? -this.cloneCount : 0;
1492
+ const hi = s.infinite ? this.n + this.cloneCount - 1 : this.maxIndex;
1493
+ for (let i = lo; i <= hi; i++) {
1494
+ const dist = Math.abs(this._pos(i) - raw);
1495
+ if (dist < bestD) {
1496
+ bestD = dist;
1497
+ best = i;
1498
+ }
1499
+ }
1500
+ target = best;
1501
+ } else {
1502
+ target = this.current + (forward ? 1 : -1) * s.slidesToScroll;
1503
+ }
1504
+ if (!s.infinite) {
1505
+ const clamped = clamp(target, 0, this.maxIndex);
1506
+ if (clamped === this.current) {
1507
+ this.emit('edge', fingerDir);
1508
+ this._snapBack();
1509
+ return;
1510
+ }
1511
+ target = clamped;
1512
+ }
1513
+ if (target === this.current) {
1514
+ this._snapBack();
1515
+ return;
1516
+ }
1517
+ this._slideTo(target);
1518
+ };
1519
+
1520
+ this._on(list, 'pointerdown', onDown);
1521
+ this._off.push(() => {
1522
+ window.removeEventListener('pointermove', onMove);
1523
+ window.removeEventListener('pointerup', onUp);
1524
+ window.removeEventListener('pointercancel', onCancel);
1525
+ });
1526
+ }
1527
+
1528
+ /* ------------------------------------------------------------------ */
1529
+ /* Autoplay */
1530
+ /* ------------------------------------------------------------------ */
1531
+
1532
+ _holdOn(reason) {
1533
+ this._pause.add(reason);
1534
+ clearTimeout(this._apTimer);
1535
+ }
1536
+
1537
+ _holdOff(reason) {
1538
+ this._pause.delete(reason);
1539
+ this._schedule();
1540
+ }
1541
+
1542
+ _live() {
1543
+ if (this.track) this.track.setAttribute('aria-live', this._autoOn && !this._pause.has('user') ? 'off' : 'polite');
1544
+ }
1545
+
1546
+ _schedule() {
1547
+ clearTimeout(this._apTimer);
1548
+ cancelAnimationFrame(this._apRaf);
1549
+ if (!this.active || !this.canNav || !this._autoOn) return;
1550
+ if (this._pause.size) return;
1551
+ const delay = this.s.autoplaySpeed;
1552
+ this._apTimer = setTimeout(() => this._tick(), delay);
1553
+ const cb = this.options.on && this.options.on.autoplayTimeLeft;
1554
+ if ((this._handlers.autoplayTimeLeft && this._handlers.autoplayTimeLeft.length) || cb) {
1555
+ const t0 = performance.now();
1556
+ const loop = () => {
1557
+ const left = Math.max(0, delay - (performance.now() - t0));
1558
+ this.emit('autoplayTimeLeft', left, left / delay);
1559
+ if (left > 0) this._apRaf = requestAnimationFrame(loop);
1560
+ };
1561
+ loop();
1562
+ }
1563
+ }
1564
+
1565
+ _tick() {
1566
+ const s = this.s;
1567
+ if (this._pause.size || !this.active) return;
1568
+ if (this.animating) {
1569
+ this._schedule();
1570
+ return;
1571
+ }
1572
+ const rev = s.autoplayReverse;
1573
+ if (s.infinite) {
1574
+ if (rev) this.prev();
1575
+ else this.next();
1576
+ return;
1577
+ }
1578
+ if (s.stopOnLastSlide && (rev ? this.current <= 0 : this.current >= this.maxIndex)) {
1579
+ this.pause();
1580
+ return;
1581
+ }
1582
+ if (this.current >= this.maxIndex) this._dir = -1;
1583
+ else if (this.current <= 0) this._dir = 1;
1584
+ if ((this._dir === 1) !== !!rev) this.next();
1585
+ else this.prev();
1586
+ }
1587
+
1588
+ /* ------------------------------------------------------------------ */
1589
+ /* Syncing and lazy loading */
1590
+ /* ------------------------------------------------------------------ */
1591
+
1592
+ _navTargets() {
1593
+ const v = this.s.asNavFor;
1594
+ if (!v) return [];
1595
+ let els = [];
1596
+ if (v instanceof Mozert) return v.active && v !== this ? [v] : [];
1597
+ if (typeof v === 'string') els = Array.from(document.querySelectorAll(v));
1598
+ else if (isEl(v)) els = [v];
1599
+ else if (v.length) els = Array.from(v);
1600
+ return els.map((e) => registry.get(e)).filter((i) => i && i !== this && i.active);
1601
+ }
1602
+
1603
+ _syncNav(index) {
1604
+ this._navTargets().forEach((t) => t._slideTo(t.s.infinite ? index : clamp(index, 0, t.maxIndex), { fromSync: true, sel: index }));
1605
+ }
1606
+
1607
+ _lazy() {
1608
+ const s = this.s;
1609
+ if (!this.track || !this.n) return;
1610
+ const mode = s.lazyLoad;
1611
+ if (mode !== 'ondemand' && mode !== 'progressive') return;
1612
+ const from = this.current - (s.centerMode ? Math.floor(this._show / 2) : 0);
1613
+ const to = this.current + this._show - 1 + (s.centerMode ? Math.floor(this._show / 2) : 1);
1614
+ const seen = new Set();
1615
+ for (let i = from; i <= to; i++) {
1616
+ const r = ((i % this.n) + this.n) % this.n;
1617
+ if (!seen.has(r)) {
1618
+ seen.add(r);
1619
+ this._loadSlide(r);
1620
+ }
1621
+ }
1622
+ if (mode === 'progressive' && !this._progressing) this._progressive();
1623
+ }
1624
+
1625
+ _progressive() {
1626
+ this._progressing = true;
1627
+ const step = () => {
1628
+ if (!this.active) {
1629
+ this._progressing = false;
1630
+ return;
1631
+ }
1632
+ const next = this._units.findIndex((u) => u.querySelector('img[data-lazy]:not([data-mozert-loading]), [data-lazy-bg]:not([data-mozert-loading])'));
1633
+ if (next === -1) {
1634
+ this._progressing = false;
1635
+ return;
1636
+ }
1637
+ this._loadSlide(next, step);
1638
+ };
1639
+ step();
1640
+ }
1641
+
1642
+ _loadSlide(index, done) {
1643
+ const unit = this._units[index];
1644
+ if (!unit) return;
1645
+ const imgs = unit.querySelectorAll('img[data-lazy]');
1646
+ const bgs = unit.querySelectorAll('[data-lazy-bg]');
1647
+ if (!imgs.length && !bgs.length) {
1648
+ if (done) done();
1649
+ return;
1650
+ }
1651
+ let left = imgs.length + bgs.length;
1652
+ const fin = () => {
1653
+ if (--left === 0 && done) done();
1654
+ };
1655
+ bgs.forEach((el) => {
1656
+ if (el.hasAttribute('data-mozert-loading')) {
1657
+ fin();
1658
+ return;
1659
+ }
1660
+ el.setAttribute('data-mozert-loading', '');
1661
+ const src = el.getAttribute('data-lazy-bg');
1662
+ const k = el.getAttribute('data-mozert-bg');
1663
+ const apply = (n) => {
1664
+ n.style.backgroundImage = 'url("' + src.replace(/"/g, '%22') + '")';
1665
+ n.removeAttribute('data-lazy-bg');
1666
+ n.removeAttribute('data-mozert-loading');
1667
+ };
1668
+ const probe = new Image();
1669
+ probe.onload = () => {
1670
+ apply(el);
1671
+ this.dom.forEach((e) => {
1672
+ if (!e.classList.contains('mozert-cloned') || parseInt(e.getAttribute('data-mozert-index'), 10) !== index) return;
1673
+ const c = e.querySelector('[data-mozert-bg="' + k + '"]');
1674
+ if (c) apply(c);
1675
+ });
1676
+ this.emit('lazyLoaded', el, src);
1677
+ fin();
1678
+ };
1679
+ probe.onerror = () => {
1680
+ el.removeAttribute('data-lazy-bg');
1681
+ el.removeAttribute('data-mozert-loading');
1682
+ el.classList.add('mozert-lazy-error');
1683
+ this.emit('lazyLoadError', el, src);
1684
+ fin();
1685
+ };
1686
+ probe.src = src;
1687
+ });
1688
+ imgs.forEach((img) => {
1689
+ if (img.hasAttribute('data-mozert-loading')) {
1690
+ fin();
1691
+ return;
1692
+ }
1693
+ img.setAttribute('data-mozert-loading', '');
1694
+ img.classList.add('mozert-loading');
1695
+ const src = img.getAttribute('data-lazy');
1696
+ const srcset = img.getAttribute('data-lazy-srcset');
1697
+ const sizes = img.getAttribute('data-lazy-sizes');
1698
+ const probe = new Image();
1699
+ probe.onload = () => {
1700
+ if (srcset) img.srcset = srcset;
1701
+ if (sizes) img.sizes = sizes;
1702
+ img.src = src;
1703
+ img.removeAttribute('data-lazy');
1704
+ img.removeAttribute('data-mozert-loading');
1705
+ img.classList.remove('mozert-loading');
1706
+ img.classList.add('mozert-loaded');
1707
+ this._lazyClones(index, src, srcset, sizes, img);
1708
+ this.emit('lazyLoaded', img, src);
1709
+ fin();
1710
+ };
1711
+ probe.onerror = () => {
1712
+ img.removeAttribute('data-lazy');
1713
+ img.removeAttribute('data-mozert-loading');
1714
+ img.classList.remove('mozert-loading');
1715
+ img.classList.add('mozert-lazy-error');
1716
+ this.emit('lazyLoadError', img, src);
1717
+ fin();
1718
+ };
1719
+ if (srcset) probe.srcset = srcset;
1720
+ probe.src = src;
1721
+ });
1722
+ }
1723
+
1724
+ _lazyClones(index, src, srcset, sizes, realImg) {
1725
+ const imgsInUnit = Array.from(this._units[index].querySelectorAll('img'));
1726
+ const pos = imgsInUnit.indexOf(realImg);
1727
+ this.dom.forEach((e) => {
1728
+ if (!e.classList.contains('mozert-cloned') || parseInt(e.getAttribute('data-mozert-index'), 10) !== index) return;
1729
+ const c = e.querySelectorAll('img')[pos];
1730
+ if (!c) return;
1731
+ if (srcset) c.srcset = srcset;
1732
+ if (sizes) c.sizes = sizes;
1733
+ c.src = src;
1734
+ c.removeAttribute('data-lazy');
1735
+ c.classList.add('mozert-loaded');
1736
+ });
1737
+ }
1738
+ }
1739
+
1740
+ Mozert._mods = [];
1741
+ Mozert._boot = [];
1742
+ /** Register a module: { name, defaults?, create(slider) => { destroy() } | null }. */
1743
+ Mozert.use = function (mod) {
1744
+ if (mod && Mozert._mods.indexOf(mod) < 0) {
1745
+ Mozert._mods.push(mod);
1746
+ Object.keys(mod.defaults || {}).forEach((k) => {
1747
+ if (!(k in DEFAULTS)) DEFAULTS[k] = mod.defaults[k];
1748
+ });
1749
+ }
1750
+ return Mozert;
1751
+ };
1752
+ Mozert.version = VERSION;
1753
+ Mozert.defaults = DEFAULTS;
1754
+ Mozert._bridge = null;
1755
+
1756
+ //#endregion core
1757
+
1758
+ // Auto-init any element with a data-mozert attribute (set window.MozertAutoInit = false to opt out).
1759
+ if (typeof window !== 'undefined' && typeof document !== 'undefined') {
1760
+ var auto = function () {
1761
+ if (window.MozertAutoInit === false) return;
1762
+ Mozert.initAll(document);
1763
+ };
1764
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', auto);
1765
+ else auto();
1766
+ }
1767
+ return Mozert;
1768
+ }));