[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 import * as __WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__ from "@wordpress/interactivity"; 2 /******/ // The require scope 3 /******/ var __webpack_require__ = {}; 4 /******/ 5 /************************************************************************/ 6 /******/ /* webpack/runtime/define property getters */ 7 /******/ !function() { 8 /******/ // define getter functions for harmony exports 9 /******/ __webpack_require__.d = function(exports, definition) { 10 /******/ for(var key in definition) { 11 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 12 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 13 /******/ } 14 /******/ } 15 /******/ }; 16 /******/ }(); 17 /******/ 18 /******/ /* webpack/runtime/hasOwnProperty shorthand */ 19 /******/ !function() { 20 /******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } 21 /******/ }(); 22 /******/ 23 /************************************************************************/ 24 var __webpack_exports__ = {}; 25 26 ;// CONCATENATED MODULE: external "@wordpress/interactivity" 27 var x = y => { var x = {}; __webpack_require__.d(x, y); return x; } 28 var y = x => () => x 29 var 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 }); 30 ;// CONCATENATED MODULE: ./node_modules/@wordpress/block-library/build-module/image/view.js 31 /** 32 * WordPress dependencies 33 */ 34 35 const focusableSelectors = ['a[href]', 'area[href]', 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', 'select:not([disabled]):not([aria-hidden])', 'textarea:not([disabled]):not([aria-hidden])', 'button:not([disabled]):not([aria-hidden])', 'iframe', 'object', 'embed', '[contenteditable]', '[tabindex]:not([tabindex^="-"])']; 36 37 /** 38 * Stores a context-bound scroll handler. 39 * 40 * This callback could be defined inline inside of the store 41 * object but it's created externally to avoid confusion about 42 * how its logic is called. This logic is not referenced directly 43 * by the directives in the markup because the scroll event we 44 * need to listen to is triggered on the window; so by defining it 45 * outside of the store, we signal that the behavior here is different. 46 * If we find a compelling reason to move it to the store, feel free. 47 * 48 * @type {Function} 49 */ 50 let scrollCallback; 51 52 /** 53 * Tracks whether user is touching screen; used to 54 * differentiate behavior for touch and mouse input. 55 * 56 * @type {boolean} 57 */ 58 let isTouching = false; 59 60 /** 61 * Tracks the last time the screen was touched; used to 62 * differentiate behavior for touch and mouse input. 63 * 64 * @type {number} 65 */ 66 let lastTouchTime = 0; 67 68 /** 69 * Lightbox page-scroll handler: prevents scrolling. 70 * 71 * This handler is added to prevent scrolling behaviors that 72 * trigger content shift while the lightbox is open. 73 * 74 * It would be better to accomplish this through CSS alone, but 75 * using overflow: hidden is currently the only way to do so, and 76 * that causes the layout to shift and prevents the zoom animation 77 * from working in some cases because we're unable to account for 78 * the layout shift when doing the animation calculations. Instead, 79 * here we use JavaScript to prevent and reset the scrolling 80 * behavior. In the future, we may be able to use CSS or overflow: hidden 81 * instead to not rely on JavaScript, but this seems to be the best approach 82 * for now that provides the best visual experience. 83 * 84 * @param {Object} ctx Context object with the `core/image` namespace. 85 */ 86 function handleScroll(ctx) { 87 // We can't override the scroll behavior on mobile devices 88 // because doing so breaks the pinch to zoom functionality, and we 89 // want to allow users to zoom in further on the high-res image. 90 if (!isTouching && Date.now() - lastTouchTime > 450) { 91 // We are unable to use event.preventDefault() to prevent scrolling 92 // because the scroll event can't be canceled, so we reset the position instead. 93 window.scrollTo(ctx.scrollLeftReset, ctx.scrollTopReset); 94 } 95 } 96 const { 97 state, 98 actions, 99 callbacks 100 } = (0,interactivity_namespaceObject.store)('core/image', { 101 state: { 102 windowWidth: window.innerWidth, 103 windowHeight: window.innerHeight, 104 get roleAttribute() { 105 const ctx = (0,interactivity_namespaceObject.getContext)(); 106 return ctx.lightboxEnabled ? 'dialog' : null; 107 }, 108 get ariaModal() { 109 const ctx = (0,interactivity_namespaceObject.getContext)(); 110 return ctx.lightboxEnabled ? 'true' : null; 111 }, 112 get dialogLabel() { 113 const ctx = (0,interactivity_namespaceObject.getContext)(); 114 return ctx.lightboxEnabled ? ctx.dialogLabel : null; 115 }, 116 get lightboxObjectFit() { 117 const ctx = (0,interactivity_namespaceObject.getContext)(); 118 if (ctx.initialized) { 119 return 'cover'; 120 } 121 }, 122 get enlargedImgSrc() { 123 const ctx = (0,interactivity_namespaceObject.getContext)(); 124 return ctx.initialized ? ctx.imageUploadedSrc : 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; 125 } 126 }, 127 actions: { 128 showLightbox(event) { 129 const ctx = (0,interactivity_namespaceObject.getContext)(); 130 // We can't initialize the lightbox until the reference 131 // image is loaded, otherwise the UX is broken. 132 if (!ctx.imageLoaded) { 133 return; 134 } 135 ctx.initialized = true; 136 ctx.lastFocusedElement = window.document.activeElement; 137 ctx.scrollDelta = 0; 138 ctx.pointerType = event.pointerType; 139 ctx.lightboxEnabled = true; 140 setStyles(ctx, ctx.imageRef); 141 ctx.scrollTopReset = window.pageYOffset || document.documentElement.scrollTop; 142 143 // In most cases, this value will be 0, but this is included 144 // in case a user has created a page with horizontal scrolling. 145 ctx.scrollLeftReset = window.pageXOffset || document.documentElement.scrollLeft; 146 147 // We define and bind the scroll callback here so 148 // that we can pass the context and as an argument. 149 // We may be able to change this in the future if we 150 // define the scroll callback in the store instead, but 151 // this approach seems to tbe clearest for now. 152 scrollCallback = handleScroll.bind(null, ctx); 153 154 // We need to add a scroll event listener to the window 155 // here because we are unable to otherwise access it via 156 // the Interactivity API directives. If we add a native way 157 // to access the window, we can remove this. 158 window.addEventListener('scroll', scrollCallback, false); 159 }, 160 hideLightbox() { 161 const ctx = (0,interactivity_namespaceObject.getContext)(); 162 ctx.hideAnimationEnabled = true; 163 if (ctx.lightboxEnabled) { 164 // We want to wait until the close animation is completed 165 // before allowing a user to scroll again. The duration of this 166 // animation is defined in the styles.scss and depends on if the 167 // animation is 'zoom' or 'fade', but in any case we should wait 168 // a few milliseconds longer than the duration, otherwise a user 169 // may scroll too soon and cause the animation to look sloppy. 170 setTimeout(function () { 171 window.removeEventListener('scroll', scrollCallback); 172 // If we don't delay before changing the focus, 173 // the focus ring will appear on Firefox before 174 // the image has finished animating, which looks broken. 175 ctx.lightboxTriggerRef.focus({ 176 preventScroll: true 177 }); 178 }, 450); 179 ctx.lightboxEnabled = false; 180 } 181 }, 182 handleKeydown(event) { 183 const ctx = (0,interactivity_namespaceObject.getContext)(); 184 if (ctx.lightboxEnabled) { 185 if (event.key === 'Tab' || event.keyCode === 9) { 186 // If shift + tab it change the direction 187 if (event.shiftKey && window.document.activeElement === ctx.firstFocusableElement) { 188 event.preventDefault(); 189 ctx.lastFocusableElement.focus(); 190 } else if (!event.shiftKey && window.document.activeElement === ctx.lastFocusableElement) { 191 event.preventDefault(); 192 ctx.firstFocusableElement.focus(); 193 } 194 } 195 if (event.key === 'Escape' || event.keyCode === 27) { 196 actions.hideLightbox(event); 197 } 198 } 199 }, 200 // This is fired just by lazily loaded 201 // images on the page, not all images. 202 handleLoad() { 203 const ctx = (0,interactivity_namespaceObject.getContext)(); 204 const { 205 ref 206 } = (0,interactivity_namespaceObject.getElement)(); 207 ctx.imageLoaded = true; 208 ctx.imageCurrentSrc = ref.currentSrc; 209 callbacks.setButtonStyles(); 210 }, 211 handleTouchStart() { 212 isTouching = true; 213 }, 214 handleTouchMove(event) { 215 const ctx = (0,interactivity_namespaceObject.getContext)(); 216 // On mobile devices, we want to prevent triggering the 217 // scroll event because otherwise the page jumps around as 218 // we reset the scroll position. This also means that closing 219 // the lightbox requires that a user perform a simple tap. This 220 // may be changed in the future if we find a better alternative 221 // to override or reset the scroll position during swipe actions. 222 if (ctx.lightboxEnabled) { 223 event.preventDefault(); 224 } 225 }, 226 handleTouchEnd() { 227 // We need to wait a few milliseconds before resetting 228 // to ensure that pinch to zoom works consistently 229 // on mobile devices when the lightbox is open. 230 lastTouchTime = Date.now(); 231 isTouching = false; 232 } 233 }, 234 callbacks: { 235 initOriginImage() { 236 const ctx = (0,interactivity_namespaceObject.getContext)(); 237 const { 238 ref 239 } = (0,interactivity_namespaceObject.getElement)(); 240 ctx.imageRef = ref; 241 if (ref.complete) { 242 ctx.imageLoaded = true; 243 ctx.imageCurrentSrc = ref.currentSrc; 244 } 245 }, 246 initTriggerButton() { 247 const ctx = (0,interactivity_namespaceObject.getContext)(); 248 const { 249 ref 250 } = (0,interactivity_namespaceObject.getElement)(); 251 ctx.lightboxTriggerRef = ref; 252 }, 253 initLightbox() { 254 const ctx = (0,interactivity_namespaceObject.getContext)(); 255 const { 256 ref 257 } = (0,interactivity_namespaceObject.getElement)(); 258 if (ctx.lightboxEnabled) { 259 const focusableElements = ref.querySelectorAll(focusableSelectors); 260 ctx.firstFocusableElement = focusableElements[0]; 261 ctx.lastFocusableElement = focusableElements[focusableElements.length - 1]; 262 263 // Move focus to the dialog when opening it. 264 ref.focus(); 265 } 266 }, 267 setButtonStyles() { 268 const { 269 ref 270 } = (0,interactivity_namespaceObject.getElement)(); 271 const { 272 naturalWidth, 273 naturalHeight, 274 offsetWidth, 275 offsetHeight 276 } = ref; 277 278 // If the image isn't loaded yet, we can't 279 // calculate where the button should be. 280 if (naturalWidth === 0 || naturalHeight === 0) { 281 return; 282 } 283 const figure = ref.parentElement; 284 const figureWidth = ref.parentElement.clientWidth; 285 286 // We need special handling for the height because 287 // a caption will cause the figure to be taller than 288 // the image, which means we need to account for that 289 // when calculating the placement of the button in the 290 // top right corner of the image. 291 let figureHeight = ref.parentElement.clientHeight; 292 const caption = figure.querySelector('figcaption'); 293 if (caption) { 294 const captionComputedStyle = window.getComputedStyle(caption); 295 if (!['absolute', 'fixed'].includes(captionComputedStyle.position)) { 296 figureHeight = figureHeight - caption.offsetHeight - parseFloat(captionComputedStyle.marginTop) - parseFloat(captionComputedStyle.marginBottom); 297 } 298 } 299 const buttonOffsetTop = figureHeight - offsetHeight; 300 const buttonOffsetRight = figureWidth - offsetWidth; 301 const ctx = (0,interactivity_namespaceObject.getContext)(); 302 303 // In the case of an image with object-fit: contain, the 304 // size of the <img> element can be larger than the image itself, 305 // so we need to calculate where to place the button. 306 if (ctx.scaleAttr === 'contain') { 307 // Natural ratio of the image. 308 const naturalRatio = naturalWidth / naturalHeight; 309 // Offset ratio of the image. 310 const offsetRatio = offsetWidth / offsetHeight; 311 if (naturalRatio >= offsetRatio) { 312 // If it reaches the width first, keep 313 // the width and compute the height. 314 const referenceHeight = offsetWidth / naturalRatio; 315 ctx.imageButtonTop = (offsetHeight - referenceHeight) / 2 + buttonOffsetTop + 16; 316 ctx.imageButtonRight = buttonOffsetRight + 16; 317 } else { 318 // If it reaches the height first, keep 319 // the height and compute the width. 320 const referenceWidth = offsetHeight * naturalRatio; 321 ctx.imageButtonTop = buttonOffsetTop + 16; 322 ctx.imageButtonRight = (offsetWidth - referenceWidth) / 2 + buttonOffsetRight + 16; 323 } 324 } else { 325 ctx.imageButtonTop = buttonOffsetTop + 16; 326 ctx.imageButtonRight = buttonOffsetRight + 16; 327 } 328 }, 329 setStylesOnResize() { 330 const ctx = (0,interactivity_namespaceObject.getContext)(); 331 const { 332 ref 333 } = (0,interactivity_namespaceObject.getElement)(); 334 if (ctx.lightboxEnabled && (state.windowWidth || state.windowHeight)) { 335 setStyles(ctx, ref); 336 } 337 } 338 } 339 }); 340 window.addEventListener('resize', debounce(() => { 341 state.windowWidth = window.innerWidth; 342 state.windowHeight = window.innerHeight; 343 })); 344 345 /** 346 * Computes styles for the lightbox and adds them to the document. 347 * 348 * @function 349 * @param {Object} ctx - Context for the `core/image` namespace. 350 * @param {Object} ref - The element reference. 351 */ 352 function setStyles(ctx, ref) { 353 // The reference img element lies adjacent 354 // to the event target button in the DOM. 355 let { 356 naturalWidth, 357 naturalHeight, 358 offsetWidth: originalWidth, 359 offsetHeight: originalHeight 360 } = ref; 361 let { 362 x: screenPosX, 363 y: screenPosY 364 } = ref.getBoundingClientRect(); 365 366 // Natural ratio of the image clicked to open the lightbox. 367 const naturalRatio = naturalWidth / naturalHeight; 368 // Original ratio of the image clicked to open the lightbox. 369 let originalRatio = originalWidth / originalHeight; 370 371 // If it has object-fit: contain, recalculate the original sizes 372 // and the screen position without the blank spaces. 373 if (ctx.scaleAttr === 'contain') { 374 if (naturalRatio > originalRatio) { 375 const heightWithoutSpace = originalWidth / naturalRatio; 376 // Recalculate screen position without the top space. 377 screenPosY += (originalHeight - heightWithoutSpace) / 2; 378 originalHeight = heightWithoutSpace; 379 } else { 380 const widthWithoutSpace = originalHeight * naturalRatio; 381 // Recalculate screen position without the left space. 382 screenPosX += (originalWidth - widthWithoutSpace) / 2; 383 originalWidth = widthWithoutSpace; 384 } 385 } 386 originalRatio = originalWidth / originalHeight; 387 388 // Typically, we use the image's full-sized dimensions. If those 389 // dimensions have not been set (i.e. an external image with only one size), 390 // the image's dimensions in the lightbox are the same 391 // as those of the image in the content. 392 let imgMaxWidth = parseFloat(ctx.targetWidth !== 'none' ? ctx.targetWidth : naturalWidth); 393 let imgMaxHeight = parseFloat(ctx.targetHeight !== 'none' ? ctx.targetHeight : naturalHeight); 394 395 // Ratio of the biggest image stored in the database. 396 let imgRatio = imgMaxWidth / imgMaxHeight; 397 let containerMaxWidth = imgMaxWidth; 398 let containerMaxHeight = imgMaxHeight; 399 let containerWidth = imgMaxWidth; 400 let containerHeight = imgMaxHeight; 401 // Check if the target image has a different ratio than the original one (thumbnail). 402 // Recalculate the width and height. 403 if (naturalRatio.toFixed(2) !== imgRatio.toFixed(2)) { 404 if (naturalRatio > imgRatio) { 405 // If the width is reached before the height, we keep the maxWidth 406 // and recalculate the height. 407 // Unless the difference between the maxHeight and the reducedHeight 408 // is higher than the maxWidth, where we keep the reducedHeight and 409 // recalculate the width. 410 const reducedHeight = imgMaxWidth / naturalRatio; 411 if (imgMaxHeight - reducedHeight > imgMaxWidth) { 412 imgMaxHeight = reducedHeight; 413 imgMaxWidth = reducedHeight * naturalRatio; 414 } else { 415 imgMaxHeight = imgMaxWidth / naturalRatio; 416 } 417 } else { 418 // If the height is reached before the width, we keep the maxHeight 419 // and recalculate the width. 420 // Unless the difference between the maxWidth and the reducedWidth 421 // is higher than the maxHeight, where we keep the reducedWidth and 422 // recalculate the height. 423 const reducedWidth = imgMaxHeight * naturalRatio; 424 if (imgMaxWidth - reducedWidth > imgMaxHeight) { 425 imgMaxWidth = reducedWidth; 426 imgMaxHeight = reducedWidth / naturalRatio; 427 } else { 428 imgMaxWidth = imgMaxHeight * naturalRatio; 429 } 430 } 431 containerWidth = imgMaxWidth; 432 containerHeight = imgMaxHeight; 433 imgRatio = imgMaxWidth / imgMaxHeight; 434 435 // Calculate the max size of the container. 436 if (originalRatio > imgRatio) { 437 containerMaxWidth = imgMaxWidth; 438 containerMaxHeight = containerMaxWidth / originalRatio; 439 } else { 440 containerMaxHeight = imgMaxHeight; 441 containerMaxWidth = containerMaxHeight * originalRatio; 442 } 443 } 444 445 // If the image has been pixelated on purpose, keep that size. 446 if (originalWidth > containerWidth || originalHeight > containerHeight) { 447 containerWidth = originalWidth; 448 containerHeight = originalHeight; 449 } 450 451 // Calculate the final lightbox image size and the 452 // scale factor. MaxWidth is either the window container 453 // (accounting for padding) or the image resolution. 454 let horizontalPadding = 0; 455 if (window.innerWidth > 480) { 456 horizontalPadding = 80; 457 } else if (window.innerWidth > 1920) { 458 horizontalPadding = 160; 459 } 460 const verticalPadding = 80; 461 const targetMaxWidth = Math.min(window.innerWidth - horizontalPadding, containerWidth); 462 const targetMaxHeight = Math.min(window.innerHeight - verticalPadding, containerHeight); 463 const targetContainerRatio = targetMaxWidth / targetMaxHeight; 464 if (originalRatio > targetContainerRatio) { 465 // If targetMaxWidth is reached before targetMaxHeight 466 containerWidth = targetMaxWidth; 467 containerHeight = containerWidth / originalRatio; 468 } else { 469 // If targetMaxHeight is reached before targetMaxWidth 470 containerHeight = targetMaxHeight; 471 containerWidth = containerHeight * originalRatio; 472 } 473 const containerScale = originalWidth / containerWidth; 474 const lightboxImgWidth = imgMaxWidth * (containerWidth / containerMaxWidth); 475 const lightboxImgHeight = imgMaxHeight * (containerHeight / containerMaxHeight); 476 477 // Add the CSS variables needed. 478 let styleTag = document.getElementById('wp-lightbox-styles'); 479 if (!styleTag) { 480 styleTag = document.createElement('style'); 481 styleTag.id = 'wp-lightbox-styles'; 482 document.head.appendChild(styleTag); 483 } 484 485 // As of this writing, using the calculations above will render the lightbox 486 // with a small, erroneous whitespace on the left side of the image in iOS Safari, 487 // perhaps due to an inconsistency in how browsers handle absolute positioning and CSS 488 // transformation. In any case, adding 1 pixel to the container width and height solves 489 // the problem, though this can be removed if the issue is fixed in the future. 490 styleTag.innerHTML = ` 491 :root { 492 --wp--lightbox-initial-top-position: $screenPosY}px; 493 --wp--lightbox-initial-left-position: $screenPosX}px; 494 --wp--lightbox-container-width: $containerWidth + 1}px; 495 --wp--lightbox-container-height: $containerHeight + 1}px; 496 --wp--lightbox-image-width: $lightboxImgWidth}px; 497 --wp--lightbox-image-height: $lightboxImgHeight}px; 498 --wp--lightbox-scale: $containerScale}; 499 --wp--lightbox-scrollbar-width: $window.innerWidth - document.documentElement.clientWidth}px; 500 } 501 `; 502 } 503 504 /** 505 * Debounces a function call. 506 * 507 * @function 508 * @param {Function} func - A function to be called 509 * @param {number} wait - The time to wait before calling the function 510 */ 511 function debounce(func, wait = 50) { 512 let timeout; 513 return () => { 514 const later = () => { 515 timeout = null; 516 func(); 517 }; 518 clearTimeout(timeout); 519 timeout = setTimeout(later, wait); 520 }; 521 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Wed Jan 31 08:20:02 2024 | Cross-referenced by PHPXref |