document.addEventListener("DOMContentLoaded", () => { const pageHeader = document.querySelector(".page-header"); const toggleMenu = pageHeader.querySelector(".toggle-menu"); const menuWrapper = pageHeader.querySelector(".mobile-menu-wrapper"); const level1Links = pageHeader.querySelectorAll(".level-1 > li > a"); const listWrapper2 = pageHeader.querySelector(".mobile-menu-section:nth-child(2)"); const subMenuWrapper2 = listWrapper2.querySelector(".sub-menu-wrapper"); const backOneLevelBtns = pageHeader.querySelectorAll(".back-button"); const isVisibleClass = "is-visible"; const isActiveClass = "is-active"; toggleMenu.addEventListener("click", function () { menuWrapper.classList.toggle(isVisibleClass); if (!this.classList.contains(isVisibleClass)) { listWrapper2.classList.remove(isVisibleClass); const menuLinks = menuWrapper.querySelectorAll("a"); for (const menuLink of menuLinks) { menuLink.classList.remove(isActiveClass); } } }); for (const level1Link of level1Links) { level1Link.addEventListener("click", function (e) { const siblingList = level1Link.nextElementSibling; if (siblingList) { e.preventDefault(); this.classList.add(isActiveClass); const cloneSiblingList = siblingList.cloneNode(true); subMenuWrapper2.innerHTML = ""; subMenuWrapper2.append(cloneSiblingList); listWrapper2.classList.add(isVisibleClass); } }); } for (const backOneLevelBtn of backOneLevelBtns) { backOneLevelBtn.addEventListener("click", function () { const parent = this.closest(".mobile-menu-section"); parent.classList.remove(isVisibleClass); parent.previousElementSibling .querySelector(".is-active") .classList.remove(isActiveClass); }); } });; 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"; };; // Debounce function from: https://stackoverflow.com/q/24004791/1814486 const debounce = (func, wait, immediate) => { let timeout; return function () { const context = this, args = arguments; const later = function () { timeout = null; if (!immediate) func.apply(context, args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; var searchRequest = true; var searchRequestAbortController = new AbortController(); const DisplaySuggestions = async (searchInputId, suggestionsDivId, fullPreview) => { let query = document.getElementById(searchInputId)?.value; let suggestionsDiv = document.getElementById(suggestionsDivId); if (!query || query.length < 2) { suggestionsDiv.style.display = "none"; return; } try { // abort any pending requests if (searchRequest) { searchRequestAbortController.abort(); searchRequestAbortController = new AbortController(); } // Get results searchRequest = fetch(`${window.SurfaceControllerPath}/umbraco/surface/Search/suggest?query=${query}&fullPreview=${fullPreview}`, { signal: searchRequestAbortController.signal, }); let response = await searchRequest; if (!response.ok) { throw new Error(`HTTP error, status = ${response.status}`); } suggestionsDiv.innerHTML = await response.text(); suggestionsDiv.style.display = "block"; addKeyListenersToSuggestions(searchInputId, suggestionsDivId); } catch (err) { console.error(err); searchRequest = null; suggestionsDiv.style.display = "none"; } }; const addKeyListenersToSuggestions = (searchInputId, suggestionsDivId) => { elements = Array.from(document.getElementById(suggestionsDivId).querySelectorAll("a")); elements.forEach(element => { element.addEventListener('keydown', evt => { if (evt.key === "ArrowDown") { evt.preventDefault(); let index = elements.indexOf(element); if (index < elements.length - 1) { elements[index + 1].focus(); } } if (evt.key === "ArrowUp") { evt.preventDefault(); let index = elements.indexOf(element); if (index > 0) { elements[index - 1].focus(); } if (index === 0) { document.getElementById(searchInputId).focus(); } } }) }); };; 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", () => { 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", function () { var zoomFactor = 2.2; var isCurrentlyDragging = false; var isZoomedIn = false; var img = document.querySelector('.zoomed-image img'); // Variables to track click vs drag let startX = 0, startY = 0; const dragThreshold = 5; // Movement threshold to determine if it's a drag // Function to center the zoomed image function centerZoomedImage() { img.style.transform = 'translate(-50%, -50%) scale(1)'; img.style.top = '50%'; img.style.left = '50%'; } // Click event handler for zoomed image (toggle zoom in/out) const toggleZoom = function (e) { e.stopPropagation(); // Get the image's position and dimensions const rect = img.getBoundingClientRect(); const clickX = e.clientX - rect.left; const clickY = e.clientY - rect.top; // Only toggle zoom if it wasn't a drag if (!isCurrentlyDragging) { console.log('here', isZoomedIn) if (!isZoomedIn) { img.style.transform = 'translate(-50%, -50%) scale(' + zoomFactor + ')'; img.style.cursor = 'grab'; isZoomedIn = true; img.style.transition = 'transform 0.3s ease-in-out'; } else { img.style.transition = 'transform 0.3s ease-in-out, top 0.3s ease-in-out, left 0.3s ease-in-out'; centerZoomedImage(); isZoomedIn = false; } } setTimeout(() => { img.style.transition = 'transform 0.3s ease-in-out'; }, 500) }; document.querySelector('.zoomed-image-container').addEventListener('click', () => { closeImagePopup() }) // Click event handler for zooming in and out document.querySelectorAll('.banner-visual img').forEach(function (imgElement) { centerZoomedImage(); imgElement.addEventListener('click', function (e) { e.stopPropagation(); img.src = this.src; const zoomedContainer = document.querySelector('.zoomed-image'); zoomedContainer.classList.add('active'); // Show zoomed image with smooth fade-in document.body.style.overflow = 'hidden'; // Prevent scrolling // Center the zoomed image const ele = document.querySelector('.zoomed-image.active img'); dragElement(ele); // Add drag end event listener ele.addEventListener('dragend', function (e) { console.log('Drag with offsets:', e.detail.offsetX, e.detail.offsetY); }); // Handle click for toggling zoom ele.addEventListener('click', toggleZoom); }); }); const closeImagePopup = () => { isZoomedIn = false; document.querySelector('.zoomed-image').classList.remove('active'); centerZoomedImage(); document.body.style.overflow = 'auto'; // Restore scrolling } // Close button event handler document.querySelector('.zoomed-image .close-btn').addEventListener('click', function () { closeImagePopup(); }); // Handle click for centering the image if it's zoomed in document.querySelector('.zoomed-image').addEventListener('click', function () { if (isZoomedIn && !isCurrentlyDragging) { centerZoomedImage(); this.style.cursor = 'zoom-in'; } }); // Function to handle dragging function dragElement(elmnt) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; // On mouse down elmnt.onmousedown = function (e) { e = e || window.event; e.preventDefault(); startX = e.clientX; startY = e.clientY; isCurrentlyDragging = false; pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; }; // On dragging function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; // Set the element's new position elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; // If mouse moves more than the threshold, it's a drag if (Math.abs(e.clientX - startX) > dragThreshold || Math.abs(e.clientY - startY) > dragThreshold) { isCurrentlyDragging = true; } } // On mouse up function closeDragElement() { document.onmouseup = null; document.onmousemove = null; if (!isZoomedIn) { img.style.top = '50%'; img.style.left = '50%'; img.style.transition = 'transform 0.3s ease-in-out, top 0.3s ease-in-out, left 0.3s ease-in-out'; img.style.transform = `${img.style.transform.split(' ').find(ele => ele.startsWith('scale'))} translate(-50%, -50%) `; setTimeout(() => { img.style.transition = 'transform 0.3s ease-in-out'; }, 200) } else { img.style.transition = 'transform 0.3s ease-in-out'; } } } });; const FILTERS_KEY = "savedFilters"; const AllResourcesFiltersURL = `/umbraco/Surface/AllResources/GetFilters`;; document.addEventListener("DOMContentLoaded", async () => { if (!document.getElementById("form-all-resources-filter")) { return; } // Load previous filters if available and if no searchParams are set let currentUrl = new URL(window.location.href); let savedFilters = window.localStorage?.getItem(FILTERS_KEY); if ( savedFilters && currentUrl.searchParams.get("filtersLoaded") === null && currentUrl.searchParams.get("sq") === null && currentUrl.searchParams.get("yg") === null && currentUrl.searchParams.get("cc") === null && currentUrl.searchParams.get("sj") === null && currentUrl.searchParams.get("sc") === null && currentUrl.searchParams.get("rt") === null && currentUrl.searchParams.get("sort") === null && currentUrl.searchParams.get("skip") === null ) { let filtersLoaded = false; let filters = JSON.parse(savedFilters); if (filters["sq"]) { filtersLoaded = true; currentUrl.searchParams.set("sq", filters["sq"]); } if (filters["yg"]?.length > 0) { filtersLoaded = true; filters["yg"].forEach((yg) => { currentUrl.searchParams.append("yg", yg); }); } if (filters["cc"]?.length > 0) { filtersLoaded = true; filters["cc"].forEach((cc) => { currentUrl.searchParams.append("cc", cc); }); } if (filters["sj"]) { filtersLoaded = true; currentUrl.searchParams.append("sj", filters["sj"]); } if (filters["sc"]?.length > 0) { filtersLoaded = true; filters["sc"].forEach((sc) => { currentUrl.searchParams.append("sc", sc); }); } if (filters["rt"]?.length > 0) { filtersLoaded = true; filters["rt"].forEach((rt) => { currentUrl.searchParams.append("rt", rt); }); } if (filters["sort"]) { filtersLoaded = true; currentUrl.searchParams.append("sort", filters["sort"]); } if (filters["skip"]) { filtersLoaded = true; currentUrl.searchParams.append("skip", filters["skip"]); } if (filtersLoaded) { currentUrl.searchParams.append("filtersLoaded", "true"); currentUrl.hash = "#div-all-resources-results"; window.location.href = currentUrl.href; } } document .getElementById("form-all-resources-filter") .addEventListener("submit", () => { let form = document.getElementById("form-all-resources-filter"); let searchQuery = form.querySelector("#all-resources-input-search").value; let selectedYearLevelGroups = [...yearLevelSelect.getValue()]; let selectedCurriculum = [...curriculumSelect.getValue()]; let selectedSubject = subjectSelect.getValue(); let selectedLearningFocus = [...learningFocusSelect.getValue()]; let selectedResourceFormat = [...resourceFormatSelect.getValue()]; let selectedSort = document.getElementById( "all-resources-sort-dropdown" ).value; let selectedFilters = { sq: searchQuery, yg: selectedYearLevelGroups, cc: selectedCurriculum, sj: selectedSubject, sc: selectedLearningFocus, rt: selectedResourceFormat, sort: selectedSort, skip: currentUrl.searchParams.get("skip"), }; window.localStorage.setItem(FILTERS_KEY, JSON.stringify(selectedFilters)); return true; }); document .getElementById("btn-all-resources-apply-filters") .addEventListener("click", (e) => { e.preventDefault(); document.getElementById("form-all-resources-filter").submit(); }); document .getElementById("btn-all-resources-clear-filters") .addEventListener("click", () => { window.localStorage.removeItem(FILTERS_KEY); const url = new URL(window.location.href); url.searchParams.delete("sq"); url.searchParams.delete("yg"); url.searchParams.delete("cc"); url.searchParams.delete("sj"); url.searchParams.delete("sc"); url.searchParams.delete("rt"); url.searchParams.delete("sort"); url.searchParams.delete("skip"); url.hash = "#div-all-resources-results"; if (window.location.href == url.href) { window.location.reload(); } window.location.href = url.href; }); const defaultTomSelectOptions = { allowEmptyOption: true, create: false, hidePlaceholder: true, duplicates: false, hideSelected: false, plugins: [ "checkbox_options", "clear_button", "no_active_items", "remove_button", ], sortField: [{ field: "$order" }, { field: "$score" }], onItemRemove: async () => { await reloadFilters(); }, onDropdownClose: async () => { await reloadFilters(); }, }; let yearLevelSelect = new TomSelect("#select-all-resources-year-level", { ...defaultTomSelectOptions, maxItems: null, }); let curriculumSelect = new TomSelect("#select-all-resources-curriculum", { ...defaultTomSelectOptions, maxItems: null, }); let subjectSelect = new TomSelect("#select-all-resources-subject", { ...defaultTomSelectOptions, maxItems: 1, }); let learningFocusSelect = new TomSelect( "#select-all-resources-learning-focus", { ...defaultTomSelectOptions, maxItems: null, } ); let resourceFormatSelect = new TomSelect( "#select-all-resources-resource-format", { ...defaultTomSelectOptions, maxItems: null, } ); const toggleLoading = (isLoading) => { document.getElementById("label-all-resources-loading").style.display = isLoading ? "flex" : "none"; document.getElementById("all-resources-input-search").disabled = isLoading; if (isLoading) { yearLevelSelect.disable(); curriculumSelect.disable(); subjectSelect.disable(); learningFocusSelect.disable(); resourceFormatSelect.disable(); } else { yearLevelSelect.enable(); curriculumSelect.enable(); subjectSelect.enable(); learningFocusSelect.enable(); resourceFormatSelect.enable(); } }; const initFilters = async () => { let currentUrl = new URL(window.location.href); let getFiltersUrl = new URL( `${window.SurfaceControllerPath}${AllResourcesFiltersURL}`, window.location.origin ); getFiltersUrl.searchParams.append( "sq", currentUrl.searchParams.get("sq") ?? "" ); getFiltersUrl.searchParams.append( "yg", currentUrl.searchParams.get("yg") ?? "" ); getFiltersUrl.searchParams.append( "cc", currentUrl.searchParams.get("cc") ?? "" ); getFiltersUrl.searchParams.append( "sj", currentUrl.searchParams.get("sj") ?? "" ); getFiltersUrl.searchParams.append( "sc", currentUrl.searchParams.get("sc") ?? "" ); getFiltersUrl.searchParams.append( "rt", currentUrl.searchParams.get("rt") ?? "" ); try { toggleLoading(true); let response = await fetch(getFiltersUrl.href, { method: "GET", headers: { "Content-Type": "application/json", }, }); let data = await response.json(); for (let option in data["YearLevelGroups"]) { yearLevelSelect.addOption({ value: option, text: option, disabled: data["YearLevelGroups"][option], }); if (currentUrl.searchParams.getAll("yg").includes(option)) { yearLevelSelect.addItem(option, true); } } for (let option in data["Curriculums"]) { curriculumSelect.addOption({ value: option, text: option, disabled: data["Curriculums"][option], }); if (currentUrl.searchParams.getAll("cc").includes(option)) { curriculumSelect.addItem(option, true); } } for (let option in data["Subjects"]) { subjectSelect.addOption({ value: option, text: option, disabled: data["Subjects"][option], }); if (currentUrl.searchParams.get("sj") === option) { subjectSelect.addItem(option, true); } } for (let option in data["LearningFocus"]) { learningFocusSelect.addOption({ value: option, text: option, disabled: data["LearningFocus"][option], }); if (currentUrl.searchParams.getAll("sc").includes(option)) { learningFocusSelect.addItem(option, true); } } for (let option in data["ResourceFormats"]) { resourceFormatSelect.addOption({ value: option, text: option, disabled: data["ResourceFormats"][option], }); if (currentUrl.searchParams.getAll("rt").includes(option)) { resourceFormatSelect.addItem(option, true); } } } catch (e) { console.error(`Error fetching filters: ${e}`); } finally { toggleLoading(false); } }; initFilters(); let searchInputField = document.getElementById("all-resources-input-search"); searchInputField.addEventListener("change", () => { searchInputField.hasChanged = true; }); searchInputField.addEventListener("blur", () => { if (searchInputField.hasChanged) { searchInputField.hasChanged = false; reloadFilters(); } }); const reloadFilters = async () => { let getFiltersUrl = new URL( `${window.SurfaceControllerPath}${AllResourcesFiltersURL}`, window.location.origin ); let filtersForm = document.getElementById("form-all-resources-filter"); let searchQuery = filtersForm.querySelector("#all-resources-input-search")?.value; let selectedYearLevelGroups = [...yearLevelSelect.getValue()]; let selectedCurriculum = [...curriculumSelect.getValue()]; let selectedSubject = subjectSelect.getValue(); let selectedLearningFocus = [...learningFocusSelect.getValue()]; let selectedResourceFormat = [...resourceFormatSelect.getValue()]; if (searchQuery) { getFiltersUrl.searchParams.append("sq", searchQuery); } if (selectedYearLevelGroups) { selectedYearLevelGroups.forEach((yearLevel) => { getFiltersUrl.searchParams.append("yg", yearLevel); }); } if (selectedCurriculum) { selectedCurriculum.forEach((curriculum) => { getFiltersUrl.searchParams.append("cc", curriculum); }); } if (selectedSubject) { getFiltersUrl.searchParams.append("sj", selectedSubject) } if (selectedLearningFocus) { selectedLearningFocus.forEach((learningFocus) => { getFiltersUrl.searchParams.append("sc", learningFocus); }); } if (selectedResourceFormat) { selectedResourceFormat.forEach((resourceFormat) => { getFiltersUrl.searchParams.append("rt", resourceFormat); }); } try { toggleLoading(true); let response = await fetch(getFiltersUrl.href, { method: "GET", headers: { "Content-Type": "application/json", }, }); let data = await response.json(); refreshSingleFilter(yearLevelSelect, data["YearLevelGroups"]); refreshSingleFilter(curriculumSelect, data["Curriculums"]); refreshSingleFilter(subjectSelect, data["Subjects"]); refreshSingleFilter(learningFocusSelect, data["LearningFocus"]); refreshSingleFilter(resourceFormatSelect, data["ResourceFormats"]); } catch (e) { console.error(`Error fetching filters: ${e}`); } finally { toggleLoading(false); } }; const refreshSingleFilter = (filter, data) => { for (let option in data) { filter.updateOption(option, { text: option, value: option, disabled: data[option], }); if (data[option]) { filter.removeItem(option, true); } } }; });; 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; }); });; 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', () => { const seriesNavbar = document.getElementById('series-nav'); const headerNavbar = document.getElementById('navbar_top'); const dropdownButtonText = document.getElementById('dropdownMenuButtonText'); if (!seriesNavbar) { return; } // Register listener for nav select change event const seriesNavSelect = document.getElementById('series-nav-select'); if (seriesNavSelect) { seriesNavSelect.addEventListener('change', (event) => { location.href = `#${event.target.value}`; let option = event.target.options[event.target.selectedIndex]; if (option.getAttribute("data-type") === "link") { window.open(event.target.value, "_blank", "noopener, noreferrer"); const prevValue = event.target.getAttribute("data-prev-value"); event.target.value = prevValue; } else { event.target.setAttribute("data-prev-value", event.target.value); } }); } configureNavbarObserver(); if (headerNavbar) { headerNavbar.addEventListener('transitionrun', () => { const headerPos = headerNavbar.getBoundingClientRect(); const seriesPos = seriesNavbar.getBoundingClientRect(); if (seriesPos.top < headerPos.height + 5) { let factor = headerPos.bottom > 15 ? -1 : 1; seriesNavbar.style.transition = 'transform 0.5s'; seriesNavbar.style.transform = `translateY(${factor * headerPos.height}px)`; } }); headerNavbar.addEventListener('transitionend', () => { const headerPos = headerNavbar.getBoundingClientRect(); seriesNavbar.style.transition = ''; seriesNavbar.style.transform = 'none'; if (headerPos.bottom > 15) { seriesNavbar.style.top = headerPos.height + 'px'; } else { seriesNavbar.style.top = '0px'; } }); } }); /* If we are scrolling down the page, elements are appearing at the bottom and disappearing off the top. isIntersecting is true for appearing, false once they disappear off screen. Once an element disappears off the top of screen, we get the _next_ element. This is what getTargetSection returns; If we are scrolling up the page, elements are appearing at the top of the page, and disappearing off the bottom. isIntersecting is true for appearing, false once they disappear off screen. Once an element appears at the top of screen, we activate that one. If an element scrolls on to the top of the screen (scrolling up), activate it. If an element scrolls off the top of the screen (scrolling down), activate the _next_ element. */ const configureNavbarObserver = () => { const navbar = document.querySelector('.nav.series-navbar-outer'); const allSections = document.querySelectorAll('.series-nav-section'); const allNavLinks = document.querySelectorAll('.series-nav-link'); const dropdownButtonText = document.getElementById('dropdownMenuButtonText'); const items = document.querySelectorAll('.dropdown-item'); if (!navbar || allSections.length === 0) return; const options = { rootMargin: `${navbar.offsetHeight * -1}px`, threshold: 0 }; let direction = 'up'; let prevYPosition = 0; window.addEventListener('scroll', () => { direction = window.scrollY > prevYPosition ? 'down' : 'up'; prevYPosition = window.scrollY; }); const getTargetSection = (target) => { return direction === 'up' ? target : target.nextElementSibling ?? target; }; const shouldUpdate = (entry) => { return (direction === 'down' && !entry.isIntersecting) || (direction === 'up' && entry.isIntersecting); }; const onIntersect = (entries) => { entries.forEach((entry) => { if (!shouldUpdate(entry)) return; const targetSection = getTargetSection(entry.target); const section = targetSection.querySelector(":scope > section"); if (section) { const id = section.id; const targetLink = document.querySelector(`[href='#${id}']`); if (targetLink) { activateLink(targetLink); } activateOption(id); updateDropdownText(id); } }); }; const observer = new IntersectionObserver(onIntersect, options); allSections.forEach((section) => observer.observe(section)); const activateLink = (link) => { allNavLinks.forEach((item) => item.classList.remove('active')); link.classList.add('active'); }; const activateOption = (id) => { const select = document.getElementById('series-nav-select'); if (select && select.querySelector(`option[value='${id}']`)) { select.value = id; } }; const updateDropdownText = (id) => { if (!dropdownButtonText) return; items.forEach((item) => { item.classList.remove('selected'); if (item.getAttribute('href') === `#${id}`) { item.classList.add('selected'); dropdownButtonText.innerText = item.innerText; } }); }; };; 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", function () { // Collapse accordion if on mobile if (window.innerWidth < 992) { var accordionItems = document.querySelectorAll(".accordion-collapse.show"); accordionItems.forEach(function (element) { bootstrap.Collapse.getOrCreateInstance(element) }); } });; 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", () => { 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', () => { 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', () => { // 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