https://www.smilesolutions.com.au/wp-content/plugins/block-library…/view.js?ver=6bfc07b95ed4cfa6f245
vendor: 112 bytes, lines 1-5
1/******/ (() => { // webpackBootstrap 2/******/ "use strict"; 3/******/ var __webpack_modules__ = ({ 4 5/***/ "./s
5rc/common/animations/parrallaxImages.js": 6/*!**************************************************!*\ 7 !*** ./src/common/animations/parrallaxImages.js ***! 8 \**************************************************/ 9/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 10 11__webpack_require__.r(__webpack_exports__); 12/* harmony export */ __webpack_require__.d(__webpack_exports__, { 13/* harmony export */ parallaxImages: () => (/* binding */ parallaxImages) 14/* harmony export */ }); 15// Add parallax effect to images 16const parallaxImages = wrapper => { 17 wrapper.forEach(wrapper => { 18 const images = wrapper.querySelectorAll("img"); 19 if (images.length > 0) { 20 images.forEach(img => { 21 scrollSmoother.effects(img, { 22 speed: "auto" 23 }); 24 }); 25 } 26 }); 27}; 28 29/***/ })
29 30 31/******/ }); 32/************************************************************************/ 33/******/ // The module cache 34/******/ var __webpack_module_cache__ = {}; 35/******/ 36/******/ // The require function 37/******/ function __webpack_require__(moduleId) { 38/******/ // Check if module is in cache 39/******/ var cachedModule = __webpack_module_cache__[moduleId]; 40/******/ if (cachedModule !== undefined) { 41/******/ return cachedModule.exports; 42/******/ } 43/******/ // Create a new module (and put it into the cache) 44/******/ var module = __webpack_module_cache__[moduleId] = { 45/******/ // no module.id needed 46/******/ // no module.loaded needed 47/******/ exports: {} 48/******/ }; 49/******/ 50/******/ // Execute the module function 51/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 52/******/ 53/******/ // Return the exports of the module 54/******/ return module.exports; 55/******/ } 56/******/ 57/************************************************************************/ 58/******/ /* webpack/runtime/define property getters */ 59/******/ (() => { 60/******/ // define getter functions for harmony exports 61/******/ __webpack_require__.d = (exports, definition) => { 62/******/ for(var key in definition) { 63/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 64/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 65/******/ } 66/******/ } 67/******/ }; 68/******/ })(); 69/******/ 70/******/ /* webpack/runtime/hasOwnProperty shorthand */ 71/******/ (() => { 72/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 73/******/ })(); 74/******/ 75/******/ /* webpack/runtime/make namespace object */ 76/******/ (() => { 77/******/ // define __esModule on exports 78/******/ __webpack_require__.r = (exports) => { 79/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 80/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 81/******/ } 82/******/ Object.defineProperty(exports, '__esModule', { value: true }); 83/******/ }; 84/******/ })(); 85/******/ 86/************************************************************************/ 87var __webpack_exports__ = {}; 88// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. 89(() => { 90/*!*************************************************!*\ 91 !*** ./src/staggered-media-and-content/view.js ***! 92 \*************************************************/ 93__webpack_require__.r(__webpack_exports__); 94/* harmony import */ var _common_animations_parrallaxImages_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/animations/parrallaxImages.js */ "./src/common/animations/parrallaxImages.js"); 95/** 96 * Use this file for JavaScript code that you want to run in the front-end 97 * on posts/pages that contain this block. 98 * 99 * When this file is defined as the value of the `viewScript` property 100 * in `block.json` it will be enqueued on the front end of the site. 101 * 102 * Example: 103 * 104 * ```js 105 * { 106 * "viewScript": "file:./view.js" 107 * } 108 * ``` 109 * 110 * If you're not making any changes to this file because your project doesn't need any 111 * JavaScript running in the front-end, then you should delete this file and remove 112 * the `viewScript` property from `block.json`. 113 * 114 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script 115 */ 116 117 118function ensureYoutubeAPIReady() { 119 return new Promise((resolve, reject) => { 120 if (window.YT && window.YT.Player) { 121 return resolve(); 122 } 123 let otherYoutubeReadyCb = null; 124 if (typeof window.onYouTubeIframeAPIReady !== "undefined") { 125 otherYoutubeReadyCb = window.onYouTubeIframeAPIReady; 126 } 127 window.onYouTubeIframeAPIReady = () => { 128 otherYoutubeReadyCb && otherYoutubeReadyCb(); 129 resolve(); 130 }; 131 let script = document.createElement("script"); 132 script.id = "youtube-iframe-script"; 133 script.src = "https://www.youtube.com/iframe_api"; 134 let firstScriptTag = document.getElementsByTagName("script")[0]; 135 firstScriptTag.parentNode.insertBefore(script, firstScriptTag); 136 }); 137} 138function ensureVimeoAPIReady() { 139 return new Promise((resolve, reject) => { 140 if (window.Vimeo && window.Vimeo.Player) { 141 return resolve(); 142 } 143 let script = document.createElement("script"); 144 script.id = "vimeo-iframe-script"; 145 script.src = "https://player.vimeo.com/api/player.js"; 146 147 // Set up onload event handler 148 script.onload = () => { 149 // Check if the necessary objects are now available 150 if (window.Vimeo && window.Vimeo.Player) { 151 resolve(); 152 } else { 153 reject(new Error("Vimeo API script loaded, but Vimeo or Vimeo.Player is still not available")); 154 } 155 }; 156 157 // Set up onerror event handler 158 script.onerror = () => { 159 reject(new Error("Error loading Vimeo API script")); 160 }; 161 let firstScriptTag = document.getElementsByTagName("script")[0]; 162 firstScriptTag.parentNode.insertBefore(script, firstScriptTag); 163 }); 164} 165const removePosterAndOverlay = video => { 166 const poster = video.querySelector(".staggered-media-and-content-video__poster");
167 const overlay = video.querySelector(".staggered-media-and-content-video__overlay"); 168 if (poster) { 169 poster.style.opacity = 0; 170 poster.style.pointerEvents = "none"; 171 } 172 if (overlay) { 173 overlay.style.opacity = 0; 174 overlay.style.pointerEvents = "none"; 175 } 176}; 177const enterKeyPressPlay = (video, play) => { 178 const playButton = video.querySelector(".staggered-media-and-content-video__overlay-icon"); 179 if (playButton) { 180 playButton.addEventListener("keydown", e => { 181 if (e.key === "Enter") { 182 removePosterAndOverlay(video); 183 play(); 184 } 185 }); 186 } 187}; 188const handleIframeVideos = () => { 189 const videoCollection = document.getElementsByClassName("staggered-media-and-content-video__stacking-grid"); 190 if (videoCollection) { 191 const videoArray = Array.from(videoCollection); 192 ensureYoutubeAPIReady().then(() => { 193 for (let i = 0; i < videoArray.length; i++) { 194 const videoItem = videoArray[i]; 195 // Check if the current videoItem has the class 'source-video-youtube' 196 if (videoItem.dataset.videoType === "video-youtube") { 197 // If the class is found, run the function and stop the loop 198 videoArray.forEach((video, i) => { 199 // events if youtube video is loaded 200 const frame = video.getElementsByTagName("iframe"); 201 if (frame.length) { 202 if (video.dataset.videoType === "video-youtube") { 203 frame[0].style.pointerEvents = "none"; 204 let playAfterReady = false; 205 let isPlayerReady = false; 206 const player = new YT.Player(frame[0], { 207 events: { 208 onReady: () => { 209 isPlayerReady = true; 210 if (playAfterReady) { 211 player.playVideo(); 212 } 213 }, 214 onStateChange: e => { 215 if (e.target.getPlayerState() === 1) { 216 frame[0].style.visibility = "visible"; 217 frame[0].style.pointerEvents = "auto"; 218 removePosterAndOverlay(video); 219 } 220 if (video.dataset.loop === "true" && e.target.getPlayerState() === 0) { 221 player.playVideo(); 222 } 223 } 224 } 225 }); 226 function handlePlay() { 227 if (!isPlayerReady) { 228 playAfterReady = true; 229 } else { 230 player.playVideo(); 231 } 232 } 233 enterKeyPressPlay(video, handlePlay); 234 if (video.dataset.autoplay === "true") { 235 handlePlay(); 236 } 237 video.addEventListener("click", () => { 238 handlePlay(); 239 }); 240 } 241 } 242 }); 243 break; 244 } 245 } 246 }); 247 for (let i = 0; i < videoArray.length; i++) { 248 const videoItem = videoArray[i]; 249 // Check if the current videoItem has the class 'source-video-vimeo' 250 if (videoItem.dataset.videoType === "video-vimeo") { 251 // If the class is found, run the function and stop the loop 252 ensureVimeoAPIReady().then(() => { 253 videoArray.forEach((video, i) => { 254 // events if Vimeo video is loaded 255 const frame = video.getElementsByTagName("iframe"); 256 if (frame.length) { 257 if (video.dataset.videoType === "video-vimeo") { 258 frame[0].onload = function () { 259 ensureVimeoAPIReady().then(() => { 260 const player = new Vimeo.Player(frame[0]); 261 const play = () => { 262 player.play(); 263 }; 264 const poster = video.querySelector(".staggered-media-and-content-video__poster"); 265 const overlay = video.querySelector(".staggered-media-and-content-video__overlay"); 266 if (poster) { 267 poster.addEventListener("click", () => { 268 play(); 269 }); 270 } 271 if (overlay) { 272 overlay.addEventListener("click", () => { 273 play(); 274 }); 275 } 276 enterKeyPressPlay(video, play); 277 player.on("play", () => { 278 removePosterAndOverlay(video); 279 }); 280 }); 281 }; 282 } 283 } 284 }); 285 }); 286 break; 287 } 288 } 289 } 290}; 291const handleLocalVideos = () => { 292 const fullWidthVideoBlocks = document.querySelectorAll(".wp-block-block-builder-by-arc-staggered-media-and-content"); 293 fullWidthVideoBlocks.forEach(block => { 294 const video = block.querySelector(".staggered-media-and-content__local-video"); 295 const playButton = block.querySelector(".staggered-media-and-content-video__overlay"); 296 const stackingGrid = block.querySelector(".staggered-media-and-content-video__stacking-grid"); 297 if (video) { 298 const play = () => { 299 video.play(); 300 }; 301 const poster = block.querySelector(".staggered-media-and-content-video__poster");
302 const overlay = block.querySelector(".staggered-media-and-content-video__overlay"); 303 if (poster) { 304 if (stackingGrid.dataset.controls === "true") { 305 video.controls = false; 306 } 307 poster.addEventListener("click", () => { 308 if (stackingGrid.dataset.controls === "true") { 309 video.controls = true; 310 } 311 play(); 312 }); 313 } 314 if (overlay) { 315 overlay.addEventListener("click", () => { 316 if (stackingGrid.dataset.controls === "true") { 317 video.controls = true; 318 } 319 play(); 320 }); 321 } 322 if (playButton) { 323 playButton.addEventListener("keydown", e => { 324 if (e.key === "Enter") { 325 if (stackingGrid.dataset.controls === "true") { 326 video.controls = true; 327 } 328 removePosterAndOverlay(video); 329 play(); 330 } 331 }); 332 } 333 video.addEventListener("playing", function () { 334 removePosterAndOverlay(block); 335 }); 336 } 337 }); 338}; 339 340// Position caption absolutely at bottom of image using Y position 341const positionCaptions = () => { 342 const imageContainers = document.querySelectorAll(".staggered-media-and-content__image"); 343 const TABLET_MAX = 1025; // Match $tablet-max breakpoint 344 345 const updateCaptionPosition = (imageContainer, isInitial = false) => { 346 // Find caption as child of image container 347 const caption = imageContainer.querySelector(".staggered-media-and-content__caption"); 348 const img = imageContainer.querySelector("img"); 349 if (caption && img) { 350 const isMobile = window.innerWidth < TABLET_MAX; 351 if (isMobile) { 352 // For mobile/tablet: move caption outside image container to avoid overflow hidden 353 const columnOne = imageContainer.closest(".staggered-media-and-content__column-one"); 354 if (!columnOne) return; 355 356 // Check if caption is already moved outside 357 if (caption.parentElement === imageContainer) { 358 // Move caption to be sibling of image container 359 imageContainer.parentNode.insertBefore(caption, imageContainer.nextSibling); 360 } 361 362 // Set column-one to relative positioning if not already 363 columnOne.style.position = "relative"; 364 365 // Calculate image container bottom position relative to column-one 366 const imageContainerRect = imageContainer.getBoundingClientRect(); 367 const columnRect = columnOne.getBoundingClientRect(); 368 const imageContainerBottom = imageContainerRect.bottom - columnRect.top; 369 caption.style.position = "absolute"; 370 caption.style.top = `${imageContainerBottom + 20}px`; 371 caption.style.left = "0"; 372 caption.style.width = "100%"; 373 caption.style.minHeight = "fit-content"; 374 375 // Update column-one height to account for caption 376 setTimeout(() => { 377 const captionRect = caption.getBoundingClientRect(); 378 const updatedColumnRect = columnOne.getBoundingClientRect(); 379 const captionBottom = captionRect.bottom - updatedColumnRect.top; 380 const currentColumnHeight = columnOne.offsetHeight; 381 if (captionBottom > currentColumnHeight) { 382 columnOne.style.minHeight = `${captionBottom}px`; 383 } 384 }, 0); 385 } else { 386 // For desktop: keep caption inside image container 387 // Move caption back inside if it was moved out 388 const columnOneDesktop = imageContainer.closest(".staggered-media-and-content__column-one"); 389 if (columnOneDesktop && caption.parentElement !== imageContainer) { 390 imageContainer.appendChild(caption); 391 } 392 393 // Set image container to relative positioning if not already 394 imageContainer.style.position = "relative"; 395 396 // Calculate image element's bottom position relative to image container 397 const imageRect = img.getBoundingClientRect(); 398 const containerRect = imageContainer.getBoundingClientRect(); 399 const imageBottomY = imageRect.bottom - containerRect.top; 400 caption.style.position = "absolute"; 401 caption.style.top = `${imageBottomY + 20}px`; 402 caption.style.left = "0"; 403 caption.style.width = "100%"; 404 caption.style.height = "fit-content"; 405 406 // Update column-one height to account for caption on desktop 407 if (columnOneDesktop) { 408 setTimeout(() => { 409 const captionRect = caption.getBoundingClientRect(); 410 const columnRect = columnOneDesktop.getBoundingClientRect(); 411 const captionBottom = captionRect.bottom - columnRect.top; 412 const currentColumnHeight = columnOneDesktop.offsetHeight; 413 if (captionBottom > currentColumnHeight) { 414 columnOneDesktop.style.minHeight = `${captionBottom}px`; 415 } 416 }, 0); 417 } 418 } 419 420 // Fade in up animation after position is calculated 421 if (isInitial && typeof gsap !== "undefined" && typeof ScrollTrigger !== "undefined" && !caption.dataset.animated) { 422 // Set initial state 423 gsap.set(caption, { 424 opacity: 0, 425 y: 30 426 }); 427 428 // Create fade-in-up animation with ScrollTrigger 429 var tl = gsap.timeline({ 430 scrollTrigger: { 431 trigger: caption, 432 start: "top 99%", 433 end: "bottom 10%", 434 toggleActions: "play none none none" 435 } 436 }); 437 tl.to(caption, { 438 duration: 1, 439 opacity: 1, 440 y: 0, 441 ease: "power2.out" 442 }); 443 444 // Refresh ScrollTrigger after positioning is complete (longer delay for mobile DOM manipulation) 445 setTimeout(() => { 446 if (typeof ScrollTrigger !== "undefined") { 447 ScrollTrigger.refresh(); 448 } 449 }, isMobile ? 200 : 100); 450 451 // Mark as animated to prevent re-animating 452 caption.dataset.animated = "true"; 453 } 454 } 455 }; 456 457 // Initial positioning with fade-in animation
458 imageContainers.forEach(imageContainer => { 459 updateCaptionPosition(imageContainer, true); 460 }); 461 462 // Update on scroll and resize 463 const updateAllCaptions = () => { 464 imageContainers.forEach(imageContainer => { 465 updateCaptionPosition(imageContainer); 466 }); 467 }; 468 469 // Use ScrollTrigger to update on scroll 470 if (typeof ScrollTrigger !== "undefined") { 471 ScrollTrigger.addEventListener("refresh", updateAllCaptions); 472 } 473 474 // Also listen to window resize 475 window.addEventListener("resize", updateAllCaptions); 476 477 // Use requestAnimationFrame for smooth constant updates during scroll 478 let ticking = false; 479 const onScroll = () => { 480 if (!ticking) { 481 window.requestAnimationFrame(() => { 482 updateAllCaptions(); 483 ticking = false; 484 }); 485 ticking = true; 486 } 487 }; 488 window.addEventListener("scroll", onScroll, { 489 passive: true 490 }); 491 492 // Also update constantly using requestAnimationFrame loop 493 const constantUpdate = () => { 494 updateAllCaptions(); 495 requestAnimationFrame(constantUpdate); 496 }; 497 requestAnimationFrame(constantUpdate); 498}; 499 500// initialize gsap animations 501const gsapMediaAndContentAnimations = () => { 502 const wrappers = document.querySelectorAll(".wp-block-create-block-staggered-media-and-content"); 503 const stickyImage = wrapper => { 504 const image = wrapper.querySelector(".staggered-media-and-content__image"); 505 const video = wrapper.querySelector(".staggered-media-and-content__video"); 506 const backgroundImage = wrapper.querySelector(".staggered-media-and-content__background-image"); 507 if (wrapper && (image || video || backgroundImage)) { 508 // Create ScrollTrigger instance to pin the image 509 ScrollTrigger.create({ 510 trigger: wrapper, 511 start: "top top", 512 end: "bottom bottom", 513 pin: image || video, 514 pinSpacing: false, 515 onUpdate: () => { 516 // Update caption position when scroll trigger updates using Y position 517 const imageContainer = wrapper.querySelector(".staggered-media-and-content__image"); 518 if (imageContainer) { 519 const columnOne = imageContainer.closest(".staggered-media-and-content__column-one"); 520 if (!columnOne) return; 521 522 // Find caption - could be inside image container or as sibling on mobile 523 const caption = imageContainer.querySelector(".staggered-media-and-content__caption") || columnOne.querySelector(".staggered-media-and-content__caption"); 524 const img = imageContainer.querySelector("img"); 525 if (caption && img) { 526 const TABLET_MAX = 1025; // Match $tablet-max breakpoint 527 const isMobile = window.innerWidth < TABLET_MAX; 528 if (isMobile) { 529 // For mobile/tablet: position based on image container relative to column-one 530 const imageContainerRect = imageContainer.getBoundingClientRect(); 531 const columnRect = columnOne.getBoundingClientRect(); 532 const imageContainerBottom = imageContainerRect.bottom - columnRect.top; 533 const imageHeight = imageContainerRect.height; 534 535 // Position at image bottom - 40% of image height 536 const topPosition = imageContainerBottom - imageHeight * 0.4; 537 caption.style.top = `${topPosition}px`; 538 caption.style.minHeight = "fit-content"; 539 540 // Update column-one height to account for caption 541 setTimeout(() => { 542 const captionRect = caption.getBoundingClientRect(); 543 const updatedColumnRect = columnOne.getBoundingClientRect(); 544 const captionBottom = captionRect.bottom - updatedColumnRect.top; 545 const currentColumnHeight = columnOne.offsetHeight; 546 if (captionBottom > currentColumnHeight) { 547 columnOne.style.minHeight = `${captionBottom}px`; 548 } 549 }, 0); 550 } else { 551 // For desktop: position at image bottom Y + 20px 552 const imageRect = img.getBoundingClientRect(); 553 const containerRect = imageContainer.getBoundingClientRect(); 554 const imageBottomY = imageRect.bottom - containerRect.top; 555 caption.style.top = `${imageBottomY + 20}px`; 556 557 // Update column-one height to account for caption 558 setTimeout(() => { 559 const captionRect = caption.getBoundingClientRect(); 560 const updatedColumnRect = columnOne.getBoundingClientRect(); 561 const captionBottom = captionRect.bottom - updatedColumnRect.top; 562 const currentColumnHeight = columnOne.offsetHeight; 563 if (captionBottom > currentColumnHeight) { 564 columnOne.style.minHeight = `${captionBottom}px`; 565 } 566 }, 0); 567 } 568 } 569 } 570 } 571 }); 572 573 // Add parallax scrolling effect to background image 574 if (backgroundImage) { 575 gsap.set(backgroundImage, { 576 y: "40%" 577 }); 578 gsap.to(backgroundImage, { 579 y: "-10%", 580 ease: "none", 581 scrollTrigger: { 582 trigger: wrapper, 583 start: "top 100%", 584 end: "bottom 105%", 585 scrub: true 586 } 587 }); 588 } 589 } 590 }; 591 592 // media query to prevent animations from running on wrong different screen sizes 593 let mm = gsap.matchMedia(); 594 const breakPoint = 1025; 595 596 // Desktop: sticky image animation 597 mm.add({ 598 isDesktop: `(min-width: ${breakPoint}px)`, 599 isMobile: `(max-width: ${breakPoint - 1}px)` 600 }
600, context => { 601 if (context.conditions.isDesktop) { 602 wrappers.forEach(wrapper => { 603 stickyImage(wrapper); 604 }); 605 } else if (context.conditions.isMobile) { 606 (0,_common_animations_parrallaxImages_js__WEBPACK_IMPORTED_MODULE_0__.parallaxImages)(wrappers); 607 } 608 }); 609}; 610 611// Main initialization function 612const initStaggeredMediaAndContent = () => { 613 // Run all initialization functions in correct order 614 handleIframeVideos(); 615 handleLocalVideos(); 616 positionCaptions(); // Run BEFORE GSAP animations 617 gsapMediaAndContentAnimations(); 618}; 619 620// Initialize based on document ready state 621if (document.readyState === "loading") { 622 document.addEventListener("DOMContentLoaded", initStaggeredMediaAndContent); 623} else { 624 // Document already loaded, initialize immediately with slight delay 625 setTimeout(initStaggeredMediaAndContent, 0); 626} 627})(); 628 629/******/ })() 630; 631//# sourceMappingURL=view.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.