1/* 2 * Copyright 2024 Adobe. All rights reserved. 3 * This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. You may obtain a copy 5 * of the License at http://www.apache.org/licenses/LICENSE-2.0 6 * 7 * Unless required by applicable law or agreed to in writing, software distributed under 8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 9 * OF ANY KIND, either express or implied. See the License for the specific language 10 * governing permissions and limitations under the License. 11 */ 12 13/* eslint-env browser */ 14 15/** 16 * log RUM if part of the sample. 17 * @param {string} checkpoint identifies the checkpoint in funnel 18 * @param {Object} data additional data for RUM sample 19 * @param {string} data.source DOM node that is the source of a checkpoint event, 20 * identified by #id or .classname 21 * @param {string} data.target subject of the checkpoint event, 22 * for instance the href of a link, or a search term 23 */ 24function sampleRUM(checkpoint, data = {}) { 25 const SESSION_STORAGE_KEY = 'aem-rum'; 26 sampleRUM.baseURL = sampleRUM.baseURL 27 || new URL(window.RUM_BASE == null ? 'https://rum.hlx.page' : window.RUM_BASE, window.location); 28 sampleRUM.defer = sampleRUM.defer || []; 29 const defer = (fnname) => { 30 sampleRUM[fnname] = sampleRUM[fnname] || ((...args) => sampleRUM.defer.push({ fnname, args })); 31 }; 32 sampleRUM.drain = sampleRUM.drain 33 || ((dfnname, fn) => { 34 sampleRUM[dfnname] = fn; 35 sampleRUM.defer 36 .filter(({ fnname }) => dfnname === fnname) 37 .forEach(({ fnname, args }) => sampleRUM[fnname](...args)); 38 }); 39 sampleRUM.always = sampleRUM.always || []; 40 sampleRUM.always.on = (chkpnt, fn) => { 41 sampleRUM.always[chkpnt] = fn; 42 }; 43 sampleRUM.on = (chkpnt, fn) => { 44 sampleRUM.cases[chkpnt] = fn; 45 }; 46 defer('observe'); 47 defer('cwv'); 48 try { 49 window.hlx = window.hlx || {}; 50 if (!window.hlx.rum) { 51 const usp = new URLSearchParams(window.location.search); 52 const weight = usp.get('rum') === 'on' ? 1 : 100; // with parameter, weight is 1. Defaults to 100. 53 const id = Array.from({ length: 75 }, (_, i) => String.fromCharCode(48 + i)) 54 .filter((a) => /\d|[A-Z]/i.test(a)) 55 .filter(() => Math.random() * 75 > 70) 56 .join(''); 57 const random = Math.random(); 58 const isSelected = random * weight < 1; 59 const firstReadTime = window.performance ? window.performance.timeOrigin : Date.now(); 60 const urlSanitizers = { 61 full: () => window.location.href, 62 origin: () => window.location.origin, 63 path: () => window.location.href.replace(/\?.*$/, ''), 64 }; 65 // eslint-disable-next-line max-len 66 const rumSessionStorage = sessionStorage.getItem(SESSION_STORAGE_KEY) 67 ? JSON.parse(sessionStorage.getItem(SESSION_STORAGE_KEY)) 68 : {}; 69 // eslint-disable-next-line max-len 70 rumSessionStorage.pages = (rumSessionStorage.pages ? rumSessionStorage.pages : 0) 71 + 1 72 /* noise */ + (Math.floor(Math.random() * 20) - 10); 73 sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(rumSessionStorage)); 74 // eslint-disable-next-line object-curly-newline, max-len 75 window.hlx.rum = { 76 weight, 77 id, 78 random, 79 isSelected, 80 firstReadTime, 81 sampleRUM, 82 sanitizeURL: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'], 83 rumSessionStorage, 84 }; 85 } 86 87 const { weight, id, firstReadTime } = window.hlx.rum; 88 if (window.hlx && window.hlx.rum && window.hlx.rum.isSelected) { 89 const knownProperties = [ 90 'weight', 91 'id', 92 'referer', 93 'checkpoint', 94 't', 95 'source', 96 'target', 97 'cwv', 98 'CLS', 99 'FID', 100 'LCP', 101 'INP', 102 'TTFB', 103 ]; 104 const sendPing = (pdata = data) => { 105 // eslint-disable-next-line max-len 106 const t = Math.round( 107 window.performance ? window.performance.now() : Date.now() - firstReadTime, 108 ); 109 // eslint-disable-next-line object-curly-newline, max-len, no-u
109se-before-define 110 const body = JSON.stringify( 111 { 112 weight, id, referer: window.hlx.rum.sanitizeURL(), checkpoint, t, ...data, 113 }, 114 knownProperties, 115 ); 116 const url = new URL(`.rum/${weight}`, sampleRUM.baseURL).href; 117 navigator.sendBeacon(url, body); 118 // eslint-disable-next-line no-console 119 console.debug(`ping:${checkpoint}`, pdata); 120 }; 121 sampleRUM.cases = sampleRUM.cases || { 122 load: () => sampleRUM('pagesviewed', { source: window.hlx.rum.rumSessionStorage.pages }) || true, 123 cwv: () => sampleRUM.cwv(data) || true, 124 lazy: () => { 125 // use classic script to avoid CORS issues 126 const script = document.createElement('script'); 127 script.src = new URL( 128 '.rum/@adobe/helix-rum-enhancer@^1/src/index.js', 129 sampleRUM.baseURL, 130 ).href; 131 document.head.appendChild(script); 132 return true; 133 }, 134 }; 135 sendPing(data); 136 if (sampleRUM.cases[checkpoint]) { 137 sampleRUM.cases[checkpoint](); 138 } 139 } 140 if (sampleRUM.always[checkpoint]) { 141 sampleRUM.always[checkpoint](data); 142 } 143 } catch (error) { 144 // something went wrong 145 } 146} 147 148/** 149 * Setup block utils. 150 */ 151function setup() { 152 window.hlx = window.hlx || {}; 153 window.hlx.RUM_MASK_URL = 'full'; 154 window.hlx.codeBasePath = ''; 155 window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on'; 156 157 const scriptEl = document.querySelector('script[src$="/scripts/scripts.js"]'); 158 if (scriptEl) { 159 try { 160 const scriptURL = new URL(scriptEl.src, window.location); 161 if (scriptURL.host === window.location.host) { 162 [window.hlx.codeBasePath] = scriptURL.pathname.split('/scripts/scripts.js'); 163 } else { 164 [window.hlx.codeBasePath] = scriptURL.href.split('/scripts/scripts.js'); 165 } 166 } catch (error) { 167 // eslint-disable-next-line no-console 168 console.log(error); 169 } 170 } 171} 172 173/** 174 * Sets attributes for created document elements 175 * @param {HTMLElement} el element that needs attributes 176 * @param {object} attrs attributes to set on element 177 */ 178function setAttributes(el, attrs) { 179 Object.keys(attrs).forEach((attr) => { 180 el.setAttribute(attr, attrs[attr]); 181 }); 182} 183 184/** 185 * Helper function to create multiple elements 186 * @param {string} el element type to be created 187 * @param {object} props attributes to set on element 188 * @param {string} text innerHTML text to add to new element 189 */ 190const generateElement = (type, props, text) => { 191 const el = document.createElement(type); 192 setAttributes(el, props); 193 el.innerHTML = text; 194 return el; 195}; 196 197/** 198 * Helper functions for creating Schema on product detail and recipe detail blocks 199*/ 200const stripHtmlTags = (value) => { 201 if (!value) { 202 return ''; 203 } 204 const container = document.createElement('div'); 205 container.innerHTML = String(value); 206 return container.textContent || container.innerText || ''; 207}; 208const normalizeText = (value) => stripHtmlTags(value).replace(/\s+/g, ' ').trim(); 209 210/** 211 * Gets site locale on page load 212*/ 213const getLocale = () => { 214 let locale = ''; 215 const { pathname } = window.location; 216 const { hostname } = window.location; 217 const pathSplit = pathname.split('/').filter((path) => path); 218 if (hostname.includes('localhost')) { 219 locale = 'en_US'; 220 return locale; 221 } 222 if (hostname.includes('live')) { 223 if (hostname.includes('-ca-') && pathSplit[0].includes('en')) { 224 locale = 'en_CA'; 225 return locale; 226 } 227 if (hostname.includes('-ca-') && pathSplit.includes('fr')) { 228 locale = 'fr_CA'; 229 return locale; 230 } 231 if (pathSplit.includes('es')) { 232 locale = 'es_US'; 233 return locale; 234 } 235 locale = 'en_US'; 236 return locale; 237 } 238 if (hostname.includes('.ca') && pathSplit.includes('en')) { 239 locale = 'en_CA'; 240 return locale; 241 } 242 if (pathSplit.includes('fr')) { 243 locale = 'fr_CA'; 244 return locale; 245 } 246 if (pathSplit.includes('es')) { 247 locale = 'es_US'; 248 return locale; 249 } 250 locale = 'en_US'; 251 return locale; 252}; 253 254/** 255 * Get brand from window.location.origin on page load 256*/ 257const getBrand = () => { 258 let brand = ''; 259 const { hostname } = window.location; 260 if (hostname.includes('localhost')) { 261 brand = 'wkkc'; 262 return brand; 263 } 264 if (hostname.includes('ricekrispies')) { 265 brand = 'Rice Krispies'; 266 return brand; 267 } 268 if (hostname.includes('frostedflakes')) { 269 brand = 'Frosted Flakes'; 270 return brand; 271 } 272 if (hostname.includes('specialk')) { 273 brand = 'special-k'; 274 return brand; 275 } 276 if (hostname.includes('kashi')) { 277 brand = 'kashi'; 278 return brand; 279 } 280 if (hostname.includes('wkkelloggawayfromhome') || hostname.includes('away-from-home')) { 281 brand = 'away-from-home'; 282 return brand; 283 } 284 if (hostname.includes('bear-naked') || hostname.includes('bearnaked')) { 285 brand = 'bear-naked'; 286 return brand; 287 } 288 brand = 'wkkc'; 289 return brand; 290}; 291 292/** 293 * Removes all non-alphanumeric characters 294 * @param {string} alphaInput the string to strip 295 */ 296function removeNonAlphabetChars(alphaInput) {
297 const cleanString = alphaInput?.replace(/[^A-Za-züé -]/g, '').toLowerCase(); 298 return cleanString; 299} 300 301function removeNonAlphabetCharsEpsilon(alphaInput) { 302 const cleanString = alphaInput?.replace(/[^A-Za-züé -]/g, ''); 303 return cleanString; 304} 305 306/** 307 * Removes 'Kelloggs' and 'cereal' from string 308 * @param {string} removeInput the string that needs removal 309 */ 310function removeKelloggs(removeInput) { 311 const cleanName = removeInput.replace('kelloggs', '').replace('cereal', '').toLowerCase().trim(); 312 return cleanName; 313} 314 315function removeKelloggsEpsilon(removeInput) { 316 const cleanName = removeInput.replace('Kelloggs', '').replace('cereal', '').trim(); 317 return cleanName; 318} 319 320/** 321 * Gets wkkellogg home url for Schema.org breadcrumbList. 322 */ 323const getWkkcHomeUrl = () => { 324 const { hostname } = window.location; 325 const locale = getLocale(); 326 327 if (hostname.includes('localhost') || hostname.includes('wkkellogg')) { 328 if (locale === 'fr_CA') { 329 return 'https://www.wkkellogg.ca/fr/'; 330 } 331 if (locale === 'en_CA') { 332 return 'https://www.wkkellogg.ca/en/'; 333 } 334 return 'https://www.wkkellogg.com'; 335 } 336 337 return ''; 338}; 339 340/** 341 * Chooses the locale-specific path segment for Schema breadcrumbList. 342 */ 343const getLocalizedPathByLocale = (enPath, frPath, inputLocale = getLocale()) => { 344 if (inputLocale === 'fr_CA') { 345 return frPath; 346 } 347 return enPath; 348}; 349 350/** 351 * Appends a path segment to a base URL while normalizing slashes for Schema breadcrumbList. 352 */ 353const appendPathToUrl = (baseUrl, path) => { 354 if (!baseUrl || !path) { 355 return baseUrl || ''; 356 } 357 358 const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; 359 const normalizedPath = String(path).replace(/^\/+/, ''); 360 return `${normalizedBaseUrl}/${normalizedPath}`; 361}; 362 363/** 364 * Converts text into a URL-safe slug for Schema breadcrumb/category links. 365 */ 366const toUrlSlug = (value) => stripHtmlTags(value) 367 .toLowerCase() 368 .replace(/[^a-z0-9\s-]/g, '') 369 .trim() 370 .replace(/\s+/g, '-') 371 .replace(/-+/g, '-'); 372 373/** 374 * Builds a Schema BreadcrumbList from ordered breadcrumb items. 375 */ 376function buildBreadcrumbListSchema(id, items = []) { 377 return { 378 '@type': 'BreadcrumbList', 379 '@id': id, 380 itemListElement: items 381 .filter((item) => item?.id && item?.name) 382 .map((item, index) => ({ 383 '@type': 'ListItem', 384 position: index + 1, 385 item: { 386 '@id': item.id, 387 name: item.name, 388 }, 389 })), 390 }; 391} 392 393// updating a seo title in product/recipe detail pages 394 395function updateSeoTitle(title) { 396 if (!title) return; 397 const Brand = getBrand(); 398 let locale = getLocale(); 399 let upDatedBrand = ''; 400 switch (Brand) { 401 case 'rice-krispies': 402 upDatedBrand = 'Rice Krispies®'; 403 break; 404 case 'kashi': 405 upDatedBrand = 'Kashi®'; 406 break; 407 case 'wkkc': 408 upDatedBrand = 'WK Kellogg Co'; 409 break; 410 case 'bear-naked': 411 upDatedBrand = 'Bear Naked®'; 412 break; 413 default: 414 upDatedBrand = ''; 415 } 416 if (!upDatedBrand) return; 417 if (locale === 'en_CA' || locale === 'fr_CA') { 418 if (upDatedBrand === 'Kashi®' && locale === 'fr_CA') return; 419 locale = 'Canada'; 420 document.title = `${title} - ${upDatedBrand} ${locale}`; 421 } 422} 423// redirection 424function redirection() { 425 const Locale = getLocale(); 426 let locale = ''; 427 428 switch (Locale) { 429 case 'en_CA': 430 locale = '/en'; 431 break; 432 case 'fr_CA': 433 locale = '/fr'; 434 break; 435 default: 436 locale = ''; 437 } 438 439 /** 440 * redirection from www.wkkellogg.com/who-we-are/board-management to 441 investor.wkkellogg.com/governance/board-of-directors/default.aspx 442*/ 443 444 if (window.location.href === `${window.location.origin}${locale}/who-we-are/board-management`) { 445 window.location.href = 'https://investor.wkkellogg.com/governance/board-of-directors/default.aspx'; 446 } 447} 448redirection(); 449 450/** 451 * Auto initializiation. 452 */ 453 454function init() { 455 setup(); 456 sampleRUM('top'); 457 458 window.addEventListener('load', () => sampleRUM('load')); 459 460 window.addEventListener('unhandledrejection', (event) => { 461 sampleRUM('error', { source: event.reason.sourceURL, target: event.reason.line }); 462 }); 463 464 window.addEventListener('error', (event) => { 465 sampleRUM('error', { source: event.filename, target: event.lineno }); 466 }); 467} 468 469/** 470 * Sanitizes a string for use as class name. 471 * @param {string} name The unsanitized string 472 * @returns {string} The class name 473 */ 474function toClassName(name) { 475 return typeof name === 'string' 476 ? name 477 .toLowerCase() 478 .replace(/[^0-9a-z]/gi, '-') 479 .replace(/-+/g, '-') 480 .replace(/^-|-$/g, '') 481 : ''; 482} 483 484/** 485 * Sanitizes a string for use as a js property name. 486 * @param {string} name The unsanitized string 487 * @returns {string} The camelCased name 488 */ 489function toCamelCase(name) { 490 return toClassName(name).replace(/-([a-z])/g, (g) => g[1].toUpperCase()); 491} 492 493/** 494 * Extracts the config from a block. 495 * @param {Element} block The block element 496 * @returns {object} The block config 497 */ 498// eslint-disable-next-line import/prefer-default-export 499function readBlockConfig(block) { 500 const config = {}; 501 block.querySelectorAll(':scope > div').forEach((row) => {
502 if (row.children) { 503 const cols = [...row.children]; 504 if (cols[1]) { 505 const col = cols[1]; 506 const name = toClassName(cols[0].textContent); 507 let value = ''; 508 if (col.querySelector('a')) { 509 const as = [...col.querySelectorAll('a')]; 510 if (as.length === 1) { 511 value = as[0].href; 512 } else { 513 value = as.map((a) => a.href); 514 } 515 } else if (col.querySelector('img')) { 516 const imgs = [...col.querySelectorAll('img')]; 517 if (imgs.length === 1) { 518 value = imgs[0].src; 519 } else { 520 value = imgs.map((img) => img.src); 521 } 522 } else if (col.querySelector('p')) { 523 const ps = [...col.querySelectorAll('p')]; 524 if (ps.length === 1) { 525 value = ps[0].textContent; 526 } else { 527 value = ps.map((p) => p.textContent); 528 } 529 } else value = row.children[1].textContent; 530 config[name] = value; 531 } 532 } 533 }); 534 return config; 535} 536 537/** 538 * Loads a CSS file. 539 * @param {string} href URL to the CSS file 540 */ 541async function loadCSS(href) { 542 return new Promise((resolve, reject) => { 543 if (!document.querySelector(`head > link[href="${href}"]`)) { 544 const link = document.createElement('link'); 545 link.rel = 'stylesheet'; 546 link.href = href; 547 link.onload = resolve; 548 link.onerror = reject; 549 document.head.append(link); 550 } else { 551 resolve(); 552 } 553 }); 554} 555 556/** 557 * Loads a non module JS file. 558 * @param {string} src URL to the JS file 559 * @param {Object} attrs additional optional attributes 560 */ 561async function loadScript(src, attrs) { 562 return new Promise((resolve, reject) => { 563 if (!document.querySelector(`head > script[src="${src}"]`)) { 564 const script = document.createElement('script'); 565 script.src = src; 566 if (attrs) { 567 // eslint-disable-next-line no-restricted-syntax, guard-for-in 568 for (const attr in attrs) { 569 script.setAttribute(attr, attrs[attr]); 570 } 571 } 572 script.onload = resolve; 573 script.onerror = reject; 574 document.head.append(script); 575 } else { 576 resolve(); 577 } 578 }); 579} 580 581/** 582 * Extracts an image URL from a CSS background-image declaration. 583 * Accepts either css url(...) values or direct URLs. 584 * @param {string} value CSS background-image value or direct URL 585 * @returns {string} Resolved image URL or empty string 586 */ 587function getBackgroundImageUrl(url) { 588 const input = String(url).trim(); 589 const match = input.match(/^url\(([^)]+)\)$/i); 590 const rawUrl = (match ? match[1] : input).trim().replace(/^['"]|['"]$/g, ''); 591 try { 592 return new URL(rawUrl, window.location.href).href; 593 } catch (error) { 594 return ''; 595 } 596} 597 598/** 599 * Adds a high-priority preload link for an image when not already present. 600 * @param {string} imageUrl Image URL to preload 601 */ 602function preloadSectionBackgroundImage(imageUrl) { 603 if (!imageUrl) return; 604 605 const normalizedImageUrl = getBackgroundImageUrl(imageUrl); 606 const existingPreloadLinks = [...document.head.querySelectorAll('link[rel="preload"][as="image"]')]; 607 const hasMatchingPreloadLink = existingPreloadLinks.some((link) => { 608 const href = link.getAttribute('href'); 609 if (!href) return false; 610 return getBackgroundImageUrl(href) === normalizedImageUrl; 611 }); 612 if (hasMatchingPreloadLink) { 613 return; 614 } 615 616 const preloadLink = document.createElement('link'); 617 preloadLink.setAttribute('rel', 'preload'); 618 preloadLink.setAttribute('as', 'image'); 619 preloadLink.setAttribute('href', normalizedImageUrl); 620 preloadLink.setAttribute('fetchpriority', 'high'); 621 document.head.append(preloadLink); 622} 623 624/** 625 * Retrieves the content of metadata tags. 626 * @param {string} name The metadata name (or property) 627 * @param {Document} doc Document object to query for metadata. Defaults to the window's document 628 * @returns {string} The metadata value(s) 629 */ 630function getMetadata(name, doc = document) { 631 const attr = name && name.includes(':') ? 'property' : 'name'; 632 const meta = [...doc.head.querySelectorAll(`meta[${attr}="${name}"]`)] 633 .map((m) => m.content) 634 .join(', '); 635 return meta || ''; 636} 637 638/** 639 * Collects all anchor tags 640 * If the link address contains a host that does not match the current location,
641 * the `target="_blank"` attribute is assigned to the anchor tag 642 */ 643function updateLinkTags() { 644 const aTags = document.querySelectorAll('a'); 645 const theme = getMetadata('theme'); 646 aTags.forEach((item) => { 647 if (item.href) { 648 const url = new URL(item.href); 649 if (url && url.host !== window.location.host) { 650 item.setAttribute('target', '_blank'); 651 } 652 653 if (theme === 'theme-frosted-flakes' && item.href.indexOf('mission-tiger') !== -1) { 654 item.setAttribute('target', '_blank'); 655 } 656 } 657 }); 658} 659 660/** 661 * Returns a picture element with webp and fallbacks 662 * @param {string} src The image URL 663 * @param {string} [alt] The image alternative text 664 * @param {boolean} [eager] Set loading attribute to eager 665 * @param {Array} [breakpoints] Breakpoints and corresponding params (eg. width) 666 * @returns {Element} The picture element 667 */ 668function createOptimizedPicture( 669 src, 670 alt = '', 671 eager = false, 672 breakpoints = [{ media: '(min-width: 600px)', width: '2000' }, { width: '750' }], 673) { 674 const url = new URL(src, window.location.href); 675 const picture = document.createElement('picture'); 676 const { pathname } = url; 677 const ext = pathname.substring(pathname.lastIndexOf('.') + 1); 678 679 // webp 680 breakpoints.forEach((br) => { 681 const source = document.createElement('source'); 682 if (br.media) source.setAttribute('media', br.media); 683 source.setAttribute('type', 'image/webp'); 684 source.setAttribute('srcset', `${pathname}?width=${br.width}&format=webply&optimize=medium`); 685 picture.appendChild(source); 686 }); 687 688 // fallback 689 breakpoints.forEach((br, i) => { 690 if (i < breakpoints.length - 1) { 691 const source = document.createElement('source'); 692 if (br.media) source.setAttribute('media', br.media); 693 source.setAttribute('srcset', `${pathname}?width=${br.width}&format=${ext}&optimize=medium`); 694 picture.appendChild(source); 695 } else { 696 const img = document.createElement('img'); 697 img.setAttribute('loading', eager ? 'eager' : 'lazy'); 698 img.setAttribute('alt', alt); 699 picture.appendChild(img); 700 img.setAttribute('src', `${pathname}?width=${br.width}&format=${ext}&optimize=medium`); 701 } 702 }); 703 704 return picture; 705} 706 707/** 708 * Set template (page structure) and theme (page styles). 709 */ 710function decorateTemplateAndTheme() { 711 const addClasses = (element, classes) => { 712 classes.split(',').forEach((c) => { 713 element.classList.add(toClassName(c.trim())); 714 }); 715 }; 716 const template = getMetadata('template'); 717 if (template) addClasses(document.body, template); 718 const theme = getMetadata('theme'); 719 if (theme) addClasses(document.body, theme); 720} 721 722/** 723 * Wrap inline text content of block cells within a <p> tag. 724 * @param {Element} block the block element 725 */ 726function wrapTextNodes(block) { 727 const validWrappers = [ 728 'P', 729 'PRE', 730 'UL', 731 'OL', 732 'PICTURE', 733 'TABLE', 734 'H1', 735 'H2', 736 'H3', 737 'H4', 738 'H5', 739 'H6', 740 ]; 741 742 const wrap = (el) => { 743 const wrapper = document.createElement('p'); 744 wrapper.append(...el.childNodes); 745 [...el.attributes] 746 // move the instrumentation from the cell to the new paragraph, also keep the class 747 // in case the content is a buttton and the cell the button-container 748 .filter(({ nodeName }) => nodeName === 'class' 749 || nodeName.startsWith('data-aue') 750 || nodeName.startsWith('data-richtext')) 751 .forEach(({ nodeName, nodeValue }) => { 752 wrapper.setAttribute(nodeName, nodeValue); 753 el.removeAttribute(nodeName); 754 }); 755 el.append(wrapper); 756 }; 757 758 block.querySelectorAll(':scope > div > div').forEach((blockColumn) => { 759 if (blockColumn.hasChildNodes()) { 760 const hasWrapper = !!blockColumn.firstElementChild 761 && validWrappers.some((tagName) => blockColumn.firstElementChild.tagName === tagName); 762 if (!hasWrapper) { 763 wrap(blockColumn); 764 } else if ( 765 blockColumn.firstElementChild.tagName === 'PICTURE' 766 && (blockColumn.children.length > 1 || !!blockColumn.textContent.trim()) 767 ) { 768 wrap(blockColumn); 769 } 770 } 771 }); 772} 773 774/** 775 * Decorates paragraphs containing a single link as buttons. 776 * @param {Element} element container element 777 */ 778function decorateButtons(element) { 779 element.querySelectorAll('a').forEach((a) => { 780 a.title = a.title || a.textContent; 781 if (a.href !== a.textContent) { 782 const up = a.parentElement; 783 const twoup = a.parentElement.parentElement; 784 if (!a.querySelector('img')) { 785 if (up.childNodes.length === 1 && (up.tagName === 'P' || up.tagName === 'DIV')) { 786 a.className = 'button default'; // default 787 up.classList.add('button-container'); 788 } 789 if ( 790 up.childNodes.length === 1 791 && up.tagName === 'STRONG' 792 && twoup.childNodes.length === 1 793 && (twoup.tagName === 'P' || twoup.tagName === 'DIV') 794 ) { 795 a.className = 'button primary'; 796 twoup.classList.add('button-container'); 797 } 798 if ( 799 up.childNodes.length === 1 800 && up.tagName === 'EM' 801 && twoup.childNodes.length === 1 802 && (twoup.tagName === 'P' || twoup.tagName === 'DIV') 803 ) { 804 a.className = 'button secondary'; 805 twoup.classList.add('button-container'); 806 } 807 } 808 } 809 }); 810} 811 812/** 813 * Add <img> for icon, prefixed with codeBasePath and optional prefix. 814 * @param {Element} [span] span element with icon classes 815 * @param {string} [prefix] prefix to be added to icon src 816 * @param {string} [alt] alt text to be added to icon 817 */ 818function decorateIcon(span, prefix = '', alt = '') { 819 const iconName = Array.from(span.classList) 820 .find((c) => c.startsWith('icon-')) 821 .substring(5); 822 const img = document.createElement('img'); 823 img.dataset.iconName = iconName; 824 img.src = `${window.hlx.codeBasePath}${prefix}/icons/${iconName}.svg`; 825 img.alt = alt || iconName; 826 img.loading = 'lazy'; 827 span.append(img); 828} 829 830/** 831 * Add <img> for icons, prefixed with codeBasePath and optional prefix. 832 * @param {Element} [element] Element containing icons 833 * @param {string} [prefix] prefix to be added to icon the src 834 */ 835function decorateIcons(element, prefix = '') { 836 const icons = [...element.querySelectorAll('span.icon')]; 837 icons.forEach((span) => { 838 decorateIcon(span, prefix); 839 }); 840} 841 842/** 843 * Decorates all sections in a container element. 844 * @param {Element} main The container element 845 */ 846function decorateSections(main) { 847 main.querySelectorAll(':scope > div:not([data-section-status])').forEach((section) => { 848 const wrappers = []; 849 let defaultContent = false;
850 [...section.children].forEach((e) => { 851 if (e.tagName === 'DIV' || !defaultContent) { 852 const wrapper = document.createElement('div'); 853 wrappers.push(wrapper); 854 defaultContent = e.tagName !== 'DIV'; 855 if (defaultContent) wrapper.classList.add('default-content-wrapper'); 856 } 857 wrappers[wrappers.length - 1].append(e); 858 }); 859 wrappers.forEach((wrapper) => section.append(wrapper)); 860 section.classList.add('section'); 861 section.dataset.sectionStatus = 'initialized'; 862 section.style.display = 'none'; 863 864 // Process section metadata 865 const sectionMeta = section.querySelector('div.section-metadata'); 866 if (sectionMeta) { 867 const meta = readBlockConfig(sectionMeta); 868 Object.keys(meta).forEach((key) => { 869 if (key === 'style') { 870 const styles = meta.style 871 .split(',') 872 .filter((style) => style) 873 .map((style) => toClassName(style.trim())); 874 styles.forEach((style) => section.classList.add(style)); 875 } else if (key === 'background') { 876 const bg = meta.background; 877 section.classList.add('has-background-image'); 878 section.style.backgroundImage = `url(${bg})`; 879 preloadSectionBackgroundImage(bg); 880 } else { 881 section.dataset[toCamelCase(key)] = meta[key]; 882 } 883 }); 884 sectionMeta.parentNode.remove(); 885 } 886 let dataBg = section.getAttribute('data-background') || section.dataset.background; 887 if (dataBg) { 888 if (/^https?:\/\//i.test(dataBg)) { 889 try { 890 const u = new URL(dataBg); 891 if (u.host !== window.location.host && (u.host.includes('bearnaked') || getBrand() === 'bear-naked')) { 892 dataBg = `${u.pathname}${u.search || ''}${u.hash || ''}`; 893 section.classList.add('has-background-image'); 894 section.style.backgroundImage = `url(${dataBg})`; 895 section.removeAttribute('data-background'); 896 } 897 } catch (e) { 898 } 899 } 900 } 901 }); 902} 903 904/** 905 * Gets placeholders object. 906 * @param {string} [prefix] Location of placeholders 907 * @returns {object} Window placeholders object 908 */ 909// eslint-disable-next-line import/prefer-default-export 910async function fetchPlaceholders(prefix = 'default') { 911 window.placeholders = window.placeholders || {}; 912 if (!window.placeholders[prefix]) { 913 window.placeholders[prefix] = new Promise((resolve) => { 914 fetch(`${prefix === 'default' ? '' : prefix}/placeholders.json`) 915 .then((resp) => { 916 if (resp.ok) { 917 return resp.json(); 918 } 919 return {}; 920 }) 921 .then((json) => { 922 const placeholders = {}; 923 json.data 924 .filter((placeholder) => placeholder.Key) 925 .forEach((placeholder) => { 926 placeholders[toCamelCase(placeholder.Key)] = placeholder.Text; 927 }); 928 window.placeholders[prefix] = placeholders; 929 resolve(window.placeholders[prefix]); 930 }) 931 .catch(() => { 932 // error loading placeholders 933 window.placeholders[prefix] = {}; 934 resolve(window.placeholders[prefix]); 935 }); 936 }); 937 } 938 return window.placeholders[`${prefix}`]; 939} 940 941/** 942 * Updates all section status in a container element. 943 * @param {Element} main The container element 944 */ 945function updateSectionsStatus(main) { 946 const sections = [...main.querySelectorAll(':scope > div.section')]; 947 for (let i = 0; i < sections.length; i += 1) { 948 const section = sections[i]; 949 const status = section.dataset.sectionStatus; 950 if (status !== 'loaded') { 951 const loadingBlock = section.querySelector( 952 '.block[data-block-status="initialized"], .block[data-block-status="loading"]', 953 ); 954 if (loadingBlock) { 955 section.dataset.sectionStatus = 'loading'; 956 break; 957 } else { 958 section.dataset.sectionStatus = 'loaded'; 959 section.style.display = null; 960 } 961 } 962 } 963} 964 965/** 966 * Builds a block DOM Element from a two dimensional array, string, or object 967 * @param {string} blockName name of the block 968 * @param {*} content two dimensional array or string or object of content 969 */ 970function buildBlock(blockName, content) {
971 const table = Array.isArray(content) ? content : [[content]]; 972 const blockEl = document.createElement('div'); 973 // build image block nested div structure 974 blockEl.classList.add(blockName); 975 table.forEach((row) => { 976 const rowEl = document.createElement('div'); 977 row.forEach((col) => { 978 const colEl = document.createElement('div'); 979 const vals = col.elems ? col.elems : [col]; 980 vals.forEach((val) => { 981 if (val) { 982 if (typeof val === 'string') { 983 colEl.innerHTML += val; 984 } else { 985 colEl.appendChild(val); 986 } 987 } 988 }); 989 rowEl.appendChild(colEl); 990 }); 991 blockEl.appendChild(rowEl); 992 }); 993 return blockEl; 994} 995 996/** 997 * Loads JS and CSS for a block. 998 * @param {Element} block The block element 999 */ 1000async function loadBlock(block) { 1001 const status = block.dataset.blockStatus; 1002 if (status !== 'loading' && status !== 'loaded') { 1003 block.dataset.blockStatus = 'loading'; 1004 const { blockName } = block.dataset; 1005 try { 1006 const cssLoaded = loadCSS(`${window.hlx.codeBasePath}/blocks/${blockName}/${blockName}.css`); 1007 const decorationComplete = new Promise((resolve) => { 1008 (async () => { 1009 try { 1010 const mod = await import( 1011 `${window.hlx.codeBasePath}/blocks/${blockName}/${blockName}.js` 1012 ); 1013 if (mod.default) { 1014 await mod.default(block); 1015 } 1016 } catch (error) { 1017 // eslint-disable-next-line no-console 1018 console.log(`failed to load module for ${blockName}`, error); 1019 } 1020 resolve(); 1021 })(); 1022 }); 1023 await Promise.all([cssLoaded, decorationComplete]); 1024 } catch (error) { 1025 // eslint-disable-next-line no-console 1026 console.log(`failed to load block ${blockName}`, error); 1027 } 1028 block.dataset.blockStatus = 'loaded'; 1029 } 1030 return block; 1031} 1032 1033/** 1034 * Loads JS and CSS for all blocks in a container element. 1035 * @param {Element} main The container element 1036 */ 1037async function loadBlocks(main) { 1038 updateSectionsStatus(main); 1039 const blocks = [...main.querySelectorAll('div.block')]; 1040 for (let i = 0; i < blocks.length; i += 1) { 1041 // eslint-disable-next-line no-await-in-loop 1042 await loadBlock(blocks[i]); 1043 updateSectionsStatus(main); 1044 } 1045} 1046 1047/** 1048 * Decorates a block. 1049 * @param {Element} block The block element 1050 */ 1051function decorateBlock(block) { 1052 const shortBlockName = block.classList[0]; 1053 if (shortBlockName && !block.dataset.blockStatus) { 1054 block.classList.add('block'); 1055 block.dataset.blockName = shortBlockName; 1056 block.dataset.blockStatus = 'initialized'; 1057 const blockWrapper = block.parentElement; 1058 blockWrapper.classList.add(`${shortBlockName}-wrapper`); 1059 const section = block.closest('.section'); 1060 if (section) section.classList.add(`${shortBlockName}-container`); 1061 block.querySelectorAll(':scope > div > div').forEach((cell) => { 1062 const firstChild = cell.firstElementChild; 1063 const cellText = cell.textContent.trim(); 1064 if ((!firstChild && cellText) 1065 || (firstChild && !firstChild.tagName.match(/^(P(RE)?|H[1-6]|(U|O)L|TABLE)$/))) { 1066 const paragraph = document.createElement('p'); 1067 [...cell.attributes] 1068 // move the instrumentation from the cell to the new paragraph, also keep the class 1069 // in case the content is a buttton and the cell the button-container 1070 .filter(({ nodeName }) => nodeName === 'class' 1071 || nodeName.startsWith('data-aue') 1072 || nodeName.startsWith('data-richtext')) 1073 .forEach(({ nodeName, nodeValue }) => { 1074 paragraph.setAttribute(nodeName, nodeValue); 1075 cell.removeAttribute(nodeName); 1076 }); 1077 paragraph.append(...cell.childNodes); 1078 cell.replaceChildren(paragraph); 1079 } 1080 }); 1081 // eslint-disable-next-line no-use-before-define 1082 decorateButtons(block); 1083 } 1084} 1085 1086/** 1087 * Decorates all blocks in a container element. 1088 * @param {Element} main The container element 1089 */ 1090function decorateBlocks(main) { 1091 main.querySelectorAll('div.section > div > div').forEach(decorateBlock); 1092} 1093 1094/** 1095 * Loads a block named 'header' into header 1096 * @param {Element} header header element 1097 * @returns {Promise} 1098 */ 1099async function loadHeader(header) { 1100 const headerBlock = buildBlock('header', ''); 1101 header.append(headerBlock); 1102 decorateBlock(headerBlock); 1103 return loadBlock(headerBlock); 1104} 1105 1106/** 1107 * Loads a block named 'footer' into footer 1108 * @param footer footer element 1109 * @returns {Promise} 1110 */ 1111async function loadFooter(footer) { 1112 const footerBlock = buildBlock('footer', ''); 1113 footer.append(footerBlock); 1114 decorateBlock(footerBlock); 1115 return loadBlock(footerBlock); 1116} 1117 1118/** 1119 * Loads a block named '404' into main section of 404.html 1120 * @param footer footer element 1121 * @returns {Promise} 1122 */ 1123async function loadErrorPage(errorPage) { 1124 const errorBlock = buildBlock('404', ''); 1125 errorPage.append(errorBlock); 1126 decorateBlock(errorBlock); 1127 return loadBlock(errorBlock); 1128} 1129 1130/**
1131 * Load LCP block and/or wait for LCP in default content. 1132 * @param {Array} lcpBlocks Array of blocks 1133 */ 1134async function waitForLCP(lcpBlocks) { 1135 const block = document.querySelector('.block'); 1136 const hasLCPBlock = block && lcpBlocks.includes(block.dataset.blockName); 1137 if (hasLCPBlock) await loadBlock(block); 1138 1139 document.body.style.display = null; 1140 const lcpCandidate = document.querySelector('main img'); 1141 1142 await new Promise((resolve) => { 1143 if (lcpCandidate && !lcpCandidate.complete) { 1144 lcpCandidate.setAttribute('loading', 'eager'); 1145 lcpCandidate.addEventListener('load', resolve); 1146 lcpCandidate.addEventListener('error', resolve); 1147 } else { 1148 resolve(); 1149 } 1150 }); 1151} 1152 1153/** 1154 * Creates an SVG sparkle icon with brand color 1155 * @param {string} color - Hex color for the sparkle (default: #0061af) 1156 * @param {boolean} isRight - Whether to use right-oriented sparkle (default: false for left) 1157 * @returns {SVGElement} SVG element with sparkle paths 1158 */ 1159function createSparkleIcon(color = '#0061af', isRight = false) { 1160 const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 1161 svg.setAttribute('viewBox', isRight ? '0 0 37 61' : '0 0 36 61'); 1162 svg.setAttribute('fill', 'none'); 1163 svg.setAttribute('class', 'sparkle-icon'); 1164 1165 // Left sparkle paths (default) 1166 const leftPaths = [ 1167 'M15.2768 19.5249C15.4078 19.526 15.5414 19.5105 15.6598 19.458C15.7781 19.4055 15.8713 19.3207 15.9235 19.2125C15.9758 19.1042 15.9842 18.9786 15.9517 18.8533C15.9192 18.7279 15.8483 18.6136 15.7659 18.5118C15.5786 18.2825 15.3859 18.0646 15.1877 17.8578C14.3949 17.0309 13.5152 16.3842 12.5485 15.9175C9.16521 14.2842 5.66353 12.896 2.0435 11.753C1.78493 11.6713 1.52575 11.5909 1.26597 11.5118C1.13224 11.4712 0.985844 11.4699 0.853046 11.5205C0.720292 11.5709 0.612751 11.6676 0.552663 11.792C0.492575 11.9165 0.483807 12.0608 0.526872 12.1961C0.569893 12.3316 0.661954 12.4454 0.776875 12.5249C1.00043 12.6791 1.22458 12.832 1.44934 12.9837C4.59598 15.1073 7.86097 16.9858 11.2443 18.6192C12.2109 19.0858 13.2645 19.3724 14.4051 19.4788C14.6903 19.5054 14.9808 19.5208 15.2768 19.5249Z', 1168 'M18.9842 15.6851C19.0747 15.7793 19.1777 15.8646 19.2961 15.9144C19.4146 15.9641 19.5385 15.9742 19.6519 15.939C19.7653 15.9038 19.8618 15.8253 19.9313 15.7172C20.0007 15.6092 20.0373 15.4805 20.0586 15.3516C20.103 15.0729 20.1354 14.7979 20.1559 14.5266C20.238 13.4413 20.129 12.4154 19.829 11.4487C18.779 8.06544 17.469 4.76279 15.899 1.54083C15.7869 1.3107 15.6734 1.08097 15.5586 0.851651C15.4961 0.727186 15.3971 0.619923 15.2698 0.55769C15.1427 0.495426 14.9992 0.482781 14.8678 0.523565C14.7364 0.56435 14.6254 0.655968 14.5558 0.779294C14.4861 0.902651 14.4652 1.0471 14.4842 1.1851C14.5194 1.43912 14.5559 1.69272 14.5938 1.94591C15.1238 5.49062 15.9138 8.95464 16.9638 12.3379C17.2638 13.3046 17.7548 14.212 18.4368 15.0601C18.6073 15.2721 18.7898 15.4804 18.9842 15.6851Z', 1169 'M23.5848 15.6636C23.5789 15.7935 23.5792 15.9227 23.6068 16.0404C23.6347 16.1582 23.6895 16.2562 23.773 16.3241C23.8565 16.3919 23.9636 16.4255 24.0846 16.4286C24.2055 16.4316 24.3321 16.4054 24.4579 16.373C24.6633 16.3185 24.8589 16.2561 25.0449 16.1859C25.7887 15.9048 26.3772 15.4976 26.8106 14.9642C28.3272 13.0976 29.6326 11.0592 30.7267 8.84928C30.8049 8.69142 30.882 8.53269 30.9579 8.37308C31.0162 8.25006 31.0458 8.11077 31.0256 7.97395C31.0055 7.8372 30.9387 7.71524 30.8357 7.63155C30.7327 7.54787 30.5996 7.50741 30.4617 7.51575C30.3236 7.52401 30.1933 7.5815 30.0848 7.66366C29.9441 7.77072 29.8046 7.87866 29.666 7.98747C27.7268 9.51081 25.9989 11.2058 24.4822 13.0724C24.0489 13.6058 23.7708 14.2652 23.6479 15.0508C23.6172 15.2472 23.5961 15.4515 23.5848 15.6636Z', 1170 'M27.0588 20.9571C26.9605 21.0446 26.8661 21.1302 26.7947 21.2191C26.7234 21.3086 26.6806 21.399 26.6742 21.4952C26.6678 21.5913 26.6981 21.6867 26.757 21.7848C26.8159 21.8824 26.8981 21.9798 26.984 22.0796C27.1007 22.2126 27.2182 22.3332 27.3365 22.4413C27.8099 22.8738 28.2966 23.1067 28.7966 23.14C30.5466 23.2567 32.3147 23.1017 34.1009 22.6752C34.2285 22.6447 34.3562 22.6128 34.484 22.5796C34.6129 22.5455 34.7384 22.4861 34.8343 22.3917C34.9302 22.2974 34.9885 22.1772 34.997 22.05C35.0055 21.9229 34.9636 21.796 34.8811 21.6898C34.7986 21.5835 34.6821 21.508 34.5588 21.4571C34.4366 21.4072 34.3143 21.3586 34.1918 21.3115C32.4781 20.6516 30.7462 20.2633 28.9962 20.1467C28.4962 20.1133 27.9829 20.2795 27.4563 20.6453C27.3246 20.7368 27.1921 20.8407 27.0588 20.9571Z', 1171 'M24.4347 25.6368C24.305 25.622 24.1774 25.6109 24.0606 25.6237C23.9434 25.6368 23.844 25.6752 23.7687 25.7446C23.6935 25.8141 23.6473 25.9
1171101 23.6249 26.0259C23.6028 26.1413 23.6037 26.2694 23.6081 26.3999C23.6162 26.593 23.6335 26.7776 23.6601 26.9538C23.7661 27.6584 24.0192 28.2274 24.4192 28.6608C25.8192 30.1774 27.4192 31.5094 29.2193 32.6568C29.3478 32.7388 29.4774 32.8198 29.6081 32.8999C29.7232 32.97 29.856 33.0165 29.9926 33.0151C30.1291 33.0138 30.257 32.9657 30.3528 32.8774C30.4485 32.789 30.5066 32.6653 30.5188 32.5293C30.5312 32.3933 30.4954 32.2571 30.4347 32.1368C30.3653 32.0002 30.2949 31.8646 30.2235 31.7298C29.2236 29.8439 28.0236 28.1426 26.6236 26.6259C26.2236 26.1926 25.6766 25.8949 24.9827 25.7329C24.8092 25.6924 24.6266 25.6603 24.4347 25.6368Z', 1172 'M19.5464 26.7203C19.5291 26.5911 19.4988 26.462 19.4365 26.3514C19.3742 26.2408 19.2849 26.1578 19.1761 26.116C19.0674 26.0742 18.9454 26.0759 18.8251 26.1162C18.7048 26.1566 18.5957 26.2321 18.4964 26.3164C18.2964 26.4882 18.108 26.6645 17.9314 26.8453C17.2247 27.5683 16.7047 28.3632 16.3714 29.2299C15.2047 32.2632 14.2921 35.3943 13.6336 38.623C13.5866 38.8537 13.5408 39.0848 13.4964 39.3164C13.4705 39.4526 13.4824 39.5973 13.5438 39.7243C13.6051 39.8512 13.7092 39.9493 13.8368 39.9983C13.9643 40.0474 14.1073 40.0443 14.2378 39.9912C14.3685 39.9381 14.4744 39.8387 14.5464 39.7203C14.6686 39.5186 14.7895 39.3163 14.9092 39.1136C16.584 36.2757 18.0047 33.3401 19.1714 30.3068C19.5047 29.4401 19.6514 28.5017 19.6114 27.4914C19.6014 27.2388 19.5797 26.9818 19.5464 26.7203Z', 1173 'M14.344 24.9792C14.3948 24.8596 14.437 24.7355 14.4453 24.6114C14.4535 24.4872 14.4266 24.3724 14.3619 24.28C14.2972 24.1876 14.1985 24.1229 14.079 24.0882C13.9596 24.0535 13.8286 24.0507 13.6988 24.0575C13.4605 24.0718 13.2293 24.0963 13.0053 24.131C12.1092 24.27 11.3279 24.5728 10.6612 25.0395C8.3279 26.6728 6.15067 28.5292 4.12956 30.6085C3.98519 30.7571 3.84163 30.9067 3.69885 31.0575C3.60449 31.1576 3.53525 31.2839 3.51601 31.4225C3.49671 31.561 3.52795 31.6989 3.60539 31.8096C3.68283 31.9202 3.80173 31.9968 3.93847 32.026C4.07528 32.0554 4.21768 32.0336 4.344 31.9792C4.53456 31.8966 4.72432 31.8129 4.91329 31.7281C7.55884 30.5408 10.0483 29.1305 12.3816 27.4972C13.0482 27.0305 13.6002 26.4 14.0375 25.6056C14.1468 25.4071 14.249 25.1982 14.344 24.9792Z', 1174 ]; 1175 1176 // Right sparkle paths (inverted) 1177 const rightPaths = [ 1178 'M19.6138 40.5453C19.5474 40.6582 19.494 40.7816 19.4803 40.9104C19.4666 41.0391 19.4934 41.1622 19.561 41.2615C19.6287 41.361 19.7332 41.431 19.858 41.4655C19.9829 41.5001 20.1173 41.4959 20.2467 41.4754C20.5389 41.4278 20.8239 41.3699 21.1021 41.3017C22.2147 41.0285 23.2146 40.59 24.1021 39.9862C27.2082 37.8728 30.1613 35.5343 32.9612 32.9708C33.1612 32.7877 33.3604 32.6035 33.5588 32.4181C33.6608 32.3225 33.7352 32.1964 33.7577 32.0561C33.7805 31.9159 33.7505 31.7744 33.6728 31.6602C33.595 31.5459 33.4745 31.4662 33.3357 31.4358C33.1969 31.4053 33.0523 31.4282 32.926 31.4879C32.6807 31.6044 32.4362 31.7221 32.1924 31.8409C28.78 33.5042 25.5207 35.3925 22.4144 37.5059C21.5271 38.1097 20.7521 38.8788 20.0896 39.8134C19.924 40.0471 19.7654 40.291 19.6138 40.5453Z', 1179 'M21.0855 45.6759C20.9587 45.7071 20.8333 45.7537 20.731 45.8313C20.6287 45.9091 20.558 46.0114 20.5318 46.1272C20.5056 46.243 20.5253 46.3658 20.5842 46.48C20.643 46.5941 20.7361 46.6902 20.8371 46.7731C21.0563 46.9509 21.2782 47.1164 21.5029 47.2698C22.4018 47.8836 23.3447 48.3021 24.3319 48.5257C27.787 49.308 31.3021 49.8248 34.8774 50.0762C35.1328 50.0942 35.3885 50.1107 35.6445 50.126C35.7835 50.1341 35.9259 50.102 36.0435 50.0228C36.1609 49.9439 36.2436 49.8259 36.274 49.6918C36.3044 49.5576 36.2805 49.4156 36.2085 49.2937C36.1366 49.1716 36.0219 49.0813 35.8929 49.0288C35.6553 48.9323 35.4174 48.8371 35.1792 48.7433C31.8444 47.4299 28.4495 46.3821 24.9945 45.5998C24.0073 45.3762 22.9759 45.3477 21.9005 45.5143C21.6316 45.556 21.36 45.6099 21.0855 45.6759Z', 1180 'M18.8038 49.6709C18.6943 49.6008 18.5822 49.5365 18.4665 49.5015C18.3505 49.4668 18.2383 49.4652 18.1377 49.5036C18.0372 49.542 17.9546 49.618 17.8914 49.7212C17.8284 49.8244 17.7878 49.9471 17.7529 50.0723C17.6974 50.2774 17.6537 50.478 17.6214 50.6742C17.493 51.4589 17.5514 52.1721 17.7966 52.8142C18.6548 55.0609 19.7674 57.2106 21.1343 59.2631C21.2319 59.4098 21.3308 59.5559 21.4311 59.7014C21.5085 59.8134 21.6143 59.9087 21.7429 59.9596C21.8714 60.0106 22.0104 60.0137 22.1344 59.9664C22.2583 59.919 22.3599 59.824 22.4216 59.7004C22.4835 59.5766 22.4989 59.4351 22.482 59.3C22.4596 59.1246 22.4359 58.9499 22.411 58.7754C22.0613 56.3344 21.4573 53.9904 20.5992 51.7436C20.3539 51.1017 19.9219 50.5311 19.303 50.0319C19.1482 49.9071 18.9819 49.7867 18.8038 49.6709Z', 1181 'M12.4825 50.0327C12.4559 49.9038 12.429 49.7792 12.3877 49.673C12.3458 49.5665 12.2889 49.4842 12.2088 49.4306C12.1288 49.377 12.031 49.3555 11.9166 49.3575C11.8026 49.3597 11.6772 49.3822 11.5478 49.4067C11.3743 49.4412 11.2111 49.4827 11.0583 49.5311C10.4471 49.7248 10.002 50.0298 9.72317 50.4462C8.7471 51.9034 7.99729 53.5121 7.47355 55.2723C7.43616 55.398 7.39994 55.5246 7.36479 55.6518C7.32987 55.7805 7.31856 55.9189 7.35237 56.0492C7.38608 56.1794 7.46103 56.29 7.56694 56.3609C7.67276 56.4318 7.80361 56.459 7.93683 56.4406C8.07014 56.4223 8.19377 56.3592 8.2995 56.2779C8.40382 56.197 8.50706 56.1154 8.6091 56.0328C10.0374 54.8787 11.2397 53.573 12.2156 52.1157C12.4946 51.6994 12.6073 51.1718 12.5538 50.5328C12.5404 50.373 12.5167 50.2063 12.4825 50.0327Z', 1182 'M9.74183 45.4203C9.81949 45.3154 9.89291 45.2104 9.94022 45.1029C9.98748 44.9948 10.0039 44.8895 9.98147 44.7896C9.95888 44.6897 9.89884 44.6017 9.80976 44.5244C9.72087 44.4476 9.60948 44.3843 9.49426 44.3229C9.32298 44.2334 9.15447 44.156 8.98857 44.091C8.32537 43.8305 7.70605 43.7652 7.13072 43.8949C5.1173 44.349 3.16376 45.0686 1.27003 46.0539C1.13477 46.1242 0.999815 46.1959 0.865096 46.269C0.746838 46.3337 0.640167 46.4254 0.57308 46.5444C0.505956 46.6633 0.483664 46.7981 0.512234 46.9252C0.540941 47.0523 0.619017 47.1645 0.730697 47.243C0.842276 47.3218 0.978129 47.3589 1.11266 47.3664C1.26566 47.3746 1.41829 47.3815 1.57073 47.387C3.70392 47.4641 5.77729 47.2755 7.79079 46.8214C8.36604 46.6916 8.89736 46.3668 9.3846 45.8468C9.50643 45.7168 9.62553 45.5747 9.74183 45.4203Z', 1183 'M11.2476 40.6451C11.3682 40.6948 11.4951 40.7331 11.6221 40.7344C11.749 40.7358 11.8655 40.6999 11.9561 40.6266C12.0467 40.5534 12.1062 40.4469 12.1315 40.3225C12.1566 40.1982 12.1458 40.0659 12.1224 39.9378C12.0736 39.6787 12.0152 39.4274 11.9469 39.184C11.6741 38.2105 11.2457 37.3627 10.6618 36.6407C8.6182 34.1137 6.36288 31.7578 3.896 29.5732C3.71971 29.4171 3.54247 29.2619 3.3641 29.1076C3.25909 29.0171 3.12783 28.9551 2.98715 28.9447C2.8466 28.9344 2.70959 28.9755 2.60335 29.0615C2.49708 29.1474 2.42827 29.2728 2.409 29.4123C2.38964 29.5521 2.42277 29.6935 2.48931 29.815C2.60288 30.0217 2.71763 30.2276 2.83333 30.4326C4.45362 33.3019 6.28557 36.0001 8.32914 38.5271C8.91307 39.2491 9.6524 39.8454 10.5473 40.3159C10.7711 40.4335 11.0045 40.5432 11.2476 40.6451Z', 1184 'M15.3567 37.0103C15.4349 37.1141 15.5212 37.2127 15.6245 37.2819C15.728 37.3511 15.8409 37.3852 15.9533 37.3754C16.0656 37.3656 16.
1184171 37.3124 16.2608 37.2263C16.3506 37.1402 16.4185 37.0282 16.4775 36.9124C16.5843 36.6989 16.6786 36.4864 16.7606 36.275C17.0883 35.4295 17.2167 34.6015 17.1459 33.7907C16.898 30.9534 16.379 28.1396 15.5888 25.3496C15.5323 25.1503 15.4745 24.9512 15.4153 24.7521C15.3758 24.6204 15.301 24.4973 15.1906 24.4113C15.0803 24.3253 14.9453 24.2834 14.8107 24.2951C14.6762 24.3069 14.5504 24.3716 14.4568 24.4754C14.3629 24.5792 14.3106 24.7134 14.2945 24.85C14.2708 25.0563 14.2484 25.2625 14.2273 25.4686C13.9328 28.3533 13.9094 31.2144 14.1572 34.0518C14.2281 34.8624 14.4981 35.6557 14.9674 36.4317C15.0847 36.6256 15.2145 36.8185 15.3567 37.0103Z', 1185 ]; 1186 1187 const pathsToUse = isRight ? rightPaths : leftPaths; 1188 pathsToUse.forEach((pathData) => { 1189 const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 1190 path.setAttribute('d', pathData); 1191 path.setAttribute('fill', color); 1192 svg.appendChild(path); 1193 }); 1194 1195 return svg; 1196} 1197 1198/** 1199 * Exports isIframe global variable and inIframeFunc function 1200 * in order to prevent JS from interfering with the 1201 * Universal Editor authoring experience 1202 */ 1203// eslint-disable-next-line import/no-mutable-exports 1204export let isIframe = false; 1205export function inIframeFunc() { 1206 if (window.location !== window.parent.location) { 1207 // The page is in an iFrames 1208 isIframe = true; 1209 } 1210} 1211 1212init(); 1213updateLinkTags(); 1214 1215export { 1216 buildBlock, 1217 createOptimizedPicture, 1218 decorateBlock, 1219 decorateBlocks, 1220 decorateButtons, 1221 decorateIcons, 1222 decorateSections, 1223 decorateTemplateAndTheme, 1224 fetchPlaceholders, 1225 getMetadata, 1226 loadBlock, 1227 loadBlocks, 1228 loadCSS, 1229 loadFooter, 1230 loadHeader, 1231 loadErrorPage, 1232 loadScript, 1233 readBlockConfig, 1234 sampleRUM, 1235 setup, 1236 toCamelCase, 1237 toClassName, 1238 stripHtmlTags, 1239 normalizeText, 1240 updateSectionsStatus, 1241 waitForLCP, 1242 wrapTextNodes, 1243 updateLinkTags, 1244 generateElement, 1245 getLocale, 1246 getBrand, 1247 removeNonAlphabetChars, 1248 removeKelloggs, 1249 removeKelloggsEpsilon, 1250 removeNonAlphabetCharsEpsilon, 1251 updateSeoTitle, 1252 getWkkcHomeUrl, 1253 getLocalizedPathByLocale, 1254 appendPathToUrl, 1255 toUrlSlug, 1256 buildBreadcrumbListSchema, 1257 createSparkleIcon, 1258};
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.