window.codexData = window.codexData || {}; let headerNavigation = false; let headerNavigationFragment = ''; const oldViewName = window.codexData.view.name; function satelliteTrack(scrollViewTrack = false) { // eslint-disable-next-line no-underscore-dangle if (typeof window._satellite !== 'undefined') { // eslint-disable-next-line no-underscore-dangle let copyCodexData = Object.assign({}, codexData); if( scrollViewTrack ) { copyCodexData.content = {}; } const data = window._satellite.track('track', copyCodexData); } } /** * Does the element `el` have at least one of the CSS classnames in `classNames`? * @param {Element} el Element to test for the presence of at least one of the CSS classnames in `classList`. * @param {Array} [classNames=[]] CSS classnames to test against. * @returns {boolean} `false` if `classNames` is empty; otherwise whether or not `el` contains one of `classNames` in its `classList`. */ function elementHasSomeClassName(el, classNames = []) { return classNames.some(className => el.classList.contains(className)); } /** * Handle carousel button click event. * @param {HTMLElement} [slickElement=null] */ function carouselClickEvent(slickElement = null) { if (window.codexData) { let slickIdentify = ''; let slickArrowParentId = ''; let slickArrowParentIdName = ''; let pageName = ''; if (window.location.pathname === '/') { pageName = 'home'; } if (slickElement) { delete codexData.flow; const { flowComplete, flowStep, login, ...restEvents } = codexData.events; codexData.events = restEvents; const slickArrowElementClass = slickElement.className.split(' '); slickArrowParentId = slickElement.closest('section'); if (slickArrowParentId) { const { name = '' } = slickArrowParentId.dataset.analytics ? JSON.parse(slickArrowParentId.dataset.analytics) : {}; slickArrowParentIdName = name != '' ? ':' + name : ''; } if ( slickElement.dataset?.carouselArrow?.toLowerCase() === 'next' || elementHasSomeClassName(slickElement, ['slick-next', 'rotator-next-btn']) ) { slickIdentify = 'next'; } else if ( slickElement.dataset?.carouselArrow?.toLowerCase() === 'prev' || elementHasSomeClassName(slickElement, ['slick-prev', 'rotator-prev-btn']) ) { slickIdentify = 'previous'; } } window.codexData.content = {}; window.codexData.events.interaction = true; window.codexData.events.view = false; pageOrViewName = pageName ? ':' + pageName : ':' + oldViewName; window.codexData.interaction = { name: (`${window.codexData.app.name}${pageOrViewName}${slickArrowParentIdName}:carousel-${slickIdentify}`), }; delete window.codexData.view; satelliteTrack(); } } function getRandomAlphanumericAsciiCharacter() { const CHARACTER_RANGE = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const randomIndex = Math.floor(Math.random() * CHARACTER_RANGE.length); return CHARACTER_RANGE[randomIndex]; } function getRandomAlphanumericAsciiId(length) { if (!(length > 0)) { throw new Error('getRandomAlphanumericAsciiId must be called with a non-zero positive length'); } const id = Array(length) .fill('') .map(() => getRandomAlphanumericAsciiCharacter()); return id.join(''); } function createSerializationId(idPrefix, id, timeMs) { return `${idPrefix}${id}${timeMs}`; } /** * @summary Use this function as a hook for successful newsletter signups. * Call this function where you know the signup is successful, and it will take care of the analytics fields. * @param {string} [flowName] Optionally override `flow.name`; otherwise will use default of `${app.name}-newsletter-signup` * @returns {void} */ function handleSuccessMessageAnalytics(flowName = null) { if (window.codexData) { // Get the app name from the codexData object, otherwise use `'corus'` as a default. const namePrefix = window?.codexData?.app?.name ?? 'corus'; // Get the 2-letter app ID from the codexData object, otherwise use `'CE'` as a default. const idPrefix = window?.codexData?.misc?.serializationIdPrefix ?? 'CE'; const oldCodexData = window.codexData; const flowNameText = flowName ? flowName : `${namePrefix}-newsletter-signup`; const newCodexData = { ...oldCodexData, events: { flowComplete: true, flowStep: true, newsletterSignup: true, view: false, // newsletter signup is not a page view }, flow: { name: flowNameText, serializationid: createSerializationId( idPrefix, getRandomAlphanumericAsciiId(4), Date.now(), ), stepName: 'complete', }, }; window.codexData = newCodexData; satelliteTrack(); } } if (window.codexData) { // Trigger analytics for anchor links, buttons, or input or option elements when they have a 'data-analytics' attribute const navOrBtnItems = document.querySelectorAll('a[data-analytics], button[data-analytics], input[data-analytics], option[data-analytics]'); let navOrBtnItemName = ''; navOrBtnItems.forEach((navOrBtnItem) => { const { position = '', name = '', module = '', destination = '' } = JSON.parse(navOrBtnItem.dataset.analytics); // By default first click should expand accordion let expand = true; navOrBtnItem.addEventListener('click', () => { const { href: url = '' } = navOrBtnItem; const [, fragmentUrl] = url.split('#'); if (fragmentUrl) { headerNavigation = true; headerNavigationFragment = fragmentUrl; } window.codexData.events = window.codexData.events || {}; window.codexData.content = {}; window.codexData.events.interaction = true; window.codexData.events.view = false; let pageName = ''; if (window.location.pathname === '/') { pageName = 'home'; } sectionNavOrBtnItem = navOrBtnItem.closest('section'); if (sectionNavOrBtnItem) { const { name = '' } = sectionNavOrBtnItem.dataset.analytics ? JSON.parse(sectionNavOrBtnItem.dataset.analytics) : {}; navOrBtnItemName = name != '' ? ':' + name : ''; } pageOrViewName = pageName ? ':' + pageName : ':' + oldViewName; if (module) { if (module == 'share') { window.codexData.content = {}; window.codexData.events.share = true; window.codexData.share = {type : name}; window.codexData.interaction = { name: (`${window.codexData.app.name}:${pageOrViewName}${navOrBtnItemName}:${module}`).replace(/(\W)\W*/g, '$1'), }; delete window.codexData.view; } if (module == 'faq') { window.codexData.content = {}; let expandOrCollapse = expand ? 'expand' : 'collapse'; if(expandOrCollapse == 'expand') { window.codexData.interaction = { name: (`${window.codexData.app.name}:${pageOrViewName}${navOrBtnItemName}:${module}:${name}:${expandOrCollapse}`).replace(/(\W)\W*/g, '$1'), }; // after expand, next on click event should collapse the accordion expand = false; } else { delete window.codexData.events.interaction; delete window.codexData.interaction; expand = true; return; } delete window.codexData.view; } /** * Set data layer fields for items that direct users to external sites and applications. * Use the `destination` property to indicate where the user is going using a slug. * For example, `apple-podcasts`, `spotify`, etc. */ if (module === 'exit-link' && destination) { window.codexData.content = {}; const interactionName = ( `${window.codexData.app.name}:${pageOrViewName}${navOrBtnItemName}:${module}:${destination}` ).replace(/(\W)\W*/g, '$1'); window.codexData.interaction = { name: interactionName, }; delete window.codexData.view; } } else { const positionName = position ? ':' + position : ':'; window.codexData.content = {}; window.codexData.interaction = { name: (`${window.codexData.app.name}:${pageOrViewName}${navOrBtnItemName}${positionName}:${name}`).replace(/(\W)\W*/g, '$1'), }; delete window.codexData.view; } satelliteTrack(); }); }); //==================================================================================================================// // NEWSLETTER PAGE FORM INITIAL FLOW SERIALIZATION ID // //==================================================================================================================// // If this is the Newsletter Page if (window.codexData.content.type === 'page' && window.codexData.content.title.toLowerCase().includes('newsletter')) { /** @type {NodeListOf} */ const newsletterForms = document.querySelectorAll('form.newsletter-form'); /** * Create the initial newsletter flow serialization ID. */ if (newsletterForms.length > 0) { // Get the 2-letter app ID from the codexData object, otherwise use `'CE'` as a default. const idPrefix = window?.codexData?.misc?.serializationIdPrefix ?? 'CE'; // Ensure codexData.flow is defined. window.codexData.flow = window.codexData.flow || {}; // Set the flow serialization ID, using the user's current client-side time in ms. window.codexData.flow.serializationid = createSerializationId( idPrefix, getRandomAlphanumericAsciiId(4), Date.now() ); } } //==================================================================================================================// // END NEWSLETTER PAGE FORM INITIAL FLOW SERIALIZATION ID // //==================================================================================================================// // Trigger page views on scroll when section has // 'data-analytics' and 'data-analytics-view' attributes (() => { let ticking = false; function triggerAnalytics() { ticking = false; const elements = document.querySelectorAll('section[data-analytics][data-analytics-view=false]'); elements.forEach((element) => { const elementTop = element.getBoundingClientRect().top; const elementBottom = element.getBoundingClientRect().bottom; const windowHeight = window.innerHeight; if (elementTop < windowHeight && windowHeight < elementBottom) { const sectionID = element.getAttribute('id'); if (sectionID && headerNavigation) { if (sectionID === headerNavigationFragment) { headerNavigation = false; } else { return; } } window.codexData = window.codexData || {}; window.codexData.view.type = 'section'; const dataAnalytics = JSON.parse(element.dataset.analytics); window.codexData.view.name = dataAnalytics.name; window.codexData.events.view = true; window.codexData.events.interaction = false; if( typeof(window.overrideCodexDataConfig) === 'function' ) { window.codexData = window.overrideCodexDataConfig(window.codexData); } satelliteTrack(true); element.setAttribute('data-analytics-view', true); } }); } /** * For Slick Slider ( Carousel ) arrow button click ( Next / Previous ) * * The arrow buttons should have one of the following to indicate it is a next / previous button: * - a `data-carousel-arrow` attribute. Direction then depends on attribute value being `next` or `prev`. * - a classlist including `slick-arrow`. Direction then depends on including a `slick-(next/prev)` class. * - a classlist including `rotator-arrow`. Direction then depends on including a `rotator-(next/prev)-btn` class. * * Additionally, the buttons should NOT have a classlist including `fullSchedule-arrow` or `gallery-next`. * * @param {MouseEvent} event */ function handleCarouselClick(event) { const slickArrowElement = event.target; const hasCarouselArrowDatasetOrClassNames = ( slickArrowElement.dataset?.carouselArrow /* data-carousel-arrrow */ || elementHasSomeClassName(slickArrowElement, ['slick-arrow', 'rotator-arrow']) ); if ( hasCarouselArrowDatasetOrClassNames && !slickArrowElement.classList.contains('fullSchedule-arrow') ) { if (!slickArrowElement.classList.contains('gallery-next')) { carouselClickEvent(slickArrowElement, true); } } } document.addEventListener('click', handleCarouselClick); function loadRequest() { // eslint-disable-next-line no-underscore-dangle if (window._satellite !== 'undefined') { ticking = ticking || requestAnimationFrame(triggerAnalytics); } } window.addEventListener('scroll', loadRequest, false); })(); //==================================================================================================================// // LIGHT / DARK MODE THEME TOGGLE // //==================================================================================================================// // Set data layer field for the state of the dark / light mode theme toggle, // otherwise the browser / device theme preference. // Field is of the form `{dark|light}:{system|user}` /** * Get the value of the `user_chosen_theme` cookie and verify it is either `'dark'` or `'light'`. * Returns `undefined` if cookie does not exist or does not contain `'dark'` or `'light'`. * @returns {'dark' | 'light' | undefined} */ const getUserChosenThemeCookieValue = () => { // Adapated from MDN example. const cookieValue = document.cookie .split('; ') .find((row) => row.startsWith('user_chosen_theme=')) ?.split('=')[1]; // Guard against unknown values if (['dark', 'light'].includes(cookieValue)) { return cookieValue; } return undefined; }; // Just in case window.codexData.view = window.codexData.view || {}; const userChosenThemeCookieValue = getUserChosenThemeCookieValue(); if (userChosenThemeCookieValue) { window.codexData.view.appearance = `${userChosenThemeCookieValue}:user`; } else { // Note that `light` could also indicate no preference / use browser or device settings const isBrowserOrSystemDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; window.codexData.view.appearance = `${isBrowserOrSystemDarkMode ? 'dark' : 'light'}:system`; } //==================================================================================================================// // END LIGHT / DARK MODE THEME TOGGLE // //==================================================================================================================// } ; /*! This file is auto-generated */ (()=>{"use strict";var t={d:(e,n)=>{for(var r in n)t.o(n,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{actions:()=>P,addAction:()=>A,addFilter:()=>m,applyFilters:()=>w,applyFiltersAsync:()=>I,createHooks:()=>h,currentAction:()=>x,currentFilter:()=>T,defaultHooks:()=>f,didAction:()=>j,didFilter:()=>z,doAction:()=>g,doActionAsync:()=>k,doingAction:()=>O,doingFilter:()=>S,filters:()=>Z,hasAction:()=>_,hasFilter:()=>v,removeAction:()=>p,removeAllActions:()=>F,removeAllFilters:()=>b,removeFilter:()=>y});const n=function(t){return"string"!=typeof t||""===t?(console.error("The namespace must be a non-empty string."),!1):!!/^[a-zA-Z][a-zA-Z0-9_.\-\/]*$/.test(t)||(console.error("The namespace can only contain numbers, letters, dashes, periods, underscores and slashes."),!1)};const r=function(t){return"string"!=typeof t||""===t?(console.error("The hook name must be a non-empty string."),!1):/^__/.test(t)?(console.error("The hook name cannot begin with `__`."),!1):!!/^[a-zA-Z][a-zA-Z0-9_.-]*$/.test(t)||(console.error("The hook name can only contain numbers, letters, dashes, periods and underscores."),!1)};const o=function(t,e){return function(o,i,s,c=10){const l=t[e];if(!r(o))return;if(!n(i))return;if("function"!=typeof s)return void console.error("The hook callback must be a function.");if("number"!=typeof c)return void console.error("If specified, the hook priority must be a number.");const a={callback:s,priority:c,namespace:i};if(l[o]){const t=l[o].handlers;let e;for(e=t.length;e>0&&!(c>=t[e-1].priority);e--);e===t.length?t[e]=a:t.splice(e,0,a),l.__current.forEach((t=>{t.name===o&&t.currentIndex>=e&&t.currentIndex++}))}else l[o]={handlers:[a],runs:0};"hookAdded"!==o&&t.doAction("hookAdded",o,i,s,c)}};const i=function(t,e,o=!1){return function(i,s){const c=t[e];if(!r(i))return;if(!o&&!n(s))return;if(!c[i])return 0;let l=0;if(o)l=c[i].handlers.length,c[i]={runs:c[i].runs,handlers:[]};else{const t=c[i].handlers;for(let e=t.length-1;e>=0;e--)t[e].namespace===s&&(t.splice(e,1),l++,c.__current.forEach((t=>{t.name===i&&t.currentIndex>=e&&t.currentIndex--})))}return"hookRemoved"!==i&&t.doAction("hookRemoved",i,s),l}};const s=function(t,e){return function(n,r){const o=t[e];return void 0!==r?n in o&&o[n].handlers.some((t=>t.namespace===r)):n in o}};const c=function(t,e,n,r){return function(o,...i){const s=t[e];s[o]||(s[o]={handlers:[],runs:0}),s[o].runs++;const c=s[o].handlers;if(!c||!c.length)return n?i[0]:void 0;const l={name:o,currentIndex:0};return(r?async function(){try{s.__current.add(l);let t=n?i[0]:void 0;for(;l.currentIndex0:Array.from(r.__current).some((t=>t.name===n))}};const u=function(t,e){return function(n){const o=t[e];if(r(n))return o[n]&&o[n].runs?o[n].runs:0}};class d{constructor(){this.actions=Object.create(null),this.actions.__current=new Set,this.filters=Object.create(null),this.filters.__current=new Set,this.addAction=o(this,"actions"),this.addFilter=o(this,"filters"),this.removeAction=i(this,"actions"),this.removeFilter=i(this,"filters"),this.hasAction=s(this,"actions"),this.hasFilter=s(this,"filters"),this.removeAllActions=i(this,"actions",!0),this.removeAllFilters=i(this,"filters",!0),this.doAction=c(this,"actions",!1,!1),this.doActionAsync=c(this,"actions",!1,!0),this.applyFilters=c(this,"filters",!0,!1),this.applyFiltersAsync=c(this,"filters",!0,!0),this.currentAction=l(this,"actions"),this.currentFilter=l(this,"filters"),this.doingAction=a(this,"actions"),this.doingFilter=a(this,"filters"),this.didAction=u(this,"actions"),this.didFilter=u(this,"filters")}}const h=function(){return new d},f=h(),{addAction:A,addFilter:m,removeAction:p,removeFilter:y,hasAction:_,hasFilter:v,removeAllActions:F,removeAllFilters:b,doAction:g,doActionAsync:k,applyFilters:w,applyFiltersAsync:I,currentAction:x,currentFilter:T,doingAction:O,doingFilter:S,didAction:j,didFilter:z,actions:P,filters:Z}=f;(window.wp=window.wp||{}).hooks=e})();;