1(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ 2var a11y = require('./src/a11y.js') 3var animation = require('./src/animation.js') 4var details = require('./src/details.js') 5var navigation = require('./src/navigation.js') 6var mentSubnav = require('./src/mh-subnav.js') 7var footer = require('./src/footer.js') 8var videos = require('./src/videos.js') 9var parallax = require('./src/parallax.js') 10var faq = require('./src/faq.js') 11var blogCta = require('./src/blog-cta.js') 12},{"./src/a11y.js":2,"./src/animation.js":3,"./src/blog-cta.js":4,"./src/details.js":5,"./src/faq.js":6,"./src/footer.js":7,"./src/mh-subnav.js":8,"./src/navigation.js":9,"./src/parallax.js":10,"./src/videos.js":11}],2:[function(require,module,exports){ 13/* ACCESSIBILITY */ 14 15// Adding aria-label to logos linking to Homepage 16let headerLogos = document.querySelectorAll('header a[href="/"]') 17if (headerLogos) { 18 headerLogos.forEach(function(each){ 19 each.ariaLabel = "Return to Homepage" 20 }) 21} 22let footerLogo = document.querySelector('footer a[href="/"]') 23if (footerLogo) { 24 footerLogo.ariaLabel = "Return to Homepage" 25} 26 27// Adding aria-label to images for Work Pages 28let workClients = document.querySelectorAll('.work-client-item') 29if (workClients) { 30 workClients.forEach(function(each){ 31 let labelingText = each.querySelector('.label-link') 32 let toBeLabeled = each.querySelector('.work-image a') 33 toBeLabeled.ariaLabel = `Click to go to the ${labelingText.textContent} client page` 34 }) 35} 36},{}],3:[function(require,module,exports){ 37// Animation Handler -- checks if BODY children have been scrolled into view 38function revealElements() { 39 // Get all elements with the "has-animation" class 40 const animatedElements = document.querySelectorAll(".has-animation"); 41 42 // Loop through each animated element 43 for (let i = 0; i < animatedElements.length; i++) { 44 const element = animatedElements[i]; 45 46 // Check if the element is partially in view 47 const isPartiallyInView = isElementPartiallyInView(element); 48 if (isPartiallyInView) { 49 // If the element is partially in view, append the "has-been-revealed" class to it 50 element.classList.add("has-been-revealed"); 51 } 52 } 53 } 54 55 function isElementPartiallyInView(element) { 56 // Get the element's bounding rect 57 const boundingRect = element.getBoundingClientRect(); 58 59 // Check if the element is partially within the viewport 60 return ( 61 boundingRect.top < (window.innerHeight || document.documentElement.clientHeight) && 62 boundingRect.bottom >= 0 63 ); 64 } 65 66// Run these checks on scroll and on page load 67window.addEventListener("scroll", revealElements); 68revealElements(); 69 70// Wrap buttons in <span> to support animated state 71const buttons = document.querySelectorAll('.wp-block-button a'); 72buttons.forEach(button => { 73 const span = document.createElement('span'); 74 span.textContent = button.textContent; 75 button.textContent = ''; 76 button.appendChild(span); 77}); 78 79// Pulsing H1 effect 80if (document.querySelector('h1.is-style-pulse-in-h1')) { 81 let toPulse = document.querySelector('.is-style-pulse-in-h1') 82 let words = toPulse.innerText.split(' ') 83 let wrappedWords = words.map(function(word){ 84 return `<span>${word}</span>` 85 }) 86 toPulse.innerHTML = wrappedWords.join(' ') 87} 88},{}],4:[function(require,module,exports){ 89// Sticky Blog CTA Position Script 90// CSS-focused approach to fix flex container sticky issues 91 92// console.log('Sticky CTA Script loaded!'); 93 94// Check if sticky CTA element exists on the page 95function hasStickyCTA() { 96 return document.querySelector('.blog-cta-sticky') !== null; 97} 98 99// Function to update sticky CTA position and fix container heights 100function updateStickyCTAPosition() { 101 const stickyCTA = document.querySelector('.blog-cta-sticky'); 102 const header = document.querySelector('header.wp-block-template-part'); 103 104 if (!stickyCTA || !header) { 105 return; 106 } 107 108 // Get header height 109 const headerHeight = header.offsetHeight; 110 111 // Calculate new top position (header height + 40px) 112 const newTopPosition = headerHeight + 40; 113 114 // Apply the new top position 115 stickyCTA.style.top = newTopPosition + 'px'; 116 stickyCTA.style.zIndex = '1'; 117 118 // console.log(`Updated sticky CTA position: ${newTopPosition}px (header: ${headerHeight}px + 40px)`); 119} 120 121// Fix the flex container to allow proper sticky behavior 122function fixContainerHeights() { 123 const stickyCTA = document.querySelector('.blog-cta-sticky'); 124 const stickyColumn = stickyCTA?.closest('.wp-block-column'); 125 const contentColumn = document.querySelector('.wp-block-column.breakout-mobile'); 126 const columnsContainer = stickyColumn?.closest('.wp-block-columns'); 127 128 if (!stickyCTA || !stickyColumn || !contentColumn || !columnsContainer) { 129 return; 130 } 131 132 // Get the height of the main content column 133 const contentHeight = contentColumn.scrollHeight; 134 135 // Set the sticky column to match or exceed the content height 136 // This gives the sticky element enough vertical space to work within 137 stickyColumn.style.minHeight = contentHeight + 'px'; 138 stickyColumn.style.alignSelf = 'flex-start'; 139 140 // Ensure the columns container doesn't constrain height 141 columnsContainer.style.alignItems = 'flex-start'; 142 143 // Make sure the sticky element uses proper positioning 144 stickyCTA.style.position = 'sticky'; 145 146 // console.log(`Set sticky column height to: ${contentHeight}px`); 147} 148 149// Throttle function to limit how often handlers run 150function throttle(func, limit) { 151 let inThrottle; 152 return function() { 153 const args = arguments; 154 const context = this; 155 if (!inThrottle) {
156 func.apply(context, args); 157 inThrottle = true; 158 setTimeout(() => inThrottle = false, limit); 159 } 160 } 161} 162 163// Initialize the sticky CTA positioning 164function initStickyCTA() { 165 // console.log('Initializing sticky CTA positioning...'); 166 167 // Fix container heights first 168 fixContainerHeights(); 169 170 // Set initial position 171 updateStickyCTAPosition(); 172 173 // Create throttled handlers 174 const throttledUpdate = throttle(updateStickyCTAPosition, 16); 175 const throttledHeightFix = throttle(fixContainerHeights, 100); 176 177 // Update position on window resize 178 window.addEventListener('resize', function() { 179 throttledHeightFix(); 180 throttledUpdate(); 181 }); 182 183 // Watch for changes to header content that might affect height 184 const header = document.querySelector('header.wp-block-template-part'); 185 if (header) { 186 const headerObserver = new MutationObserver(() => { 187 setTimeout(updateStickyCTAPosition, 10); 188 }); 189 190 headerObserver.observe(header, { 191 childList: true, 192 subtree: true, 193 attributes: true, 194 attributeFilter: ['class', 'style'] 195 }); 196 } 197 198 // Watch for content changes that might affect the main column height 199 const contentColumn = document.querySelector('.wp-block-column.breakout-mobile'); 200 if (contentColumn) { 201 const contentObserver = new MutationObserver(() => { 202 setTimeout(throttledHeightFix, 50); 203 }); 204 205 contentObserver.observe(contentColumn, { 206 childList: true, 207 subtree: true 208 }); 209 } 210} 211 212// Initialize on DOM load only if sticky CTA element exists 213document.addEventListener('DOMContentLoaded', () => { 214 // console.log('DOM loaded!'); 215 if (hasStickyCTA()) { 216 // console.log('Sticky CTA element found, initializing...'); 217 initStickyCTA(); 218 } else { 219 // console.log('No sticky CTA element found, script will not initialize'); 220 } 221}); 222 223// Handle case where elements might be added dynamically 224const observer = new MutationObserver((mutations) => { 225 mutations.forEach((mutation) => { 226 if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { 227 mutation.addedNodes.forEach((node) => { 228 if (node.nodeType === 1) { 229 // Check if the added node is the sticky CTA or contains it 230 if ((node.matches && node.matches('.blog-cta-sticky')) || 231 (node.querySelector && node.querySelector('.blog-cta-sticky'))) { 232 // console.log('Sticky CTA element dynamically added'); 233 // Small delay to ensure element is fully rendered 234 setTimeout(initStickyCTA, 100); 235 } 236 } 237 }); 238 } 239 }); 240}); 241 242if (hasStickyCTA()) { 243 // Start observing for dynamically added content 244 observer.observe(document.body, { 245 childList: true, 246 subtree: true 247 }); 248} 249 250},{}],5:[function(require,module,exports){ 251// FAQ Animation JavaScript 252(function() { 253 document.addEventListener('DOMContentLoaded', function() { 254 const detailsElements = document.querySelectorAll('.single-post .entry-content details'); 255 256 detailsElements.forEach(function(details) { 257 const summary = details.querySelector('summary'); 258 259 summary.addEventListener('click', function(e) { 260 if (details.hasAttribute('open')) { 261 // Prevent the default closing behavior 262 e.preventDefault(); 263 264 // Add closing class to trigger animation 265 details.classList.add('closing'); 266 267 // Remove open attribute after animation finishes 268 setTimeout(function() { 269 details.removeAttribute('open'); 270 details.classList.remove('closing'); 271 }, 300); 272 } 273 }); 274 }); 275 }); 276 })(); 277},{}],6:[function(require,module,exports){ 278// FAQ Item JavaScript - handles AJAX-loaded content 279// Only runs if FAQ elements exist on the page 280 281// console.log('FAQ Item Script loaded!'); 282 283// Check if FAQ elements exist on the page 284function hasFAQElements() { 285 return document.querySelector('details.is-style-faq-item') !== null; 286} 287 288// Function to initialize FAQ details
289function initFAQDetails() { 290 const faqDetails = document.querySelectorAll('details.is-style-faq-item:not([data-faq-initialized])'); 291 console.log('Initializing FAQ details:', faqDetails.length); 292 293 faqDetails.forEach((details, index) => { 294 // Mark as initialized to prevent duplicate listeners 295 details.setAttribute('data-faq-initialized', 'true'); 296 297 const summary = details.querySelector('summary'); 298 const content = Array.from(details.children).filter(child => child !== summary); 299 300 // Wrap content in a div for better animation control 301 if (!details.querySelector('.faq-content-wrapper')) { 302 const wrapper = document.createElement('div'); 303 wrapper.className = 'faq-content-wrapper'; 304 content.forEach(node => wrapper.appendChild(node)); 305 details.appendChild(wrapper); 306 } 307 308 const contentWrapper = details.querySelector('.faq-content-wrapper'); 309 310 summary.addEventListener('click', (e) => { 311 if (details.hasAttribute('open')) { 312 e.preventDefault(); 313 314 // Get current height for smooth transition 315 const currentHeight = contentWrapper.scrollHeight; 316 contentWrapper.style.height = currentHeight + 'px'; 317 contentWrapper.style.transition = 'height 0.3s ease-out, opacity 0.3s ease-out'; 318 319 // Force reflow 320 contentWrapper.offsetHeight; 321 322 // Animate to closed 323 contentWrapper.style.height = '0px'; 324 contentWrapper.style.opacity = '0'; 325 326 setTimeout(() => { 327 details.removeAttribute('open'); 328 contentWrapper.style.height = ''; 329 contentWrapper.style.opacity = ''; 330 contentWrapper.style.transition = ''; 331 }, 300); 332 } 333 }); 334 335 // Handle opening with smooth animation 336 details.addEventListener('toggle', () => { 337 if (details.hasAttribute('open')) { 338 contentWrapper.style.height = '0px'; 339 contentWrapper.style.opacity = '0'; 340 contentWrapper.style.transition = 'height 0.3s ease-out, opacity 0.3s ease-out'; 341 342 // Force reflow 343 contentWrapper.offsetHeight; 344 345 // Get target height 346 const targetHeight = contentWrapper.scrollHeight; 347 contentWrapper.style.height = targetHeight + 'px'; 348 contentWrapper.style.opacity = '1'; 349 350 // Clean up after animation 351 setTimeout(() => { 352 contentWrapper.style.height = ''; 353 contentWrapper.style.opacity = ''; 354 contentWrapper.style.transition = ''; 355 }, 300); 356 } 357 }); 358 359 // Prevent clicks within details content from triggering close 360 details.addEventListener('click', (e) => { 361 if (e.target !== summary && !summary.contains(e.target)) { 362 e.stopPropagation(); 363 } 364 }); 365 }); 366} 367 368// Initialize on DOM load only if FAQ elements exist 369document.addEventListener('DOMContentLoaded', () => { 370 // console.log('DOM loaded!'); 371 if (hasFAQElements()) { 372 // console.log('FAQ elements found, initializing...'); 373 initFAQDetails(); 374 375 // Only set up mutation observer if FAQ elements exist 376 const observer = new MutationObserver((mutations) => { 377 mutations.forEach((mutation) => { 378 if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { 379 mutation.addedNodes.forEach((node) => { 380 if (node.nodeType === 1) { 381 if (node.matches && node.matches('details.is-style-faq-item')) { 382 console.log('New FAQ details detected');
383 initFAQDetails(); 384 } else if (node.querySelector && node.querySelector('details.is-style-faq-item')) { 385 console.log('Container with FAQ details detected'); 386 initFAQDetails(); 387 } 388 } 389 }); 390 } 391 }); 392 }); 393 394 // Start observing the document for changes 395 observer.observe(document.body, { 396 childList: true, 397 subtree: true 398 }); 399 } else { 400 // console.log('No FAQ elements found, script will not initialize'); 401 } 402}); 403},{}],7:[function(require,module,exports){ 404 405},{}],8:[function(require,module,exports){ 406if (document.querySelector('.mental-health-subpage-menu')) { 407 let mhToggle = document.querySelector('.mh-nav-toggle') 408 let mhSubnav = document.querySelector('.mental-health-subpage-menu') 409 mhToggle.addEventListener('click',function(click){ 410 mhToggle.classList.toggle('toggled') 411 mhSubnav.classList.toggle('open') 412 }) 413} 414},{}],9:[function(require,module,exports){ 415if (document.querySelector('.front-end .header-wrap')) { 416 window.addEventListener("load", (event) => { 417 let topLevelPages = document.querySelectorAll('.meganav-parent > a') 418 topLevelPages.forEach(function(each){ 419 let currentPage = window.location.href 420 if (currentPage.includes(each.href)) { 421 each.classList.add('active-page') 422 } 423 }) 424 }) 425 426 window.onscroll = function() { stickyScroll() } 427 428 function stickyScroll() { 429 const headerWrap = document.querySelector('.header-wrap'); 430 const topRowMenu = document.querySelector('.top-row-menu'); 431 const meganav = document.querySelector('.meganav'); 432 const scrollingDesktopToggle = document.querySelector('.scrolling-desktop-toggle'); 433 434 const scrollPosition = window.pageYOffset; 435 const scrollThresholdAdd = 120; // Threshold for adding the 'scrolling' class 436 const scrollThresholdRemove = 75; // Threshold for removing the 'scrolling' class 437 438 if (scrollPosition >= scrollThresholdAdd) { 439 // If scroll position exceeds the 'add' threshold, add 'scrolling' class 440 headerWrap.classList.add('scrolling'); 441 if (scrollingDesktopToggle) { 442 scrollingDesktopToggle.classList.remove('hidden'); 443 } 444 } else if (scrollPosition < scrollThresholdRemove) { 445 // If scroll position is below the 'remove' threshold, remove 'scrolling' class 446 headerWrap.classList.remove('scrolling'); 447 headerWrap.classList.remove('menu-toggled'); 448 if (topRowMenu) { 449 topRowMenu.style.maxHeight = ''; 450 } 451 meganav.style.display = ''; 452 } 453 } 454 455 456 if (document.querySelector('.scrolling-menu-toggle')) { 457 let scrollingMenuToggle = document.querySelector('.scrolling-menu-toggle') 458 scrollingMenuToggle.addEventListener('click',function(event){ 459 let headerWrap = document.querySelector('.header-wrap') 460 headerWrap.classList.add('menu-toggled') 461 let topRowMenu = document.querySelector('.top-row-menu') 462 topRowMenu.style.maxHeight = '300px' 463 let meganav = document.querySelector('.meganav') 464 meganav.style.display = 'flex' 465 let scrollingDesktopToggle = document.querySelector('.scrolling-desktop-toggle') 466 scrollingDesktopToggle.classList.add('hidden') 467 }) 468 } 469 470 if (document.querySelector('.mobile-menu-toggle')) { 471 let mobileMenuToggle = document.querySelector('.mobile-menu-toggle') 472 mobileMenuToggle.addEventListener('click',function(event){ 473 let headerWrap = document.querySelector('.header-wrap') 474 let toggle = mobileMenuToggle.querySelector('.toggler') 475 if (headerWrap.classList.contains('menu-toggled')) { 476 headerWrap.classList.remove('menu-toggled') 477 document.body.classList.remove('no-scroll') 478 toggle.checked = false 479 } else { 480 headerWrap.classList.add('menu-toggled') 481 document.body.classList.add('no-scroll') 482 toggle.checked = true 483 } 484 }) 485 } 486 487 if (document.querySelectorAll('.meganav-subnav-toggle')) { 488 let mobileSubnavToggles = document.querySelectorAll('.meganav-subnav-toggle')
489 mobileSubnavToggles.forEach(function(each){ 490 each.addEventListener('click',function(event){ 491 each.classList.toggle('toggled') 492 let closestMenu = each.parentNode.querySelector('.meganav-subnav') 493 closestMenu.classList.toggle('open') 494 }) 495 }) 496 } 497} 498 499},{}],10:[function(require,module,exports){ 500// window.addEventListener("DOMContentLoaded", function () { 501// const parallaxCta = document.querySelector(".parallax-cta"); 502 503// // Ensure the parallax-cta section is snapped to the start 504// parallaxCta.scrollTop = 0; 505 506// // Optional: Smoothly scroll to the snapped position when a link is clicked 507// document.querySelectorAll('a[href^="#"]').forEach(anchor => { 508// anchor.addEventListener("click", function (e) { 509// e.preventDefault(); 510 511// const targetId = this.getAttribute("href").substring(1); 512// const targetElement = document.getElementById(targetId); 513 514// if (targetElement) { 515// // Scroll smoothly to the target element 516// targetElement.scrollIntoView({ 517// behavior: "smooth", 518// block: "start" 519// }); 520// } 521// }); 522// }); 523// }); 524},{}],11:[function(require,module,exports){ 525// --- Video consent gate (CookieYes) ------------------------------------------ 526// Reimplemented from video-consent-gating.md (surface A). A YouTube embed sets 527// third-party cookies the moment its iframe is built, so we never build the iframe 528// until consent allows it. Posture is US opt-out (our fleet): load by default, 529// block only on GPC or an explicit opt-out of any non-necessary category. 530// 531// â ï¸ NEVER gate on the meta keys (consentid / consent / action). In CookieYes's 532// opt-out config `consent:no` is the DEFAULT (untouched) state and `consent:yes` 533// appears after ANY choice â they can't tell accept from decline. Only the 534// per-category flags carry the real choice. 535var VIDEO_CONSENT = { 536 mode: 'optout', // 'categories' (GDPR) | 'optout' (US, default) 537 categories: [], // GDPR mode only; opt-out auto-detects from the cookie 538 message: 'Accept cookies to view this video.', 539}; 540 541// Cookie keys that are NOT consent categories (or are always granted), so they 542// never signal an opt-out. Everything else in the cookie is treated as a category. 543var CKY_NON_CATEGORY_KEYS = ['consentid', 'consent', 'action', 'necessary']; 544 545// LOCAL TESTING: CookieYes only fires on live. To preview the placeholder locally, 546// run testNoConsent = true in the console, then click a play button (no rebuild). 547function parseCkyConsent() { 548 var entry = document.cookie.split('; ').find(function (c) { return c.indexOf('cookieyes-consent=') === 0; }); 549 if (!entry) { return null; } 550 var map = {}; 551 decodeURIComponent(entry.substring(entry.indexOf('=') + 1)).split(',').forEach(function (pair) { 552 var i = pair.indexOf(':'); 553 if (i > -1) { map[pair.substring(0, i)] = pair.substring(i + 1); } 554 }); 555 return map; 556} 557 558function hasVideoConsent() { 559 if (window.testNoConsent === true) { return false; } // local override -> placeholder 560 var c = parseCkyConsent(); 561 if (VIDEO_CONSENT.mode === 'optout') { 562 // US opt-out: allow by default; block only on GPC, or an explicit opt-out 563 // of ANY non-necessary category. Slugs vary per site, so scan the cookie 564 // instead of a hardcoded list; skip meta keys (NOT the `consent` flag). 565 if (navigator.globalPrivacyControl === true) { return false; } 566 if (!c) { return true; } // no choice recorded yet -> allowed (fail open) 567 return !Object.keys(c).some(function (k) { 568 return CKY_NON_CATEGORY_KEYS.indexOf(k) === -1 && c[k] === 'no'; 569 }); 570 } 571 // GDPR opt-in: fail closed until EVERY required category is granted (never guess). 572 if (!VIDEO_CONSENT.categories.length) { return false; } 573 if (!c) { return false; } 574 return VIDEO_CONSENT.categories.every(function (cat) { return c[cat] === 'yes'; }); 575} 576 577// Placeholder shown inside the modal when consent is missing: routes the visitor to 578// their CookieYes settings via revisitCkyConsent(). No iframe is built. 579function videoConsentMarkup() { 580 // The .video-modal dialog is transparent and normally sized by the iframe, so 581 // the placeholder carries its own solid background and matches the iframe's box 582 // (aspect-ratio / min-width) â otherwise the text sits unreadable over the page. 583 return '' + 584 '<div class="video-consent" style="display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;padding:2rem;color:#fff;background:#262626;aspect-ratio:32/17;min-width:60vw;max-width:95vw;max-height:70vh;">' + 585 '<div>' + 586 '<p style="margin:0 0 1.25rem;font-size:1.1rem;line-height:1.5;max-width:36rem;">' + VIDEO_CONSENT.message + '</p>' + 587 '<a href="javascript:void(0)" class="video-consent-btn" style="display:inline-block;padding:1rem;border:1px solid #fff;color:#fff;text-decoration:none;cursor:pointer;">
587Revisit Cookie and Tracking Technology Consent</a>' + 588 '</div>' + 589 '</div>'; 590} 591 592if (document.querySelector('.is-style-play-button-hl')) { 593 createModal() 594 let playButton = document.querySelectorAll('.is-style-play-button-hl a') 595 playButton.forEach(function(each){ 596 let videoLink = each.href 597 each.removeAttribute('href') 598 each.addEventListener('click',function(click){ 599 populateModal(videoLink) 600 }) 601 }) 602} 603 604if (document.querySelector('.is-style-play-button-hd')) { 605 createModal() 606 let playButton = document.querySelectorAll('.is-style-play-button-hd a') 607 playButton.forEach(function(each){ 608 let videoLink = each.href 609 each.removeAttribute('href') 610 each.addEventListener('click',function(click){ 611 populateModal(videoLink) 612 }) 613 }) 614} 615 616if (document.querySelector('.is-style-play-button-image')) { 617 createModal() 618 let playImages = document.querySelectorAll('.is-style-play-button-image a') 619 playImages.forEach(function(each){ 620 let videoLink = each.href 621 each.removeAttribute('href') 622 each.addEventListener('click',function(click){ 623 populateModal(videoLink) 624 }) 625 }) 626} 627 628function createModal() { 629 let footer = document.querySelector('footer.wp-block-template-part') 630 let videoModal = document.createElement('dialog') 631 videoModal.classList.add('video-modal') 632 videoModal.addEventListener('click',function(click){ 633 videoModal.close() 634 while (videoModal.childNodes.length > 0) { 635 videoModal.removeChild(videoModal.firstChild) 636 } 637 document.body.classList.remove('no-scroll') 638 }) 639 footer.appendChild(videoModal) 640} 641 642// Pull the YouTube video ID out of any of the common URL shapes and build a 643// single canonical privacy-enhanced embed URL. Handles watch?v=ID (with trailing 644// &list/&t params), youtu.be/ID short links, and already-/embed/ID URLs. Anything 645// unrecognised is returned untouched so it degrades to the raw href. 646function toNoCookieEmbed(url) { 647 let match = url.match(/[?&]v=([A-Za-z0-9_-]+)/) // youtube.com/watch?v=ID 648 || url.match(/youtu\.be\/([A-Za-z0-9_-]+)/) // youtu.be/ID 649 || url.match(/\/embed\/([A-Za-z0-9_-]+)/) // youtube.com/embed/ID 650 if (!match) { return url } 651 return 'https://www.youtube-nocookie.com/embed/' + match[1] 652} 653 654function populateModal(video) { 655 video = toNoCookieEmbed(video) 656 let videoModal = document.querySelector('footer.wp-block-template-part dialog.video-modal') 657 while (videoModal.childNodes.length > 0) { 658 videoModal.removeChild(videoModal.firstChild) 659 } 660 if (hasVideoConsent()) { 661 // Consent allows it -> build the embed. 662 let videoFrame = document.createElement('iframe') 663 videoFrame.setAttribute('id','video-frame') 664 videoFrame.setAttribute('frameborder',0) 665 videoFrame.setAttribute('scrolling','no') 666 videoFrame.setAttribute('type','text/html') 667 videoFrame.setAttribute('src',video) 668 videoFrame.setAttribute('allowfullscreen', true) 669 videoModal.appendChild(videoFrame) 670 } else { 671 // Opted out (or GPC) -> placeholder + button to reopen consent settings. 672 // No iframe is built, so no request reaches YouTube. 673 videoModal.insertAdjacentHTML('beforeend', videoConsentMarkup()) 674 let consentBtn = videoModal.querySelector('.video-consent-btn') 675 if (consentBtn) { 676 consentBtn.addEventListener('click', function (click) { 677 click.stopPropagation() 678 // Close the modal FIRST: a <dialog> opened with showModal() sits in 679 // the browser's top layer, so the CookieYes settings would otherwise 680 // open BEHIND it and be unreachable. 681 videoModal.close() 682 document.body.classList.remove('no-scroll') 683 if (typeof window.revisitCkyConsent === 'function') { 684 window.revisitCkyConsent() 685 } 686 }) 687 } 688 } 689 videoModal.showModal() 690 document.body.classList.add('no-scroll') 691} 692},{}]},{},[1]);
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.