/*!
 * PhotoSwipe Lightbox 5.4.4 - https://photoswipe.com
 * (c) 2024 Dmytro Semenov
 */
/** @typedef {import('../photoswipe.js').Point} Point */

/**
 * @template {keyof HTMLElementTagNameMap} T
 * @param {string} className
 * @param {T} tagName
 * @param {Node} [appendToEl]
 * @returns {HTMLElementTagNameMap[T]}
 */
function createElement(className, tagName, appendToEl) {
    const el = document.createElement(tagName);

    if (className) {
        el.className = className;
    }

    if (appendToEl) {
        appendToEl.appendChild(el);
    }

    return el;
}
/**
 * Get transform string
 *
 * @param {number} x
 * @param {number} [y]
 * @param {number} [scale]
 * @returns {string}
 */

function toTransformString(x, y, scale) {
    let propValue = `translate3d(${x}px,${y || 0}px,0)`;

    if (scale !== undefined) {
        propValue += ` scale3d(${scale},${scale},1)`;
    }

    return propValue;
}
/**
 * Apply width and height CSS properties to element
 *
 * @param {HTMLElement} el
 * @param {string | number} w
 * @param {string | number} h
 */

function setWidthHeight(el, w, h) {
    el.style.width = typeof w === 'number' ? `${w}px` : w;
    el.style.height = typeof h === 'number' ? `${h}px` : h;
}
/** @typedef {LOAD_STATE[keyof LOAD_STATE]} LoadState */

/** @type {{ IDLE: 'idle'; LOADING: 'loading'; LOADED: 'loaded'; ERROR: 'error' }} */

const LOAD_STATE = {
    IDLE: 'idle',
    LOADING: 'loading',
    LOADED: 'loaded',
    ERROR: 'error'
};
/**
 * Check if click or keydown event was dispatched
 * with a special key or via mouse wheel.
 *
 * @param {MouseEvent | KeyboardEvent} e
 * @returns {boolean}
 */

function specialKeyUsed(e) {
    return 'button' in e && e.button === 1 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey;
}
/**
 * Parse `gallery` or `children` options.
 *
 * @param {import('../photoswipe.js').ElementProvider} [option]
 * @param {string} [legacySelector]
 * @param {HTMLElement | Document} [parent]
 * @returns HTMLElement[]
 */

function getElementsFromOption(option, legacySelector, parent = document) {
    /** @type {HTMLElement[]} */
    let elements = [];

    if (option instanceof Element) {
        elements = [option];
    } else if (option instanceof NodeList || Array.isArray(option)) {
        elements = Array.from(option);
    } else {
        const selector = typeof option === 'string' ? option : legacySelector;

        if (selector) {
            elements = Array.from(parent.querySelectorAll(selector));
        }
    }

    return elements;
}
/**
 * Check if variable is PhotoSwipe class
 *
 * @param {any} fn
 * @returns {boolean}
 */

function isPswpClass(fn) {
    return typeof fn === 'function' && fn.prototype && fn.prototype.goTo;
}
/**
 * Check if browser is Safari
 *
 * @returns {boolean}
 */

function isSafari() {
    return !!(navigator.vendor && navigator.vendor.match(/apple/i));
}

/** @typedef {import('../lightbox/lightbox.js').default} PhotoSwipeLightbox */

/** @typedef {import('../photoswipe.js').default} PhotoSwipe */

/** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */

/** @typedef {import('../photoswipe.js').DataSource} DataSource */

/** @typedef {import('../ui/ui-element.js').UIElementData} UIElementData */

/** @typedef {import('../slide/content.js').default} ContentDefault */

/** @typedef {import('../slide/slide.js').default} Slide */

/** @typedef {import('../slide/slide.js').SlideData} SlideData */

/** @typedef {import('../slide/zoom-level.js').default} ZoomLevel */

/** @typedef {import('../slide/get-thumb-bounds.js').Bounds} Bounds */

/**
 * Allow adding an arbitrary props to the Content
 * https://photoswipe.com/custom-content/#using-webp-image-format
 * @typedef {ContentDefault & Record<string, any>} Content
 */

/** @typedef {{ x?: number; y?: number }} Point */

/**
 * @typedef {Object} PhotoSwipeEventsMap https://photoswipe.com/events/
 *
 *
 * https://photoswipe.com/adding-ui-elements/
 *
 * @prop {undefined} uiRegister
 * @prop {{ data: UIElementData }} uiElementCreate
 *
 *
 * https://photoswipe.com/events/#initialization-events
 *
 * @prop {undefined} beforeOpen
 * @prop {undefined} firstUpdate
 * @prop {undefined} initialLayout
 * @prop {undefined} change
 * @prop {undefined} afterInit
 * @prop {undefined} bindEvents
 *
 *
 * https://photoswipe.com/events/#opening-or-closing-transition-events
 *
 * @prop {undefined} openingAnimationStart
 * @prop {undefined} openingAnimationEnd
 * @prop {undefined} closingAnimationStart
 * @prop {undefined} closingAnimationEnd
 *
 *
 * https://photoswipe.com/events/#closing-events
 *
 * @prop {undefined} close
 * @prop {undefined} destroy
 *
 *
 * https://photoswipe.com/events/#pointer-and-gesture-events
 *
 * @prop {{ originalEvent: PointerEvent }} pointerDown
 * @prop {{ originalEvent: PointerEvent }} pointerMove
 * @prop {{ originalEvent: PointerEvent }} pointerUp
 * @prop {{ bgOpacity: number }} pinchClose can be default prevented
 * @prop {{ panY: number }} verticalDrag can be default prevented
 *
 *
 * https://photoswipe.com/events/#slide-content-events
 *
 * @prop {{ content: Content }} contentInit
 * @prop {{ content: Content; isLazy: boolean }} contentLoad can be default prevented
 * @prop {{ content: Content; isLazy: boolean }} contentLoadImage can be default prevented
 * @prop {{ content: Content; slide: Slide; isError?: boolean }} loadComplete
 * @prop {{ content: Content; slide: Slide }} loadError
 * @prop {{ content: Content; width: number; height: number }} contentResize can be default prevented
 * @prop {{ content: Content; width: number; height: number; slide: Slide }} imageSizeChange
 * @prop {{ content: Content }} contentLazyLoad can be default prevented
 * @prop {{ content: Content }} contentAppend can be default prevented
 * @prop {{ content: Content }} contentActivate can be default prevented
 * @prop {{ content: Content }} contentDeactivate can be default prevented
 * @prop {{ content: Content }} contentRemove can be default prevented
 * @prop {{ content: Content }} contentDestroy can be default prevented
 *
 *
 * undocumented
 *
 * @prop {{ point: Point; originalEvent: PointerEvent }} imageClickAction can be default prevented
 * @prop {{ point: Point; originalEvent: PointerEvent }} bgClickAction can be default prevented
 * @prop {{ point: Point; originalEvent: PointerEvent }} tapAction can be default prevented
 * @prop {{ point: Point; originalEvent: PointerEvent }} doubleTapAction can be default prevented
 *
 * @prop {{ originalEvent: KeyboardEvent }} keydown can be default prevented
 * @prop {{ x: number; dragging: boolean }} moveMainScroll
 * @prop {{ slide: Slide }} firstZoomPan
 * @prop {{ slide: Slide | undefined, data: SlideData, index: number }} gettingData
 * @prop {undefined} beforeResize
 * @prop {undefined} resize
 * @prop {undefined} viewportSize
 * @prop {undefined} updateScrollOffset
 * @prop {{ slide: Slide }} slideInit
 * @prop {{ slide: Slide }} afterSetContent
 * @prop {{ slide: Slide }} slideLoad
 * @prop {{ slide: Slide }} appendHeavy can be default prevented
 * @prop {{ slide: Slide }} appendHeavyContent
 * @prop {{ slide: Slide }} slideActivate
 * @prop {{ slide: Slide }} slideDeactivate
 * @prop {{ slide: Slide }} slideDestroy
 * @prop {{ destZoomLevel: number, centerPoint: Point | undefined, transitionDuration: number | false | undefined }} beforeZoomTo
 * @prop {{ slide: Slide }} zoomPanUpdate
 * @prop {{ slide: Slide }} initialZoomPan
 * @prop {{ slide: Slide }} calcSlideSize
 * @prop {undefined} resolutionChanged
 * @prop {{ originalEvent: WheelEvent }} wheel can be default prevented
 * @prop {{ content: Content }} contentAppendImage can be default prevented
 * @prop {{ index: number; itemData: SlideData }} lazyLoadSlide can be default prevented
 * @prop {undefined} lazyLoad
 * @prop {{ slide: Slide }} calcBounds
 * @prop {{ zoomLevels: ZoomLevel, slideData: SlideData }} zoomLevelsUpdate
 *
 *
 * legacy
 *
 * @prop {undefined} init
 * @prop {undefined} initialZoomIn
 * @prop {undefined} initialZoomOut
 * @prop {undefined} initialZoomInEnd
 * @prop {undefined} initialZoomOutEnd
 * @prop {{ dataSource: DataSource | undefined, numItems: number }} numItems
 * @prop {{ itemData: SlideData; index: number }} itemData
 * @prop {{ index: number, itemData: SlideData, instance: PhotoSwipe }} thumbBounds
 */

/**
 * @typedef {Object} PhotoSwipeFiltersMap https://photoswipe.com/filters/
 *
 * @prop {(numItems: number, dataSource: DataSource | undefined) => number} numItems
 * Modify the total amount of slides. Example on Data sources page.
 * https://photoswipe.com/filters/#numitems
 *
 * @prop {(itemData: SlideData, index: number) => SlideData} itemData
 * Modify slide item data. Example on Data sources page.
 * https://photoswipe.com/filters/#itemdata
 *
 * @prop {(itemData: SlideData, element: HTMLElement, linkEl: HTMLAnchorElement) => SlideData} domItemData
 * Modify item data when it's parsed from DOM element. Example on Data sources page.
 * https://photoswipe.com/filters/#domitemdata
 *
 * @prop {(clickedIndex: number, e: MouseEvent, instance: PhotoSwipeLightbox) => number} clickedIndex
 * Modify clicked gallery item index.
 * https://photoswipe.com/filters/#clickedindex
 *
 * @prop {(placeholderSrc: string | false, content: Content) => string | false} placeholderSrc
 * Modify placeholder image source.
 * https://photoswipe.com/filters/#placeholdersrc
 *
 * @prop {(isContentLoading: boolean, content: Content) => boolean} isContentLoading
 * Modify if the content is currently loading.
 * https://photoswipe.com/filters/#iscontentloading
 *
 * @prop {(isContentZoomable: boolean, content: Content) => boolean} isContentZoomable
 * Modify if the content can be zoomed.
 * https://photoswipe.com/filters/#iscontentzoomable
 *
 * @prop {(useContentPlaceholder: boolean, content: Content) => boolean} useContentPlaceholder
 * Modify if the placeholder should be used for the content.
 * https://photoswipe.com/filters/#usecontentplaceholder
 *
 * @prop {(isKeepingPlaceholder: boolean, content: Content) => boolean} isKeepingPlaceholder
 * Modify if the placeholder should be kept after the content is loaded.
 * https://photoswipe.com/filters/#iskeepingplaceholder
 *
 *
 * @prop {(contentErrorElement: HTMLElement, content: Content) => HTMLElement} contentErrorElement
 * Modify an element when the content has error state (for example, if image cannot be loaded).
 * https://photoswipe.com/filters/#contenterrorelement
 *
 * @prop {(element: HTMLElement, data: UIElementData) => HTMLElement} uiElement
 * Modify a UI element that's being created.
 * https://photoswipe.com/filters/#uielement
 *
 * @prop {(thumbnail: HTMLElement | null | undefined, itemData: SlideData, index: number) => HTMLElement} thumbEl
 * Modify the thumbnail element from which opening zoom animation starts or ends.
 * https://photoswipe.com/filters/#thumbel
 *
 * @prop {(thumbBounds: Bounds | undefined, itemData: SlideData, index: number) => Bounds} thumbBounds
 * Modify the thumbnail bounds from which opening zoom animation starts or ends.
 * https://photoswipe.com/filters/#thumbbounds
 *
 * @prop {(srcsetSizesWidth: number, content: Content) => number} srcsetSizesWidth
 *
 * @prop {(preventPointerEvent: boolean, event: PointerEvent, pointerType: string) => boolean} preventPointerEvent
 *
 */

/**
 * @template {keyof PhotoSwipeFiltersMap} T
 * @typedef {{ fn: PhotoSwipeFiltersMap[T], priority: number }} Filter
 */

/**
 * @template {keyof PhotoSwipeEventsMap} T
 * @typedef {PhotoSwipeEventsMap[T] extends undefined ? PhotoSwipeEvent<T> : PhotoSwipeEvent<T> & PhotoSwipeEventsMap[T]} AugmentedEvent
 */

/**
 * @template {keyof PhotoSwipeEventsMap} T
 * @typedef {(event: AugmentedEvent<T>) => void} EventCallback
 */

/**
 * Base PhotoSwipe event object
 *
 * @template {keyof PhotoSwipeEventsMap} T
 */
class PhotoSwipeEvent {
    /**
     * @param {T} type
     * @param {PhotoSwipeEventsMap[T]} [details]
     */
    constructor(type, details) {
        this.type = type;
        this.defaultPrevented = false;

        if (details) {
            Object.assign(this, details);
        }
    }

    preventDefault() {
        this.defaultPrevented = true;
    }

     }
     /**
      * PhotoSwipe base class that can listen and dispatch for events.
      * Shared by PhotoSwipe Core and PhotoSwipe Lightbox, extended by base.js
      */


     class Eventable {
         constructor() {
             /**
              * @type {{ [T in keyof PhotoSwipeEventsMap]?: ((event: AugmentedEvent<T>) => void)[] }}
              */
             this._listeners = {};
             /**
              * @type {{ [T in keyof PhotoSwipeFiltersMap]?: Filter<T>[] }}
              */

             this._filters = {};
             /** @type {PhotoSwipe | undefined} */

             this.pswp = undefined;
             /** @type {PhotoSwipeOptions | undefined} */

             this.options = undefined;
         }
         /**
          * @template {keyof PhotoSwipeFiltersMap} T
          * @param {T} name
          * @param {PhotoSwipeFiltersMap[T]} fn
          * @param {number} priority
          */


         addFilter(name, fn, priority = 100) {
             var _this$_filters$name, _this$_filters$name2, _this$pswp;

             if (!this._filters[name]) {
                 this._filters[name] = [];
             }

             (_this$_filters$name = this._filters[name]) === null || _this$_filters$name === void 0 || _this$_filters$name.push({
                 fn,
                 priority
             });
             (_this$_filters$name2 = this._filters[name]) === null || _this$_filters$name2 === void 0 || _this$_filters$name2.sort((f1, f2) => f1.priority - f2.priority);
             (_this$pswp = this.pswp) === null || _this$pswp === void 0 || _this$pswp.addFilter(name, fn, priority);
         }
         /**
          * @template {keyof PhotoSwipeFiltersMap} T
          * @param {T} name
          * @param {PhotoSwipeFiltersMap[T]} fn
          */


         removeFilter(name, fn) {
             if (this._filters[name]) {
                 // @ts-expect-error
                 this._filters[name] = this._filters[name].filter(filter => filter.fn !== fn);
             }

             if (this.pswp) {
                 this.pswp.removeFilter(name, fn);
             }
         }
         /**
          * @template {keyof PhotoSwipeFiltersMap} T
          * @param {T} name
          * @param {Parameters<PhotoSwipeFiltersMap[T]>} args
          * @returns {Parameters<PhotoSwipeFiltersMap[T]>[0]}
          */


         applyFilters(name, ...args) {
             var _this$_filters$name3;

             (_this$_filters$name3 = this._filters[name]) === null || _this$_filters$name3 === void 0 || _this$_filters$name3.forEach(filter => {
                 // @ts-expect-error
                 args[0] = filter.fn.apply(this, args);
             });
             return args[0];
         }
         /**
          * @template {keyof PhotoSwipeEventsMap} T
          * @param {T} name
          * @param {EventCallback<T>} fn
          */


         on(name, fn) {
             var _this$_listeners$name, _this$pswp2;

             if (!this._listeners[name]) {
                 this._listeners[name] = [];
             }

             (_this$_listeners$name = this._listeners[name]) === null || _this$_listeners$name === void 0 || _this$_listeners$name.push(fn); // When binding events to lightbox,
             // also bind events to PhotoSwipe Core,
             // if it's open.

             (_this$pswp2 = this.pswp) === null || _this$pswp2 === void 0 || _this$pswp2.on(name, fn);
         }
         /**
          * @template {keyof PhotoSwipeEventsMap} T
          * @param {T} name
          * @param {EventCallback<T>} fn
          */


         off(name, fn) {
             var _this$pswp3;

             if (this._listeners[name]) {
                 // @ts-expect-error
                 this._listeners[name] = this._listeners[name].filter(listener => fn !== listener);
             }

             (_this$pswp3 = this.pswp) === null || _this$pswp3 === void 0 || _this$pswp3.off(name, fn);
         }
         /**
          * @template {keyof PhotoSwipeEventsMap} T
          * @param {T} name
          * @param {PhotoSwipeEventsMap[T]} [details]
          * @returns {AugmentedEvent<T>}
          */


         dispatch(name, details) {
             var _this$_listeners$name2;

             if (this.pswp) {
                 return this.pswp.dispatch(name, details);
             }

             const event =
             /** @type {AugmentedEvent<T>} */
             new PhotoSwipeEvent(name, details);
             (_this$_listeners$name2 = this._listeners[name]) === null || _this$_listeners$name2 === void 0 || _this$_listeners$name2.forEach(listener => {
                 listener.call(this, event);
             });
             return event;
         }

          }

          class Placeholder {
              /**
               * @param {string | false} imageSrc
               * @param {HTMLElement} container
               */
              constructor(imageSrc, container) {
                  // Create placeholder
                  // (stretched thumbnail or simple div behind the main image)

                  /** @type {HTMLImageElement | HTMLDivElement | null} */
                  this.element = createElement('pswp__img pswp__img--placeholder', imageSrc ? 'img' : 'div', container);

                  if (imageSrc) {
                      const imgEl =
                      /** @type {HTMLImageElement} */
                      this.element;
                      imgEl.decoding = 'async';
                      imgEl.alt = '';
                      imgEl.src = imageSrc;
                      imgEl.setAttribute('role', 'presentation');
                  }

                  this.element.setAttribute('aria-hidden', 'true');
              }
              /**
               * @param {number} width
               * @param {number} height
               */


              setDisplayedSize(width, height) {
                  if (!this.element) {
                      return;
                  }

                  if (this.element.tagName === 'IMG') {
                      // Use transform scale() to modify img placeholder size
                      // (instead of changing width/height directly).
                      // This helps with performance, specifically in iOS15 Safari.
                      setWidthHeight(this.element, 250, 'auto');
                      this.element.style.transformOrigin = '0 0';
                      this.element.style.transform = toTransformString(0, 0, width / 250);
                  } else {
                      setWidthHeight(this.element, width, height);
                  }
              }

              destroy() {
                  var _this$element;

                  if ((_this$element = this.element) !== null && _this$element !== void 0 && _this$element.parentNode) {
                      this.element.remove();
                  }

                  this.element = null;
              }

               }

               /** @typedef {import('./slide.js').default} Slide */

               /** @typedef {import('./slide.js').SlideData} SlideData */

               /** @typedef {import('../core/base.js').default} PhotoSwipeBase */

               /** @typedef {import('../util/util.js').LoadState} LoadState */

               class Content {
                   /**
                    * @param {SlideData} itemData Slide data
                    * @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox instance
                    * @param {number} index
                    */
                   constructor(itemData, instance, index) {
                       this.instance = instance;
                       this.data = itemData;
                       this.index = index;
                       /** @type {HTMLImageElement | HTMLDivElement | undefined} */

                       this.element = undefined;
                       /** @type {Placeholder | undefined} */

                       this.placeholder = undefined;
                       /** @type {Slide | undefined} */

                       this.slide = undefined;
                       this.displayedImageWidth = 0;
                       this.displayedImageHeight = 0;
                       this.width = Number(this.data.w) || Number(this.data.width) || 0;
                       this.height = Number(this.data.h) || Number(this.data.height) || 0;
                       this.isAttached = false;
                       this.hasSlide = false;
                       this.isDecoding = false;
                       /** @type {LoadState} */

                       this.state = LOAD_STATE.IDLE;

                       if (this.data.type) {
                           this.type = this.data.type;
                       } else if (this.data.src) {
                           this.type = 'image';
                       } else {
                           this.type = 'html';
                       }

                       this.instance.dispatch('contentInit', {
                           content: this
                       });
                   }

                   removePlaceholder() {
                       if (this.placeholder && !this.keepPlaceholder()) {
                           // With delay, as image might be loaded, but not rendered
                           setTimeout(() => {
                               if (this.placeholder) {
                                   this.placeholder.destroy();
                                   this.placeholder = undefined;
                               }
                           }, 1000);
                       }
                   }
                   /**
                    * Preload content
                    *
                    * @param {boolean} isLazy
                    * @param {boolean} [reload]
                    */


                   load(isLazy, reload) {
                       if (this.slide && this.usePlaceholder()) {
                           if (!this.placeholder) {
                               const placeholderSrc = this.instance.applyFilters('placeholderSrc', // use  image-based placeholder only for the first slide,
                                                                                 // as rendering (even small stretched thumbnail) is an expensive operation
                                                                                 this.data.msrc && this.slide.isFirstSlide ? this.data.msrc : false, this);
                               this.placeholder = new Placeholder(placeholderSrc, this.slide.container);
                           } else {
                               const placeholderEl = this.placeholder.element; // Add placeholder to DOM if it was already created

                               if (placeholderEl && !placeholderEl.parentElement) {
                                   this.slide.container.prepend(placeholderEl);
                               }
                           }
                       }

                       if (this.element && !reload) {
                           return;
                       }

                       if (this.instance.dispatch('contentLoad', {
                           content: this,
                           isLazy
                       }).defaultPrevented) {
                           return;
                       }

                       if (this.isImageContent()) {
                           this.element = createElement('pswp__img', 'img'); // Start loading only after width is defined, as sizes might depend on it.
                           // Due to Safari feature, we must define sizes before srcset.

                           if (this.displayedImageWidth) {
                               this.loadImage(isLazy);
                           }
                       } else {
                           this.element = createElement('pswp__content', 'div');
                           this.element.innerHTML = this.data.html || '';
                       }

                       if (reload && this.slide) {
                           this.slide.updateContentSize(true);
                       }
                   }
                   /**
                    * Preload image
                    *
                    * @param {boolean} isLazy
                    */


                   loadImage(isLazy) {
                       var _this$data$src, _this$data$alt;

                       if (!this.isImageContent() || !this.element || this.instance.dispatch('contentLoadImage', {
                           content: this,
                           isLazy
                       }).defaultPrevented) {
                           return;
                       }

                       const imageElement =
                       /** @type HTMLImageElement */
                       this.element;
                       this.updateSrcsetSizes();

                       if (this.data.srcset) {
                           imageElement.srcset = this.data.srcset;
                       }

                       imageElement.src = (_this$data$src = this.data.src) !== null && _this$data$src !== void 0 ? _this$data$src : '';
                       imageElement.alt = (_this$data$alt = this.data.alt) !== null && _this$data$alt !== void 0 ? _this$data$alt : '';
                       this.state = LOAD_STATE.LOADING;

                       if (imageElement.complete) {
                           this.onLoaded();
                       } else {
                           imageElement.onload = () => {
                               this.onLoaded();
                           };

                           imageElement.onerror = () => {
                               this.onError();
                           };
                       }
                   }
                   /**
                    * Assign slide to content
                    *
                    * @param {Slide} slide
                    */


                   setSlide(slide) {
                       this.slide = slide;
                       this.hasSlide = true;
                       this.instance = slide.pswp; // todo: do we need to unset slide?
                   }
                   /**
                    * Content load success handler
                    */


                   onLoaded() {
                       this.state = LOAD_STATE.LOADED;

                       if (this.slide && this.element) {
                           this.instance.dispatch('loadComplete', {
                               slide: this.slide,
                               content: this
                           }); // if content is reloaded

                           if (this.slide.isActive && this.slide.heavyAppended && !this.element.parentNode) {
                               this.append();
                               this.slide.updateContentSize(true);
                           }

                           if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
                               this.removePlaceholder();
                           }
                       }
                   }
                   /**
                    * Content load error handler
                    */


                   onError() {
                       this.state = LOAD_STATE.ERROR;

                       if (this.slide) {
                           this.displayError();
                           this.instance.dispatch('loadComplete', {
                               slide: this.slide,
                               isError: true,
                               content: this
                           });
                           this.instance.dispatch('loadError', {
                               slide: this.slide,
                               content: this
                           });
                       }
                   }
                   /**
                    * @returns {Boolean} If the content is currently loading
                    */


                   isLoading() {
                       return this.instance.applyFilters('isContentLoading', this.state === LOAD_STATE.LOADING, this);
                   }
                   /**
                    * @returns {Boolean} If the content is in error state
                    */


                   isError() {
                       return this.state === LOAD_STATE.ERROR;
                   }
                   /**
                    * @returns {boolean} If the content is image
                    */


                   isImageContent() {
                       return this.type === 'image';
                   }
                   /**
                    * Update content size
                    *
                    * @param {Number} width
                    * @param {Number} height
                    */


                   setDisplayedSize(width, height) {
                       if (!this.element) {
                           return;
                       }

                       if (this.placeholder) {
                           this.placeholder.setDisplayedSize(width, height);
                       }

                       if (this.instance.dispatch('contentResize', {
                           content: this,
                           width,
                           height
                       }).defaultPrevented) {
                           return;
                       }

                       setWidthHeight(this.element, width, height);

                       if (this.isImageContent() && !this.isError()) {
                           const isInitialSizeUpdate = !this.displayedImageWidth && width;
                           this.displayedImageWidth = width;
                           this.displayedImageHeight = height;

                           if (isInitialSizeUpdate) {
                               this.loadImage(false);
                           } else {
                               this.updateSrcsetSizes();
                           }

                           if (this.slide) {
                               this.instance.dispatch('imageSizeChange', {
                                   slide: this.slide,
                                   width,
                                   height,
                                   content: this
                               });
                           }
                       }
                   }
                   /**
                    * @returns {boolean} If the content can be zoomed
                    */


                   isZoomable() {
                       return this.instance.applyFilters('isContentZoomable', this.isImageContent() && this.state !== LOAD_STATE.ERROR, this);
                   }
                   /**
                    * Update image srcset sizes attribute based on width and height
                    */


                   updateSrcsetSizes() {
                       // Handle srcset sizes attribute.
                       //
                       // Never lower quality, if it was increased previously.
                       // Chrome does this automatically, Firefox and Safari do not,
                       // so we store largest used size in dataset.
                       if (!this.isImageContent() || !this.element || !this.data.srcset) {
                           return;
                       }

                       const image =
                       /** @type HTMLImageElement */
                       this.element;
                       const sizesWidth = this.instance.applyFilters('srcsetSizesWidth', this.displayedImageWidth, this);

                       if (!image.dataset.largestUsedSize || sizesWidth > parseInt(image.dataset.largestUsedSize, 10)) {
                           image.sizes = sizesWidth + 'px';
                           image.dataset.largestUsedSize = String(sizesWidth);
                       }
                   }
                   /**
                    * @returns {boolean} If content should use a placeholder (from msrc by default)
                    */


                   usePlaceholder() {
                       return this.instance.applyFilters('useContentPlaceholder', this.isImageContent(), this);
                   }
                   /**
                    * Preload content with lazy-loading param
                    */


                   lazyLoad() {
                       if (this.instance.dispatch('contentLazyLoad', {
                           content: this
                       }).defaultPrevented) {
                           return;
                       }

                       this.load(true);
                   }
                   /**
                    * @returns {boolean} If placeholder should be kept after content is loaded
                    */


                   keepPlaceholder() {
                       return this.instance.applyFilters('isKeepingPlaceholder', this.isLoading(), this);
                   }
                   /**
                    * Destroy the content
                    */


                   destroy() {
                       this.hasSlide = false;
                       this.slide = undefined;

                       if (this.instance.dispatch('contentDestroy', {
                           content: this
                       }).defaultPrevented) {
                           return;
                       }

                       this.remove();

                       if (this.placeholder) {
                           this.placeholder.destroy();
                           this.placeholder = undefined;
                       }

                       if (this.isImageContent() && this.element) {
                           this.element.onload = null;
                           this.element.onerror = null;
                           this.element = undefined;
                       }
                   }
                   /**
                    * Display error message
                    */


                   displayError() {
                       if (this.slide) {
                           var _this$instance$option, _this$instance$option2;

                           let errorMsgEl = createElement('pswp__error-msg', 'div');
                           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 : '';
                           errorMsgEl =
                           /** @type {HTMLDivElement} */
                           this.instance.applyFilters('contentErrorElement', errorMsgEl, this);
                           this.element = createElement('pswp__content pswp__error-msg-container', 'div');
                           this.element.appendChild(errorMsgEl);
                           this.slide.container.innerText = '';
                           this.slide.container.appendChild(this.element);
                           this.slide.updateContentSize(true);
                           this.removePlaceholder();
                       }
                   }
                   /**
                    * Append the content
                    */


                   append() {
                       if (this.isAttached || !this.element) {
                           return;
                       }

                       this.isAttached = true;

                       if (this.state === LOAD_STATE.ERROR) {
                           this.displayError();
                           return;
                       }

                       if (this.instance.dispatch('contentAppend', {
                           content: this
                       }).defaultPrevented) {
                           return;
                       }

                       const supportsDecode = ('decode' in this.element);

                       if (this.isImageContent()) {
                           // Use decode() on nearby slides
                           //
                           // Nearby slide images are in DOM and not hidden via display:none.
                           // However, they are placed offscreen (to the left and right side).
                           //
                           // Some browsers do not composite the image until it's actually visible,
                           // using decode() helps.
                           //
                           // You might ask "why dont you just decode() and then append all images",
                           // that's because I want to show image before it's fully loaded,
                           // as browser can render parts of image while it is loading.
                           // We do not do this in Safari due to partial loading bug.
                           if (supportsDecode && this.slide && (!this.slide.isActive || isSafari())) {
                               this.isDecoding = true; // purposefully using finally instead of then,
                               // as if srcset sizes changes dynamically - it may cause decode error

                               /** @type {HTMLImageElement} */

                               this.element.decode().catch(() => {}).finally(() => {
                                   this.isDecoding = false;
                                   this.appendImage();
                               });
                           } else {
                               this.appendImage();
                           }
                       } else if (this.slide && !this.element.parentNode) {
                           this.slide.container.appendChild(this.element);
                       }
                   }
                   /**
                    * Activate the slide,
                    * active slide is generally the current one,
                    * meaning the user can see it.
                    */


                   activate() {
                       if (this.instance.dispatch('contentActivate', {
                           content: this
                       }).defaultPrevented || !this.slide) {
                           return;
                       }

                       if (this.isImageContent() && this.isDecoding && !isSafari()) {
                           // add image to slide when it becomes active,
                           // even if it's not finished decoding
                           this.appendImage();
                       } else if (this.isError()) {
                           this.load(false, true); // try to reload
                       }

                       if (this.slide.holderElement) {
                           this.slide.holderElement.setAttribute('aria-hidden', 'false');
                       }
                   }
                   /**
                    * Deactivate the content
                    */


                   deactivate() {
                       this.instance.dispatch('contentDeactivate', {
                           content: this
                       });

                       if (this.slide && this.slide.holderElement) {
                           this.slide.holderElement.setAttribute('aria-hidden', 'true');
                       }
                   }
                   /**
                    * Remove the content from DOM
                    */


                   remove() {
                       this.isAttached = false;

                       if (this.instance.dispatch('contentRemove', {
                           content: this
                       }).defaultPrevented) {
                           return;
                       }

                       if (this.element && this.element.parentNode) {
                           this.element.remove();
                       }

                       if (this.placeholder && this.placeholder.element) {
                           this.placeholder.element.remove();
                       }
                   }
                   /**
                    * Append the image content to slide container
                    */


                   appendImage() {
                       if (!this.isAttached) {
                           return;
                       }

                       if (this.instance.dispatch('contentAppendImage', {
                           content: this
                       }).defaultPrevented) {
                           return;
                       } // ensure that element exists and is not already appended


                       if (this.slide && this.element && !this.element.parentNode) {
                           this.slide.container.appendChild(this.element);
                       }

                       if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
                           this.removePlaceholder();
                       }
                   }

               }

               /** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */

               /** @typedef {import('../core/base.js').default} PhotoSwipeBase */

               /** @typedef {import('../photoswipe.js').Point} Point */

               /** @typedef {import('../slide/slide.js').SlideData} SlideData */

               /**
                * @param {PhotoSwipeOptions} options
                * @param {PhotoSwipeBase} pswp
                * @returns {Point}
                */
               function getViewportSize(options, pswp) {
                   if (options.getViewportSizeFn) {
                       const newViewportSize = options.getViewportSizeFn(options, pswp);

                       if (newViewportSize) {
                           return newViewportSize;
                       }
                   }

                   return {
                       x: document.documentElement.clientWidth,
                       // TODO: height on mobile is very incosistent due to toolbar
                       // find a way to improve this
                       //
                       // document.documentElement.clientHeight - doesn't seem to work well
                       y: window.innerHeight
                   };
               }
               /**
                * Parses padding option.
                * Supported formats:
                *
                * // Object
                * padding: {
                *  top: 0,
                *  bottom: 0,
                *  left: 0,
                *  right: 0
                * }
                *
                * // A function that returns the object
                * paddingFn: (viewportSize, itemData, index) => {
                *  return {
                *    top: 0,
                *    bottom: 0,
                *    left: 0,
                *    right: 0
                *  };
                * }
                *
                * // Legacy variant
                * paddingLeft: 0,
                * paddingRight: 0,
                * paddingTop: 0,
                * paddingBottom: 0,
                *
                * @param {'left' | 'top' | 'bottom' | 'right'} prop
                * @param {PhotoSwipeOptions} options PhotoSwipe options
                * @param {Point} viewportSize PhotoSwipe viewport size, for example: { x:800, y:600 }
                * @param {SlideData} itemData Data about the slide
                * @param {number} index Slide index
                * @returns {number}
                */

               function parsePaddingOption(prop, options, viewportSize, itemData, index) {
                   let paddingValue = 0;

                   if (options.paddingFn) {
                       paddingValue = options.paddingFn(viewportSize, itemData, index)[prop];
                   } else if (options.padding) {
                       paddingValue = options.padding[prop];
                   } else {
                       const legacyPropName = 'padding' + prop[0].toUpperCase() + prop.slice(1); // @ts-expect-error

                       if (options[legacyPropName]) {
                           // @ts-expect-error
                           paddingValue = options[legacyPropName];
                       }
                   }

                   return Number(paddingValue) || 0;
               }
               /**
                * @param {PhotoSwipeOptions} options
                * @param {Point} viewportSize
                * @param {SlideData} itemData
                * @param {number} index
                * @returns {Point}
                */

               function getPanAreaSize(options, viewportSize, itemData, index) {
                   return {
                       x: viewportSize.x - parsePaddingOption('left', options, viewportSize, itemData, index) - parsePaddingOption('right', options, viewportSize, itemData, index),
                       y: viewportSize.y - parsePaddingOption('top', options, viewportSize, itemData, index) - parsePaddingOption('bottom', options, viewportSize, itemData, index)
                   };
               }

               const MAX_IMAGE_WIDTH = 4000;
               /** @typedef {import('../photoswipe.js').default} PhotoSwipe */

               /** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */

               /** @typedef {import('../photoswipe.js').Point} Point */

               /** @typedef {import('../slide/slide.js').SlideData} SlideData */

               /** @typedef {'fit' | 'fill' | number | ((zoomLevelObject: ZoomLevel) => number)} ZoomLevelOption */

               /**
                * Calculates zoom levels for specific slide.
                * Depends on viewport size and image size.
                */

               class ZoomLevel {
                   /**
                    * @param {PhotoSwipeOptions} options PhotoSwipe options
                    * @param {SlideData} itemData Slide data
                    * @param {number} index Slide index
                    * @param {PhotoSwipe} [pswp] PhotoSwipe instance, can be undefined if not initialized yet
                    */
                   constructor(options, itemData, index, pswp) {
                       this.pswp = pswp;
                       this.options = options;
                       this.itemData = itemData;
                       this.index = index;
                       /** @type { Point | null } */

                       this.panAreaSize = null;
                       /** @type { Point | null } */

                       this.elementSize = null;
                       this.fit = 1;
                       this.fill = 1;
                       this.vFill = 1;
                       this.initial = 1;
                       this.secondary = 1;
                       this.max = 1;
                       this.min = 1;
                   }
                   /**
                    * Calculate initial, secondary and maximum zoom level for the specified slide.
                    *
                    * It should be called when either image or viewport size changes.
                    *
                    * @param {number} maxWidth
                    * @param {number} maxHeight
                    * @param {Point} panAreaSize
                    */


                   update(maxWidth, maxHeight, panAreaSize) {
                       /** @type {Point} */
                       const elementSize = {
                           x: maxWidth,
                           y: maxHeight
                       };
                       this.elementSize = elementSize;
                       this.panAreaSize = panAreaSize;
                       const hRatio = panAreaSize.x / elementSize.x;
                       const vRatio = panAreaSize.y / elementSize.y;
                       this.fit = Math.min(1, hRatio < vRatio ? hRatio : vRatio);
                       this.fill = Math.min(1, hRatio > vRatio ? hRatio : vRatio); // zoom.vFill defines zoom level of the image
                       // when it has 100% of viewport vertical space (height)

                       this.vFill = Math.min(1, vRatio);
                       this.initial = this._getInitial();
                       this.secondary = this._getSecondary();
                       this.max = Math.max(this.initial, this.secondary, this._getMax());
                       this.min = Math.min(this.fit, this.initial, this.secondary);

                       if (this.pswp) {
                           this.pswp.dispatch('zoomLevelsUpdate', {
                               zoomLevels: this,
                               slideData: this.itemData
                           });
                       }
                   }
                   /**
                    * Parses user-defined zoom option.
                    *
                    * @private
                    * @param {'initial' | 'secondary' | 'max'} optionPrefix Zoom level option prefix (initial, secondary, max)
                    * @returns { number | undefined }
                    */


                   _parseZoomLevelOption(optionPrefix) {
                       const optionName =
                       /** @type {'initialZoomLevel' | 'secondaryZoomLevel' | 'maxZoomLevel'} */
                       optionPrefix + 'ZoomLevel';
                       const optionValue = this.options[optionName];

                       if (!optionValue) {
                           return;
                       }

                       if (typeof optionValue === 'function') {
                           return optionValue(this);
                       }

                       if (optionValue === 'fill') {
                           return this.fill;
                       }

                       if (optionValue === 'fit') {
                           return this.fit;
                       }

                       return Number(optionValue);
                   }
                   /**
                    * Get zoom level to which image will be zoomed after double-tap gesture,
                    * or when user clicks on zoom icon,
                    * or mouse-click on image itself.
                    * If you return 1 image will be zoomed to its original size.
                    *
                    * @private
                    * @return {number}
                    */


                   _getSecondary() {
                       let currZoomLevel = this._parseZoomLevelOption('secondary');

                       if (currZoomLevel) {
                           return currZoomLevel;
                       } // 3x of "fit" state, but not larger than original


                       currZoomLevel = Math.min(1, this.fit * 3);

                       if (this.elementSize && currZoomLevel * this.elementSize.x > MAX_IMAGE_WIDTH) {
                           currZoomLevel = MAX_IMAGE_WIDTH / this.elementSize.x;
                       }

                       return currZoomLevel;
                   }
                   /**
                    * Get initial image zoom level.
                    *
                    * @private
                    * @return {number}
                    */


                   _getInitial() {
                       return this._parseZoomLevelOption('initial') || this.fit;
                   }
                   /**
                    * Maximum zoom level when user zooms
                    * via zoom/pinch gesture,
                    * via cmd/ctrl-wheel or via trackpad.
                    *
                    * @private
                    * @return {number}
                    */


                   _getMax() {
                       // max zoom level is x4 from "fit state",
                       // used for zoom gesture and ctrl/trackpad zoom
                       return this._parseZoomLevelOption('max') || Math.max(1, this.fit * 4);
                   }

                    }

                    /**
                     * Lazy-load an image
                     * This function is used both by Lightbox and PhotoSwipe core,
                     * thus it can be called before dialog is opened.
                     *
                     * @param {SlideData} itemData Data about the slide
                     * @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox instance
                     * @param {number} index
                     * @returns {Content} Image that is being decoded or false.
                     */

                    function lazyLoadData(itemData, instance, index) {
                        const content = instance.createContentFromData(itemData, index);
                        /** @type {ZoomLevel | undefined} */

                        let zoomLevel;
                        const {
                            options
                        } = instance; // We need to know dimensions of the image to preload it,
                        // as it might use srcset, and we need to define sizes

                        if (options) {
                            zoomLevel = new ZoomLevel(options, itemData, -1);
                            let viewportSize;

                            if (instance.pswp) {
                                viewportSize = instance.pswp.viewportSize;
                            } else {
                                viewportSize = getViewportSize(options, instance);
                            }

                            const panAreaSize = getPanAreaSize(options, viewportSize, itemData, index);
                            zoomLevel.update(content.width, content.height, panAreaSize);
                        }

                        content.lazyLoad();

                        if (zoomLevel) {
                            content.setDisplayedSize(Math.ceil(content.width * zoomLevel.initial), Math.ceil(content.height * zoomLevel.initial));
                        }

                        return content;
                    }
                    /**
                     * Lazy-loads specific slide.
                     * This function is used both by Lightbox and PhotoSwipe core,
                     * thus it can be called before dialog is opened.
                     *
                     * By default, it loads image based on viewport size and initial zoom level.
                     *
                     * @param {number} index Slide index
                     * @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox eventable instance
                     * @returns {Content | undefined}
                     */

                    function lazyLoadSlide(index, instance) {
                        const itemData = instance.getItemData(index);

                        if (instance.dispatch('lazyLoadSlide', {
                            index,
                            itemData
                        }).defaultPrevented) {
                            return;
                        }

                        return lazyLoadData(itemData, instance, index);
                    }

                    /** @typedef {import("../photoswipe.js").default} PhotoSwipe */

                    /** @typedef {import("../slide/slide.js").SlideData} SlideData */

                    /**
                     * PhotoSwipe base class that can retrieve data about every slide.
                     * Shared by PhotoSwipe Core and PhotoSwipe Lightbox
                     */

                    class PhotoSwipeBase extends Eventable {
                        /**
                         * Get total number of slides
                         *
                         * @returns {number}
                         */
                        getNumItems() {
                            var _this$options;

                            let numItems = 0;
                            const dataSource = (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.dataSource;

                            if (dataSource && 'length' in dataSource) {
                                // may be an array or just object with length property
                                numItems = dataSource.length;
                            } else if (dataSource && 'gallery' in dataSource) {
                                // query DOM elements
                                if (!dataSource.items) {
                                    dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
                                }

                                if (dataSource.items) {
                                    numItems = dataSource.items.length;
                                }
                            } // legacy event, before filters were introduced


                            const event = this.dispatch('numItems', {
                                dataSource,
                                numItems
                            });
                            return this.applyFilters('numItems', event.numItems, dataSource);
                        }
                        /**
                         * @param {SlideData} slideData
                         * @param {number} index
                         * @returns {Content}
                         */


                        createContentFromData(slideData, index) {
                            return new Content(slideData, this, index);
                        }
                        /**
                         * Get item data by index.
                         *
                         * "item data" should contain normalized information that PhotoSwipe needs to generate a slide.
                         * For example, it may contain properties like
                         * `src`, `srcset`, `w`, `h`, which will be used to generate a slide with image.
                         *
                         * @param {number} index
                         * @returns {SlideData}
                         */


                        getItemData(index) {
                            var _this$options2;

                            const dataSource = (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.dataSource;
                            /** @type {SlideData | HTMLElement} */

                            let dataSourceItem = {};

                            if (Array.isArray(dataSource)) {
                                // Datasource is an array of elements
                                dataSourceItem = dataSource[index];
                            } else if (dataSource && 'gallery' in dataSource) {
                                // dataSource has gallery property,
                                // thus it was created by Lightbox, based on
                                // gallery and children options
                                // query DOM elements
                                if (!dataSource.items) {
                                    dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
                                }

                                dataSourceItem = dataSource.items[index];
                            }

                            let itemData = dataSourceItem;

                            if (itemData instanceof Element) {
                                itemData = this._domElementToItemData(itemData);
                            } // Dispatching the itemData event,
                            // it's a legacy verion before filters were introduced


                            const event = this.dispatch('itemData', {
                                itemData: itemData || {},
                                index
                            });
                            return this.applyFilters('itemData', event.itemData, index);
                        }
                        /**
                         * Get array of gallery DOM elements,
                         * based on childSelector and gallery element.
                         *
                         * @param {HTMLElement} galleryElement
                         * @returns {HTMLElement[]}
                         */


                        _getGalleryDOMElements(galleryElement) {
                            var _this$options3, _this$options4;

                            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) {
                                return getElementsFromOption(this.options.children, this.options.childSelector, galleryElement) || [];
                            }

                            return [galleryElement];
                        }
                        /**
                         * Converts DOM element to item data object.
                         *
                         * @param {HTMLElement} element DOM element
                         * @returns {SlideData}
                         */


                        _domElementToItemData(element) {
                            /** @type {SlideData} */
                            const itemData = {
                                element
                            };
                            const linkEl =
                            /** @type {HTMLAnchorElement} */
                            element.tagName === 'A' ? element : element.querySelector('a');

                            if (linkEl) {
                                // src comes from data-pswp-src attribute,
                                // if it's empty link href is used
                                itemData.src = linkEl.dataset.pswpSrc || linkEl.href;

                                if (linkEl.dataset.pswpSrcset) {
                                    itemData.srcset = linkEl.dataset.pswpSrcset;
                                }

                                itemData.width = linkEl.dataset.pswpWidth ? parseInt(linkEl.dataset.pswpWidth, 10) : 0;
                                itemData.height = linkEl.dataset.pswpHeight ? parseInt(linkEl.dataset.pswpHeight, 10) : 0; // support legacy w & h properties

                                itemData.w = itemData.width;
                                itemData.h = itemData.height;

                                if (linkEl.dataset.pswpType) {
                                    itemData.type = linkEl.dataset.pswpType;
                                }

                                const thumbnailEl = element.querySelector('img');

                                if (thumbnailEl) {
                                    var _thumbnailEl$getAttri;

                                    // msrc is URL to placeholder image that's displayed before large image is loaded
                                    // by default it's displayed only for the first slide
                                    itemData.msrc = thumbnailEl.currentSrc || thumbnailEl.src;
                                    itemData.alt = (_thumbnailEl$getAttri = thumbnailEl.getAttribute('alt')) !== null && _thumbnailEl$getAttri !== void 0 ? _thumbnailEl$getAttri : '';
                                }

                                if (linkEl.dataset.pswpCropped || linkEl.dataset.cropped) {
                                    itemData.thumbCropped = true;
                                }
                            }

                            return this.applyFilters('domItemData', itemData, element, linkEl);
                        }
                        /**
                         * Lazy-load by slide data
                         *
                         * @param {SlideData} itemData Data about the slide
                         * @param {number} index
                         * @returns {Content} Image that is being decoded or false.
                         */


                        lazyLoadData(itemData, index) {
                            return lazyLoadData(itemData, this, index);
                        }

                    }

                    /**
                     * @template T
                     * @typedef {import('../types.js').Type<T>} Type<T>
                     */

                    /** @typedef {import('../photoswipe.js').default} PhotoSwipe */

                    /** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */

                    /** @typedef {import('../photoswipe.js').DataSource} DataSource */

                    /** @typedef {import('../photoswipe.js').Point} Point */

                    /** @typedef {import('../slide/content.js').default} Content */

                    /** @typedef {import('../core/eventable.js').PhotoSwipeEventsMap} PhotoSwipeEventsMap */

                    /** @typedef {import('../core/eventable.js').PhotoSwipeFiltersMap} PhotoSwipeFiltersMap */

                    /**
                     * @template {keyof PhotoSwipeEventsMap} T
                     * @typedef {import('../core/eventable.js').EventCallback<T>} EventCallback<T>
                     */

                    /**
                     * PhotoSwipe Lightbox
                     *
                     * - If user has unsupported browser it falls back to default browser action (just opens URL)
                     * - Binds click event to links that should open PhotoSwipe
                     * - parses DOM strcture for PhotoSwipe (retrieves large image URLs and sizes)
                     * - Initializes PhotoSwipe
                     *
                     *
                     * Loader options use the same object as PhotoSwipe, and supports such options:
                     *
                     * gallery - Element | Element[] | NodeList | string selector for the gallery element
                     * children - Element | Element[] | NodeList | string selector for the gallery children
                     *
                     */

                    class PhotoSwipeLightbox extends PhotoSwipeBase {
                        /**
                         * @param {PhotoSwipeOptions} [options]
                         */
                        constructor(options) {
                            super();
                            /** @type {PhotoSwipeOptions} */

                            this.options = options || {};
                            this._uid = 0;
                            this.shouldOpen = false;
                            /**
                             * @private
                             * @type {Content | undefined}
                             */

                            this._preloadedContent = undefined;
                            this.onThumbnailsClick = this.onThumbnailsClick.bind(this);
                        }
                        /**
                         * Initialize lightbox, should be called only once.
                         * It's not included in the main constructor, so you may bind events before it.
                         */


                        init() {
                            // Bind click events to each gallery
                            getElementsFromOption(this.options.gallery, this.options.gallerySelector).forEach(galleryElement => {
                                galleryElement.addEventListener('click', this.onThumbnailsClick, false);
                            });
                        }
                        /**
                         * @param {MouseEvent} e
                         */


                        onThumbnailsClick(e) {
                            // Exit and allow default browser action if:
                            if (specialKeyUsed(e) // ... if clicked with a special key (ctrl/cmd...)
                                || window.pswp) {
                                // ... if PhotoSwipe is already open
                                return;
                                } // If both clientX and clientY are 0 or not defined,
                                // the event is likely triggered by keyboard,
                                // so we do not pass the initialPoint
                                //
                                // Note that some screen readers emulate the mouse position,
                                // so it's not the ideal way to detect them.
                                //

                                /** @type {Point | null} */


                                let initialPoint = {
                                    x: e.clientX,
                                    y: e.clientY
                                };

                                if (!initialPoint.x && !initialPoint.y) {
                                    initialPoint = null;
                                }

                                let clickedIndex = this.getClickedIndex(e);
                                clickedIndex = this.applyFilters('clickedIndex', clickedIndex, e, this);
                                /** @type {DataSource} */

                                const dataSource = {
                                    gallery:
                                    /** @type {HTMLElement} */
                                    e.currentTarget
                                };

                                if (clickedIndex >= 0) {
                                    e.preventDefault();
                                    this.loadAndOpen(clickedIndex, dataSource, initialPoint);
                                }
                        }
                        /**
                         * Get index of gallery item that was clicked.
                         *
                         * @param {MouseEvent} e click event
                         * @returns {number}
                         */


                        getClickedIndex(e) {
                            // legacy option
                            if (this.options.getClickedIndexFn) {
                                return this.options.getClickedIndexFn.call(this, e);
                            }

                            const clickedTarget =
                            /** @type {HTMLElement} */
                            e.target;
                            const childElements = getElementsFromOption(this.options.children, this.options.childSelector,
                                                                        /** @type {HTMLElement} */
                                                                        e.currentTarget);
                            const clickedChildIndex = childElements.findIndex(child => child === clickedTarget || child.contains(clickedTarget));

                            if (clickedChildIndex !== -1) {
                                return clickedChildIndex;
                            } else if (this.options.children || this.options.childSelector) {
                                // click wasn't on a child element
                                return -1;
                            } // There is only one item (which is the gallery)


                            return 0;
                        }
                        /**
                         * Load and open PhotoSwipe
                         *
                         * @param {number} index
                         * @param {DataSource} [dataSource]
                         * @param {Point | null} [initialPoint]
                         * @returns {boolean}
                         */


                        loadAndOpen(index, dataSource, initialPoint) {
                            // Check if the gallery is already open
                            if (window.pswp || !this.options) {
                                return false;
                            } // Use the first gallery element if dataSource is not provided


                            if (!dataSource && this.options.gallery && this.options.children) {
                                const galleryElements = getElementsFromOption(this.options.gallery);

                                if (galleryElements[0]) {
                                    dataSource = {
                                        gallery: galleryElements[0]
                                    };
                                }
                            } // set initial index


                            this.options.index = index; // define options for PhotoSwipe constructor

                            this.options.initialPointerPos = initialPoint;
                            this.shouldOpen = true;
                            this.preload(index, dataSource);
                            return true;
                        }
                        /**
                         * Load the main module and the slide content by index
                         *
                         * @param {number} index
                         * @param {DataSource} [dataSource]
                         */


                        preload(index, dataSource) {
                            const {
                                options
                            } = this;

                            if (dataSource) {
                                options.dataSource = dataSource;
                            } // Add the main module

                            /** @type {Promise<Type<PhotoSwipe>>[]} */


                            const promiseArray = [];
                            const pswpModuleType = typeof options.pswpModule;

                            if (isPswpClass(options.pswpModule)) {
                                promiseArray.push(Promise.resolve(
                                    /** @type {Type<PhotoSwipe>} */
                                    options.pswpModule));
                            } else if (pswpModuleType === 'string') {
                                throw new Error('pswpModule as string is no longer supported');
                            } else if (pswpModuleType === 'function') {
                                promiseArray.push(
                                    /** @type {() => Promise<Type<PhotoSwipe>>} */
                                    options.pswpModule());
                            } else {
                                throw new Error('pswpModule is not valid');
                            } // Add custom-defined promise, if any


                            if (typeof options.openPromise === 'function') {
                                // allow developers to perform some task before opening
                                promiseArray.push(options.openPromise());
                            }

                            if (options.preloadFirstSlide !== false && index >= 0) {
                                this._preloadedContent = lazyLoadSlide(index, this);
                            } // Wait till all promises resolve and open PhotoSwipe


                            const uid = ++this._uid;
                            Promise.all(promiseArray).then(iterableModules => {
                                if (this.shouldOpen) {
                                    const mainModule = iterableModules[0];

                                    this._openPhotoswipe(mainModule, uid);
                                }
                            });
                        }
                        /**
                         * @private
                         * @param {Type<PhotoSwipe> | { default: Type<PhotoSwipe> }} module
                         * @param {number} uid
                         */


                        _openPhotoswipe(module, uid) {
                            // Cancel opening if UID doesn't match the current one
                            // (if user clicked on another gallery item before current was loaded).
                            //
                            // Or if shouldOpen flag is set to false
                            // (developer may modify it via public API)
                            if (uid !== this._uid && this.shouldOpen) {
                                return;
                            }

                            this.shouldOpen = false; // PhotoSwipe is already open

                            if (window.pswp) {
                                return;
                            }
                            /**
                             * Pass data to PhotoSwipe and open init
                             *
                             * @type {PhotoSwipe}
                             */


                            const pswp = typeof module === 'object' ? new module.default(this.options) // eslint-disable-line
                            : new module(this.options); // eslint-disable-line

                            this.pswp = pswp;
                            window.pswp = pswp; // map listeners from Lightbox to PhotoSwipe Core

                            /** @type {(keyof PhotoSwipeEventsMap)[]} */

                            Object.keys(this._listeners).forEach(name => {
                                var _this$_listeners$name;

                                (_this$_listeners$name = this._listeners[name]) === null || _this$_listeners$name === void 0 || _this$_listeners$name.forEach(fn => {
                                    pswp.on(name,
                                            /** @type {EventCallback<typeof name>} */
                                            fn);
                                });
                            }); // same with filters

                            /** @type {(keyof PhotoSwipeFiltersMap)[]} */

                            Object.keys(this._filters).forEach(name => {
                                var _this$_filters$name;

                                (_this$_filters$name = this._filters[name]) === null || _this$_filters$name === void 0 || _this$_filters$name.forEach(filter => {
                                    pswp.addFilter(name, filter.fn, filter.priority);
                                });
                            });

                            if (this._preloadedContent) {
                                pswp.contentLoader.addToCache(this._preloadedContent);
                                this._preloadedContent = undefined;
                            }

                            pswp.on('destroy', () => {
                                // clean up public variables
                                this.pswp = undefined;
                                delete window.pswp;
                            });
                            pswp.init();
                        }
                        /**
                         * Unbinds all events, closes PhotoSwipe if it's open.
                         */


                        destroy() {
                            var _this$pswp;

                            (_this$pswp = this.pswp) === null || _this$pswp === void 0 || _this$pswp.destroy();
                            this.shouldOpen = false;
                            this._listeners = {};
                            getElementsFromOption(this.options.gallery, this.options.gallerySelector).forEach(galleryElement => {
                                galleryElement.removeEventListener('click', this.onThumbnailsClick, false);
                            });
                        }

                         }

                         export { PhotoSwipeLightbox as default };
                         //# sourceMappingURL=photoswipe-lightbox.esm.js.map
