lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js in intranet-pictures-2.0.0 vs lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js in intranet-pictures-2.1.0
- old
+ new
@@ -1,240 +1,539 @@
/*!
- * PhotoSwipe Lightbox 5.2.4 - https://photoswipe.com
- * (c) 2022 Dmytro Semenov
+ * PhotoSwipe Lightbox 5.4.2 - https://photoswipe.com
+ * (c) 2023 Dmytro Semenov
*/
+/** @typedef {import('../photoswipe.js').Point} Point */
+
/**
- * Creates element and optionally appends it to another.
- *
- * @param {String} className
- * @param {String|NULL} tagName
- * @param {Element|NULL} appendToEl
- */
+ * @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 || 'div');
+ 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|null} y
- * @param {Number|null} scale
+ * @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)';
+ let propValue = `translate3d(${x}px,${y || 0}px,0)`;
if (scale !== undefined) {
- propValue += ' scale3d('
- + scale + ',' + scale
- + ',1)';
+ 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;
+ 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',
+ ERROR: 'error'
};
-
-
/**
* Check if click or keydown event was dispatched
* with a special key or via mouse wheel.
*
- * @param {Event} e
+ * @param {MouseEvent | KeyboardEvent} e
+ * @returns {boolean}
*/
+
function specialKeyUsed(e) {
- if (e.which === 2 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) {
- return true;
- }
+ return 'button' in e && e.button === 1 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey;
}
-
/**
* Parse `gallery` or `children` options.
*
- * @param {Element|NodeList|String} option
- * @param {String|null} legacySelector
- * @param {Element|null} parent
- * @returns Element[]
+ * @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 {*} fn
- * @returns Boolean
+ * @param {any} fn
+ * @returns {boolean}
*/
+
function isPswpClass(fn) {
- return typeof fn === 'function'
- && fn.prototype
- && fn.prototype.goTo;
+ 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].push({ fn, priority });
- this._filters[name].sort((f1, f2) => f1.priority - f2.priority);
-
- if (this.pswp) {
- this.pswp.addFilter(name, fn, priority);
- }
+ (_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]) {
- this._filters[name] = this._filters[name].filter(filter => (filter.fn !== fn));
+ // @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) {
- if (this._filters[name]) {
- this._filters[name].forEach((filter) => {
- args[0] = filter.fn.apply(this, 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].push(fn);
- // When binding events to lightbox,
+ (_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.
- if (this.pswp) {
- this.pswp.on(name, fn);
- }
+
+ (_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]) {
- this._listeners[name] = this._listeners[name].filter(listener => (fn !== listener));
+ // @ts-expect-error
+ this._listeners[name] = this._listeners[name].filter(listener => fn !== listener);
}
- if (this.pswp) {
- this.pswp.off(name, fn);
- }
+ (_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 = new PhotoSwipeEvent(name, details);
-
- if (!this._listeners) {
- return event;
- }
-
- if (this._listeners[name]) {
- this._listeners[name].forEach((listener) => {
- listener.call(this, event);
- });
- }
-
+ 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 {Element} container
+ * @param {string | false} imageSrc
+ * @param {HTMLElement} container
*/
constructor(imageSrc, container) {
// Create placeholder
// (stretched thumbnail or simple div behind the main image)
- this.element = createElement(
- 'pswp__img pswp__img--placeholder',
- imageSrc ? 'img' : '',
- container
- );
+ /** @type {HTMLImageElement | HTMLDivElement | null} */
+ this.element = createElement('pswp__img pswp__img--placeholder', imageSrc ? 'img' : 'div', container);
+
if (imageSrc) {
- this.element.decoding = 'async';
- this.element.alt = '';
- this.element.src = imageSrc;
- this.element.setAttribute('role', 'presentation');
+ const imgEl =
+ /** @type {HTMLImageElement} */
+ this.element;
+ imgEl.decoding = 'async';
+ imgEl.alt = '';
+ imgEl.src = imageSrc;
+ imgEl.setAttribute('role', 'presentation');
}
- this.element.setAttribute('aria-hiden', 'true');
+ this.element.setAttribute('aria-hidden', 'true');
}
+ /**
+ * @param {number} width
+ * @param {number} height
+ */
+
setDisplayedSize(width, height) {
if (!this.element) {
return;
}
@@ -249,345 +548,443 @@
setWidthHeight(this.element, width, height);
}
}
destroy() {
- if (this.element.parentNode) {
+ 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 {Object} itemData Slide data
+ * @param {SlideData} itemData Slide data
* @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox instance
- * @param {Slide|undefined} slide Slide that requested the image,
- * can be undefined if image was requested by something else
- * (for example by lazy-loader)
+ * @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 });
+ 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 = null;
+ this.placeholder = undefined;
}
- }, 500);
+ }, 1000);
}
}
-
/**
* Preload content
*
- * @param {Boolean} isLazy
+ * @param {boolean} isLazy
+ * @param {boolean} [reload]
*/
+
+
load(isLazy, reload) {
- if (!this.placeholder && this.slide && this.usePlaceholder()) {
- // use -based placeholder only for the first slide,
- // as rendering (even small stretched thumbnail) is an expensive operation
- const placeholderSrc = this.instance.applyFilters(
- 'placeholderSrc',
- (this.data.msrc && this.slide.isFirstSlide) ? this.data.msrc : false,
- this
- );
- this.placeholder = new Placeholder(
- placeholderSrc,
- this.slide.container
- );
+ 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) {
+ if (this.instance.dispatch('contentLoad', {
+ content: this,
+ isLazy
+ }).defaultPrevented) {
return;
}
if (this.isImageContent()) {
- this.loadImage(isLazy);
+ 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');
+ 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
+ * @param {boolean} isLazy
*/
+
+
loadImage(isLazy) {
- this.element = createElement('pswp__img', 'img');
+ var _this$data$src, _this$data$alt;
- if (this.instance.dispatch('contentLoadImage', { content: this, isLazy }).defaultPrevented) {
+ 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) {
- this.element.srcset = this.data.srcset;
+ imageElement.srcset = this.data.srcset;
}
- this.element.src = this.data.src;
-
- this.element.alt = this.data.alt || '';
-
+ 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 (this.element.complete) {
+ if (imageElement.complete) {
this.onLoaded();
} else {
- this.element.onload = () => {
+ imageElement.onload = () => {
this.onLoaded();
};
- this.element.onerror = () => {
+ 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?
+ 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.instance.dispatch('loadComplete', { slide: this.slide, content: this });
+ if (this.slide && this.element) {
+ this.instance.dispatch('loadComplete', {
+ slide: this.slide,
+ content: this
+ }); // if content is reloaded
- // if content is reloaded
- if (this.slide.isActive
- && this.slide.heavyAppended
- && !this.element.parentNode) {
- this.slide.container.innerHTML = '';
+ 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 });
+ 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
- );
+ 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
+ * @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) {
+ if (this.instance.dispatch('contentResize', {
+ content: this,
+ width,
+ height
+ }).defaultPrevented) {
return;
}
setWidthHeight(this.element, width, height);
if (this.isImageContent() && !this.isError()) {
- const image = this.element;
- // 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 (image.srcset
- && (!image.dataset.largestUsedSize || width > image.dataset.largestUsedSize)) {
- image.sizes = width + 'px';
- image.dataset.largestUsedSize = width;
+ 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 });
+ this.instance.dispatch('imageSizeChange', {
+ slide: this.slide,
+ width,
+ height,
+ content: this
+ });
}
}
}
-
/**
- * @returns {Boolean} If the content can be zoomed
+ * @returns {boolean} If the content can be zoomed
*/
+
+
isZoomable() {
- return this.instance.applyFilters(
- 'isContentZoomable',
- this.isImageContent() && (this.state !== LOAD_STATE.ERROR),
- this
- );
+ 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)
+ * @returns {boolean} If content should use a placeholder (from msrc by default)
*/
+
+
usePlaceholder() {
- return this.instance.applyFilters(
- 'useContentPlaceholder',
- this.isImageContent(),
- this
- );
+ return this.instance.applyFilters('useContentPlaceholder', this.isImageContent(), this);
}
-
/**
* Preload content with lazy-loading param
- *
- * @param {Boolean} isLazy
*/
+
+
lazyLoad() {
- if (this.instance.dispatch('contentLazyLoad', { content: this }).defaultPrevented) {
+ if (this.instance.dispatch('contentLazyLoad', {
+ content: this
+ }).defaultPrevented) {
return;
}
this.load(true);
}
-
/**
- * @returns {Boolean} If placeholder should be kept after content is loaded
+ * @returns {boolean} If placeholder should be kept after content is loaded
*/
+
+
keepPlaceholder() {
- return this.instance.applyFilters(
- 'isKeepingPlaceholder',
- this.isLoading(),
- this
- );
+ return this.instance.applyFilters('isKeepingPlaceholder', this.isLoading(), this);
}
-
/**
* Destroy the content
*/
+
+
destroy() {
this.hasSlide = false;
- this.slide = null;
+ this.slide = undefined;
- if (this.instance.dispatch('contentDestroy', { content: this }).defaultPrevented) {
+ 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 = null;
+ this.element = undefined;
}
}
-
/**
* Display error message
*/
+
+
displayError() {
if (this.slide) {
- let errorMsgEl = createElement('pswp__error-msg');
- errorMsgEl.innerText = this.instance.options.errorMsg;
- errorMsgEl = this.instance.applyFilters(
- 'contentErrorElement',
- errorMsgEl,
- this
- );
- this.element = createElement('pswp__content pswp__error-msg-container');
+ 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.innerHTML = '';
+ 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) {
+ 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).
@@ -596,283 +993,149 @@
// 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.
- if (this.slide
- && !this.slide.isActive
- && ('decode' in this.element)) {
- this.isDecoding = true;
- // Make sure that we start decoding on the next frame
- requestAnimationFrame(() => {
- // element might change
- if (this.element && this.element.tagName === 'IMG') {
- this.element.decode().then(() => {
- this.isDecoding = false;
- requestAnimationFrame(() => {
- this.appendImage();
- });
- }).catch(() => {
- this.isDecoding = false;
- });
- }
+ // 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 {
- if (this.placeholder
- && (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR)) {
- this.removePlaceholder();
- }
this.appendImage();
}
- } else if (this.element && !this.element.parentNode) {
+ } 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) {
+ if (this.instance.dispatch('contentActivate', {
+ content: this
+ }).defaultPrevented || !this.slide) {
return;
}
- if (this.slide) {
- if (this.isImageContent() && this.isDecoding) {
- // 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.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 });
- }
+ 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) {
+ 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) {
+ if (this.instance.dispatch('contentAppendImage', {
+ content: this
+ }).defaultPrevented) {
return;
- }
+ } // ensure that element exists and is not already appended
- // 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.placeholder
- && (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR)) {
- this.removePlaceholder();
- }
}
- }
-}
-/**
- * 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
- */
- getNumItems() {
- let numItems;
- const { dataSource } = this.options;
- if (!dataSource) {
- numItems = 0;
- } else if (dataSource.length) {
- // may be an array or just object with length property
- numItems = dataSource.length;
- } else if (dataSource.gallery) {
- // query DOM elements
- if (!dataSource.items) {
- dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
- }
-
- if (dataSource.items) {
- numItems = dataSource.items.length;
- }
+ if (this.state === LOAD_STATE.LOADED || this.state === LOAD_STATE.ERROR) {
+ this.removePlaceholder();
}
-
- // legacy event, before filters were introduced
- const event = this.dispatch('numItems', {
- dataSource,
- numItems
- });
- return this.applyFilters('numItems', event.numItems, dataSource);
}
- 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 {Integer} index
- */
- getItemData(index) {
- const { dataSource } = this.options;
- let dataSourceItem;
- if (Array.isArray(dataSource)) {
- // Datasource is an array of elements
- dataSourceItem = dataSource[index];
- } else if (dataSource && dataSource.gallery) {
- // dataSource has gallery property,
- // thus it was created by Lightbox, based on
- // gallerySelecor and childSelector options
+/** @typedef {import('../photoswipe.js').PhotoSwipeOptions} PhotoSwipeOptions */
- // query DOM elements
- if (!dataSource.items) {
- dataSource.items = this._getGalleryDOMElements(dataSource.gallery);
- }
+/** @typedef {import('../core/base.js').default} PhotoSwipeBase */
- dataSourceItem = dataSource.items[index];
- }
+/** @typedef {import('../photoswipe.js').Point} Point */
- let itemData = dataSourceItem;
+/** @typedef {import('../slide/slide.js').SlideData} SlideData */
- 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 {Element} galleryElement
- */
- _getGalleryDOMElements(galleryElement) {
- if (this.options.children || this.options.childSelector) {
- return getElementsFromOption(
- this.options.children,
- this.options.childSelector,
- galleryElement
- ) || [];
- }
-
- return [galleryElement];
- }
-
- /**
- * Converts DOM element to item data object.
- *
- * @param {Element} element DOM element
- */
- // eslint-disable-next-line class-methods-use-this
- _domElementToItemData(element) {
- const itemData = {
- element
- };
-
- const linkEl = 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 = parseInt(linkEl.dataset.pswpWidth, 10);
- itemData.height = parseInt(linkEl.dataset.pswpHeight, 10);
-
- // 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) {
- // 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.getAttribute('alt');
- }
-
- if (linkEl.dataset.pswpCropped || linkEl.dataset.cropped) {
- itemData.thumbCropped = true;
- }
- }
-
- this.applyFilters('domItemData', itemData, element, linkEl);
-
- return itemData;
- }
-}
-
+/**
+ * @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
@@ -897,121 +1160,145 @@
* paddingLeft: 0,
* paddingRight: 0,
* paddingTop: 0,
* paddingBottom: 0,
*
- * @param {String} prop 'left', 'top', 'bottom', 'right'
- * @param {Object} options PhotoSwipe options
- * @param {Object} viewportSize PhotoSwipe viewport size, for example: { x:800, y:600 }
- * @param {Object} itemData Data about the slide
- * @param {Integer} index Slide index
- * @returns {Number}
+ * @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;
+ 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);
+ const legacyPropName = 'padding' + prop[0].toUpperCase() + prop.slice(1); // @ts-expect-error
+
if (options[legacyPropName]) {
+ // @ts-expect-error
paddingValue = options[legacyPropName];
}
}
- return paddingValue || 0;
+ 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)
+ 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.
*/
-const MAX_IMAGE_WIDTH = 4000;
-
class ZoomLevel {
/**
- * @param {Object} options PhotoSwipe options
- * @param {Object} itemData Slide data
- * @param {Integer} index Slide index
- * @param {PhotoSwipe|undefined} pswp PhotoSwipe instance, can be undefined if not initialized yet
+ * @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 {Slide} slide
+ * @param {number} maxWidth
+ * @param {number} maxHeight
+ * @param {Point} panAreaSize
*/
+
+
update(maxWidth, maxHeight, panAreaSize) {
- this.elementSize = {
+ /** @type {Point} */
+ const elementSize = {
x: maxWidth,
y: maxHeight
};
-
+ this.elementSize = elementSize;
this.panAreaSize = panAreaSize;
-
- const hRatio = this.panAreaSize.x / this.elementSize.x;
- const vRatio = this.panAreaSize.y / this.elementSize.y;
-
+ 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
+ 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.vFill = Math.min(1, vRatio);
this.initial = this._getInitial();
this.secondary = this._getSecondary();
- this.max = Math.max(
- this.initial,
- this.secondary,
- this._getMax()
- );
+ this.max = Math.max(this.initial, this.secondary, this._getMax());
+ this.min = Math.min(this.fit, this.initial, this.secondary);
- this.min = Math.min(
- this.fit,
- this.initial,
- this.secondary
- );
-
if (this.pswp) {
- this.pswp.dispatch('zoomLevelsUpdate', { zoomLevels: this, slideData: this.itemData });
+ this.pswp.dispatch('zoomLevelsUpdate', {
+ zoomLevels: this,
+ slideData: this.itemData
+ });
}
}
-
/**
* Parses user-defined zoom option.
*
- * @param {Mixed} optionPrefix Zoom level option prefix (initial, secondary, max)
+ * @private
+ * @param {'initial' | 'secondary' | 'max'} optionPrefix Zoom level option prefix (initial, secondary, max)
+ * @returns { number | undefined }
*/
+
+
_parseZoomLevelOption(optionPrefix) {
- // zoom.initial
- // zoom.secondary
- // zoom.max
- const optionValue = this.options[optionPrefix + 'ZoomLevel'];
+ const optionName =
+ /** @type {'initialZoomLevel' | 'secondaryZoomLevel' | 'maxZoomLevel'} */
+ optionPrefix + 'ZoomLevel';
+ const optionValue = this.options[optionName];
if (!optionValue) {
return;
}
@@ -1027,125 +1314,348 @@
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.
*
- * @return {Number}
+ * @private
+ * @return {number}
*/
+
+
_getSecondary() {
let currZoomLevel = this._parseZoomLevelOption('secondary');
if (currZoomLevel) {
return currZoomLevel;
- }
+ } // 3x of "fit" state, but not larger than original
- // 3x of "fit" state, but not larger than original
+
currZoomLevel = Math.min(1, this.fit * 3);
- if (currZoomLevel * this.elementSize.x > MAX_IMAGE_WIDTH) {
+ if (this.elementSize && currZoomLevel * this.elementSize.x > MAX_IMAGE_WIDTH) {
currZoomLevel = MAX_IMAGE_WIDTH / this.elementSize.x;
}
return currZoomLevel;
}
-
/**
* Get initial image zoom level.
*
- * @return {Number}
+ * @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.
*
- * @return {Number}
+ * @private
+ * @return {number}
*/
- _getMax() {
- const currZoomLevel = this._parseZoomLevelOption('max');
- if (currZoomLevel) {
- return currZoomLevel;
- }
+ _getMax() {
// max zoom level is x4 from "fit state",
// used for zoom gesture and ctrl/trackpad zoom
- return Math.max(1, this.fit * 4);
+ 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 {Object} itemData Data about the slide
- * @param {PhotoSwipeBase} instance PhotoSwipe or PhotoSwipeLightbox
- * @param {Integer} index
- * @returns {Object|Boolean} Image that is being decoded or false.
+ * @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) {
- // src/slide/content/content.js
const content = instance.createContentFromData(itemData, index);
+ /** @type {ZoomLevel | undefined} */
- if (!content || !content.lazyLoad) {
- return;
- }
+ 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
- const { options } = instance;
+ if (options) {
+ zoomLevel = new ZoomLevel(options, itemData, -1);
+ let viewportSize;
- // We need to know dimensions of the image to preload it,
- // as it might use srcset and we need to define sizes
- const viewportSize = instance.viewportSize || getViewportSize(options);
- const panAreaSize = getPanAreaSize(options, viewportSize, itemData, index);
+ if (instance.pswp) {
+ viewportSize = instance.pswp.viewportSize;
+ } else {
+ viewportSize = getViewportSize(options, instance);
+ }
- const zoomLevel = new ZoomLevel(options, itemData, -1);
- zoomLevel.update(content.width, content.height, panAreaSize);
+ const panAreaSize = getPanAreaSize(options, viewportSize, itemData, index);
+ zoomLevel.update(content.width, content.height, panAreaSize);
+ }
content.lazyLoad();
- content.setDisplayedSize(
- Math.ceil(content.width * zoomLevel.initial),
- Math.ceil(content.height * zoomLevel.initial)
- );
+ 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.
+ * By default, it loads image based on viewport size and initial zoom level.
*
- * @param {Integer} index Slide index
- * @param {Object} instance PhotoSwipe or PhotoSwipeLightbox eventable instance
+ * @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) {
+ 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 lightbox
+ * 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
@@ -1157,226 +1667,294 @@
* 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}
+ */
- init() {
+ 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);
- });
+ 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
- || window.navigator.onLine === false) { // ... if offline
+ || window.pswp) {
+ // ... if PhotoSwipe is already open
return;
- }
-
- // If both clientX and clientY are 0 or not defined,
+ } // 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 ideal way to detect them.
+ // so it's not the ideal way to detect them.
//
- let initialPoint = { x: e.clientX, y: e.clientY };
+ /** @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: e.currentTarget
+ gallery:
+ /** @type {HTMLElement} */
+ e.currentTarget
};
if (clickedIndex >= 0) {
e.preventDefault();
this.loadAndOpen(clickedIndex, dataSource, initialPoint);
}
}
-
/**
* Get index of gallery item that was clicked.
*
- * @param {Event} e click event
+ * @param {MouseEvent} e click event
+ * @returns {number}
*/
+
+
getClickedIndex(e) {
// legacy option
if (this.options.getClickedIndexFn) {
return this.options.getClickedIndexFn.call(this, e);
}
- const clickedTarget = e.target;
- const childElements = getElementsFromOption(
- this.options.children,
- this.options.childSelector,
- e.currentTarget
- );
- const clickedChildIndex = childElements.findIndex(
- child => child === clickedTarget || child.contains(clickedTarget)
- );
+ 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)
- // There is only one item (which is the gallery)
+
return 0;
}
-
/**
* Load and open PhotoSwipe
*
- * @param {Integer} index
- * @param {Array|Object|null} dataSource
- * @param {Point|null} initialPoint
+ * @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) {
+ if (window.pswp || !this.options) {
return false;
- }
+ } // Use the first gallery element if dataSource is not provided
- // set initial index
- this.options.index = index;
- // define options for PhotoSwipe constructor
- this.options.initialPointerPos = initialPoint;
+ 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 {Integer} index
+ * @param {number} index
+ * @param {DataSource} [dataSource]
*/
+
+
preload(index, dataSource) {
- const { options } = this;
+ const {
+ options
+ } = this;
if (dataSource) {
options.dataSource = dataSource;
- }
+ } // Add the main module
- // Add the main module
- const promiseArray = [];
+ /** @type {Promise<Type<PhotoSwipe>>[]} */
+
+ const promiseArray = [];
const pswpModuleType = typeof options.pswpModule;
+
if (isPswpClass(options.pswpModule)) {
- promiseArray.push(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(options.pswpModule());
+ promiseArray.push(
+ /** @type {() => Promise<Type<PhotoSwipe>>} */
+ options.pswpModule());
} else {
throw new Error('pswpModule is not valid');
- }
+ } // Add custom-defined promise, if any
- // 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
- // Wait till all promises resolve and open PhotoSwipe
+
const uid = ++this._uid;
- Promise.all(promiseArray).then((iterableModules) => {
+ 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;
+ this.shouldOpen = false; // PhotoSwipe is already open
- // PhotoSwipe is already open
if (window.pswp) {
return;
}
+ /**
+ * Pass data to PhotoSwipe and open init
+ *
+ * @type {PhotoSwipe}
+ */
- // Pass data to PhotoSwipe and open init
- const pswp = typeof module === 'object'
- ? new module.default(this.options) // eslint-disable-line
- : new module(this.options); // eslint-disable-line
+ 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;
+ window.pswp = pswp; // map listeners from Lightbox to PhotoSwipe Core
- // map listeners from Lightbox to PhotoSwipe Core
- Object.keys(this._listeners).forEach((name) => {
- this._listeners[name].forEach((fn) => {
- pswp.on(name, fn);
+ /** @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
- // same with filters
- Object.keys(this._filters).forEach((name) => {
- this._filters[name].forEach((filter) => {
+ /** @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 = null;
+ this._preloadedContent = undefined;
}
pswp.on('destroy', () => {
// clean up public variables
- this.pswp = null;
- window.pswp = null;
+ this.pswp = undefined;
+ delete window.pswp;
});
-
pswp.init();
}
+ /**
+ * Unbinds all events, closes PhotoSwipe if it's open.
+ */
+
destroy() {
- if (this.pswp) {
- this.pswp.destroy();
- }
+ var _this$pswp;
+ (_this$pswp = this.pswp) === null || _this$pswp === void 0 || _this$pswp.destroy();
this.shouldOpen = false;
- this._listeners = null;
-
- getElementsFromOption(this.options.gallery, this.options.gallerySelector)
- .forEach((galleryElement) => {
- galleryElement.removeEventListener('click', this.onThumbnailsClick, false);
- });
+ this._listeners = {};
+ getElementsFromOption(this.options.gallery, this.options.gallerySelector).forEach(galleryElement => {
+ galleryElement.removeEventListener('click', this.onThumbnailsClick, false);
+ });
}
+
}
-export default PhotoSwipeLightbox;
+export { PhotoSwipeLightbox as default };
//# sourceMappingURL=photoswipe-lightbox.esm.js.map