document.addEventListener('DOMContentLoaded', () => { let url = new URL(window.location.href); if (url.searchParams.get("tfa_next")) { // Assuming there is only one of these on a page, select the first (document.querySelector(".component--find-contact") || document.querySelector(".component--form-assembly"))?.scrollIntoView(); } });; document.addEventListener("DOMContentLoaded", () => { clearSearch = () => { document.getElementById("header-input-search").value = ""; document.getElementById("header-input-search").dispatchEvent(new Event("input")); } document.getElementById("btn-header-search-cancel").addEventListener("click", () => { clearSearch(); document.getElementById("header-input-search").blur(); }); document.getElementById("btn-header-search-cancel").addEventListener("click", () => { document.getElementById("div-header-search").classList.remove("mobile-show"); document.getElementById("search-icon-activate").blur(); clearSearch(); }); document.getElementById("header-input-search").addEventListener("blur", (e) => { if (document.getElementById("search-form").contains(e.relatedTarget)) { return; } hideHeaderSuggestions(); }); document.querySelector("div.search-form-inner").addEventListener("focusout", (e) => { if (document.querySelector("div.search-form-inner").contains(e.relatedTarget)) { // Newly focused element still lives inside this div. // Not sure why focusout fires in this case, but leave the suggestions visible. return; } hideHeaderSuggestions(); }); document.getElementById("input-search")?.parentNode.addEventListener("focusout", (e) => { if (document.getElementById("input-search").parentNode.contains(e.relatedTarget)) { // Newly focused element still lives inside this div. // Not sure why focusout fires in this case, but leave the suggestions visible. return; } document.getElementById("search-suggestions").style.display = "none"; }); document.getElementById("header-input-search").addEventListener("focus", () => { document.getElementById("div-header-search").classList.add("search-active"); }); document.getElementById("search-icon-activate").addEventListener("click", () => { document.getElementById("div-header-search").classList.add("search-active"); document.getElementById("div-header-search").classList.add("mobile-show"); document.getElementById("header-input-search").focus(); }); document.getElementById("header-input-search").addEventListener( "input", debounce(async () => { await DisplaySuggestions("header-input-search", "header-search-suggestions", true); }, 250) ); document.getElementById('header-input-search').addEventListener( 'keydown', evt => { if (evt.key === "ArrowDown") { evt.preventDefault(); document.querySelector('#header-search-suggestions > a').focus(); } } ); document.getElementById('input-search')?.addEventListener( 'keydown', evt => { if (evt.key === "ArrowDown") { evt.preventDefault(); document.querySelector('#search-suggestions a').focus(); } } ); // When clicking within the suggestion div, the mousedown event triggers the searchbox blur, which hides the link and prevents navigating to it. // https://stackoverflow.com/a/62849598/5329728 document.getElementById('header-search-suggestions')?.addEventListener("mousedown", ev => ev.preventDefault()); document.getElementById('search-suggestions')?.addEventListener("mousedown", ev => ev.preventDefault()); let lastScrollTop = 0; const navbar = document.getElementById('navbar_top'); const updatePadding = () => { document.body.style.paddingTop = navbar.offsetHeight + 'px'; }; const hideHeaderSuggestions = () => { clearSearch(); document.getElementById("div-header-search").classList.remove("search-active"); document.getElementById("div-header-search").classList.remove("mobile-show"); document.getElementById("header-search-suggestions").style.display = "none"; } updatePadding(); window.addEventListener('scroll', function () { updatePadding(); const st = window.pageYOffset || document.documentElement.scrollTop; // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426" // Scroll down if (st > lastScrollTop) { if (window.scrollY > navbar.offsetHeight) { navbar.classList.remove('semi-stick'); // If we are hiding the menu, collapse any sub-menus // https://getbootstrap.com/docs/5.2/components/dropdowns/#via-javascript const dropdownElementList = navbar.querySelectorAll('.dropdown-toggle'); dropdownElementList.forEach((dd) => { const dropdown = new bootstrap.Dropdown(dd); dropdown.hide(); }); } } // Scroll up else if (st < lastScrollTop) { if (window.scrollY > navbar.offsetHeight) { navbar.classList.add('semi-stick'); } } lastScrollTop = st <= 0 ? 0 : st; // For Mobile or negative scrolling }); });; document.addEventListener("DOMContentLoaded", () => { // Tag dropdown let dropdown = document.querySelector(".component--browse-tagged-content .filter-dropdown"); dropdown?.addEventListener("change", (e) => { let url = new URL(window.location.href); let tag = e.target.value; if (tag === "") { url.searchParams.delete("tag"); } else { url.searchParams.set("tag", tag); } url.searchParams.delete("skip"); url.hash = "#results"; window.location.href = url.href; }); });; document.addEventListener("DOMContentLoaded", async () => { let savedFilters = window.localStorage?.getItem(FILTERS_KEY); document .getElementById("all-resources-sort-dropdown") ?.addEventListener("change", (e) => { const url = new URL(window.location.href); url.searchParams.set("sort", e.target.value); url.searchParams.delete("skip"); url.hash = "#div-all-resources-results"; if (!savedFilters) { savedFilters = {}; } savedFilters["sort"] = e.target.value; window.localStorage.setItem(FILTERS_KEY, JSON.stringify(savedFilters)); window.location.href = url.href; }); });; document.addEventListener("DOMContentLoaded", async () => { if (!window.location.hash) { return; } // Remove # from the hash let anchor = window.location.hash.substring(1); let element = document.querySelector(`[data-anchor="${anchor}"]`); if (!element) { return; } // Browser default behavior is to scroll to the top of the element which makes the item hidden behind the header. element.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest", }); // Handle accordion items if (element.classList.contains("accordion-button")) { let accordion = element.closest("div.accordion"); if (!accordion) { return; } let collapseList = [...accordion.querySelectorAll("div.collapse")].map( (collapseEl) => new bootstrap.Collapse(collapseEl, { toggle: false }) ); for (let collapse of collapseList) { if (collapse._element.id === element.getAttribute("data-bs-target")?.substring(1)) { collapse.show(); } else { collapse.hide(); } } } // Handle Switchable containers if (element.getAttribute("role") === "tab") { let parentSwitchableContainer = element.closest("section.component--switchable-container"); if (!parentSwitchableContainer) { return; } parentSwitchableContainer.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest", }); element.click(); let dropdown = parentSwitchableContainer.querySelector("select.sc-nav-select"); dropdown.value = element.getAttribute("data-bs-target")?.substring(1); } });; document.addEventListener("DOMContentLoaded", () => { let cardCarousels = document.querySelectorAll(".card-carousel"); cardCarousels.forEach((carousel) => configureCarousel(carousel)); }); const configureCarousel = (carousel) => { new bootstrap.Carousel(carousel, { interval: false, }); const prevButton = carousel.querySelector(".carousel-control-prev"); const nextButton = carousel.querySelector(".carousel-control-next"); const carouselInner = carousel.querySelector(".carousel-inner"); let carouselWidth = carouselInner.scrollWidth; let cardWidth = carouselInner.firstElementChild.clientWidth; let scrollPosition = 0; updateScrollButtons(); window.addEventListener('resize', function (event) { carouselWidth = carouselInner.scrollWidth; cardWidth = carouselInner.firstElementChild.clientWidth; }, true); let buttonUpdate; carouselInner.addEventListener('scroll', (event) => { scrollPosition = event.srcElement.scrollLeft; // Prevent rapid-firing the button update, only trigger once after last scroll clearTimeout(buttonUpdate); buttonUpdate = setTimeout(updateScrollButtons, 100); }); carousel.addEventListener('slide.bs.carousel', event => { // Yes, these event directions looks bass-ackwards. // It's the direction the carousel is physically sliding. // So if I want to "move right", the cards slide left. if (event.direction === 'left' && canScrollRight()) { scrollPosition += cardWidth; carouselInner.scrollTo({ left: scrollPosition, behavior: 'smooth' }); updateScrollButtons(); } if (event.direction === 'right' && canScrollLeft()) { scrollPosition = Math.max(scrollPosition - cardWidth, 0); carouselInner.scrollTo({ left: scrollPosition, behavior: 'smooth' }); updateScrollButtons(); } }); function canScrollLeft() { return scrollPosition > cardWidth/2; } function canScrollRight() { return scrollPosition < carouselWidth - carouselInner.clientWidth - cardWidth / 2; }; function updateScrollButtons() { if (canScrollLeft()) { prevButton.classList.remove('disabled'); } else { prevButton.classList.add('disabled'); } if (canScrollRight()) { nextButton.classList.remove('disabled'); } else { nextButton.classList.add('disabled'); } }; };; document.addEventListener("DOMContentLoaded", () => { const prevButton = document.querySelector("button.filter-bar-button-prev"); const nextButton = document.querySelector("button.filter-bar-button-next"); const filterScroll = document.querySelector("div.filter-bar-scrollable"); if (!(prevButton && nextButton && filterScroll)) { return; } const scrollDuration = 500; let filterPositions = getFilterPositions(); let activeIndex = 0; getActiveIndex(); function getFilterPositions() { fp = []; document.querySelectorAll('.filter-bar-scrollable a.nav-link').forEach(function (div) { fp.push([div.offsetLeft, div.offsetLeft + div.offsetWidth]); // add to array the positions information }) return fp; } function getActiveIndex() { let arr = Array.from(filterScroll.children); let child = arr.filter(c => c.classList.contains('search-type-filter-active'))[0]; // Temporarily set the activeIndex to the "active" element activeIndex = arr.indexOf(child); // Scroll the viewport scrollFilters(0); // Wait for the scroll to finish. // Note, there is no (official/consistent) scrollEnd event setTimeout(function () { // Find the first element that is fully visible activeIndex = arr.findIndex(e => e.offsetLeft >= filterScroll.scrollLeft); }, scrollDuration); } function scrollFilters(offset) { // Scroll next if (offset === 1 && canScrollRight()) { activeIndex++; } // Scroll prev if (offset === -1 && canScrollLeft()) { activeIndex--; } filterScroll.scrollTo({ left: filterPositions[activeIndex][0], behavior: 'smooth' }); setTimeout(function () { updateScrollButtons(); }, scrollDuration); } function canScrollLeft() { return activeIndex > 0 || filterScroll.scrollLeft > 0; } function canScrollRight() { const right = filterScroll.scrollLeft + filterScroll.clientWidth; const lastElement = filterPositions[filterPositions.length - 1]; return right < lastElement[1] - 1; }; function updateScrollButtons() { const prev = document.querySelector(".filter-bar-control.filter-bar-prev"); const next = document.querySelector(".filter-bar-control.filter-bar-next"); if (canScrollLeft()) { prev.classList.remove('disabled'); } else { prev.classList.add('disabled'); } if (canScrollRight()) { next.classList.remove('disabled'); } else { next.classList.add('disabled'); } }; nextButton.addEventListener("click", () => { scrollFilters(1); }); prevButton.addEventListener("click", () => { scrollFilters(-1); }); });; document.addEventListener("DOMContentLoaded", () => { document.getElementById("input-search")?.addEventListener("input", debounce(async () => { await DisplaySuggestions("input-search", "search-suggestions", false); }, 250) ); // Search dropdown document.querySelector(".search-type-filter-dropdown")?.addEventListener("change",(e) => { let url = new URL(window.location.href); let filterType = e.target.value; if (filterType === "None") { url.searchParams.delete("type"); } else { url.searchParams.set("type", filterType); } url.searchParams.delete("skip"); url.hash = "#results"; window.location.href = url.href; }); });; const FILTERS_KEY = "savedFilters"; const AllResourcesFiltersURL = `/umbraco/Surface/AllResources/GetFilters`;; const stateStorageName = 'schoolsEducatorConsultantState'; document.addEventListener('DOMContentLoaded', () => { if (typeof requestTypeSelectId === 'undefined' || typeof stateIds === 'undefined' || typeof levelIds === 'undefined') { return; } // Page must register an array of strings state select IDs, and education level stateIds?.forEach(stateId => { let stateSelect = document.getElementById(stateId); stateSelect && stateSelect.addEventListener('change', stateChangeListener); }); levelIds?.forEach(levelId => { let levelSelect = document.getElementById(levelId); levelSelect && levelSelect.addEventListener('change', levelChangeListener); }); // Register request type select box listener document.getElementById(requestTypeSelectId)?.addEventListener('change', sendRequest); prefillLevels(); checkForStoredState(); sendRequest(); }); const prefillLevels = () => { var path = new URL(window.location.href).pathname; if (path.includes(window.ApplicationVirtualPath)) { path = path.substring(window.ApplicationVirtualPath.length); } var urlPathParts = path.split('/'); let level = urlPathParts.at(1)?.toLowerCase(); if (!isLevelValid(level)) { return; } setAllDropdowns(levelIds, level); }; const saveState = (state) => { if (!state) { return; } localStorage.setItem(stateStorageName, state); }; const checkForStoredState = () => { let state = localStorage.getItem(stateStorageName); if (!state) { return; } setAllDropdowns(stateIds, state); }; const setAllDropdowns = (dropdownList, text) => { dropdownList && dropdownList.forEach(id => { let select = document.getElementById(id); setSelection(select, text); }); }; const setSelection = (dropdown, text) => { if (!dropdown) { return; } const index = Array .from(dropdown.options) .findIndex(option => option.label.toLowerCase() === text.toLowerCase()); if (!index) { return; } dropdown.selectedIndex = index }; const stateChangeListener = (event) => { let state = event.target.selectedOptions[0].text.toLowerCase(); saveState(state); setAllDropdowns(stateIds, state); sendRequest(); }; const levelChangeListener = (event) => { let level = event.target.selectedOptions[0].text.toLowerCase(); setAllDropdowns(levelIds, level); sendRequest(); }; const sendRequest = () => { // If a national consultant is set, don't send AJAX request if (document.getElementById("findConsultantResultsDiv").dataset.nationalConsultant === "True") { return; } let stateSelectedOption = document.getElementById(stateIds[0]).selectedOptions[0]; let state = stateSelectedOption.text.toLowerCase(); let level = document.getElementById(levelIds[0]).selectedOptions[0].text.toLowerCase(); // States come from FormAssembly // The initial "Please select..." option isn't identified by a 'disabled' attribute, // but we can identify it because it doesn't have a 'value' attribute if (!isLevelValid(level) || !stateSelectedOption.value) { return; } let requestTypeSelect = document.getElementById(requestTypeSelectId); // If the selected request type isn't in the list of IDs, clear and quit if (!showConsultantForRequestIds.includes(requestTypeSelect.value)) { updateConsultantResults(); return; } fetch(`${window.SurfaceControllerPath}/umbraco/Surface/EducationConsultant/GetConsultant?state=${state}&educationLevel=${level}`) .then((response) => response.text()) .then((data) => updateConsultantResults(data)) .catch((error) => { console.error('Error:', error); updateConsultantResults(); }); }; const isLevelValid = (level) => { return level === "primary" || level === "secondary"; }; const updateConsultantResults = (consultantDetails) => { document.getElementById("findConsultantResultsCard").innerHTML = consultantDetails; document.getElementById("findConsultantResultsDiv").style.display = consultantDetails ? "block" : "none"; };; document.addEventListener("DOMContentLoaded", () => { let copyLinks = document.querySelectorAll(".blog-copy-link"); copyLinks.forEach((link) => { let toolTip = new bootstrap.Tooltip(link); link.addEventListener("click", (event) => { event.preventDefault(); navigator .clipboard .writeText(window.location.href) // If for some reason permission is denied, fall-back to the old-school hack of copying text .catch(err => { console.error(err); copyStringToClipboard(window.location.href); }); toolTip.show(); setTimeout(() => { toolTip.hide(); }, 1500) }); }); document.querySelectorAll(".blog-device-share").forEach((link) => { if (!navigator.share) { console.error("sharing not supported"); return; } link.addEventListener("click", event => { event.preventDefault(); navigator.share({ title: document.title, url: location.href }) .then(() => console.log('Successful share')) .catch((error) => console.log('Error sharing', error)); }) }); }); // Fallback method for writing to clipboard // https://techoverflow.net/2018/03/30/copying-strings-to-the-clipboard-using-pure-javascript/ function copyStringToClipboard(str) { console.log() // Create new element var el = document.createElement('textarea'); // Set value (string to be copied) el.value = str; // Set non-editable to avoid focus and move outside of view el.setAttribute('readonly', ''); el.style = { position: 'absolute', left: '-9999px' }; document.body.appendChild(el); // Select text inside element el.select(); // Copy text to clipboard document.execCommand('copy'); // Remove temporary element document.body.removeChild(el); }; document.addEventListener('DOMContentLoaded', () => { // Register listener for a nav select change event document.querySelectorAll('select.sc-nav-select').forEach((select) => { select.addEventListener('change', function(event) { // Remove the "show" and "active" classes from any associated panel Array.from(this.options).forEach((option) => { // I would like to skip any entries that don't have a Value set. // For some unknown reason, if the