photoswipe
Version:
JavaScript gallery
187 lines (158 loc) • 5.48 kB
JavaScript
import Eventable from './eventable.js';
import { getElementsFromOption } from '../util/util.js';
import Content from '../slide/content.js';
import { lazyLoadData } from '../slide/loader.js';
/** @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() {
let numItems = 0;
const dataSource = 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) {
const dataSource = this.options?.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) {
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 {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) {
// 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;
}
}
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);
}
}
export default PhotoSwipeBase;