// This file includes scripts that should be applied to every page. // Either because they are needed for functionality (e.g. nav or identity) // or because they are needed on most pages and are diffuclt to apply // individually without repetition. // Capture and store UTM parameters from URL const captureUTMParameters = () => { // Check if Cookiebot consent object exists and marketing cookies are accepted if (typeof Cookiebot !== 'undefined' && !Cookiebot.consent.marketing) { return; // Don't capture UTM parameters if marketing cookies not accepted } const urlParams = new URLSearchParams(window.location.search); const utmParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']; utmParams.forEach(param => { const value = urlParams.get(param); // Only store if there's a value AND it doesn't already exist in localStorage if (value && !localStorage.getItem(param)) { localStorage.setItem(param, value); } }); }; // Run UTM capture on page load and when consent changes captureUTMParameters(); window.addEventListener('CookiebotOnAccept', captureUTMParameters); window.addEventListener('CookiebotOnDecline', captureUTMParameters); // Populate HubSpot form hidden fields with UTM parameters from local storage const populateHubSpotUTMFields = (formElement) => { setTimeout(() => { if (!formElement) { return; } const utmParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']; utmParams.forEach(param => { const value = localStorage.getItem(param); if (value) { const field = formElement.querySelector(`input[name="${param}"]`); if (field) { field.value = value; } } }); }, 500); }; // Clear UTM parameters from local storage after successful form submission const clearUTMParameters = () => { const utmParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']; utmParams.forEach(param => { localStorage.removeItem(param); }); }; // Make functions globally available for use with HubSpot forms window.populateHubSpotUTMFields = populateHubSpotUTMFields; window.clearUTMParameters = clearUTMParameters; // Detect touchscreen devices const detectTouchscreen = () => { let result = false; if (window.PointerEvent && "maxTouchPoints" in navigator) { // if Pointer Events are supported, just check maxTouchPoints if (navigator.maxTouchPoints > 0) { result = true; } } else { // no Pointer Events... if ( window.matchMedia && window.matchMedia("(any-pointer:coarse)").matches ) { // check for any-pointer:coarse which mostly means touchscreen result = true; } else if (window.TouchEvent || "ontouchstart" in window) { // last resort - check for exposed touch events API / event handler result = true; } } return result; }; // Transform polyfilling anchor links [...document.querySelectorAll("a[data-a11y-dialog-show]")].map((item) => { item.setAttribute("role", "button"); item.setAttribute("tabindex", "0"); item.removeAttribute("href"); }); // Field invalidation labels const fieldHints = [...document.querySelectorAll("[data-field-hint]")]; if (fieldHints) { fieldHints.map((hint) => { const input = document.getElementById(hint.dataset.fieldHint); const alertIcon = document.getElementById(`icon-${hint.dataset.fieldHint}`); if (input) { input.addEventListener("invalid", (event) => { event.target.setAttribute("aria-invalid", true); event.target.setAttribute("aria-describedby", hint.getAttribute("id")); hint.removeAttribute("hidden"), input.classList.add("input--invalid"), alertIcon.removeAttribute("hidden"); }); input.addEventListener("input", (event) => { event.target.removeAttribute("aria-invalid"); event.target.removeAttribute("aria-describedby"); hint.setAttribute("hidden", ""), input.classList.remove("input--invalid"), alertIcon && alertIcon.setAttribute("hidden", ""); }); } }); } // Desktop navigation scroll behaviour let previousScrollPosition = 0; const isScrollingUp = () => { let goingUp = false; let scrollPosition = window.scrollY; if (scrollPosition < previousScrollPosition) { goingUp = true; } previousScrollPosition = scrollPosition; return goingUp; }; const nav = document.querySelector(".navigation--primary"); const navCta = document.querySelector(".navigation__cta"); const handleScroll = () => { if (isScrollingUp()) { nav.classList.add("scroll-up"); nav.classList.remove("scroll-down"); navCta ? navCta.classList.remove("button--inverse") : undefined; } else if (window.scrollY > 50) { nav.classList.add("scroll-down"); nav.classList.remove("scroll-up"); } else { nav.classList.remove("scroll-up"); } if (window.scrollY === 0) { nav.classList.remove("scroll-up"); if (nav.classList.contains("navigation--inverse")) { navCta ? navCta.classList.add("button--inverse") : undefined; } } }; window.addEventListener("scroll", _.throttle(handleScroll, 100)); // Primary navigation toggle const navContainer = document.querySelector("[data-toggle]"); const navInnerContainer = document.querySelector(".navigation__container"); const navToggleButtonTemplate = document.querySelector( "#navToggleButtonTemplate" ); const navListWrapper = document.querySelector("#navListWrapper"); const dropdowns = [ ...document.querySelectorAll( ".navigation__dropdown, .navigation__dropdown-large" ), ]; if ( window.innerWidth < 847 && navContainer && navListWrapper && navToggleButtonTemplate ) { console.log("Adding mobile nav toggle"); // Insert hamburger before the list wrapper navListWrapper.insertAdjacentHTML( "beforebegin", navToggleButtonTemplate.innerHTML ); // Hide the wrapper initially navListWrapper.setAttribute("aria-hidden", true); // Select the inserted hamburger button const navToggle = navContainer.querySelector(".hamburger"); if (navToggle) { navToggle.setAttribute("aria-controls", "navListWrapper"); navToggle.setAttribute("aria-expanded", false); navToggle.addEventListener("click", () => { const isExpanded = navToggle.getAttribute("aria-expanded") === "true"; navToggle.setAttribute("aria-expanded", !isExpanded); navListWrapper.setAttribute("aria-hidden", isExpanded); if (isExpanded) { navInnerContainer.classList.remove("navigation__container--open"); navContainer.classList.remove("navigation--open"); if (nav.classList.contains("navigation--inverse")) { navCta && navCta.classList.add("button--inverse"); } } else { navInnerContainer.classList.add("navigation__container--open"); navContainer.classList.add("navigation--open"); if (nav.classList.contains("navigation--inverse")) { navCta && navCta.classList.remove("button--inverse"); } } }); } } // Primary navigation dropdowns const navItems = [ ...document.querySelectorAll(".navigation--primary .navigation__item"), ]; const backButtonTemplate = document.querySelector("#backButtonTemplate"); const navDropdowns = [...document.querySelectorAll("[data-dropdown]")]; if (navDropdowns && detectTouchscreen()) { navDropdowns.map((dropdown) => { // Grab the dropdown label and list let dropdownLabel = dropdown.querySelector("[data-dropdown-label]"); let dropdownList = document.getElementById(dropdown.dataset.dropdown); // Create a new button and replace the label with it let button = document.createElement("button"); button.classList.add("navigation__button"); button.setAttribute("aria-controls", dropdownList.getAttribute("id")); button.setAttribute("aria-expanded", false); button.innerHTML = dropdownLabel.dataset.dropdownLabel; dropdown.insertBefore(button, dropdown.childNodes[0]); dropdownLabel.setAttribute("hidden", ""); // Initially hide the dropdown list dropdownList.setAttribute("aria-hidden", true); // Open and close the dropdown list when the button is clicked or hovered const toggleDropdown = () => { let isExpanded = button.getAttribute("aria-expanded") === "true" || false; // Hide all the other dropdowns before showing the requested one if (!isExpanded) { navDropdowns.map((dropdown) => { dropdown.querySelector("button").setAttribute("aria-expanded", false); document .getElementById(dropdown.dataset.dropdown) .setAttribute("aria-hidden", true); }); navItems.map((item) => { if (item.dataset.dropdown !== dropdown.dataset.dropdown) { item.classList.add("mobile-hidden"); } else { item.classList.add("mobile-active"); button.innerHTML = backButtonTemplate.innerHTML; } }); } else { navItems.map((item) => { if (item.dataset.dropdown !== dropdown.dataset.dropdown) { item.classList.remove("mobile-hidden"); } else { item.classList.remove("mobile-active"); button.innerHTML = dropdownLabel.dataset.dropdownLabel; } }); } button.setAttribute("aria-expanded", !isExpanded); dropdownList.setAttribute("aria-hidden", isExpanded); }; button.addEventListener("click", toggleDropdown); }); } // Dialogs const dialogs = [...document.querySelectorAll("[data-a11y-dialog]")]; if (dialogs) { dialogs.map((dialog) => { dialog.addEventListener("show", function (event) { const iframe = dialog.querySelector("iframe"); if (iframe) { iframe.contentWindow.postMessage( JSON.stringify({ event: "command", func: "playVideo" }), "*" ); } }); dialog.addEventListener("hide", function (event) { const iframe = dialog.querySelector("iframe"); if (iframe) { iframe.contentWindow.postMessage( JSON.stringify({ event: "command", func: "pauseVideo" }), "*" ); } }); }); } // Netlify Identity const identityButtons = document.querySelectorAll("[data-identity-button]"); const urlParams = new URLSearchParams(window.location.search); const returnPath = urlParams.get("return"); netlifyIdentity.on("init", (user) => { if (user) { netlifyIdentity.refresh().catch((err) => console.error(err)); } // Prompt the login when the URL ends with "#login" if (location.href.endsWith("#login")) { netlifyIdentity.open(); } // Redirect to the process docs page when logging in for the first time if (!user) { netlifyIdentity.on("login", () => { location.href = `/${returnPath}/`; }); } // Redirect to the homepage when logging out netlifyIdentity.on("logout", () => { location.href = "/"; }); // Hide / show and add events to custom identity buttons if (identityButtons) { identityButtons.forEach((button) => { const buttonType = button.dataset.identityButton; // Only show the button if: // It's a log in button and there's no user to be found, or... // there is a user and it's a log out button if ( (!user && buttonType == "login") || (user && buttonType == "logout") ) { button.style.display = "inline-block"; } // Prompt the login when the login button is clicked, or... // log out if the log out button is clicked button.addEventListener("click", () => { if (buttonType == "login") { netlifyIdentity.open(); } if (buttonType == "logout") { netlifyIdentity.logout(); } }); }); } }); // Contact form hidden field for hubspot. // We update a hidden input field with the value of the company type and dietary requirements select input // because hubspot uses the label of select inputs instead of the value 🙄 // Hubspot can then use the correct value in the hidden field. const companyTypeSelect = document.getElementById("company_type_select"); const companyTypeHubspotField = document.getElementById("company_type"); const assetLevelSelect = document.getElementById("level_of_assets_select"); const assetLevelHubspotField = document.getElementById("level_of_assets"); companyTypeSelect && companyTypeSelect.addEventListener("change", (event) => { if (companyTypeHubspotField) { companyTypeHubspotField.setAttribute("value", event.target.value); } }); assetLevelSelect && assetLevelSelect.addEventListener("change", (event) => { if (assetLevelHubspotField) { assetLevelHubspotField.setAttribute("value", event.target.value); } });