gp-designer 1.0.103 → 1.0.104

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,1960 @@
1
+ /*!
2
+ * PhotoSwipe Lightbox 5.4.4 - https://photoswipe.com
3
+ * (c) 2024 Dmytro Semenov
4
+ */
5
+ /** @typedef {import('../photoswipe.js').Point} Point */
6
+
7
+ /**
8
+ * @template {keyof HTMLElementTagNameMap} T
9
+ * @param {string} className
10
+ * @param {T} tagName
11
+ * @param {Node} [appendToEl]
12
+ * @returns {HTMLElementTagNameMap[T]}
13
+ */
14
+ function createElement(className, tagName, appendToEl) {
15
+ const el = document.createElement(tagName);
16
+
17
+ if (className) {
18
+ el.className = className;
19
+ }
20
+
21
+ if (appendToEl) {
22
+ appendToEl.appendChild(el);
23
+ }
24
+
25
+ return el;
26
+ }
27
+ /**
28
+ * Get transform string
29
+ *
30
+ * @param {number} x
31
+ * @param {number} [y]
32
+ * @param {number} [scale]
33
+ * @returns {string}
34
+ */
35
+
36
+ function toTransformString(x, y, scale) {
37
+ let propValue = `translate3d(${x}px,${y || 0}px,0)`;
38
+
39
+ if (scale !== undefined) {
40
+ propValue += ` scale3d(${scale},${scale},1)`;
41
+ }
42
+
43
+ return propValue;
44
+ }
45
+ /**
46
+ * Apply width and height CSS properties to element
47
+ *
48
+ * @param {HTMLElement} el
49
+ * @param {string | number} w
50
+ * @param {string | number} h
51
+ */
52
+
53
+ function setWidthHeight(el, w, h) {
54
+ el.style.width = typeof w === 'number' ? `${w}px` : w;
55
+ el.style.height = typeof h === 'number' ? `${h}px` : h;
56
+ }
57
+ /** @typedef {LOAD_STATE[keyof LOAD_STATE]} LoadState */
58
+
59
+ /** @type {{ IDLE: 'idle'; LOADING: 'loading'; LOADED: 'loaded'; ERROR: 'error' }} */
60
+
61
+ const LOAD_STATE = {
62
+ IDLE: 'idle',
63
+ LOADING: 'loading',
64
+ LOADED: 'loaded',
65
+ ERROR: 'error'
66
+ };
67
+ /**
68
+ * Check if click or keydown event was dispatched
69
+ * with a special key or via mouse wheel.
70
+ *
71
+ * @param {MouseEvent | KeyboardEvent} e
72
+ * @returns {boolean}
73
+ */
74
+
75
+ function specialKeyUsed(e) {
76
+ return 'button' in e && e.button === 1 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey;
77
+ }
78
+ /**
79
+ * Parse `gallery` or `children` options.
80
+ *
81
+ * @param {import('../photoswipe.js').ElementProvider} [option]
82
+ * @param {string} [legacySelector]
83
+ * @param {HTMLElement | Document} [parent]
84
+ * @returns HTMLElement[]
85
+ */
86
+
87
+ function getElementsFromOption(option, legacySelector, parent = document) {
88
+ /** @type {HTMLElement[]} */
89
+ let elements = [];
90
+
91
+ if (option instanceof Element) {
92
+ elements = [option];
93
+ } else if (option instanceof NodeList || Array.isArray(option)) {
94
+ elements = Array.from(option);
95
+ } else {
96
+ const selector = typeof option === 'string' ? option : legacySelector;
97
+
98
+ if (selector) {
99
+ elements = Array.from(parent.querySelectorAll(selector));
100
+ }
101
+ }
102
+
103
+ return elements;
104
+ }
105
+ /**
106
+ * Check if variable is PhotoSwipe class
107
+ *
108
+ * @param {any} fn
109
+ * @returns {boolean}
110
+ */
111
+
112
+ function isPswpClass(fn) {
113
+ return typeof fn === 'function' && fn.prototype && fn.prototype.goTo;
114
+ }
115
+ /**
116
+ * Check if browser is Safari
117
+ *
118
+ * @returns {boolean}
119
+ */
120
+
121
+ function isSafari() {
122
+ return !!(navigator.vendor && navigator.vendor.match(/apple/i));
123
+ }
124
+
125
+ /** @typedef {import('../lightbox/lightbox.js').default} PhotoSwipeLightbox */
126
+
127
+ /** @typedef {import('../photoswipe.js').default} PhotoSwipe */
128
+
129
+ /** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
130
+
131
+ /** @typedef {import('../photoswipe.js').DataSource} DataSource */
132
+
133
+ /** @typedef {import('../ui/ui-element.js').UIElementData} UIElementData */
134
+
135
+ /** @typedef {import('../slide/content.js').default} ContentDefault */
136
+
137
+ /** @typedef {import('../slide/slide.js').default} Slide */
138
+
139
+ /** @typedef {import('../slide/slide.js').SlideData} SlideData */
140
+
141
+ /** @typedef {import('../slide/zoom-level.js').default} ZoomLevel */
142
+
143
+ /** @typedef {import('../slide/get-thumb-bounds.js').Bounds} Bounds */
144
+
145
+ /**
146
+ * Allow adding an arbitrary props to the Content
147
+ * https://photoswipe.com/custom-content/#using-webp-image-format
148
+ * @typedef {ContentDefault & Record<string, any>} Content
149
+ */
150
+
151
+ /** @typedef {{ x?: number; y?: number }} Point */
152
+
153
+ /**
154
+ * @typedef {Object} PhotoSwipeEventsMap https://photoswipe.com/events/
155
+ *
156
+ *
157
+ * https://photoswipe.com/adding-ui-elements/
158
+ *
159
+ * @prop {undefined} uiRegister
160
+ * @prop {{ data: UIElementData }} uiElementCreate
161
+ *
162
+ *
163
+ * https://photoswipe.com/events/#initialization-events
164
+ *
165
+ * @prop {undefined} beforeOpen
166
+ * @prop {undefined} firstUpdate
167
+ * @prop {undefined} initialLayout
168
+ * @prop {undefined} change
169
+ * @prop {undefined} afterInit
170
+ * @prop {undefined} bindEvents
171
+ *
172
+ *
173
+ * https://photoswipe.com/events/#opening-or-closing-transition-events
174
+ *
175
+ * @prop {undefined} openingAnimationStart
176
+ * @prop {undefined} openingAnimationEnd
177
+ * @prop {undefined} closingAnimationStart
178
+ * @prop {undefined} closingAnimationEnd
179
+ *
180
+ *
181
+ * https://photoswipe.com/events/#closing-events
182
+ *
183
+ * @prop {undefined} close
184
+ * @prop {undefined} destroy
185
+ *
186
+ *
187
+ * https://photoswipe.com/events/#pointer-and-gesture-events
188
+ *
189
+ * @prop {{ originalEvent: PointerEvent }} pointerDown
190
+ * @prop {{ originalEvent: PointerEvent }} pointerMove
191
+ * @prop {{ originalEvent: PointerEvent }} pointerUp
192
+ * @prop {{ bgOpacity: number }} pinchClose can be default prevented
193
+ * @prop {{ panY: number }} verticalDrag can be default prevented
194
+ *
195
+ *
196
+ * https://photoswipe.com/events/#slide-content-events
197
+ *
198
+ * @prop {{ content: Content }} contentInit
199
+ * @prop {{ content: Content; isLazy: boolean }} contentLoad can be default prevented
200
+ * @prop {{ content: Content; isLazy: boolean }} contentLoadImage can be default prevented
201
+ * @prop {{ content: Content; slide: Slide; isError?: boolean }} loadComplete
202
+ * @prop {{ content: Content; slide: Slide }} loadError
203
+ * @prop {{ content: Content; width: number; height: number }} contentResize can be default prevented
204
+ * @prop {{ content: Content; width: number; height: number; slide: Slide }} imageSizeChange
205
+ * @prop {{ content: Content }} contentLazyLoad can be default prevented
206
+ * @prop {{ content: Content }} contentAppend can be default prevented
207
+ * @prop {{ content: Content }} contentActivate can be default prevented
208
+ * @prop {{ content: Content }} contentDeactivate can be default prevented
209
+ * @prop {{ content: Content }} contentRemove can be default prevented
210
+ * @prop {{ content: Content }} contentDestroy can be default prevented
211
+ *
212
+ *
213
+ * undocumented
214
+ *
215
+ * @prop {{ point: Point; originalEvent: PointerEvent }} imageClickAction can be default prevented
216
+ * @prop {{ point: Point; originalEvent: PointerEvent }} bgClickAction can be default prevented
217
+ * @prop {{ point: Point; originalEvent: PointerEvent }} tapAction can be default prevented
218
+ * @prop {{ point: Point; originalEvent: PointerEvent }} doubleTapAction can be default prevented
219
+ *
220
+ * @prop {{ originalEvent: KeyboardEvent }} keydown can be default prevented
221
+ * @prop {{ x: number; dragging: boolean }} moveMainScroll
222
+ * @prop {{ slide: Slide }} firstZoomPan
223
+ * @prop {{ slide: Slide | undefined, data: SlideData, index: number }} gettingData
224
+ * @prop {undefined} beforeResize
225
+ * @prop {undefined} resize
226
+ * @prop {undefined} viewportSize
227
+ * @prop {undefined} updateScrollOffset
228
+ * @prop {{ slide: Slide }} slideInit
229
+ * @prop {{ slide: Slide }} afterSetContent
230
+ * @prop {{ slide: Slide }} slideLoad
231
+ * @prop {{ slide: Slide }} appendHeavy can be default prevented
232
+ * @prop {{ slide: Slide }} appendHeavyContent
233
+ * @prop {{ slide: Slide }} slideActivate
234
+ * @prop {{ slide: Slide }} slideDeactivate
235
+ * @prop {{ slide: Slide }} slideDestroy
236
+ * @prop {{ destZoomLevel: number, centerPoint: Point | undefined, transitionDuration: number | false | undefined }} beforeZoomTo
237
+ * @prop {{ slide: Slide }} zoomPanUpdate
238
+ * @prop {{ slide: Slide }} initialZoomPan
239
+ * @prop {{ slide: Slide }} calcSlideSize
240
+ * @prop {undefined} resolutionChanged
241
+ * @prop {{ originalEvent: WheelEvent }} wheel can be default prevented
242
+ * @prop {{ content: Content }} contentAppendImage can be default prevented
243
+ * @prop {{ index: number; itemData: SlideData }} lazyLoadSlide can be default prevented
244
+ * @prop {undefined} lazyLoad
245
+ * @prop {{ slide: Slide }} calcBounds
246
+ * @prop {{ zoomLevels: ZoomLevel, slideData: SlideData }} zoomLevelsUpdate
247
+ *
248
+ *
249
+ * legacy
250
+ *
251
+ * @prop {undefined} init
252
+ * @prop {undefined} initialZoomIn
253
+ * @prop {undefined} initialZoomOut
254
+ * @prop {undefined} initialZoomInEnd
255
+ * @prop {undefined} initialZoomOutEnd
256
+ * @prop {{ dataSource: DataSource | undefined, numItems: number }} numItems
257
+ * @prop {{ itemData: SlideData; index: number }} itemData
258
+ * @prop {{ index: number, itemData: SlideData, instance: PhotoSwipe }} thumbBounds
259
+ */
260
+
261
+ /**
262
+ * @typedef {Object} PhotoSwipeFiltersMap https://photoswipe.com/filters/
263
+ *
264
+ * @prop {(numItems: number, dataSource: DataSource | undefined) => number} numItems
265
+ * Modify the total amount of slides. Example on Data sources page.
266
+ * https://photoswipe.com/filters/#numitems
267
+ *
268
+ * @prop {(itemData: SlideData, index: number) => SlideData} itemData
269
+ * Modify slide item data. Example on Data sources page.
270
+ * https://photoswipe.com/filters/#itemdata
271
+ *
272
+ * @prop {(itemData: SlideData, element: HTMLElement, linkEl: HTMLAnchorElement) => SlideData} domItemData
273
+ * Modify item data when it's parsed from DOM element. Example on Data sources page.
274
+ * https://photoswipe.com/filters/#domitemdata
275
+ *
276
+ * @prop {(clickedIndex: number, e: MouseEvent, instance: PhotoSwipeLightbox) => number} clickedIndex
277
+ * Modify clicked gallery item index.
278
+ * https://photoswipe.com/filters/#clickedindex
279
+ *
280
+ * @prop {(placeholderSrc: string | false, content: Content) => string | false} placeholderSrc
281
+ * Modify placeholder image source.
282
+ * https://photoswipe.com/filters/#placeholdersrc
283
+ *
284
+ * @prop {(isContentLoading: boolean, content: Content) => boolean} isContentLoading
285
+ * Modify if the content is currently loading.
286
+ * https://photoswipe.com/filters/#iscontentloading
287
+ *
288
+ * @prop {(isContentZoomable: boolean, content: Content) => boolean} isContentZoomable
289
+ * Modify if the content can be zoomed.
290
+ * https://photoswipe.com/filters/#iscontentzoomable
291
+ *
292
+ * @prop {(useContentPlaceholder: boolean, content: Content) => boolean} useContentPlaceholder
293
+ * Modify if the placeholder should be used for the content.
294
+ * https://photoswipe.com/filters/#usecontentplaceholder
295
+ *
296
+ * @prop {(isKeepingPlaceholder: boolean, content: Content) => boolean} isKeepingPlaceholder
297
+ * Modify if the placeholder should be kept after the content is loaded.
298
+ * https://photoswipe.com/filters/#iskeepingplaceholder
299
+ *
300
+ *
301
+ * @prop {(contentErrorElement: HTMLElement, content: Content) => HTMLElement} contentErrorElement
302
+ * Modify an element when the content has error state (for example, if image cannot be loaded).
303
+ * https://photoswipe.com/filters/#contenterrorelement
304
+ *
305
+ * @prop {(element: HTMLElement, data: UIElementData) => HTMLElement} uiElement
306
+ * Modify a UI element that's being created.
307
+ * https://photoswipe.com/filters/#uielement
308
+ *
309
+ * @prop {(thumbnail: HTMLElement | null | undefined, itemData: SlideData, index: number) => HTMLElement} thumbEl
310
+ * Modify the thumbnail element from which opening zoom animation starts or ends.
311
+ * https://photoswipe.com/filters/#thumbel
312
+ *
313
+ * @prop {(thumbBounds: Bounds | undefined, itemData: SlideData, index: number) => Bounds} thumbBounds
314
+ * Modify the thumbnail bounds from which opening zoom animation starts or ends.
315
+ * https://photoswipe.com/filters/#thumbbounds
316
+ *
317
+ * @prop {(srcsetSizesWidth: number, content: Content) => number} srcsetSizesWidth
318
+ *
319
+ * @prop {(preventPointerEvent: boolean, event: PointerEvent, pointerType: string) => boolean} preventPointerEvent
320
+ *
321
+ */
322
+
323
+ /**
324
+ * @template {keyof PhotoSwipeFiltersMap} T
325
+ * @typedef {{ fn: PhotoSwipeFiltersMap[T], priority: number }} Filter
326
+ */
327
+
328
+ /**
329
+ * @template {keyof PhotoSwipeEventsMap} T
330
+ * @typedef {PhotoSwipeEventsMap[T] extends undefined ? PhotoSwipeEvent<T> : PhotoSwipeEvent<T> & PhotoSwipeEventsMap[T]} AugmentedEvent
331
+ */
332
+
333
+ /**
334
+ * @template {keyof PhotoSwipeEventsMap} T
335
+ * @typedef {(event: AugmentedEvent<T>) => void} EventCallback
336
+ */
337
+
338
+ /**
339
+ * Base PhotoSwipe event object
340
+ *
341
+ * @template {keyof PhotoSwipeEventsMap} T
342
+ */
343
+ class PhotoSwipeEvent {
344
+ /**
345
+ * @param {T} type
346
+ * @param {PhotoSwipeEventsMap[T]} [details]
347
+ */
348
+ constructor(type, details) {
349
+ this.type = type;
350
+ this.defaultPrevented = false;
351
+
352
+ if (details) {
353
+ Object.assign(this, details);
354
+ }
355
+ }
356
+
357
+ preventDefault() {
358
+ this.defaultPrevented = true;
359
+ }
360
+
361
+ }
362
+ /**
363
+ * PhotoSwipe base class that can listen and dispatch for events.
364
+ * Shared by PhotoSwipe Core and PhotoSwipe Lightbox, extended by base.js
365
+ */
366
+
367
+
368
+ class Eventable {
369
+ constructor() {
370
+ /**
371
+ * @type {{ [T in keyof PhotoSwipeEventsMap]?: ((event: AugmentedEvent<T>) => void)[] }}
372
+ */
373
+ this._listeners = {};
374
+ /**
375
+ * @type {{ [T in keyof PhotoSwipeFiltersMap]?: Filter<T>[] }}
376
+ */
377
+
378
+ this._filters = {};
379
+ /** @type {PhotoSwipe | undefined} */
380
+
381
+ this.pswp = undefined;
382
+ /** @type {PhotoSwipeOptions | undefined} */
383
+
384
+ this.options = undefined;
385
+ }
386
+ /**
387
+ * @template {keyof PhotoSwipeFiltersMap} T
388
+ * @param {T} name
389
+ * @param {PhotoSwipeFiltersMap[T]} fn
390
+ * @param {number} priority
391
+ */
392
+
393
+
394
+ addFilter(name, fn, priority = 100) {
395
+ var _this$_filters$name, _this$_filters$name2, _this$pswp;
396
+
397
+ if (!this._filters[name]) {
398
+ this._filters[name] = [];
399
+ }
400
+
401
+ (_this$_filters$name = this._filters[name]) === null || _this$_filters$name === void 0 || _this$_filters$name.push({
402
+ fn,
403
+ priority
404
+ });
405
+ (_this$_filters$name2 = this._filters[name]) === null || _this$_filters$name2 === void 0 || _this$_filters$name2.sort((f1, f2) => f1.priority - f2.priority);
406
+ (_this$pswp = this.pswp) === null || _this$pswp === void 0 || _this$pswp.addFilter(name, fn, priority);
407
+ }
408
+ /**
409
+ * @template {keyof PhotoSwipeFiltersMap} T
410
+ * @param {T} name
411
+ * @param {PhotoSwipeFiltersMap[T]} fn
412
+ */
413
+
414
+
415
+ removeFilter(name, fn) {
416
+ if (this._filters[name]) {
417
+ // @ts-expect-error
418
+ this._filters[name] = this._filters[name].filter(filter => filter.fn !== fn);
419
+ }
420
+
421
+ if (this.pswp) {
422
+ this.pswp.removeFilter(name, fn);
423
+ }
424
+ }
425
+ /**
426
+ * @template {keyof PhotoSwipeFiltersMap} T
427
+ * @param {T} name
428
+ * @param {Parameters<PhotoSwipeFiltersMap[T]>} args
429
+ * @returns {Parameters<PhotoSwipeFiltersMap[T]>[0]}
430
+ */
431
+
432
+
433
+ applyFilters(name, ...args) {
434
+ var _this$_filters$name3;
435
+
436
+ (_this$_filters$name3 = this._filters[name]) === null || _this$_filters$name3 === void 0 || _this$_filters$name3.forEach(filter => {
437
+ // @ts-expect-error
438
+ args[0] = filter.fn.apply(this, args);
439
+ });
440
+ return args[0];
441
+ }
442
+ /**
443
+ * @template {keyof PhotoSwipeEventsMap} T
444
+ * @param {T} name
445
+ * @param {EventCallback<T>} fn
446
+ */
447
+
448
+
449
+ on(name, fn) {
450
+ var _this$_listeners$name, _this$pswp2;
451
+
452
+ if (!this._listeners[name]) {
453
+ this._listeners[name] = [];
454
+ }
455
+
456
+ (_this$_listeners$name = this._listeners[name]) === null || _this$_listeners$name === void 0 || _this$_listeners$name.push(fn); // When binding events to lightbox,
457
+ // also bind events to PhotoSwipe Core,
458
+ // if it's open.
459
+
460
+ (_this$pswp2 = this.pswp) === null || _this$pswp2 === void 0 || _this$pswp2.on(name, fn);
461
+ }
462
+ /**
463
+ * @template {keyof PhotoSwipeEventsMap} T
464
+ * @param {T} name
465
+ * @param {EventCallback<T>} fn
466
+ */
467
+
468
+
469
+ off(name, fn) {
470
+ var _this$pswp3;
471
+
472
+ if (this._listeners[name]) {
473
+ // @ts-expect-error
474
+ this._listeners[name] = this._listeners[name].filter(listener => fn !== listener);
475
+ }
476
+
477
+ (_this$pswp3 = this.pswp) === null || _this$pswp3 === void 0 || _this$pswp3.off(name, fn);
478
+ }
479
+ /**
480
+ * @template {keyof PhotoSwipeEventsMap} T
481
+ * @param {T} name
482
+ * @param {PhotoSwipeEventsMap[T]} [details]
483
+ * @returns {AugmentedEvent<T>}
484
+ */
485
+
486
+
487
+ dispatch(name, details) {
488
+ var _this$_listeners$name2;
489
+
490
+ if (this.pswp) {
491
+ return this.pswp.dispatch(name, details);
492
+ }
493
+
494
+ const event =
495
+ /** @type {AugmentedEvent<T>} */
496
+ new PhotoSwipeEvent(name, details);
497
+ (_this$_listeners$name2 = this._listeners[name]) === null || _this$_listeners$name2 === void 0 || _this$_listeners$name2.forEach(listener => {
498
+ listener.call(this, event);
499
+ });
500
+ return event;
501
+ }
502
+
503
+ }
504
+
505
+ class Placeholder {
506
+ /**
507
+ * @param {string | false} imageSrc
508
+ * @param {HTMLElement} container
509
+ */
510
+ constructor(imageSrc, container) {
511
+ // Create placeholder
512
+ // (stretched thumbnail or simple div behind the main image)
513
+
514
+ /** @type {HTMLImageElement | HTMLDivElement | null} */
515
+ this.element = createElement('pswp__img pswp__img--placeholder', imageSrc ? 'img' : 'div', container);
516
+
517
+ if (imageSrc) {
518
+ const imgEl =
519
+ /** @type {HTMLImageElement} */
520
+ this.element;
521
+ imgEl.decoding = 'async';
522
+ imgEl.alt = '';
523
+ imgEl.src = imageSrc;
524
+ imgEl.setAttribute('role', 'presentation');
525
+ }
526
+
527
+ this.element.setAttribute('aria-hidden', 'true');
528
+ }
529
+ /**
530
+ * @param {number} width
531
+ * @param {number} height
532
+ */
533
+
534
+
535
+ setDisplayedSize(width, height) {
536
+ if (!this.element) {
537
+ return;
538
+ }
539
+
540
+ if (this.element.tagName === 'IMG') {
541
+ // Use transform scale() to modify img placeholder size
542
+ // (instead of changing width/height directly).
543
+ // This helps with performance, specifically in iOS15 Safari.
544
+ setWidthHeight(this.element, 250, 'auto');
545
+ this.element.style.transformOrigin = '0 0';
546
+ this.element.style.transform = toTransformString(0, 0, width / 250);
547
+ } else {
548
+ setWidthHeight(this.element, width, height);
549
+ }
550
+ }
551
+
552
+ destroy() {
553
+ var _this$element;
554
+
555
+ if ((_this$element = this.element) !== null && _this$element !== void 0 && _this$element.parentNode) {
556
+ this.element.remove();
557
+ }
558
+
559
+ this.element = null;
560
+ }
561
+
562
+ }
563
+
564
+ /** @typedef {import('./slide.js').default} Slide */
565
+
566
+ /** @typedef {import('./slide.js').SlideData} SlideData */
567
+
568
+ /** @typedef {import('../core/base.js').default} PhotoSwipeBase */
569
+
570
+ /** @typedef {import('../util/util.js').LoadState} LoadState */
571
+
572
+ class Content {
573
+ /**
574
+ * @param {SlideData} itemData Slide data
575
+ * @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox instance
576
+ * @param {number} index
577
+ */
578
+ constructor(itemData, instance, index) {
579
+ this.instance = instance;
580
+ this.data = itemData;
581
+ this.index = index;
582
+ /** @type {HTMLImageElement | HTMLDivElement | undefined} */
583
+
584
+ this.element = undefined;
585
+ /** @type {Placeholder | undefined} */
586
+
587
+ this.placeholder = undefined;
588
+ /** @type {Slide | undefined} */
589
+
590
+ this.slide = undefined;
591
+ this.displayedImageWidth = 0;
592
+ this.displayedImageHeight = 0;
593
+ this.width = Number(this.data.w) || Number(this.data.width) || 0;
594
+ this.height = Number(this.data.h) || Number(this.data.height) || 0;
595
+ this.isAttached = false;
596
+ this.hasSlide = false;
597
+ this.isDecoding = false;
598
+ /** @type {LoadState} */
599
+
600
+ this.state = LOAD_STATE.IDLE;
601
+
602
+ if (this.data.type) {
603
+ this.type = this.data.type;
604
+ } else if (this.data.src) {
605
+ this.type = 'image';
606
+ } else {
607
+ this.type = 'html';
608
+ }
609
+
610
+ this.instance.dispatch('contentInit', {
611
+ content: this
612
+ });
613
+ }
614
+
615
+ removePlaceholder() {
616
+ if (this.placeholder && !this.keepPlaceholder()) {
617
+ // With delay, as image might be loaded, but not rendered
618
+ setTimeout(() => {
619
+ if (this.placeholder) {
620
+ this.placeholder.destroy();
621
+ this.placeholder = undefined;
622
+ }
623
+ }, 1000);
624
+ }
625
+ }
626
+ /**
627
+ * Preload content
628
+ *
629
+ * @param {boolean} isLazy
630
+ * @param {boolean} [reload]
631
+ */
632
+
633
+
634
+ load(isLazy, reload) {
635
+ if (this.slide && this.usePlaceholder()) {
636
+ if (!this.placeholder) {
637
+ const placeholderSrc = this.instance.applyFilters('placeholderSrc', // use image-based placeholder only for the first slide,
638
+ // as rendering (even small stretched thumbnail) is an expensive operation
639
+ this.data.msrc && this.slide.isFirstSlide ? this.data.msrc : false, this);
640
+ this.placeholder = new Placeholder(placeholderSrc, this.slide.container);
641
+ } else {
642
+ const placeholderEl = this.placeholder.element; // Add placeholder to DOM if it was already created
643
+
644
+ if (placeholderEl && !placeholderEl.parentElement) {
645
+ this.slide.container.prepend(placeholderEl);
646
+ }
647
+ }
648
+ }
649
+
650
+ if (this.element && !reload) {
651
+ return;
652
+ }
653
+
654
+ if (this.instance.dispatch('contentLoad', {
655
+ content: this,
656
+ isLazy
657
+ }).defaultPrevented) {
658
+ return;
659
+ }
660
+
661
+ if (this.isImageContent()) {
662
+ this.element = createElement('pswp__img', 'img'); // Start loading only after width is defined, as sizes might depend on it.
663
+ // Due to Safari feature, we must define sizes before srcset.
664
+
665
+ if (this.displayedImageWidth) {
666
+ this.loadImage(isLazy);
667
+ }
668
+ } else {
669
+ this.element = createElement('pswp__content', 'div');
670
+ this.element.innerHTML = this.data.html || '';
671
+ }
672
+
673
+ if (reload && this.slide) {
674
+ this.slide.updateContentSize(true);
675
+ }
676
+ }
677
+ /**
678
+ * Preload image
679
+ *
680
+ * @param {boolean} isLazy
681
+ */
682
+
683
+
684
+ loadImage(isLazy) {
685
+ var _this$data$src, _this$data$alt;
686
+
687
+ if (!this.isImageContent() || !this.element || this.instance.dispatch('contentLoadImage', {
688
+ content: this,
689
+ isLazy
690
+ }).defaultPrevented) {
691
+ return;
692
+ }
693
+
694
+ const imageElement =
695
+ /** @type HTMLImageElement */
696
+ this.element;
697
+ this.updateSrcsetSizes();
698
+
699
+ if (this.data.srcset) {
700
+ imageElement.srcset = this.data.srcset;
701
+ }
702
+
703
+ imageElement.src = (_this$data$src = this.data.src) !== null && _this$data$src !== void 0 ? _this$data$src : '';
704
+ imageElement.alt = (_this$data$alt = this.data.alt) !== null && _this$data$alt !== void 0 ? _this$data$alt : '';
705
+ this.state = LOAD_STATE.LOADING;
706
+
707
+ if (imageElement.complete) {
708
+ this.onLoaded();
709
+ } else {
710
+ imageElement.onload = () => {
711
+ this.onLoaded();
712
+ };
713
+
714
+ imageElement.onerror = () => {
715
+ this.onError();
716
+ };
717
+ }
718
+ }
719
+ /**
720
+ * Assign slide to content
721
+ *
722
+ * @param {Slide} slide
723
+ */
724
+
725
+
726
+ setSlide(slide) {
727
+ this.slide = slide;
728
+ this.hasSlide = true;
729
+ this.instance = slide.pswp; // todo: do we need to unset slide?
730
+ }
731
+ /**
732
+ * Content load success handler
733
+ */
734
+
735
+
736
+ onLoaded() {
737
+ this.state = LOAD_STATE.LOADED;
738
+
739
+ if (this.slide && this.element) {
740
+ this.instance.dispatch('loadComplete', {
741
+ slide: this.slide,
742
+ content: this
743
+ }); // if content is reloaded
744
+
745
+ if (this.slide.isActive && this.slide.heavyAppended && !this.element.parentNode) {
746
+ this.append();
747
+ this.slide.updateContentSize(true);
748
+ }
749
+
750
+ if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
751
+ this.removePlaceholder();
752
+ }
753
+ }
754
+ }
755
+ /**
756
+ * Content load error handler
757
+ */
758
+
759
+
760
+ onError() {
761
+ this.state = LOAD_STATE.ERROR;
762
+
763
+ if (this.slide) {
764
+ this.displayError();
765
+ this.instance.dispatch('loadComplete', {
766
+ slide: this.slide,
767
+ isError: true,
768
+ content: this
769
+ });
770
+ this.instance.dispatch('loadError', {
771
+ slide: this.slide,
772
+ content: this
773
+ });
774
+ }
775
+ }
776
+ /**
777
+ * @returns {Boolean} If the content is currently loading
778
+ */
779
+
780
+
781
+ isLoading() {
782
+ return this.instance.applyFilters('isContentLoading', this.state === LOAD_STATE.LOADING, this);
783
+ }
784
+ /**
785
+ * @returns {Boolean} If the content is in error state
786
+ */
787
+
788
+
789
+ isError() {
790
+ return this.state === LOAD_STATE.ERROR;
791
+ }
792
+ /**
793
+ * @returns {boolean} If the content is image
794
+ */
795
+
796
+
797
+ isImageContent() {
798
+ return this.type === 'image';
799
+ }
800
+ /**
801
+ * Update content size
802
+ *
803
+ * @param {Number} width
804
+ * @param {Number} height
805
+ */
806
+
807
+
808
+ setDisplayedSize(width, height) {
809
+ if (!this.element) {
810
+ return;
811
+ }
812
+
813
+ if (this.placeholder) {
814
+ this.placeholder.setDisplayedSize(width, height);
815
+ }
816
+
817
+ if (this.instance.dispatch('contentResize', {
818
+ content: this,
819
+ width,
820
+ height
821
+ }).defaultPrevented) {
822
+ return;
823
+ }
824
+
825
+ setWidthHeight(this.element, width, height);
826
+
827
+ if (this.isImageContent() && !this.isError()) {
828
+ const isInitialSizeUpdate = !this.displayedImageWidth && width;
829
+ this.displayedImageWidth = width;
830
+ this.displayedImageHeight = height;
831
+
832
+ if (isInitialSizeUpdate) {
833
+ this.loadImage(false);
834
+ } else {
835
+ this.updateSrcsetSizes();
836
+ }
837
+
838
+ if (this.slide) {
839
+ this.instance.dispatch('imageSizeChange', {
840
+ slide: this.slide,
841
+ width,
842
+ height,
843
+ content: this
844
+ });
845
+ }
846
+ }
847
+ }
848
+ /**
849
+ * @returns {boolean} If the content can be zoomed
850
+ */
851
+
852
+
853
+ isZoomable() {
854
+ return this.instance.applyFilters('isContentZoomable', this.isImageContent() && this.state !== LOAD_STATE.ERROR, this);
855
+ }
856
+ /**
857
+ * Update image srcset sizes attribute based on width and height
858
+ */
859
+
860
+
861
+ updateSrcsetSizes() {
862
+ // Handle srcset sizes attribute.
863
+ //
864
+ // Never lower quality, if it was increased previously.
865
+ // Chrome does this automatically, Firefox and Safari do not,
866
+ // so we store largest used size in dataset.
867
+ if (!this.isImageContent() || !this.element || !this.data.srcset) {
868
+ return;
869
+ }
870
+
871
+ const image =
872
+ /** @type HTMLImageElement */
873
+ this.element;
874
+ const sizesWidth = this.instance.applyFilters('srcsetSizesWidth', this.displayedImageWidth, this);
875
+
876
+ if (!image.dataset.largestUsedSize || sizesWidth > parseInt(image.dataset.largestUsedSize, 10)) {
877
+ image.sizes = sizesWidth + 'px';
878
+ image.dataset.largestUsedSize = String(sizesWidth);
879
+ }
880
+ }
881
+ /**
882
+ * @returns {boolean} If content should use a placeholder (from msrc by default)
883
+ */
884
+
885
+
886
+ usePlaceholder() {
887
+ return this.instance.applyFilters('useContentPlaceholder', this.isImageContent(), this);
888
+ }
889
+ /**
890
+ * Preload content with lazy-loading param
891
+ */
892
+
893
+
894
+ lazyLoad() {
895
+ if (this.instance.dispatch('contentLazyLoad', {
896
+ content: this
897
+ }).defaultPrevented) {
898
+ return;
899
+ }
900
+
901
+ this.load(true);
902
+ }
903
+ /**
904
+ * @returns {boolean} If placeholder should be kept after content is loaded
905
+ */
906
+
907
+
908
+ keepPlaceholder() {
909
+ return this.instance.applyFilters('isKeepingPlaceholder', this.isLoading(), this);
910
+ }
911
+ /**
912
+ * Destroy the content
913
+ */
914
+
915
+
916
+ destroy() {
917
+ this.hasSlide = false;
918
+ this.slide = undefined;
919
+
920
+ if (this.instance.dispatch('contentDestroy', {
921
+ content: this
922
+ }).defaultPrevented) {
923
+ return;
924
+ }
925
+
926
+ this.remove();
927
+
928
+ if (this.placeholder) {
929
+ this.placeholder.destroy();
930
+ this.placeholder = undefined;
931
+ }
932
+
933
+ if (this.isImageContent() && this.element) {
934
+ this.element.onload = null;
935
+ this.element.onerror = null;
936
+ this.element = undefined;
937
+ }
938
+ }
939
+ /**
940
+ * Display error message
941
+ */
942
+
943
+
944
+ displayError() {
945
+ if (this.slide) {
946
+ var _this$instance$option, _this$instance$option2;
947
+
948
+ let errorMsgEl = createElement('pswp__error-msg', 'div');
949
+ errorMsgEl.innerText = (_this$instance$option = (_this$instance$option2 = this.instance.options) === null || _this$instance$option2 === void 0 ? void 0 : _this$instance$option2.errorMsg) !== null && _this$instance$option !== void 0 ? _this$instance$option : '';
950
+ errorMsgEl =
951
+ /** @type {HTMLDivElement} */
952
+ this.instance.applyFilters('contentErrorElement', errorMsgEl, this);
953
+ this.element = createElement('pswp__content pswp__error-msg-container', 'div');
954
+ this.element.appendChild(errorMsgEl);
955
+ this.slide.container.innerText = '';
956
+ this.slide.container.appendChild(this.element);
957
+ this.slide.updateContentSize(true);
958
+ this.removePlaceholder();
959
+ }
960
+ }
961
+ /**
962
+ * Append the content
963
+ */
964
+
965
+
966
+ append() {
967
+ if (this.isAttached || !this.element) {
968
+ return;
969
+ }
970
+
971
+ this.isAttached = true;
972
+
973
+ if (this.state === LOAD_STATE.ERROR) {
974
+ this.displayError();
975
+ return;
976
+ }
977
+
978
+ if (this.instance.dispatch('contentAppend', {
979
+ content: this
980
+ }).defaultPrevented) {
981
+ return;
982
+ }
983
+
984
+ const supportsDecode = ('decode' in this.element);
985
+
986
+ if (this.isImageContent()) {
987
+ // Use decode() on nearby slides
988
+ //
989
+ // Nearby slide images are in DOM and not hidden via display:none.
990
+ // However, they are placed offscreen (to the left and right side).
991
+ //
992
+ // Some browsers do not composite the image until it's actually visible,
993
+ // using decode() helps.
994
+ //
995
+ // You might ask "why dont you just decode() and then append all images",
996
+ // that's because I want to show image before it's fully loaded,
997
+ // as browser can render parts of image while it is loading.
998
+ // We do not do this in Safari due to partial loading bug.
999
+ if (supportsDecode && this.slide && (!this.slide.isActive || isSafari())) {
1000
+ this.isDecoding = true; // purposefully using finally instead of then,
1001
+ // as if srcset sizes changes dynamically - it may cause decode error
1002
+
1003
+ /** @type {HTMLImageElement} */
1004
+
1005
+ this.element.decode().catch(() => {}).finally(() => {
1006
+ this.isDecoding = false;
1007
+ this.appendImage();
1008
+ });
1009
+ } else {
1010
+ this.appendImage();
1011
+ }
1012
+ } else if (this.slide && !this.element.parentNode) {
1013
+ this.slide.container.appendChild(this.element);
1014
+ }
1015
+ }
1016
+ /**
1017
+ * Activate the slide,
1018
+ * active slide is generally the current one,
1019
+ * meaning the user can see it.
1020
+ */
1021
+
1022
+
1023
+ activate() {
1024
+ if (this.instance.dispatch('contentActivate', {
1025
+ content: this
1026
+ }).defaultPrevented || !this.slide) {
1027
+ return;
1028
+ }
1029
+
1030
+ if (this.isImageContent() && this.isDecoding && !isSafari()) {
1031
+ // add image to slide when it becomes active,
1032
+ // even if it's not finished decoding
1033
+ this.appendImage();
1034
+ } else if (this.isError()) {
1035
+ this.load(false, true); // try to reload
1036
+ }
1037
+
1038
+ if (this.slide.holderElement) {
1039
+ this.slide.holderElement.setAttribute('aria-hidden', 'false');
1040
+ }
1041
+ }
1042
+ /**
1043
+ * Deactivate the content
1044
+ */
1045
+
1046
+
1047
+ deactivate() {
1048
+ this.instance.dispatch('contentDeactivate', {
1049
+ content: this
1050
+ });
1051
+
1052
+ if (this.slide && this.slide.holderElement) {
1053
+ this.slide.holderElement.setAttribute('aria-hidden', 'true');
1054
+ }
1055
+ }
1056
+ /**
1057
+ * Remove the content from DOM
1058
+ */
1059
+
1060
+
1061
+ remove() {
1062
+ this.isAttached = false;
1063
+
1064
+ if (this.instance.dispatch('contentRemove', {
1065
+ content: this
1066
+ }).defaultPrevented) {
1067
+ return;
1068
+ }
1069
+
1070
+ if (this.element && this.element.parentNode) {
1071
+ this.element.remove();
1072
+ }
1073
+
1074
+ if (this.placeholder && this.placeholder.element) {
1075
+ this.placeholder.element.remove();
1076
+ }
1077
+ }
1078
+ /**
1079
+ * Append the image content to slide container
1080
+ */
1081
+
1082
+
1083
+ appendImage() {
1084
+ if (!this.isAttached) {
1085
+ return;
1086
+ }
1087
+
1088
+ if (this.instance.dispatch('contentAppendImage', {
1089
+ content: this
1090
+ }).defaultPrevented) {
1091
+ return;
1092
+ } // ensure that element exists and is not already appended
1093
+
1094
+
1095
+ if (this.slide && this.element && !this.element.parentNode) {
1096
+ this.slide.container.appendChild(this.element);
1097
+ }
1098
+
1099
+ if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
1100
+ this.removePlaceholder();
1101
+ }
1102
+ }
1103
+
1104
+ }
1105
+
1106
+ /** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
1107
+
1108
+ /** @typedef {import('../core/base.js').default} PhotoSwipeBase */
1109
+
1110
+ /** @typedef {import('../photoswipe.js').Point} Point */
1111
+
1112
+ /** @typedef {import('../slide/slide.js').SlideData} SlideData */
1113
+
1114
+ /**
1115
+ * @param {PhotoSwipeOptions} options
1116
+ * @param {PhotoSwipeBase} pswp
1117
+ * @returns {Point}
1118
+ */
1119
+ function getViewportSize(options, pswp) {
1120
+ if (options.getViewportSizeFn) {
1121
+ const newViewportSize = options.getViewportSizeFn(options, pswp);
1122
+
1123
+ if (newViewportSize) {
1124
+ return newViewportSize;
1125
+ }
1126
+ }
1127
+
1128
+ return {
1129
+ x: document.documentElement.clientWidth,
1130
+ // TODO: height on mobile is very incosistent due to toolbar
1131
+ // find a way to improve this
1132
+ //
1133
+ // document.documentElement.clientHeight - doesn't seem to work well
1134
+ y: window.innerHeight
1135
+ };
1136
+ }
1137
+ /**
1138
+ * Parses padding option.
1139
+ * Supported formats:
1140
+ *
1141
+ * // Object
1142
+ * padding: {
1143
+ * top: 0,
1144
+ * bottom: 0,
1145
+ * left: 0,
1146
+ * right: 0
1147
+ * }
1148
+ *
1149
+ * // A function that returns the object
1150
+ * paddingFn: (viewportSize, itemData, index) => {
1151
+ * return {
1152
+ * top: 0,
1153
+ * bottom: 0,
1154
+ * left: 0,
1155
+ * right: 0
1156
+ * };
1157
+ * }
1158
+ *
1159
+ * // Legacy variant
1160
+ * paddingLeft: 0,
1161
+ * paddingRight: 0,
1162
+ * paddingTop: 0,
1163
+ * paddingBottom: 0,
1164
+ *
1165
+ * @param {'left' | 'top' | 'bottom' | 'right'} prop
1166
+ * @param {PhotoSwipeOptions} options PhotoSwipe options
1167
+ * @param {Point} viewportSize PhotoSwipe viewport size, for example: { x:800, y:600 }
1168
+ * @param {SlideData} itemData Data about the slide
1169
+ * @param {number} index Slide index
1170
+ * @returns {number}
1171
+ */
1172
+
1173
+ function parsePaddingOption(prop, options, viewportSize, itemData, index) {
1174
+ let paddingValue = 0;
1175
+
1176
+ if (options.paddingFn) {
1177
+ paddingValue = options.paddingFn(viewportSize, itemData, index)[prop];
1178
+ } else if (options.padding) {
1179
+ paddingValue = options.padding[prop];
1180
+ } else {
1181
+ const legacyPropName = 'padding' + prop[0].toUpperCase() + prop.slice(1); // @ts-expect-error
1182
+
1183
+ if (options[legacyPropName]) {
1184
+ // @ts-expect-error
1185
+ paddingValue = options[legacyPropName];
1186
+ }
1187
+ }
1188
+
1189
+ return Number(paddingValue) || 0;
1190
+ }
1191
+ /**
1192
+ * @param {PhotoSwipeOptions} options
1193
+ * @param {Point} viewportSize
1194
+ * @param {SlideData} itemData
1195
+ * @param {number} index
1196
+ * @returns {Point}
1197
+ */
1198
+
1199
+ function getPanAreaSize(options, viewportSize, itemData, index) {
1200
+ return {
1201
+ x: viewportSize.x - parsePaddingOption('left', options, viewportSize, itemData, index) - parsePaddingOption('right', options, viewportSize, itemData, index),
1202
+ y: viewportSize.y - parsePaddingOption('top', options, viewportSize, itemData, index) - parsePaddingOption('bottom', options, viewportSize, itemData, index)
1203
+ };
1204
+ }
1205
+
1206
+ const MAX_IMAGE_WIDTH = 4000;
1207
+ /** @typedef {import('../photoswipe.js').default} PhotoSwipe */
1208
+
1209
+ /** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
1210
+
1211
+ /** @typedef {import('../photoswipe.js').Point} Point */
1212
+
1213
+ /** @typedef {import('../slide/slide.js').SlideData} SlideData */
1214
+
1215
+ /** @typedef {'fit' | 'fill' | number | ((zoomLevelObject: ZoomLevel) => number)} ZoomLevelOption */
1216
+
1217
+ /**
1218
+ * Calculates zoom levels for specific slide.
1219
+ * Depends on viewport size and image size.
1220
+ */
1221
+
1222
+ class ZoomLevel {
1223
+ /**
1224
+ * @param {PhotoSwipeOptions} options PhotoSwipe options
1225
+ * @param {SlideData} itemData Slide data
1226
+ * @param {number} index Slide index
1227
+ * @param {PhotoSwipe} [pswp] PhotoSwipe instance, can be undefined if not initialized yet
1228
+ */
1229
+ constructor(options, itemData, index, pswp) {
1230
+ this.pswp = pswp;
1231
+ this.options = options;
1232
+ this.itemData = itemData;
1233
+ this.index = index;
1234
+ /** @type { Point | null } */
1235
+
1236
+ this.panAreaSize = null;
1237
+ /** @type { Point | null } */
1238
+
1239
+ this.elementSize = null;
1240
+ this.fit = 1;
1241
+ this.fill = 1;
1242
+ this.vFill = 1;
1243
+ this.initial = 1;
1244
+ this.secondary = 1;
1245
+ this.max = 1;
1246
+ this.min = 1;
1247
+ }
1248
+ /**
1249
+ * Calculate initial, secondary and maximum zoom level for the specified slide.
1250
+ *
1251
+ * It should be called when either image or viewport size changes.
1252
+ *
1253
+ * @param {number} maxWidth
1254
+ * @param {number} maxHeight
1255
+ * @param {Point} panAreaSize
1256
+ */
1257
+
1258
+
1259
+ update(maxWidth, maxHeight, panAreaSize) {
1260
+ /** @type {Point} */
1261
+ const elementSize = {
1262
+ x: maxWidth,
1263
+ y: maxHeight
1264
+ };
1265
+ this.elementSize = elementSize;
1266
+ this.panAreaSize = panAreaSize;
1267
+ const hRatio = panAreaSize.x / elementSize.x;
1268
+ const vRatio = panAreaSize.y / elementSize.y;
1269
+ this.fit = Math.min(1, hRatio < vRatio ? hRatio : vRatio);
1270
+ this.fill = Math.min(1, hRatio > vRatio ? hRatio : vRatio); // zoom.vFill defines zoom level of the image
1271
+ // when it has 100% of viewport vertical space (height)
1272
+
1273
+ this.vFill = Math.min(1, vRatio);
1274
+ this.initial = this._getInitial();
1275
+ this.secondary = this._getSecondary();
1276
+ this.max = Math.max(this.initial, this.secondary, this._getMax());
1277
+ this.min = Math.min(this.fit, this.initial, this.secondary);
1278
+
1279
+ if (this.pswp) {
1280
+ this.pswp.dispatch('zoomLevelsUpdate', {
1281
+ zoomLevels: this,
1282
+ slideData: this.itemData
1283
+ });
1284
+ }
1285
+ }
1286
+ /**
1287
+ * Parses user-defined zoom option.
1288
+ *
1289
+ * @private
1290
+ * @param {'initial' | 'secondary' | 'max'} optionPrefix Zoom level option prefix (initial, secondary, max)
1291
+ * @returns { number | undefined }
1292
+ */
1293
+
1294
+
1295
+ _parseZoomLevelOption(optionPrefix) {
1296
+ const optionName =
1297
+ /** @type {'initialZoomLevel' | 'secondaryZoomLevel' | 'maxZoomLevel'} */
1298
+ optionPrefix + 'ZoomLevel';
1299
+ const optionValue = this.options[optionName];
1300
+
1301
+ if (!optionValue) {
1302
+ return;
1303
+ }
1304
+
1305
+ if (typeof optionValue === 'function') {
1306
+ return optionValue(this);
1307
+ }
1308
+
1309
+ if (optionValue === 'fill') {
1310
+ return this.fill;
1311
+ }
1312
+
1313
+ if (optionValue === 'fit') {
1314
+ return this.fit;
1315
+ }
1316
+
1317
+ return Number(optionValue);
1318
+ }
1319
+ /**
1320
+ * Get zoom level to which image will be zoomed after double-tap gesture,
1321
+ * or when user clicks on zoom icon,
1322
+ * or mouse-click on image itself.
1323
+ * If you return 1 image will be zoomed to its original size.
1324
+ *
1325
+ * @private
1326
+ * @return {number}
1327
+ */
1328
+
1329
+
1330
+ _getSecondary() {
1331
+ let currZoomLevel = this._parseZoomLevelOption('secondary');
1332
+
1333
+ if (currZoomLevel) {
1334
+ return currZoomLevel;
1335
+ } // 3x of "fit" state, but not larger than original
1336
+
1337
+
1338
+ currZoomLevel = Math.min(1, this.fit * 3);
1339
+
1340
+ if (this.elementSize && currZoomLevel * this.elementSize.x > MAX_IMAGE_WIDTH) {
1341
+ currZoomLevel = MAX_IMAGE_WIDTH / this.elementSize.x;
1342
+ }
1343
+
1344
+ return currZoomLevel;
1345
+ }
1346
+ /**
1347
+ * Get initial image zoom level.
1348
+ *
1349
+ * @private
1350
+ * @return {number}
1351
+ */
1352
+
1353
+
1354
+ _getInitial() {
1355
+ return this._parseZoomLevelOption('initial') || this.fit;
1356
+ }
1357
+ /**
1358
+ * Maximum zoom level when user zooms
1359
+ * via zoom/pinch gesture,
1360
+ * via cmd/ctrl-wheel or via trackpad.
1361
+ *
1362
+ * @private
1363
+ * @return {number}
1364
+ */
1365
+
1366
+
1367
+ _getMax() {
1368
+ // max zoom level is x4 from "fit state",
1369
+ // used for zoom gesture and ctrl/trackpad zoom
1370
+ return this._parseZoomLevelOption('max') || Math.max(1, this.fit * 4);
1371
+ }
1372
+
1373
+ }
1374
+
1375
+ /**
1376
+ * Lazy-load an image
1377
+ * This function is used both by Lightbox and PhotoSwipe core,
1378
+ * thus it can be called before dialog is opened.
1379
+ *
1380
+ * @param {SlideData} itemData Data about the slide
1381
+ * @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox instance
1382
+ * @param {number} index
1383
+ * @returns {Content} Image that is being decoded or false.
1384
+ */
1385
+
1386
+ function lazyLoadData(itemData, instance, index) {
1387
+ const content = instance.createContentFromData(itemData, index);
1388
+ /** @type {ZoomLevel | undefined} */
1389
+
1390
+ let zoomLevel;
1391
+ const {
1392
+ options
1393
+ } = instance; // We need to know dimensions of the image to preload it,
1394
+ // as it might use srcset, and we need to define sizes
1395
+
1396
+ if (options) {
1397
+ zoomLevel = new ZoomLevel(options, itemData, -1);
1398
+ let viewportSize;
1399
+
1400
+ if (instance.pswp) {
1401
+ viewportSize = instance.pswp.viewportSize;
1402
+ } else {
1403
+ viewportSize = getViewportSize(options, instance);
1404
+ }
1405
+
1406
+ const panAreaSize = getPanAreaSize(options, viewportSize, itemData, index);
1407
+ zoomLevel.update(content.width, content.height, panAreaSize);
1408
+ }
1409
+
1410
+ content.lazyLoad();
1411
+
1412
+ if (zoomLevel) {
1413
+ content.setDisplayedSize(Math.ceil(content.width * zoomLevel.initial), Math.ceil(content.height * zoomLevel.initial));
1414
+ }
1415
+
1416
+ return content;
1417
+ }
1418
+ /**
1419
+ * Lazy-loads specific slide.
1420
+ * This function is used both by Lightbox and PhotoSwipe core,
1421
+ * thus it can be called before dialog is opened.
1422
+ *
1423
+ * By default, it loads image based on viewport size and initial zoom level.
1424
+ *
1425
+ * @param {number} index Slide index
1426
+ * @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox eventable instance
1427
+ * @returns {Content | undefined}
1428
+ */
1429
+
1430
+ function lazyLoadSlide(index, instance) {
1431
+ const itemData = instance.getItemData(index);
1432
+
1433
+ if (instance.dispatch('lazyLoadSlide', {
1434
+ index,
1435
+ itemData
1436
+ }).defaultPrevented) {
1437
+ return;
1438
+ }
1439
+
1440
+ return lazyLoadData(itemData, instance, index);
1441
+ }
1442
+
1443
+ /** @typedef {import("../photoswipe.js").default} PhotoSwipe */
1444
+
1445
+ /** @typedef {import("../slide/slide.js").SlideData} SlideData */
1446
+
1447
+ /**
1448
+ * PhotoSwipe base class that can retrieve data about every slide.
1449
+ * Shared by PhotoSwipe Core and PhotoSwipe Lightbox
1450
+ */
1451
+
1452
+ class PhotoSwipeBase extends Eventable {
1453
+ /**
1454
+ * Get total number of slides
1455
+ *
1456
+ * @returns {number}
1457
+ */
1458
+ getNumItems() {
1459
+ var _this$options;
1460
+
1461
+ let numItems = 0;
1462
+ const dataSource = (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.dataSource;
1463
+
1464
+ if (dataSource && 'length' in dataSource) {
1465
+ // may be an array or just object with length property
1466
+ numItems = dataSource.length;
1467
+ } else if (dataSource && 'gallery' in dataSource) {
1468
+ // query DOM elements
1469
+ if (!dataSource.items) {
1470
+ dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
1471
+ }
1472
+
1473
+ if (dataSource.items) {
1474
+ numItems = dataSource.items.length;
1475
+ }
1476
+ } // legacy event, before filters were introduced
1477
+
1478
+
1479
+ const event = this.dispatch('numItems', {
1480
+ dataSource,
1481
+ numItems
1482
+ });
1483
+ return this.applyFilters('numItems', event.numItems, dataSource);
1484
+ }
1485
+ /**
1486
+ * @param {SlideData} slideData
1487
+ * @param {number} index
1488
+ * @returns {Content}
1489
+ */
1490
+
1491
+
1492
+ createContentFromData(slideData, index) {
1493
+ return new Content(slideData, this, index);
1494
+ }
1495
+ /**
1496
+ * Get item data by index.
1497
+ *
1498
+ * "item data" should contain normalized information that PhotoSwipe needs to generate a slide.
1499
+ * For example, it may contain properties like
1500
+ * `src`, `srcset`, `w`, `h`, which will be used to generate a slide with image.
1501
+ *
1502
+ * @param {number} index
1503
+ * @returns {SlideData}
1504
+ */
1505
+
1506
+
1507
+ getItemData(index) {
1508
+ var _this$options2;
1509
+
1510
+ const dataSource = (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.dataSource;
1511
+ /** @type {SlideData | HTMLElement} */
1512
+
1513
+ let dataSourceItem = {};
1514
+
1515
+ if (Array.isArray(dataSource)) {
1516
+ // Datasource is an array of elements
1517
+ dataSourceItem = dataSource[index];
1518
+ } else if (dataSource && 'gallery' in dataSource) {
1519
+ // dataSource has gallery property,
1520
+ // thus it was created by Lightbox, based on
1521
+ // gallery and children options
1522
+ // query DOM elements
1523
+ if (!dataSource.items) {
1524
+ dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
1525
+ }
1526
+
1527
+ dataSourceItem = dataSource.items[index];
1528
+ }
1529
+
1530
+ let itemData = dataSourceItem;
1531
+
1532
+ if (itemData instanceof Element) {
1533
+ itemData = this._domElementToItemData(itemData);
1534
+ } // Dispatching the itemData event,
1535
+ // it's a legacy verion before filters were introduced
1536
+
1537
+
1538
+ const event = this.dispatch('itemData', {
1539
+ itemData: itemData || {},
1540
+ index
1541
+ });
1542
+ return this.applyFilters('itemData', event.itemData, index);
1543
+ }
1544
+ /**
1545
+ * Get array of gallery DOM elements,
1546
+ * based on childSelector and gallery element.
1547
+ *
1548
+ * @param {HTMLElement} galleryElement
1549
+ * @returns {HTMLElement[]}
1550
+ */
1551
+
1552
+
1553
+ _getGalleryDOMElements(galleryElement) {
1554
+ var _this$options3, _this$options4;
1555
+
1556
+ if ((_this$options3 = this.options) !== null && _this$options3 !== void 0 && _this$options3.children || (_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.childSelector) {
1557
+ return getElementsFromOption(this.options.children, this.options.childSelector, galleryElement) || [];
1558
+ }
1559
+
1560
+ return [galleryElement];
1561
+ }
1562
+ /**
1563
+ * Converts DOM element to item data object.
1564
+ *
1565
+ * @param {HTMLElement} element DOM element
1566
+ * @returns {SlideData}
1567
+ */
1568
+
1569
+
1570
+ _domElementToItemData(element) {
1571
+ /** @type {SlideData} */
1572
+ const itemData = {
1573
+ element
1574
+ };
1575
+ const linkEl =
1576
+ /** @type {HTMLAnchorElement} */
1577
+ element.tagName === 'A' ? element : element.querySelector('a');
1578
+
1579
+ if (linkEl) {
1580
+ // src comes from data-pswp-src attribute,
1581
+ // if it's empty link href is used
1582
+ itemData.src = linkEl.dataset.pswpSrc || linkEl.href;
1583
+
1584
+ if (linkEl.dataset.pswpSrcset) {
1585
+ itemData.srcset = linkEl.dataset.pswpSrcset;
1586
+ }
1587
+
1588
+ itemData.width = linkEl.dataset.pswpWidth ? parseInt(linkEl.dataset.pswpWidth, 10) : 0;
1589
+ itemData.height = linkEl.dataset.pswpHeight ? parseInt(linkEl.dataset.pswpHeight, 10) : 0; // support legacy w & h properties
1590
+
1591
+ itemData.w = itemData.width;
1592
+ itemData.h = itemData.height;
1593
+
1594
+ if (linkEl.dataset.pswpType) {
1595
+ itemData.type = linkEl.dataset.pswpType;
1596
+ }
1597
+
1598
+ const thumbnailEl = element.querySelector('img');
1599
+
1600
+ if (thumbnailEl) {
1601
+ var _thumbnailEl$getAttri;
1602
+
1603
+ // msrc is URL to placeholder image that's displayed before large image is loaded
1604
+ // by default it's displayed only for the first slide
1605
+ itemData.msrc = thumbnailEl.currentSrc || thumbnailEl.src;
1606
+ itemData.alt = (_thumbnailEl$getAttri = thumbnailEl.getAttribute('alt')) !== null && _thumbnailEl$getAttri !== void 0 ? _thumbnailEl$getAttri : '';
1607
+ }
1608
+
1609
+ if (linkEl.dataset.pswpCropped || linkEl.dataset.cropped) {
1610
+ itemData.thumbCropped = true;
1611
+ }
1612
+ }
1613
+
1614
+ return this.applyFilters('domItemData', itemData, element, linkEl);
1615
+ }
1616
+ /**
1617
+ * Lazy-load by slide data
1618
+ *
1619
+ * @param {SlideData} itemData Data about the slide
1620
+ * @param {number} index
1621
+ * @returns {Content} Image that is being decoded or false.
1622
+ */
1623
+
1624
+
1625
+ lazyLoadData(itemData, index) {
1626
+ return lazyLoadData(itemData, this, index);
1627
+ }
1628
+
1629
+ }
1630
+
1631
+ /**
1632
+ * @template T
1633
+ * @typedef {import('../types.js').Type<T>} Type<T>
1634
+ */
1635
+
1636
+ /** @typedef {import('../photoswipe.js').default} PhotoSwipe */
1637
+
1638
+ /** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
1639
+
1640
+ /** @typedef {import('../photoswipe.js').DataSource} DataSource */
1641
+
1642
+ /** @typedef {import('../photoswipe.js').Point} Point */
1643
+
1644
+ /** @typedef {import('../slide/content.js').default} Content */
1645
+
1646
+ /** @typedef {import('../core/eventable.js').PhotoSwipeEventsMap} PhotoSwipeEventsMap */
1647
+
1648
+ /** @typedef {import('../core/eventable.js').PhotoSwipeFiltersMap} PhotoSwipeFiltersMap */
1649
+
1650
+ /**
1651
+ * @template {keyof PhotoSwipeEventsMap} T
1652
+ * @typedef {import('../core/eventable.js').EventCallback<T>} EventCallback<T>
1653
+ */
1654
+
1655
+ /**
1656
+ * PhotoSwipe Lightbox
1657
+ *
1658
+ * - If user has unsupported browser it falls back to default browser action (just opens URL)
1659
+ * - Binds click event to links that should open PhotoSwipe
1660
+ * - parses DOM strcture for PhotoSwipe (retrieves large image URLs and sizes)
1661
+ * - Initializes PhotoSwipe
1662
+ *
1663
+ *
1664
+ * Loader options use the same object as PhotoSwipe, and supports such options:
1665
+ *
1666
+ * gallery - Element | Element[] | NodeList | string selector for the gallery element
1667
+ * children - Element | Element[] | NodeList | string selector for the gallery children
1668
+ *
1669
+ */
1670
+
1671
+ class PhotoSwipeLightbox extends PhotoSwipeBase {
1672
+ /**
1673
+ * @param {PhotoSwipeOptions} [options]
1674
+ */
1675
+ constructor(options) {
1676
+ super();
1677
+ /** @type {PhotoSwipeOptions} */
1678
+
1679
+ this.options = options || {};
1680
+ this._uid = 0;
1681
+ this.shouldOpen = false;
1682
+ /**
1683
+ * @private
1684
+ * @type {Content | undefined}
1685
+ */
1686
+
1687
+ this._preloadedContent = undefined;
1688
+ this.onThumbnailsClick = this.onThumbnailsClick.bind(this);
1689
+ }
1690
+ /**
1691
+ * Initialize lightbox, should be called only once.
1692
+ * It's not included in the main constructor, so you may bind events before it.
1693
+ */
1694
+
1695
+
1696
+ init() {
1697
+ // Bind click events to each gallery
1698
+ getElementsFromOption(this.options.gallery, this.options.gallerySelector).forEach(galleryElement => {
1699
+ galleryElement.addEventListener('click', this.onThumbnailsClick, false);
1700
+ });
1701
+ }
1702
+ /**
1703
+ * @param {MouseEvent} e
1704
+ */
1705
+
1706
+
1707
+ onThumbnailsClick(e) {
1708
+ // Exit and allow default browser action if:
1709
+ if (specialKeyUsed(e) // ... if clicked with a special key (ctrl/cmd...)
1710
+ || window.pswp) {
1711
+ // ... if PhotoSwipe is already open
1712
+ return;
1713
+ } // If both clientX and clientY are 0 or not defined,
1714
+ // the event is likely triggered by keyboard,
1715
+ // so we do not pass the initialPoint
1716
+ //
1717
+ // Note that some screen readers emulate the mouse position,
1718
+ // so it's not the ideal way to detect them.
1719
+ //
1720
+
1721
+ /** @type {Point | null} */
1722
+
1723
+
1724
+ let initialPoint = {
1725
+ x: e.clientX,
1726
+ y: e.clientY
1727
+ };
1728
+
1729
+ if (!initialPoint.x && !initialPoint.y) {
1730
+ initialPoint = null;
1731
+ }
1732
+
1733
+ let clickedIndex = this.getClickedIndex(e);
1734
+ clickedIndex = this.applyFilters('clickedIndex', clickedIndex, e, this);
1735
+ /** @type {DataSource} */
1736
+
1737
+ const dataSource = {
1738
+ gallery:
1739
+ /** @type {HTMLElement} */
1740
+ e.currentTarget
1741
+ };
1742
+
1743
+ if (clickedIndex >= 0) {
1744
+ e.preventDefault();
1745
+ this.loadAndOpen(clickedIndex, dataSource, initialPoint);
1746
+ }
1747
+ }
1748
+ /**
1749
+ * Get index of gallery item that was clicked.
1750
+ *
1751
+ * @param {MouseEvent} e click event
1752
+ * @returns {number}
1753
+ */
1754
+
1755
+
1756
+ getClickedIndex(e) {
1757
+ // legacy option
1758
+ if (this.options.getClickedIndexFn) {
1759
+ return this.options.getClickedIndexFn.call(this, e);
1760
+ }
1761
+
1762
+ const clickedTarget =
1763
+ /** @type {HTMLElement} */
1764
+ e.target;
1765
+ const childElements = getElementsFromOption(this.options.children, this.options.childSelector,
1766
+ /** @type {HTMLElement} */
1767
+ e.currentTarget);
1768
+ const clickedChildIndex = childElements.findIndex(child => child === clickedTarget || child.contains(clickedTarget));
1769
+
1770
+ if (clickedChildIndex !== -1) {
1771
+ return clickedChildIndex;
1772
+ } else if (this.options.children || this.options.childSelector) {
1773
+ // click wasn't on a child element
1774
+ return -1;
1775
+ } // There is only one item (which is the gallery)
1776
+
1777
+
1778
+ return 0;
1779
+ }
1780
+ /**
1781
+ * Load and open PhotoSwipe
1782
+ *
1783
+ * @param {number} index
1784
+ * @param {DataSource} [dataSource]
1785
+ * @param {Point | null} [initialPoint]
1786
+ * @returns {boolean}
1787
+ */
1788
+
1789
+
1790
+ loadAndOpen(index, dataSource, initialPoint) {
1791
+ // Check if the gallery is already open
1792
+ if (window.pswp || !this.options) {
1793
+ return false;
1794
+ } // Use the first gallery element if dataSource is not provided
1795
+
1796
+
1797
+ if (!dataSource && this.options.gallery && this.options.children) {
1798
+ const galleryElements = getElementsFromOption(this.options.gallery);
1799
+
1800
+ if (galleryElements[0]) {
1801
+ dataSource = {
1802
+ gallery: galleryElements[0]
1803
+ };
1804
+ }
1805
+ } // set initial index
1806
+
1807
+
1808
+ this.options.index = index; // define options for PhotoSwipe constructor
1809
+
1810
+ this.options.initialPointerPos = initialPoint;
1811
+ this.shouldOpen = true;
1812
+ this.preload(index, dataSource);
1813
+ return true;
1814
+ }
1815
+ /**
1816
+ * Load the main module and the slide content by index
1817
+ *
1818
+ * @param {number} index
1819
+ * @param {DataSource} [dataSource]
1820
+ */
1821
+
1822
+
1823
+ preload(index, dataSource) {
1824
+ const {
1825
+ options
1826
+ } = this;
1827
+
1828
+ if (dataSource) {
1829
+ options.dataSource = dataSource;
1830
+ } // Add the main module
1831
+
1832
+ /** @type {Promise<Type<PhotoSwipe>>[]} */
1833
+
1834
+
1835
+ const promiseArray = [];
1836
+ const pswpModuleType = typeof options.pswpModule;
1837
+
1838
+ if (isPswpClass(options.pswpModule)) {
1839
+ promiseArray.push(Promise.resolve(
1840
+ /** @type {Type<PhotoSwipe>} */
1841
+ options.pswpModule));
1842
+ } else if (pswpModuleType === 'string') {
1843
+ throw new Error('pswpModule as string is no longer supported');
1844
+ } else if (pswpModuleType === 'function') {
1845
+ promiseArray.push(
1846
+ /** @type {() => Promise<Type<PhotoSwipe>>} */
1847
+ options.pswpModule());
1848
+ } else {
1849
+ throw new Error('pswpModule is not valid');
1850
+ } // Add custom-defined promise, if any
1851
+
1852
+
1853
+ if (typeof options.openPromise === 'function') {
1854
+ // allow developers to perform some task before opening
1855
+ promiseArray.push(options.openPromise());
1856
+ }
1857
+
1858
+ if (options.preloadFirstSlide !== false && index >= 0) {
1859
+ this._preloadedContent = lazyLoadSlide(index, this);
1860
+ } // Wait till all promises resolve and open PhotoSwipe
1861
+
1862
+
1863
+ const uid = ++this._uid;
1864
+ Promise.all(promiseArray).then(iterableModules => {
1865
+ if (this.shouldOpen) {
1866
+ const mainModule = iterableModules[0];
1867
+
1868
+ this._openPhotoswipe(mainModule, uid);
1869
+ }
1870
+ });
1871
+ }
1872
+ /**
1873
+ * @private
1874
+ * @param {Type<PhotoSwipe> | { default: Type<PhotoSwipe> }} module
1875
+ * @param {number} uid
1876
+ */
1877
+
1878
+
1879
+ _openPhotoswipe(module, uid) {
1880
+ // Cancel opening if UID doesn't match the current one
1881
+ // (if user clicked on another gallery item before current was loaded).
1882
+ //
1883
+ // Or if shouldOpen flag is set to false
1884
+ // (developer may modify it via public API)
1885
+ if (uid !== this._uid && this.shouldOpen) {
1886
+ return;
1887
+ }
1888
+
1889
+ this.shouldOpen = false; // PhotoSwipe is already open
1890
+
1891
+ if (window.pswp) {
1892
+ return;
1893
+ }
1894
+ /**
1895
+ * Pass data to PhotoSwipe and open init
1896
+ *
1897
+ * @type {PhotoSwipe}
1898
+ */
1899
+
1900
+
1901
+ const pswp = typeof module === 'object' ? new module.default(this.options) // eslint-disable-line
1902
+ : new module(this.options); // eslint-disable-line
1903
+
1904
+ this.pswp = pswp;
1905
+ window.pswp = pswp; // map listeners from Lightbox to PhotoSwipe Core
1906
+
1907
+ /** @type {(keyof PhotoSwipeEventsMap)[]} */
1908
+
1909
+ Object.keys(this._listeners).forEach(name => {
1910
+ var _this$_listeners$name;
1911
+
1912
+ (_this$_listeners$name = this._listeners[name]) === null || _this$_listeners$name === void 0 || _this$_listeners$name.forEach(fn => {
1913
+ pswp.on(name,
1914
+ /** @type {EventCallback<typeof name>} */
1915
+ fn);
1916
+ });
1917
+ }); // same with filters
1918
+
1919
+ /** @type {(keyof PhotoSwipeFiltersMap)[]} */
1920
+
1921
+ Object.keys(this._filters).forEach(name => {
1922
+ var _this$_filters$name;
1923
+
1924
+ (_this$_filters$name = this._filters[name]) === null || _this$_filters$name === void 0 || _this$_filters$name.forEach(filter => {
1925
+ pswp.addFilter(name, filter.fn, filter.priority);
1926
+ });
1927
+ });
1928
+
1929
+ if (this._preloadedContent) {
1930
+ pswp.contentLoader.addToCache(this._preloadedContent);
1931
+ this._preloadedContent = undefined;
1932
+ }
1933
+
1934
+ pswp.on('destroy', () => {
1935
+ // clean up public variables
1936
+ this.pswp = undefined;
1937
+ delete window.pswp;
1938
+ });
1939
+ pswp.init();
1940
+ }
1941
+ /**
1942
+ * Unbinds all events, closes PhotoSwipe if it's open.
1943
+ */
1944
+
1945
+
1946
+ destroy() {
1947
+ var _this$pswp;
1948
+
1949
+ (_this$pswp = this.pswp) === null || _this$pswp === void 0 || _this$pswp.destroy();
1950
+ this.shouldOpen = false;
1951
+ this._listeners = {};
1952
+ getElementsFromOption(this.options.gallery, this.options.gallerySelector).forEach(galleryElement => {
1953
+ galleryElement.removeEventListener('click', this.onThumbnailsClick, false);
1954
+ });
1955
+ }
1956
+
1957
+ }
1958
+
1959
+ export { PhotoSwipeLightbox as default };
1960
+ //# sourceMappingURL=photoswipe-lightbox.esm.js.map