Spaces:
Sleeping
Sleeping
import * as __WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__ from "@wordpress/interactivity"; | |
/******/ // The require scope | |
/******/ var __webpack_require__ = {}; | |
/******/ | |
/************************************************************************/ | |
/******/ /* webpack/runtime/define property getters */ | |
/******/ (() => { | |
/******/ // define getter functions for harmony exports | |
/******/ __webpack_require__.d = (exports, definition) => { | |
/******/ for(var key in definition) { | |
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { | |
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); | |
/******/ } | |
/******/ } | |
/******/ }; | |
/******/ })(); | |
/******/ | |
/******/ /* webpack/runtime/hasOwnProperty shorthand */ | |
/******/ (() => { | |
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) | |
/******/ })(); | |
/******/ | |
/************************************************************************/ | |
var __webpack_exports__ = {}; | |
;// CONCATENATED MODULE: external "@wordpress/interactivity" | |
var x = (y) => { | |
var x = {}; __webpack_require__.d(x, y); return x | |
} | |
var y = (x) => (() => (x)) | |
const interactivity_namespaceObject = x({ ["getContext"]: () => (__WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__.getContext), ["getElement"]: () => (__WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__.getElement), ["store"]: () => (__WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__.store) }); | |
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-library/build-module/image/view.js | |
/** | |
* WordPress dependencies | |
*/ | |
/** | |
* Tracks whether user is touching screen; used to differentiate behavior for | |
* touch and mouse input. | |
* | |
* @type {boolean} | |
*/ | |
let isTouching = false; | |
/** | |
* Tracks the last time the screen was touched; used to differentiate behavior | |
* for touch and mouse input. | |
* | |
* @type {number} | |
*/ | |
let lastTouchTime = 0; | |
/** | |
* Stores the image reference of the currently opened lightbox. | |
* | |
* @type {HTMLElement} | |
*/ | |
let imageRef; | |
/** | |
* Stores the button reference of the currently opened lightbox. | |
* | |
* @type {HTMLElement} | |
*/ | |
let buttonRef; | |
const { | |
state, | |
actions, | |
callbacks | |
} = (0,interactivity_namespaceObject.store)('core/image', { | |
state: { | |
currentImage: {}, | |
get overlayOpened() { | |
return state.currentImage.currentSrc; | |
}, | |
get roleAttribute() { | |
return state.overlayOpened ? 'dialog' : null; | |
}, | |
get ariaModal() { | |
return state.overlayOpened ? 'true' : null; | |
}, | |
get enlargedSrc() { | |
return state.currentImage.uploadedSrc || 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; | |
}, | |
get imgStyles() { | |
return state.overlayOpened && `${state.currentImage.imgStyles?.replace(/;$/, '')}; object-fit:cover;`; | |
} | |
}, | |
actions: { | |
showLightbox() { | |
const ctx = (0,interactivity_namespaceObject.getContext)(); | |
// Bails out if the image has not loaded yet. | |
if (!ctx.imageRef?.complete) { | |
return; | |
} | |
// Stores the positons of the scroll to fix it until the overlay is | |
// closed. | |
state.scrollTopReset = document.documentElement.scrollTop; | |
state.scrollLeftReset = document.documentElement.scrollLeft; | |
// Moves the information of the expaned image to the state. | |
ctx.currentSrc = ctx.imageRef.currentSrc; | |
imageRef = ctx.imageRef; | |
buttonRef = ctx.buttonRef; | |
state.currentImage = ctx; | |
state.overlayEnabled = true; | |
// Computes the styles of the overlay for the animation. | |
callbacks.setOverlayStyles(); | |
}, | |
hideLightbox() { | |
if (state.overlayEnabled) { | |
// Waits until the close animation has completed before allowing a | |
// user to scroll again. The duration of this animation is defined in | |
// the `styles.scss` file, but in any case we should wait a few | |
// milliseconds longer than the duration, otherwise a user may scroll | |
// too soon and cause the animation to look sloppy. | |
setTimeout(function () { | |
// Delays before changing the focus. Otherwise the focus ring will | |
// appear on Firefox before the image has finished animating, which | |
// looks broken. | |
buttonRef.focus({ | |
preventScroll: true | |
}); | |
// Resets the current image to mark the overlay as closed. | |
state.currentImage = {}; | |
imageRef = null; | |
buttonRef = null; | |
}, 450); | |
// Starts the overlay closing animation. The showClosingAnimation | |
// class is used to avoid showing it on page load. | |
state.showClosingAnimation = true; | |
state.overlayEnabled = false; | |
} | |
}, | |
handleKeydown(event) { | |
if (state.overlayEnabled) { | |
// Focuses the close button when the user presses the tab key. | |
if (event.key === 'Tab') { | |
event.preventDefault(); | |
const { | |
ref | |
} = (0,interactivity_namespaceObject.getElement)(); | |
ref.querySelector('button').focus(); | |
} | |
// Closes the lightbox when the user presses the escape key. | |
if (event.key === 'Escape') { | |
actions.hideLightbox(); | |
} | |
} | |
}, | |
handleTouchMove(event) { | |
// On mobile devices, prevents triggering the scroll event because | |
// otherwise the page jumps around when it resets the scroll position. | |
// This also means that closing the lightbox requires that a user | |
// perform a simple tap. This may be changed in the future if there is a | |
// better alternative to override or reset the scroll position during | |
// swipe actions. | |
if (state.overlayEnabled) { | |
event.preventDefault(); | |
} | |
}, | |
handleTouchStart() { | |
isTouching = true; | |
}, | |
handleTouchEnd() { | |
// Waits a few milliseconds before resetting to ensure that pinch to | |
// zoom works consistently on mobile devices when the lightbox is open. | |
lastTouchTime = Date.now(); | |
isTouching = false; | |
}, | |
handleScroll() { | |
// Prevents scrolling behaviors that trigger content shift while the | |
// lightbox is open. It would be better to accomplish through CSS alone, | |
// but using overflow: hidden is currently the only way to do so and | |
// that causes a layout to shift and prevents the zoom animation from | |
// working in some cases because it's not possible to account for the | |
// layout shift when doing the animation calculations. Instead, it uses | |
// JavaScript to prevent and reset the scrolling behavior. | |
if (state.overlayOpened) { | |
// Avoids overriding the scroll behavior on mobile devices because | |
// doing so breaks the pinch to zoom functionality, and users should | |
// be able to zoom in further on the high-res image. | |
if (!isTouching && Date.now() - lastTouchTime > 450) { | |
// It doesn't rely on `event.preventDefault()` to prevent scrolling | |
// because the scroll event can't be canceled, so it resets the | |
// position instead. | |
window.scrollTo(state.scrollLeftReset, state.scrollTopReset); | |
} | |
} | |
} | |
}, | |
callbacks: { | |
setOverlayStyles() { | |
if (!imageRef) { | |
return; | |
} | |
let { | |
naturalWidth, | |
naturalHeight, | |
offsetWidth: originalWidth, | |
offsetHeight: originalHeight | |
} = imageRef; | |
let { | |
x: screenPosX, | |
y: screenPosY | |
} = imageRef.getBoundingClientRect(); | |
// Natural ratio of the image clicked to open the lightbox. | |
const naturalRatio = naturalWidth / naturalHeight; | |
// Original ratio of the image clicked to open the lightbox. | |
let originalRatio = originalWidth / originalHeight; | |
// If it has object-fit: contain, recalculates the original sizes | |
// and the screen position without the blank spaces. | |
if (state.currentImage.scaleAttr === 'contain') { | |
if (naturalRatio > originalRatio) { | |
const heightWithoutSpace = originalWidth / naturalRatio; | |
// Recalculates screen position without the top space. | |
screenPosY += (originalHeight - heightWithoutSpace) / 2; | |
originalHeight = heightWithoutSpace; | |
} else { | |
const widthWithoutSpace = originalHeight * naturalRatio; | |
// Recalculates screen position without the left space. | |
screenPosX += (originalWidth - widthWithoutSpace) / 2; | |
originalWidth = widthWithoutSpace; | |
} | |
} | |
originalRatio = originalWidth / originalHeight; | |
// Typically, it uses the image's full-sized dimensions. If those | |
// dimensions have not been set (i.e. an external image with only one | |
// size), the image's dimensions in the lightbox are the same | |
// as those of the image in the content. | |
let imgMaxWidth = parseFloat(state.currentImage.targetWidth !== 'none' ? state.currentImage.targetWidth : naturalWidth); | |
let imgMaxHeight = parseFloat(state.currentImage.targetHeight !== 'none' ? state.currentImage.targetHeight : naturalHeight); | |
// Ratio of the biggest image stored in the database. | |
let imgRatio = imgMaxWidth / imgMaxHeight; | |
let containerMaxWidth = imgMaxWidth; | |
let containerMaxHeight = imgMaxHeight; | |
let containerWidth = imgMaxWidth; | |
let containerHeight = imgMaxHeight; | |
// Checks if the target image has a different ratio than the original | |
// one (thumbnail). Recalculates the width and height. | |
if (naturalRatio.toFixed(2) !== imgRatio.toFixed(2)) { | |
if (naturalRatio > imgRatio) { | |
// If the width is reached before the height, it keeps the maxWidth | |
// and recalculates the height unless the difference between the | |
// maxHeight and the reducedHeight is higher than the maxWidth, | |
// where it keeps the reducedHeight and recalculate the width. | |
const reducedHeight = imgMaxWidth / naturalRatio; | |
if (imgMaxHeight - reducedHeight > imgMaxWidth) { | |
imgMaxHeight = reducedHeight; | |
imgMaxWidth = reducedHeight * naturalRatio; | |
} else { | |
imgMaxHeight = imgMaxWidth / naturalRatio; | |
} | |
} else { | |
// If the height is reached before the width, it keeps the maxHeight | |
// and recalculate the width unlesss the difference between the | |
// maxWidth and the reducedWidth is higher than the maxHeight, where | |
// it keeps the reducedWidth and recalculate the height. | |
const reducedWidth = imgMaxHeight * naturalRatio; | |
if (imgMaxWidth - reducedWidth > imgMaxHeight) { | |
imgMaxWidth = reducedWidth; | |
imgMaxHeight = reducedWidth / naturalRatio; | |
} else { | |
imgMaxWidth = imgMaxHeight * naturalRatio; | |
} | |
} | |
containerWidth = imgMaxWidth; | |
containerHeight = imgMaxHeight; | |
imgRatio = imgMaxWidth / imgMaxHeight; | |
// Calculates the max size of the container. | |
if (originalRatio > imgRatio) { | |
containerMaxWidth = imgMaxWidth; | |
containerMaxHeight = containerMaxWidth / originalRatio; | |
} else { | |
containerMaxHeight = imgMaxHeight; | |
containerMaxWidth = containerMaxHeight * originalRatio; | |
} | |
} | |
// If the image has been pixelated on purpose, it keeps that size. | |
if (originalWidth > containerWidth || originalHeight > containerHeight) { | |
containerWidth = originalWidth; | |
containerHeight = originalHeight; | |
} | |
// Calculates the final lightbox image size and the scale factor. | |
// MaxWidth is either the window container (accounting for padding) or | |
// the image resolution. | |
let horizontalPadding = 0; | |
if (window.innerWidth > 480) { | |
horizontalPadding = 80; | |
} else if (window.innerWidth > 1920) { | |
horizontalPadding = 160; | |
} | |
const verticalPadding = 80; | |
const targetMaxWidth = Math.min(window.innerWidth - horizontalPadding, containerWidth); | |
const targetMaxHeight = Math.min(window.innerHeight - verticalPadding, containerHeight); | |
const targetContainerRatio = targetMaxWidth / targetMaxHeight; | |
if (originalRatio > targetContainerRatio) { | |
// If targetMaxWidth is reached before targetMaxHeight. | |
containerWidth = targetMaxWidth; | |
containerHeight = containerWidth / originalRatio; | |
} else { | |
// If targetMaxHeight is reached before targetMaxWidth. | |
containerHeight = targetMaxHeight; | |
containerWidth = containerHeight * originalRatio; | |
} | |
const containerScale = originalWidth / containerWidth; | |
const lightboxImgWidth = imgMaxWidth * (containerWidth / containerMaxWidth); | |
const lightboxImgHeight = imgMaxHeight * (containerHeight / containerMaxHeight); | |
// As of this writing, using the calculations above will render the | |
// lightbox with a small, erroneous whitespace on the left side of the | |
// image in iOS Safari, perhaps due to an inconsistency in how browsers | |
// handle absolute positioning and CSS transformation. In any case, | |
// adding 1 pixel to the container width and height solves the problem, | |
// though this can be removed if the issue is fixed in the future. | |
state.overlayStyles = ` | |
:root { | |
--wp--lightbox-initial-top-position: ${screenPosY}px; | |
--wp--lightbox-initial-left-position: ${screenPosX}px; | |
--wp--lightbox-container-width: ${containerWidth + 1}px; | |
--wp--lightbox-container-height: ${containerHeight + 1}px; | |
--wp--lightbox-image-width: ${lightboxImgWidth}px; | |
--wp--lightbox-image-height: ${lightboxImgHeight}px; | |
--wp--lightbox-scale: ${containerScale}; | |
--wp--lightbox-scrollbar-width: ${window.innerWidth - document.documentElement.clientWidth}px; | |
} | |
`; | |
}, | |
setButtonStyles() { | |
const ctx = (0,interactivity_namespaceObject.getContext)(); | |
const { | |
ref | |
} = (0,interactivity_namespaceObject.getElement)(); | |
ctx.imageRef = ref; | |
const { | |
naturalWidth, | |
naturalHeight, | |
offsetWidth, | |
offsetHeight | |
} = ref; | |
// If the image isn't loaded yet, it can't calculate where the button | |
// should be. | |
if (naturalWidth === 0 || naturalHeight === 0) { | |
return; | |
} | |
const figure = ref.parentElement; | |
const figureWidth = ref.parentElement.clientWidth; | |
// It needs special handling for the height because a caption will cause | |
// the figure to be taller than the image, which means it needs to | |
// account for that when calculating the placement of the button in the | |
// top right corner of the image. | |
let figureHeight = ref.parentElement.clientHeight; | |
const caption = figure.querySelector('figcaption'); | |
if (caption) { | |
const captionComputedStyle = window.getComputedStyle(caption); | |
if (!['absolute', 'fixed'].includes(captionComputedStyle.position)) { | |
figureHeight = figureHeight - caption.offsetHeight - parseFloat(captionComputedStyle.marginTop) - parseFloat(captionComputedStyle.marginBottom); | |
} | |
} | |
const buttonOffsetTop = figureHeight - offsetHeight; | |
const buttonOffsetRight = figureWidth - offsetWidth; | |
// In the case of an image with object-fit: contain, the size of the | |
// <img> element can be larger than the image itself, so it needs to | |
// calculate where to place the button. | |
if (ctx.scaleAttr === 'contain') { | |
// Natural ratio of the image. | |
const naturalRatio = naturalWidth / naturalHeight; | |
// Offset ratio of the image. | |
const offsetRatio = offsetWidth / offsetHeight; | |
if (naturalRatio >= offsetRatio) { | |
// If it reaches the width first, it keeps the width and compute the | |
// height. | |
const referenceHeight = offsetWidth / naturalRatio; | |
ctx.imageButtonTop = (offsetHeight - referenceHeight) / 2 + buttonOffsetTop + 16; | |
ctx.imageButtonRight = buttonOffsetRight + 16; | |
} else { | |
// If it reaches the height first, it keeps the height and compute | |
// the width. | |
const referenceWidth = offsetHeight * naturalRatio; | |
ctx.imageButtonTop = buttonOffsetTop + 16; | |
ctx.imageButtonRight = (offsetWidth - referenceWidth) / 2 + buttonOffsetRight + 16; | |
} | |
} else { | |
ctx.imageButtonTop = buttonOffsetTop + 16; | |
ctx.imageButtonRight = buttonOffsetRight + 16; | |
} | |
}, | |
setOverlayFocus() { | |
if (state.overlayEnabled) { | |
// Moves the focus to the dialog when it opens. | |
const { | |
ref | |
} = (0,interactivity_namespaceObject.getElement)(); | |
ref.focus(); | |
} | |
}, | |
initTriggerButton() { | |
const ctx = (0,interactivity_namespaceObject.getContext)(); | |
const { | |
ref | |
} = (0,interactivity_namespaceObject.getElement)(); | |
ctx.buttonRef = ref; | |
} | |
} | |
}, { | |
lock: true | |
}); | |