import { PopupPagination } from "../../map/popup/PopupPagination.js";
import { MediaCaption } from "./utils.js";
import { CloseButton, ShareButton, MetadataButton, ZoomText } from "./uiElements.js"
import { EventObject } from "../../events/EventObject.js";
import { DeviceProperties, ElementProperties } from "../utils/helperTools.js";
import { PhotoswipeLightbox } from "./PhotoswipeLightbox.js";
import { PhotoswipeLightboxEvents } from "./PhotoswipeLightboxEvents.js";
export { Lightbox }
/**
* Lightbox class to build a media lightbox using the core functionalities of the photoswipe lightbox. However, the pswp UI is replaced by own UI design, thus own DOM structure and elements (e.g., buttons, text).
* @extends EventObject
* @author ckraemme <ckraemme@awi.de>
*/
class Lightbox extends EventObject {
/**
* Creates the lightbox.
* @param {Object} lightboxData - LightboxData class object.
* @param {Object} lightboxConfig - LightboxConfig class object.
*/
constructor(lightboxData, lightboxConfig) {
super({
"lightbox_open": [], // [1]
"lightbox_close": [],// [1]
"lightbox_critical_state": [],
//[1]: required for enabling/disabling scrollbars
});
this.lightboxData = lightboxData;
this.lightboxConfig = lightboxConfig;
}
init() {
if (this.lightboxConfig.slideOnStartIndex != null) {
this._build();
}
const gallery = this.lightboxConfig.targetElement.querySelector('.pswp-gallery');
if (gallery) {
gallery.addEventListener("click", (e) => {
this.lightboxConfig.slideOnStartIndex = this._getIndexOfItemInGallery(e.target);
if (Number.isInteger(this.lightboxConfig.slideOnStartIndex)) {
e.preventDefault();
this._build();
}
})
}
}
_opening() {
this.lightboxWrapper.classList.remove('state-before-opening');
this.lightboxWrapper.classList.add('state-opening');
this._initLightboxHistoryEntry();
}
_open() {
this.lightboxWrapper.classList.remove('state-opening');
}
_closing() {
this.lightboxWrapper.classList.add('state-closing');
this.shareButton.closeContent();
this._disableClosingAnimationBasedOnItemVisibility();
this._removeLightboxHistoryEntry();
}
_closed() {
this.lightboxWrapper.remove();
this.pswpLightbox = null;
this.lightboxWrapper.classList.remove('state-closing');
this.lightboxWrapper = null;
}
/**
* FixMe: This feature collides with the VUE routing in the portal. Somehow a page reload is triggered when
* history.back is called.
*
* On touch screens, allow the user to close the lightbox using the browser's back button (i.e. swipe back).
* @private
*/
_initLightboxHistoryEntry() {
//if (DeviceProperties.isTouchDevice()) {
// if (history.state?.name != "Lightbox")
// history.pushState({ name: "Lightbox" }, null, document.URL);
//
// this.historyStateMethod = this._removeLightboxHistoryEntry.bind(this);
// window.addEventListener('popstate', this.historyStateMethod);
//}
}
_removeLightboxHistoryEntry() {
//if (DeviceProperties.isTouchDevice()) {
// window.removeEventListener('popstate', this.historyStateMethod);
// if (history.state?.name == "Lightbox")
// history.back();
// this.pswpLightbox.lightbox.pswp?.close();
//}
}
/**
* Disable lightbox closing animation if the gallery item is not in the viewport.
* @private
*/
_disableClosingAnimationBasedOnItemVisibility() {
const index = this.pswpLightbox.lightbox.pswp.currIndex;
const placeholderElement = document.querySelectorAll('.pswp-gallery__item')[index];
const deviation = 1.0;
const isInViewport = ElementProperties.isInViewport(placeholderElement, deviation);
if (!isInViewport)
this.pswpLightbox.lightbox.pswp.options.showHideAnimationType = 'none';
}
_getIndexOfItemInGallery(itemNode) {
const galleryItemNodes = document.querySelectorAll('.pswp-gallery__item');
let itemIndex;
for (let index = 0; index < galleryItemNodes.length; index++) {
const item = galleryItemNodes[index];
if (item.contains(itemNode)) {
itemIndex = index;
break
}
}
return itemIndex
}
/**
* Set the height of the lightbox to window.innerHeight to prevent overflowing
* content below the address bar in mobile browsers.
* @private
*/
_setLightboxResizeListener() {
this._resetLightboxResizeListener();
this._resizeListener = () => {
this.lightboxWrapper.style.height = window.innerHeight + "px";
};
window.addEventListener("resize", this._resizeListener);
this._resizeListener();
}
_resetLightboxResizeListener() {
if (this._resizeListener) {
window.removeEventListener("resize", this._resizeListener);
}
}
_build() {
//Observe: 0 to slideOnStartIndex is required so all items before current item are filtered, thus slideOnStartIndex matches with filtered grid
this.lightboxData.loadItems(0, this.lightboxConfig.slideOnStartIndex || 1, false, {})
//this.lightboxData.getItemsQuantity() //TODO: why is it not working?
//this.lightboxData.getItem(this.lightboxConfig.slideOnStartIndex, [-3, 3]) //replaces: options.preloadFirstSlide=true //OR put in 'itemData' and activate preloadFirstSlide?
.then(() => {
this._initDOM();
this.pswpLightbox = new PhotoswipeLightbox(this.lightboxConfig.targetElement.querySelector('.pswp-lightbox'), this.lightboxData, this.lightboxConfig.slideOnStartIndex);
this._handlePswpEvents();
this._setLightboxResizeListener();
this.fire("lightbox_open");
}).catch((error) => {
this.fire("lightbox_critical_state", error)
})
}
_initDOM() {
this.lightboxWrapper = document.createElement('div');
this.lightboxWrapper.classList.add(...['vef-ui', 'custom-lightbox', 'state-before-opening']);
this._initDOMHeader();
this._initDOMContent();
this._initDOMPagination();
this.lightboxConfig.targetElement.append(this.lightboxWrapper);
}
_initDOMHeader() {
let lightboxHeader = document.createElement('div');
lightboxHeader.className = 'vef-ui lightbox-header';
const headerLeft = document.createElement('div');
headerLeft.classList.add('header-left');
this.mobileMetadataButton = new MetadataButton(headerLeft);
const headerRight = document.createElement('div');
headerRight.classList.add('header-right');
this.zoomText = new ZoomText(headerRight);
this.shareButton = new ShareButton(headerRight);
this.closeButton = new CloseButton(headerRight);
if (this.lightboxConfig.showShareButton === false)
this.shareButton.hideButton()
lightboxHeader.append(headerLeft);
lightboxHeader.append(headerRight);
this.lightboxWrapper.prepend(lightboxHeader);
}
_initDOMContent() {
const pswpLightbox = document.createElement('div');
pswpLightbox.classList.add(...['vef-ui', 'lightbox-content', 'pswp-lightbox']);
this.lightboxWrapper.append(pswpLightbox);
this.shareButton.createBox(pswpLightbox);
PhotoswipeLightboxEvents.preventOnShareBox(this.shareButton.contentBoxElement);
}
_initDOMPagination() {
const lightboxFooter = document.createElement('div');
lightboxFooter.classList.add(...['vef-ui', 'lightbox-footer']);
this.pagination = new PopupPagination(lightboxFooter, { pageCount: this.lightboxData.dataItemAmount });
this.lightboxWrapper.append(lightboxFooter);
}
_handlePswpEvents() {
this.pswpLightbox.on('pswp_lightbox_opening', () => {
this._opening();
})
this.pswpLightbox.on('pswp_lightbox_open', () => {
this._open();
})
this.pswpLightbox.on('pswp_lightbox_ready', () => {
const lightbox = this.pswpLightbox.lightbox;
this.pagination.on('next', () => { lightbox.pswp.next() });
this.pagination.on('previous', () => { lightbox.pswp.prev() });
this.closeButton.onClick(() => { lightbox.pswp.close() })
})
this.pswpLightbox.on("pswp_lightbox_slide_change", (index) => {
//console.log(`slide change [${index + 1}/${this.lightboxData.galleryItemAmount}]`);
if (this.lightboxConfig.showShareButton === true) {
const shareLink = this.lightboxData.getShareLink(index)
this.shareButton.setLink(shareLink);
const deactivateShareButton = shareLink == undefined;
deactivateShareButton ? this.shareButton.deactivateButton() : this.shareButton.activateButton();
}
this.pagination.setPage(this.lightboxData.shownItemPosition(index));
this.lightboxData.getSidebarTemplate().then((metadataTemplate) => {
const isItemLoaded = this.lightboxData['galleryItems'][index] != null;
if (isItemLoaded) {
const caption = MediaCaption.getCaption(this.lightboxData['galleryItems'][index]['sidebarData'], metadataTemplate);
const captionTarget = document.querySelector('.lightbox-content');
this.mobileMetadataButton.setContent(caption, captionTarget);
}
})
})
this.pswpLightbox.on("pswp_lightbox_zoom", ([zoomLevel, showZoomLevel]) => {
this.zoomText.isContentVisible = showZoomLevel;
this.zoomText.setZoom(zoomLevel);
})
this.pswpLightbox.on('pswp_lightbox_close', () => {
this.fire("lightbox_close");
this._resetLightboxResizeListener();
this._closing();
})
this.pswpLightbox.on('pswp_lightbox_destroy', () => {
this._closed();
})
this.pswpLightbox.on('pswp_critical_state', (message) => {
this.fire("lightbox_critical_state", message);
})
}
}