PageSourceSearch

https://www.dambenois.fr/_assets/45f48b3f522541d56a70aadffc9d15f6/JavaScript/photoswipe-lightbox.esm.js

js dambenois.fr collected 2026-10-03 02:47:13 UTC 54,556 bytes, 1,960 lines download raw bytes

vendor: 5,426 bytes, lines 1-203
1/*!
2  * PhotoSwipe Lightbox 5.4.3 - https://photoswipe.com
3  * (c) 2023 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 */
14function 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
36function 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
53function 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
61const 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
75function 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
87function 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
112function 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
121function 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 }}
203 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 *
vendor: 43,509 bytes, lines 317-1960
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 */
343class 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
368class 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
505class 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
572class 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 */
1119function 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
1173function 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
1199function 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
1206const 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
1222class 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
1386function 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
1430function 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
1452class 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
1671class 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
1959export { PhotoSwipeLightbox as default };
1960//# sourceMappingURL=photoswipe-lightbox.esm.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.