class MakeModelSearchManager { AllModels = {}; } const makeModelManager = new MakeModelSearchManager(); class SearchManager { constructor(config = {}) { this.CurrentPage = 1; Object.assign(this, config); } } const searchManager = new SearchManager(); document.addEventListener("DOMContentLoaded", () => { SetupMakeModelSearch(); document.getElementById('make-select').addEventListener('change', ChangeMakeSearch); RunSearchPage(); document.querySelectorAll('.top-seo-search-wrapper .tab-header-container').forEach(elem => elem.addEventListener('click', TopSEOTabIsClicked)); document.querySelectorAll('.mobile-tab-and-content-parent-wrapper .tab-header-container').forEach(elem => elem.addEventListener('click', TopSEOMobileTabIsClicked)); }) const TopSEOMobileTabIsClicked = (event) => { const tab = event.currentTarget; tab.scrollIntoView({ behavior: 'smooth' }); } const TopSEOTabIsClicked = (event) => { const tab = event.currentTarget; const tabIndex = tab.getAttribute('index'); const mobileTabAndContentParentWrapper = document.querySelector('.mobile-tab-and-content-parent-wrapper'); const selectedMobileTab = mobileTabAndContentParentWrapper.querySelector('.tab-header-container.selected[index="' + tabIndex + '"]') // remove class selected from all elements under the main wrapper. document.querySelectorAll('.top-seo-search-wrapper div').forEach(div => { div.classList.remove('selected'); }); // re-add class selected to all elemenets with the same index under the main wrapper. document.querySelectorAll('.top-seo-search-wrapper div[index="' + tabIndex + '"]').forEach(div => { div.classList.add('selected'); }); if (selectedMobileTab) { mobileTabAndContentParentWrapper.querySelectorAll('.mobile-tab-and-content-wrapper .selected').forEach(elem => elem.classList.remove('selected')); } } const RunSearchPage = async () => { document.getElementById('switch-view-button').addEventListener('click', ToggleCarLayoutView); let searchViewType = localStorage.getItem("search-view-type"); if (searchViewType) { if (searchViewType == "list") { ToggleCarLayoutView(); } } let page = Values.GetValue("currentPage"); if (page) searchManager.CurrentPage = page; const urlParams = new URLSearchParams(window.location.search); const pageParam = urlParams.get('page'); if (pageParam && parseInt(pageParam) > 1) { searchManager.CurrentPage = pageParam; } SetupSearchFromParams(); RefreshAllModalSelects(); let financeDetails = JSON.parse(localStorage.getItem("financeDetails")); if (financeDetails) { let deposit = parseFloat(financeDetails.Deposit); if (financeDetails.IncludePXDeposit) { deposit += Math.floor(parseFloat(financeDetails.PXDeposit) / 1000) * 1000; // we round down to nearest 1000 } financeHolder.Deposit = Math.floor(deposit / 1000) * 1000; financeHolder.AnnualMileage = financeDetails.AnnualMileage; if (financeHolder.Deposit <= 0) financeHolder.Deposit = 0; } document.getElementById("mini-filters-button").addEventListener("click", MiniFilterButtonTrigger); document.getElementById("sort-select-mobile").addEventListener("change", () => { document.getElementById("sort-select").value = document.getElementById("sort-select-mobile").value; }); document.getElementById("close-filter-section-button").addEventListener("click", MiniFilterButtonCloseTrigger); document.getElementById("mobile-filter-section-button").addEventListener("click", MiniFilterButtonCloseTrigger); document.querySelectorAll("input[type='checkbox']").forEach(ele => ele.addEventListener("change", () => { Values.RemoveValue("storyblokFilters"); ResetSearchToPageZero(); })); document.querySelectorAll("select").forEach(ele => ele.addEventListener("change", () => { Values.RemoveValue("storyblokFilters"); ResetSearchToPageZero(); })); document.getElementById("clear-filters-button").addEventListener("click", () => { const url = new URL(window.location); url.pathname = window.location.pathname; searchManager.CurrentPage = 1; if (url.pathname.includes("/used-cars/")) url.pathname = "/used-cars/"; location.href = url.pathname; // Just navigate to base url }); if (document.getElementById("clear-filters-results-button")) { document.getElementById("clear-filters-results-button").addEventListener("click", () => { const url = new URL(window.location); url.pathname = window.location.pathname; searchManager.CurrentPage = 1; if (url.pathname.includes("/used-cars/")) url.pathname = "/used-cars/"; location.href = url.pathname; // Just navigate to base url }); } // this just makes our 'make model' checkboxes work - this should be in its own methods, it just adds 'selected' to the parent wrapper on click (and sets them up on load where needed) document.querySelectorAll("input.make-model-checkbox").forEach(ele => { if (ele.checked) { ele.parentElement.classList.add("selected"); } ele.addEventListener("change", (e) => { if (e.currentTarget.checked) { e.currentTarget.parentElement.classList.add("selected"); } else { e.currentTarget.parentElement.classList.remove("selected"); } }); ele.parentElement.addEventListener("click", (e) => { // on tick toggle checkbox? ele.click(); }); }); window.onscroll = function () { AdjustSearchMenuPadding(window); }; window.onpopstate = function (event) { if (event) { SetupSearchFromParams(); ResetSearchToPageZero(); } else { } } TriggerNextSearchResults(); AdjustSearchMenuPadding(window); }; const AdjustSearchMenuPadding = (window) => { // search-start. Is our start position // search-end is our end position let startingEle = document.getElementById("search-start").getBoundingClientRect(); let topOffset = startingEle.top; let endingEle = document.getElementById("search-end").getBoundingClientRect(); let bottomOffset = endingEle.top; bottomOffset = (window.innerHeight - endingEle.bottom) * -1; let filterSection = document.getElementById('filter-section'); let filteredSectionStyle = getComputedStyle(filterSection); let filterSectionPaddingTop = parseFloat(filteredSectionStyle.paddingTop); let filterSectionPaddingBottom = parseFloat(filteredSectionStyle.paddingBottom); bottomOffset = bottomOffset - filterSectionPaddingTop - filterSectionPaddingBottom; if (startingEle.top <= 0) { topOffset = 0; } filterSection.style.top = topOffset + 'px'; if (bottomOffset <= 0) { filterSection.style.top = bottomOffset + 'px'; } } const ToggleCarLayoutView = () => { let carResults = document.getElementById('car-results'); let isNowList = carResults.classList.toggle('list'); // store if user should default see items in list view or not if (isNowList) { localStorage.setItem("search-view-type", "list"); } else { localStorage.removeItem("search-view-type"); } let iconGrid = document.querySelector('#switch-view-button .grid'); let iconList = document.querySelector('#switch-view-button .list'); iconGrid.classList.toggle('hidden'); iconList.classList.toggle('hidden'); } const AddBreadcrumbFilter = (name, searchKey, searchVal, searchKey2, searchVal2) => { let wrapper = document.getElementById("filter-breadcrumb-badges-section"); let label = BIG.CreateDiv("label breadcrumb-badge"); label.setAttribute("searchKey", searchKey); label.setAttribute("searchVal", searchVal); if (searchKey2) label.setAttribute("searchKey2", searchKey2); if (searchVal2) label.setAttribute("searchVal2", searchVal2); label.AddImage("/statics/images/icons/cross.svg", true, "/statics/images/icons/cross.svg"); let text = BIG.CreateDiv("text", name); label.appendChild(text); wrapper.appendChild(label); label.addEventListener("click", () => { UncheckFilter(searchKey, searchVal, searchKey2, searchVal2, true); Values.RemoveValue("storyblokFilters"); label.remove(); }); } const UncheckFilter = (searchKey, searchVal, searchKey2, searchVal2, toggleSearch) => { RemoveTopAndBottomSearchSEO(); // Edit the url? let tmpUrl = new URL(window.location); let urlParams = new URLSearchParams(tmpUrl.search); if (searchKey.toLowerCase() == "makes") { // todo: uncheck all models with this make? let modelLabels = document.querySelectorAll(`#filter-breadcrumb-badges-section .label[searchval2='${searchVal.toLowerCase()}']`); for (let ele of modelLabels) { let key = ele.getAttribute("searchkey"); let val = ele.getAttribute("searchval"); UncheckFilter(key, val, undefined, undefined, false); } } tmpUrl = new URL(window.location); urlParams = new URLSearchParams(tmpUrl.search); if (urlParams.entries.length == 0) { // its in the url? if (searchKey.toLowerCase() == "makes") { // check if its in our actual url let pathname = location.pathname.split('/'); if (pathname.length > 2) { tmpUrl.pathname = window.location.pathname;// "/used-cars/"; if (tmpUrl.pathname.includes("/used-cars/")) tmpUrl.pathname = "/used-cars/"; } } if (searchKey.toLowerCase() == "models") { // check if its in our actual url\ tmpUrl.pathname = window.location.pathname;// "/used-cars/"; if (tmpUrl.pathname.includes("/used-cars/")) { let pathname = location.pathname.split('/'); if (pathname.length > 2) { tmpUrl.pathname = "/used-cars/" + pathname[2] + "/"; } } } } for (const entry of urlParams) { let key = entry[0]; let fullVal = entry[1].replace(/\-/g, " "); if (key.toLowerCase() != searchKey.toLowerCase()) continue; if (fullVal.indexOf("_") > 0) { // it has multiple entries; let newList = []; let vals = fullVal.split("_"); for (let val of vals) { if (val != searchVal) newList.push(val); } urlParams.set(searchKey, newList.join("_")); } else { urlParams.delete(key); } } tmpUrl.search = urlParams; if (tmpUrl != location.href) window.history.pushState({}, '', tmpUrl.href); // set current page to 1 so we refresh stock entirely searchManager.CurrentPage = 1; removeUrlParam('page'); SetupSearchFromParams(); document.querySelector(".car-results").innerHTML = ""; if (toggleSearch) TriggerNextSearchResults(); } const SetupSearchFromParams = () => { let urlParams = new URLSearchParams(window.location.search); // got filters let filters = Values.GetValue("storyblokFilters"); // get urlParams in lower case for comparison, as the storyblokFilters is in lower case let urlParamsInLowerCase = new URLSearchParams(); let filterKey, filterValue; if (filters) { const filterArray = filters.split('='); filterKey = filterArray[0]; if (filterArray.length > 1) filterValue = filterArray[1]; urlParams.forEach((value, key) => { urlParamsInLowerCase.append(key.toLowerCase(), value.toLowerCase()); }); } // add storyblok filters to urlParams if they don't exist yet if (filters && urlParamsInLowerCase.has(filterKey) == false) { urlParams.append(filterKey, filterValue); } document.getElementById("filter-breadcrumb-badges-section").innerHTML = ""; // untick all boxes (to be reticked) document.querySelectorAll("#filter-accordion input").forEach(input => input.checked = false); document.querySelectorAll("#filter-accordion select").forEach(input => input.value = -1); document.querySelectorAll("#filter-accordion .checkbox.selected").forEach(div => { div.classList.remove("selected"); div.querySelector("input").checked = false; }); // if prevSearch exists & val == true if (urlParams.has("prevsearch")) { let searchTerms = JSON.parse(localStorage.getItem("searchTerms")); if (searchTerms) { if (searchTerms.page) { searchTerms.page = 1; } SetupWindowUrlHistoryFromSearchRequest(searchTerms); if (searchTerms.page) { searchManager.CurrentPage = searchTerms.page; } urlParams = new URLSearchParams(window.location.search); } } // get query from url if it exists? let pathParts = window.location.pathname.split('/'); if (pathParts.length >= 3) { let uriBase = 0; if (pathParts[2].toLowerCase() == "cars") uriBase = 1; let make = pathParts[2 + uriBase].replace(/\-/g, ' '); // if our make is part of parameters instead of window.location.pathname.split('/'), get the make (storyblok driven content) if (urlParams.has('makes') == true) { make = urlParams.get('makes').replace(/\-/g, ' '); } //If we select a make, then remove any term that was there from 'Used car quick search' if (make.length > 0 && window.location.search.includes("utm_term=") == false && window.location.search.includes("term=")) { window.location.search = ""; } if (make.includes('mercedes')) make = 'Mercedes-Benz'; let checkboxWrappers = document.querySelectorAll(`.make-checkbox[make='${make.toLowerCase()}']`); checkboxWrappers.forEach(input => { input.checked = true; input.closest(".checkbox").classList.add("selected"); AddBreadcrumbFilter(make, "makes", make); }); let makeSelect = document.getElementById('make-select'); let modelSelect = document.getElementById('model-select'); if (make.length > 0 && make != -1) { makeSelect.value = make.toLowerCase(); AddBreadcrumbFilter(make, "makes", make); if (modelSelect) { modelSelect.closest('.input-wrapper').classList.remove('hidden'); // hide model select if make is not selected (i.e 'Any') // unhide all models related to this make. This is for storyblok driven content page (i.e: Price Reduced). // When the page is refreshed, it does not have make value in 'hidden-info-div' and does not filter models. // Storyblok driven content modelSelect.querySelectorAll("option").forEach(opt => { if (opt.getAttribute('make') == make.toLowerCase()) opt.classList.remove('hidden'); }); } } else { modelSelect.closest('.input-wrapper').classList.add('hidden'); } let model; if (pathParts.length >= 4 + uriBase) { let encodedModel = pathParts[3 + uriBase]; encodedModel = encodedModel.replace(/-/g, ' '); model = decodeURIComponent(encodedModel); let checkboxWrappers = document.querySelectorAll(`.model-checkbox[model='${model.toLowerCase()}']`); checkboxWrappers.forEach(input => { input.checked = true; input.closest(".checkbox").classList.add("selected"); let make = input.parentElement.getAttribute("make"); AddBreadcrumbFilter(model, "models", model, "make", make); }); } // if our model is part of parameters get it from urlParams (storyblok driven content) if (urlParams.has('models') == true) { if (urlParams.has('models') == true) { model = urlParams.get('models').replace(/\-/g, ' '); } } if (model && model.toLowerCase() != 'any') { modelSelect.value = model.toLowerCase(); AddBreadcrumbFilter(model, "models", model, "make", make); } else { modelSelect.value = '-1'; } document.getElementById('hide-cars-no-images-checkbox').checked = false; } for (const entry of urlParams) { let key = entry[0]; //let val = entry[1].replace(/\+/g, " "); //We need not replace plus sign with space due to car makes such as 'Ford Ka+' let val = entry[1]; switch (key.toLowerCase()) { case "makes": let makes = val.replace(/\-/g, " ").split('_'); for (let make of makes) { let checkboxWrappers = document.querySelectorAll(`.make-checkbox[make='${make.toLowerCase()}']`); checkboxWrappers.forEach(input => { input.checked = true; input.closest(".checkbox").classList.add("selected"); AddBreadcrumbFilter(make, "makes", make); }); } break; case "models": let models = val.replace(/\-/g, " ").split('_'); for (let model of models) { let checkboxWrappers = document.querySelectorAll(`.model-checkbox[model='${model.toLowerCase()}']`); checkboxWrappers.forEach(input => { input.checked = true; input.closest(".checkbox").classList.add("selected"); let make = input.parentElement.getAttribute("make"); AddBreadcrumbFilter(model, "models", model, "make", make); }); } break; case "derivative": let derivatives = val.split("_"); for (let derivative of derivatives) { let checkboxWrappers = document.getElementById("derivatives-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${derivative.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(derivative, key, derivative); } }); } break; case "tags": let tags = val.split("_"); for (let tag of tags) { AddBreadcrumbFilter(tag, key, tag); let tagCheckbox = document.querySelector(`.tag-checkbox[tag='${tag}']`); if (tagCheckbox) tagCheckbox.checked = true; } break; case "showcarsinprep": if (val.toLowerCase() == "true") { document.getElementById("hide-cars-no-images-checkbox").checked = true; } break; case "minfullprice": let minFullPrice = val; SelectSwitch(document.getElementById("price-switch-2")); document.getElementById("pay-in-full-select").value = minFullPrice; AddBreadcrumbFilter("From £" + NumberWithCommas(minFullPrice), "minFullPrice", minFullPrice); break; case "maxfullprice": let maxFullPrice = val; SelectSwitch(document.getElementById("price-switch-2")); document.getElementById("pay-in-full-to-select").value = maxFullPrice; AddBreadcrumbFilter("To £" + NumberWithCommas(maxFullPrice), "maxFullPrice", maxFullPrice); break; case "monthminprice": break; case "monthlyprice": document.getElementById("monthly-budget-select").value = val; AddBreadcrumbFilter("From £" + NumberWithCommas(val) + " Monthly", "monthlyPrice", val); break; case "monthlypriceto": document.getElementById("monthly-budget-to-select").value = val; AddBreadcrumbFilter("To £" + NumberWithCommas(val) + " Monthly", "monthlyPriceTo", val); break; case "minimumage": let minAge = val; document.getElementById("minimum-age-select").value = minAge; AddBreadcrumbFilter("> " + minAge + " years", "minimumAge", minAge); break; case "maximumage": let maxAge = val; document.getElementById("maximum-age-select").value = maxAge; AddBreadcrumbFilter("< " + val + " years", key, val); break; case "minimummileage": let minMileage = val; document.getElementById("minimum-mileage-select").value = minMileage; AddBreadcrumbFilter("> " + NumberWithCommas(val) + " miles", key, val); break; case "maximummileage": let maxMileage = val; document.getElementById("maximum-mileage-select").value = maxMileage; AddBreadcrumbFilter("< " + NumberWithCommas(val) + " miles", key, val); break; case "minimumelectricmileage": let minElectricMileage = val; document.getElementById("minimum-electric-mileage-select").value = minElectricMileage; AddBreadcrumbFilter("> " + NumberWithCommas(val) + " miles (electric)", key, val); break; case "maximumelectricmileage": let maxElectricMileage = val; document.getElementById("maximum-electric-mileage-select").value = maxElectricMileage; AddBreadcrumbFilter("< " + NumberWithCommas(val) + " miles (electric)", key, val); break; case "minimumengine": let minEngine = val; document.getElementById("minimum-engine-select").value = minEngine; AddBreadcrumbFilter("> " + NumberWithCommas(val) + "L Engine", key, val); break; case "maximumengine": let maxEngine = val; document.getElementById("maximum-engine-select").value = maxEngine; AddBreadcrumbFilter("< " + NumberWithCommas(val) + "L Engine", key, val); break; case "mpg": let mpg = val; document.getElementById("mpg-select").value = mpg; AddBreadcrumbFilter(NumberWithCommas(val) + "MPG", key, val); break; case "fuel": let fuelTypes = val.split("_"); for (let fuelType of fuelTypes) { let checkboxWrappers = document.getElementById("fuel-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${fuelType.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(fuelType, key, fuelType); } }); } break; case "colour": let colours = val.split("_"); for (let colour of colours) { let checkboxWrappers = document.getElementById("colours-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${colour.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(colour, key, colour); } }); } case "location": let locations = val.split("_"); for (let location of locations) { let checkboxWrappers = document.getElementById("locations-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${location.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(location, key, location); } }); } case "spec": let specs = val.split("_"); for (let spec of specs) { let checkboxWrappers = document.getElementById("spec-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${spec.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { AddBreadcrumbFilter(spec, key, spec); input.checked = true; } }); } break; case "body": let bodyTypes = val.split("_"); for (let body of bodyTypes) { let checkboxWrappers = document.getElementById("body-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${body.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(body, key, body); } }); } break; case "doors": let doors = val.split("_"); for (let doorType of doors) { let checkboxWrappers = document.getElementById("doors-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${doorType.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(doorType, key, doorType); } }); } break; case "seats": let seats = val.split("_"); for (let seatType of seats) { let checkboxWrappers = document.getElementById("seats-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${seatType.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(seatType, key, seatType); } }); } break; case "transmission": let transmissionTypes = val.split("_"); for (let transmission of transmissionTypes) { let checkboxWrappers = document.getElementById("transmission-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${transmission.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(transmission, key, transmission); } }); } break; case "ulez": let ulez = val.split("_"); let ulezCheckbox = document.getElementById("mpg-search-checkbox-ulez"); ulezCheckbox.checked = true; AddBreadcrumbFilter("Ultra Low Emission Zone", key, ulez); break; case "pricereduced": let priceReduced = val.split("_"); let priceReducedCheckbox = document.getElementById("price-reduced-search-checkbox"); priceReducedCheckbox.checked = true; AddBreadcrumbFilter("Price Reduced", key, priceReduced); break; case "vatqualifying": let vatQualifying = val.split("_"); let vatQualifyingdCheckbox = document.getElementById("vat-qualifying-search-checkbox"); vatQualifyingdCheckbox.checked = true; AddBreadcrumbFilter("VAT Qualifying", key, vatQualifying); break; case "sort": document.getElementById("sort-select").value = val; break; case "group1": let dodValue = val.split("_"); let dodInput = document.getElementById("group1-input"); dodInput.value = dodValue; break; case "group2": let disValue = val.split("_"); let disInput = document.getElementById("group2-input"); disInput.value = disValue; break; case "insurance": let insuranceTypes = val.split("_"); for (let insurance of insuranceTypes) { let checkboxWrappers = document.getElementById("insurance-checkboxes-container").querySelectorAll(`.checkbox[search-slug='${insurance.toLowerCase()}']`); checkboxWrappers.forEach(wrapper => { let input = wrapper.querySelector("input"); if (input) { input.checked = true; AddBreadcrumbFilter(insurance, key, insurance); } }); } break; } } } const MiniFilterButtonCloseTrigger = (event) => { document.getElementById("filter-section").classList.remove("shown"); document.querySelector(".mobile-filter-button-fixed-holder").classList.remove("shown"); document.querySelector("body").classList.remove("no-scroll"); } const MiniFilterButtonTrigger = (event) => { document.getElementById("filter-section").classList.add("shown"); document.querySelector(".mobile-filter-button-fixed-holder").classList.add("shown"); document.querySelector("body").classList.add("no-scroll"); } const triggerPageUrlUpdate = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { if (entry.intersectionRatio > 0) { let currentPage = entry.target.getAttribute("page"); try { const tmpUrl = new URL(window.location); //tmpUrl.searchParams.set('page', currentPage); if (currentPage <= 0) tmpUrl.searchParams.delete("page"); if (tmpUrl != location.href) window.history.pushState({}, '', tmpUrl.href); } catch { } // try catch just in case ie8 or similar.. } }); }); const ResetSearchToPageZero = () => { searchManager.CurrentPage = 1; removeUrlParam('page'); document.querySelector(".car-results").innerHTML = ""; const searchStart = document.getElementById('search-start'); searchStart.scrollIntoView({ behaviour: 'smooth' }); // todo: add preloader inside car results? TriggerNextSearchResults(); } const SetupPageUrlFromSearchRequest = (req) => { if (searchManager.CurrentPage > 1) req.page = searchManager.CurrentPage; const url = new URL(window.location); if (url.searchParams.has("term")) { req.Term = url.searchParams.get("term"); } // get search query params from url let makes = [...document.querySelectorAll(".make-checkbox:checked")].map(ele => ele.parentElement.getAttribute("make").replace(/\s/g, '-')); let models = [...document.querySelectorAll(".model-checkbox:checked")].map(ele => ele.parentElement.getAttribute("model").replace(/\s/g, '-')); let make = document.getElementById('make-select').value; if (make != -1) { makes = [make]; } let model = document.getElementById('model-select').value; if (model != -1) { models = [model]; } // show/hide models and derivatives let allModelWrappers = document.querySelectorAll(`.model-checkbox-wrapper .make-model-wrapper`); let untickedAModel = false; let modelsShown = 0; allModelWrappers.forEach(wrapper => { let makeAttr = wrapper.getAttribute("make"); if (makes.some(make => makeAttr == make.toLowerCase().replace(/\-/g, ' '))) { wrapper.classList.remove("hidden"); modelsShown++; } else { wrapper.classList.add("hidden"); wrapper.querySelectorAll("input").forEach(checkbox => { if (checkbox.checked) { checkbox.checked = false; untickedAModel = true; checkbox.parentElement.classList.remove("selected"); } }); // uncheck all if hidden } }); /* let modelOpt = document.getElementById("model-option"); if (modelsShown == 0) { if (modelOpt.classList.contains("hidden") == false) modelOpt.classList.add("hidden"); } else { if (modelOpt.classList.contains("hidden")) modelOpt.classList.remove("hidden"); }*/ let derivativesOpt = document.getElementById("derivatives-option"); let isModelChecked = models.length > 0; if (isModelChecked) { derivativesOpt.classList.remove("hidden"); } else { derivativesOpt.classList.add("hidden"); } if (untickedAModel) { UpdatesModalSelect("model-input"); } // setup req from fields if (makes.length > 0) { req.makes = makes.join("_"); } else { } if (models.length > 0) { req.models = models.join("_"); } let monthlyBudget = document.getElementById("monthly-budget-select").value; let monthlyBudgetTo = document.getElementById("monthly-budget-to-select").value; if (monthlyBudget > 0 && document.getElementById("price-switch-1").classList.contains("active")) { req.MonthlyPrice = Number(monthlyBudget); } if (monthlyBudgetTo > 0 && document.getElementById("price-switch-1").classList.contains("active")) { req.MonthlyPriceTo = Number(monthlyBudgetTo); } let fullPaymentBudget = document.getElementById("pay-in-full-select").value; if (fullPaymentBudget > 0 && document.getElementById("price-switch-1").classList.contains("active") == false) { req.MinFullPrice = fullPaymentBudget; } let fullPaymentToBudget = document.getElementById("pay-in-full-to-select").value; if (fullPaymentToBudget > 0 && document.getElementById("price-switch-1").classList.contains("active") == false) { req.MaxFullPrice = fullPaymentToBudget; } let derivatives = GetLabelsFromCheckboxes("derivatives-checkboxes-container"); if (derivatives.length > 0) { req.Derivative = derivatives.join("_"); } let fuelTypes = GetLabelsFromCheckboxes("fuel-checkboxes-container"); if (fuelTypes.length > 0) { req.Fuel = fuelTypes.join("_"); } let colours = GetLabelsFromCheckboxes("colours-checkboxes-container"); if (colours.length > 0) { req.Colour = colours.join("_"); } let hideCarsNoImages = document.getElementById("hide-cars-no-images-checkbox").checked; if (hideCarsNoImages) { req.hideNoImages = false; } else { req.hideNoImages = true; } let locations = GetLabelsFromCheckboxes("locations-checkboxes-container"); if (locations.length > 0) { req.Location = locations.join("_"); } let transmissionTypes = GetLabelsFromCheckboxes("transmission-checkboxes-container"); if (transmissionTypes.length > 0) { req.Transmission = transmissionTypes.join("_"); } let bodyTypes = GetLabelsFromCheckboxes("body-checkboxes-container"); if (bodyTypes.length > 0) { req.BodyTypes = bodyTypes.join("_"); } let specTypes = GetLabelsFromCheckboxes("spec-checkboxes-container"); if (specTypes.length > 0) { req.SpecTypes = specTypes.join("_"); } let doorTypes = GetLabelsFromCheckboxes("doors-checkboxes-container"); if (doorTypes.length > 0) { req.Doors = doorTypes.join("_"); } let seatTypes = GetLabelsFromCheckboxes("seats-checkboxes-container"); if (seatTypes.length > 0) { req.Seats = seatTypes.join("_"); } let mpgType = document.getElementById("mpg-select").value; if (mpgType > 0) { req.MPG = mpgType; } let minimumAge = document.getElementById("minimum-age-select").value; if (minimumAge > 0) { req.MinimumAge = minimumAge; } let maximumAge = document.getElementById("maximum-age-select").value; if (maximumAge > 0) { req.MaximumAge = maximumAge; } let minimumMileage = document.getElementById("minimum-mileage-select").value; if (minimumMileage > 0) { req.MinimumMileage = minimumMileage; } let maximumMileage = document.getElementById("maximum-mileage-select").value; if (maximumMileage > 0) { req.MaximumMileage = maximumMileage; } let minimumElectricMileage = document.getElementById("minimum-electric-mileage-select").value; if (minimumElectricMileage > 0) { req.MinimumElectricMileage = minimumElectricMileage; } let maximumElectricMileage = document.getElementById("maximum-electric-mileage-select").value; if (maximumElectricMileage > 0) { req.MaximumElectricMileage = maximumElectricMileage; } let minimumEngineSpec = document.getElementById("minimum-engine-select").value; if (minimumEngineSpec > 0) { req.MinimumEngine = minimumEngineSpec; } let maximumEngineSpec = document.getElementById("maximum-engine-select").value; if (maximumEngineSpec > 0) { req.MaximumEngine = maximumEngineSpec; } const sortOrderSelect = document.getElementById("sort-select"); let sortOrder = sortOrderSelect.value; if (sortOrder.length > 0) { req.Sort = sortOrder; } let tags = []; document.querySelectorAll(".tag-checkbox").forEach(tagCheckbox => { if (tagCheckbox.checked) { let tag = tagCheckbox.getAttribute("tag"); tags.push(tag); } }); if (tags.length > 0) req.Tags = tags.join("_"); let mpg = document.getElementById("mpg-select").value; if (mpg > 0) { req.MPG = mpg; } let ulez = document.getElementById("mpg-search-checkbox-ulez").checked; if (ulez) { req.Ulez = true; } let priceReduced = document.getElementById("price-reduced-search-checkbox").checked; if (priceReduced) { req.PriceReduced = true; } let vatQualifying = document.getElementById("vat-qualifying-search-checkbox").checked; if (vatQualifying) { req.VatQualifying = true; } let dodValue = document.getElementById("group1-input").value; if (dodValue.length > 0) { req.group1 = dodValue; } let disValue = document.getElementById("group2-input").value; if (disValue.length > 0) { req.group2 = disValue; } let insuranceTypes = GetLabelsFromCheckboxes("insurance-checkboxes-container"); if (insuranceTypes.length > 0) { req.Insurance = insuranceTypes.join("_"); } // Setup url from req SetupWindowUrlHistoryFromSearchRequest(req); localStorage.setItem("searchTerms", JSON.stringify(req)); return req; } const SetupWindowUrlHistoryFromSearchRequest = (req) => { let url = new URL(window.location); // todo: what is the base of this url? without query params url.pathname = window.location.pathname;// "/used-cars/"; if (url.pathname.includes("/used-cars/")) url.pathname = "/used-cars/"; url.searchParams.delete("prevsearch"); let gotSingleMake = false; let isStoryblokSearch = false; // How do we know this isnt run via storyblok? if (Values.GetValue("isStoryBlokSearch")) isStoryblokSearch = true; if (req.makes) { if (isStoryblokSearch || req.makes.includes("_")) { url.searchParams.set('makes', req.makes.replace(/ /g, '-')); document.getElementById("used-cars-header").textContent = "Find your next used car"; } else { gotSingleMake = true; url.pathname += req.makes.replace(/ /g, '-').toLowerCase() + "/"; url.searchParams.delete('makes'); let urlMake = req.makes.replace(/\-/g, ' '); let makeWrapper = document.querySelector(`.make-model-checkbox-wrapper .checkbox[make='${urlMake}']`); if (makeWrapper) { urlMake = makeWrapper.querySelector("label").innerText; } urlMake = document.querySelector(`#make-select option[value='${req.makes}']`).getAttribute('make'); document.getElementById("used-cars-header").textContent = "Used " + urlMake + " cars"; } } else { url.searchParams.delete('makes'); document.getElementById("used-cars-header").textContent = "Find your next used car"; } if (req.models) { if (isStoryblokSearch || gotSingleMake == false || req.models.includes("_")) { url.searchParams.set('models', req.models.replace(/ /g, '-')); document.getElementById("used-cars-header").textContent = "Find your next used car"; } else { url.pathname += encodeURIComponent(req.models.replace(/ /g, '-').toLowerCase()) + "/"; url.pathname = url.pathname.toLowerCase(); url.searchParams.delete('models'); let urlMake = req.makes.replace(/\-/g, ' '); let makeWrapper = document.querySelector(`.make-model-checkbox-wrapper .checkbox[make='${urlMake}']`); if (makeWrapper) { urlMake = makeWrapper.querySelector("label").innerText; } let urlModel = req.models.replace(/\-/g, ' '); let modelWrapper = document.querySelector(`.make-model-checkbox-wrapper .checkbox[model='${urlModel}']`); if (modelWrapper) { urlModel = modelWrapper.querySelector("label").innerText; } urlModel = document.querySelector(`#model-select option[make='${req.makes}'][model='${req.models}']`).text; urlMake = document.querySelector(`#make-select option[value='${req.makes}']`).getAttribute('make'); document.getElementById("used-cars-header").textContent = "Used " + urlMake + " " + urlModel + " cars"; } } else { url.searchParams.delete('models'); } if (req.MinFullPrice) url.searchParams.set("minFullPrice", req.MinFullPrice); else url.searchParams.delete("minFullPrice"); if (req.MaxFullPrice) url.searchParams.set("maxFullPrice", req.MaxFullPrice); else url.searchParams.delete("maxFullPrice"); if (req.MonthlyPrice) url.searchParams.set("monthlyPrice", req.MonthlyPrice); else { url.searchParams.delete("monthlyPrice"); } if (req.MonthlyPriceTo) url.searchParams.set("monthlyPriceTo", req.MonthlyPriceTo); else { url.searchParams.delete("monthlyPriceTo"); } if (req.Derivative) url.searchParams.set('derivative', req.Derivative); else url.searchParams.delete("derivative"); if (req.Fuel) url.searchParams.set('fuel', req.Fuel); else url.searchParams.delete("fuel"); if (req.hideNoImages) { url.searchParams.delete("showCarsInPrep"); } else { url.searchParams.set("showCarsInPrep", 'true'); } if (req.Colour) url.searchParams.set('colour', req.Colour); else url.searchParams.delete("colour"); if (req.Location) url.searchParams.set('location', req.Location); else url.searchParams.delete("location"); if (req.Transmission) url.searchParams.set('transmission', req.Transmission); else url.searchParams.delete("transmission"); if (req.BodyTypes) url.searchParams.set("body", req.BodyTypes); else url.searchParams.delete("body"); if (req.SpecTypes) url.searchParams.set("spec", req.SpecTypes); else url.searchParams.delete("spec"); if (req.Doors) url.searchParams.set("doors", req.Doors); else url.searchParams.delete("doors"); if (req.Seats) url.searchParams.set("seats", req.Seats); else url.searchParams.delete("seats"); if (req.MPG) url.searchParams.set("mpg", req.MPG); else url.searchParams.delete("mpg"); if (req.Tags) url.searchParams.set("tags", req.Tags); else url.searchParams.delete("tags"); if (req.MinimumAge) url.searchParams.set("minimumAge", req.MinimumAge); else url.searchParams.delete("minimumAge"); if (req.MaximumAge) url.searchParams.set("maximumAge", req.MaximumAge); else url.searchParams.delete("maximumAge"); if (req.MinimumMileage) url.searchParams.set("minimumMileage", req.MinimumMileage); else url.searchParams.delete("minimumMileage"); if (req.MaximumMileage) url.searchParams.set("maximumMileage", req.MaximumMileage); else url.searchParams.delete("maximumMileage"); if (req.MinimumElectricMileage) url.searchParams.set("minimumElectricMileage", req.MinimumElectricMileage); else url.searchParams.delete("minimumElectricMileage"); if (req.MaximumElectricMileage) url.searchParams.set("maximumElectricMileage", req.MaximumElectricMileage); else url.searchParams.delete("maximumElectricMileage"); if (req.MinimumEngine) url.searchParams.set("minimumEngine", req.MinimumEngine); else url.searchParams.delete("minimumEngine"); if (req.MaximumEngine) url.searchParams.set("maximumEngine", req.MaximumEngine); else url.searchParams.delete("maximumEngine"); if (req.Ulez) url.searchParams.set("ulez", req.Ulez); else url.searchParams.delete("ulez"); if (req.PriceReduced) url.searchParams.set("priceReduced", req.PriceReduced); else url.searchParams.delete("priceReduced"); if (req.VatQualifying) url.searchParams.set("vatQualifying", req.VatQualifying); else url.searchParams.delete("vatQualifying"); if (req.Sort && req.Sort != Values.GetValue('sort')) url.searchParams.set("sort", req.Sort); else url.searchParams.delete("sort"); if (req.group1) url.searchParams.set("group1", req.group1); else url.searchParams.delete("group1"); if (req.group2) url.searchParams.set("group2", req.group2); else url.searchParams.delete("group2"); if (req.Insurance) url.searchParams.set('insurance', req.Insurance); else url.searchParams.delete("insurance"); let allMatch = false; // if storyblok filters exist, and they match exactly then dont print the history if (Values.GetValue("storyblokFilters")) { allMatch = true; // all match to true by default if storyblokFilters exists let actualParams = new URLSearchParams(url.search); let filterParams = new URLSearchParams(Values.GetValue("storyblokFilters")); // if key doesnt exist, then we dont match. actualParams.forEach((value, key) => { if (filterParams.has(key) == false) { allMatch = false; } else { // if key matches but value doesnt, then we dont match if (filterParams.get(key) != value) allMatch = false; } }); } if (allMatch == false && url != location.href) window.history.pushState({}, '', url.href); return url; } const GetParamsForStockAlert = (req) => { let addedUrlParams = []; if (req.makes) { addedUrlParams.push('makes=' + req.makes); } if (req.models) { addedUrlParams.push('models=' + req.models); } if (req.Fuel) { addedUrlParams.push('Fuel=' + req.Fuel); } if (req.MaxFullPrice) { addedUrlParams.push('MaxFullPrice=' + req.MaxFullPrice); } if (req.MinFullPrice) { addedUrlParams.push('minFullPrice=' + req.MinFullPrice); } if (req.MinimumMileage) { addedUrlParams.push('MinimumMileage=' + req.MinimumMileage); } if (req.MaximumMileage) { addedUrlParams.push('MaximumMileage=' + req.MaximumMileage); } if (req.MinimumElectricMileage) { addedUrlParams.push('MinimumElectricMileage=' + req.MinimumElectricMileage); } if (req.MaximumElectricMileage) { addedUrlParams.push('MaximumElectricMileage=' + req.MaximumElectricMileage); } if (req.MinimumAge) { addedUrlParams.push('MinimumAge=' + req.MinimumAge); } if (req.MaximumAge) { addedUrlParams.push('MaximumAge=' + req.MaximumAge); } if (req.Transmission) { addedUrlParams.push('Transmission=' + req.Transmission); } if (req.Colour) { addedUrlParams.push('Colour=' + req.Colour); } if (req.Insurance) { addedUrlParams.push('Insurance=' + req.Insurance); } if (req.Derivative) { addedUrlParams.push('Derivative=' + req.Derivative); } return addedUrlParams.join('&'); } const RemoveTopAndBottomSearchSEO = () => { document.querySelectorAll("#search-wrapper .result-section .storyblok-search-seo").forEach(ele => ele.remove()); document.querySelectorAll('#search-wrapper .result-section .model-section').forEach(ele => ele.remove()); document.querySelectorAll("#search-wrapper .top-seo-search-wrapper").forEach(ele => { let eleWrapper = ele.closest('big-topsearchseo'); if (eleWrapper) eleWrapper.remove(); else ele.remove(); }); } let adCount = 0; let hasMoreCars = true; const carsPerPageCount = 39; const inlineAdsMaxEveryXCards = 10; let previousUrlParams; const TriggerNextSearchResults = () => { // reseting view for pagination document.getElementById('car-results').innerHTML = ''; let req = new StockAPISearchRequest(); req = SetupPageUrlFromSearchRequest(req); SetupSearchFromParams(); req.Deposit = financeHolder.Deposit; let stockAlertParams = GetParamsForStockAlert(req); document.querySelectorAll('.create-stock-alert-button-top-result-section').forEach(ele => { ele.addEventListener('click', () => { location.href = `/create-stock-alert?${stockAlertParams}`; }); }); if (document.getElementById('create-stock-alert-button')) { document.getElementById('create-stock-alert-button').addEventListener('click', () => { location.href = `/create-stock-alert?${stockAlertParams}`; }); } let reqPage = req.page; if (!reqPage) reqPage = 1; let hasAddedFinanceExample = false; if (document.querySelector(".specific-search-section")) document.querySelector(".specific-search-section").classList.add("hidden"); let carResults = document.querySelector("#search-wrapper .car-results"); let oldPlaceholders = document.querySelectorAll('.ph-item'); if (oldPlaceholders) { oldPlaceholders.forEach(el => carResults.removeChild(el)); } for (let x = 0; x <= 14; x++) { carResults.appendChild(GetPlaceholderCarCard(carResults.classList.contains("list"))); } req.Count = carsPerPageCount; if (req.models) req.models = req.models.replaceAll(' ', '-'); if (req.makes) req.makes = req.makes.replaceAll(' ', '-'); // run storyblok here // get make model of search (if applicable) let storyMake = req.makes; let storyModel = req.models; if (!req.makes) storyMake = 'all'; try { if (storyModel) { StoryBlok.GetMakeModelStory(storyMake, storyModel) .then(story => { if (story) { // delete the other seo entries if applicable RemoveTopAndBottomSearchSEO(); let newEle = StoryBlok.BuildStory(story); document.querySelector("#search-wrapper .result-section").appendChild(newEle); document.querySelector(".seo-search-wrapper").classList.add("hidden"); if (story.content.top_seo && story.content.top_seo[0]) { let topSEOElem = StoryBlok.BuildStory(story.content.top_seo[0]); let parentElement = document.getElementById('search-wrapper'); parentElement.insertBefore(topSEOElem, parentElement.firstChild); // add the element as first child of the parent } } }).catch(() => { RemoveTopAndBottomSearchSEO(); }); // do nothing if error } else { // if theres only a make, show the 'model select' element if (storyMake && storyMake.includes("_") == false) { let modelSection = BIG.CreateDiv("model-section"); let makeText = ''; let header = BIG.CreateDiv("h1-style", ''); let modelsList = BIG.CreateDiv("models-list"); modelSection.appendChild(header); modelSection.appendChild(modelsList); let myCheckboxes = document.querySelectorAll(".make-model-wrapper:not(.hidden) .input-wrapper.checkbox"); myCheckboxes.forEach(checkbox => { let modelText = checkbox.querySelector("label").innerText; let modelCount = checkbox.getAttribute("count"); if (!modelCount) return; // if model count doesnt exist, then skip it? if (modelCount == undefined) return; if (makeText.length == 0) makeText = document.querySelector('.make-model-checkbox-wrapper .checkbox[make="' + checkbox.getAttribute('make') + '"]').innerText; let chooseModelLink = BIG.CreateDiv('choose-model-link', modelText); modelsList.appendChild(chooseModelLink); chooseModelLink.innerText = modelText + ' (' + modelCount + ')'; chooseModelLink.setAttribute('make', storyMake); chooseModelLink.setAttribute('model', modelText); chooseModelLink.setAttribute('count', modelCount); chooseModelLink.addEventListener('click', (event) => { checkbox.click(); }); }); let makeTextFirstLetter = makeText.trim().toLowerCase().substring(0, 1); let vowels = ['a', 'e', 'o', 'u']; let makeHeader = `Choose a ${makeText} model`; vowels.forEach(letter => { if (makeTextFirstLetter == letter) { makeHeader = `Choose an ${makeText} model`; } }) header.innerText = makeHeader; document.querySelector("#search-wrapper .result-section").appendChild(modelSection); StoryBlok.GetMakeStory(storyMake) .then(story => { RemoveTopAndBottomSearchSEO(); if (story) { let newEle = StoryBlok.BuildStory(story); document.querySelector("#search-wrapper .result-section").appendChild(newEle); document.querySelector(".seo-search-wrapper").classList.add("hidden"); if (story.content.top_seo && story.content.top_seo[0]) { let topSEOElem = StoryBlok.BuildStory(story.content.top_seo[0]); let parentElement = document.getElementById('search-wrapper'); parentElement.insertBefore(topSEOElem, parentElement.firstChild); // add the element as first child of the parent } } }).catch(() => { RemoveTopAndBottomSearchSEO(); }); // do nothing if error } else { RemoveTopAndBottomSearchSEO(); } } } catch { } const latestUrlParams = window.location.href; setTimeout(() => { API.Stock.Search(req) .then(carsResponse => { if (window.location.href != latestUrlParams) return; document.querySelector(".stock-alert-container").classList.remove("hidden"); hasMoreCars = carsResponse.HasMoreCars; let cars = carsResponse.Cars; let specCounter = 0; document.getElementById("in-results-counter").innerText = NumberWithCommas(carsResponse.TotalCount) + " cars in results"; document.getElementById("mobile-filter-section-button").innerText = "See " + NumberWithCommas(carsResponse.TotalCount) + " Results"; if (reqPage == 1) { // setup 'spec' filter options let filterOptions = carsResponse.FilterOptions; filterOptions .forEach(filter => { // if filter parent == model if (filter.Parent == 'model') { filter.Options.forEach(modelOption => { let modelAttributeValue = modelOption.Name.toLowerCase(); modelAttributeValue = modelAttributeValue.replace('-', ' '); let checkboxWrappers = document.querySelectorAll(`.input-wrapper.checkbox[model='${modelAttributeValue}']`); checkboxWrappers.forEach(input => { let inputModelAttribute = input.getAttribute('model').toLowerCase() let modelOptionName = modelOption.Name.toLowerCase(); modelOptionName = modelOptionName.replace('-', ' '); if (inputModelAttribute == modelOptionName) { input.setAttribute('count', modelOption.Count); } }); }); } //mpg if (filter.Parent == 'mpg') { let mpgSelect = document.getElementById('mpg-select'); let selectedValue = mpgSelect.value; mpgSelect.innerHTML = ''; filter.Options.forEach(opt => { let option = document.createElement('option'); option.text = opt.Name + ' Mpg'; option.value = opt.Name; mpgSelect.appendChild(option); }); mpgSelect.value = selectedValue; return; } //age if (filter.Parent == 'age') { let minimumAgeSelect = document.getElementById('minimum-age-select'); let minimumAgeSelectValue = minimumAgeSelect.value; minimumAgeSelect.innerHTML = ''; let maximumAgeSelect = document.getElementById('maximum-age-select'); let maximumAgeSelectValue = maximumAgeSelect.value; maximumAgeSelect.innerHTML = ''; filter.Options.forEach(opt => { // min age let minimumAgeOption = document.createElement('option'); minimumAgeOption.text = opt.Name + ' Years'; minimumAgeOption.value = opt.Name; minimumAgeSelect.appendChild(minimumAgeOption); //max age let maximumAgeOption = document.createElement('option'); maximumAgeOption.text = opt.Name + ' Years'; maximumAgeOption.value = opt.Name; maximumAgeSelect.appendChild(maximumAgeOption); }); if (minimumAgeSelectValue == "") minimumAgeSelectValue = 0; if (maximumAgeSelectValue == "") maximumAgeSelectValue = 0; minimumAgeSelect.value = minimumAgeSelectValue; maximumAgeSelect.value = maximumAgeSelectValue; return; } //engine size if (filter.Parent == 'engine-size') { let minimumEngineSelect = document.getElementById('minimum-engine-select'); let minimumEngineSelectValue = minimumEngineSelect.value; minimumEngineSelect.innerHTML = ''; let maximumEngineSelect = document.getElementById('maximum-engine-select'); let maximumEngineSelectValue = maximumEngineSelect.value; maximumEngineSelect.innerHTML = ''; filter.Options.forEach(opt => { // min engine size let minimumEngineOption = document.createElement('option'); minimumEngineOption.text = opt.Name + 'L'; minimumEngineOption.value = opt.Name; minimumEngineSelect.appendChild(minimumEngineOption); //max engine size let maximumEngineOption = document.createElement('option'); maximumEngineOption.text = opt.Name + 'L'; maximumEngineOption.value = opt.Name; maximumEngineSelect.appendChild(maximumEngineOption); }); if (minimumEngineSelectValue == "") minimumEngineSelectValue = 0; if (maximumEngineSelectValue == "") maximumEngineSelectValue = 0; minimumEngineSelect.value = minimumEngineSelectValue; maximumEngineSelect.value = maximumEngineSelectValue; return; } let specOptions = filter.Options; let specContainer = document.querySelector(`.checkbox-flex[group=${filter.Parent}]`); if (specContainer == null) return; let checkedSpec = [...specContainer.querySelectorAll("input:checked")].map(o => o.parentElement.getAttribute("search-slug")); specContainer.innerHTML = ""; specOptions .forEach(opt => { let name = opt.Name; let nameWithCount = opt.Name + ` (${opt.Count})`; if (name.toLowerCase().includes('"')) return; let count = opt.Count; let slug = opt.Slug; let wrapper = BIG.CreateDiv("input-wrapper checkbox"); let slugValue = slug.toLowerCase().replace(/-/g, ' '); wrapper.setAttribute("search-slug", slugValue); wrapper.setAttribute("display-name", name); let label = document.createElement("label"); label.className = "label"; let newID = `${filter.Parent}-search-checkbox-` + specCounter++; label.setAttribute("for", newID); label.textContent = nameWithCount; let input = document.createElement("input"); input.className = `${filter.Parent}-checkbox`; input.id = newID; input.setAttribute("type", "checkbox"); input.autocomplete = "off"; input.addEventListener("change", ResetSearchToPageZero); wrapper.appendChild(label); wrapper.appendChild(input); let slugColor = slugValue; if (input.classList.contains("colour-checkbox")) { switch (slugValue) { case "cream": slugColor = "#FFFDD0"; break; case "bronze": slugColor = "#CD7F32"; break; default: } let colourSwatch = BIG.CreateDiv("colour-swatch"); colourSwatch.setAttribute("style", "background-color:" + slugColor); wrapper.appendChild(colourSwatch); } if (input.classList.contains("body-checkbox")) { const validSVGiconNames = [ '4x4', 'convertible', 'coupe', 'estate', 'hatchback', 'mpv', 'saloon', 'suv', 'panel-van' ]; if (!validSVGiconNames.includes(slugValue)) slugValue = 'estate'; let bodyTypeIconContainer = BIG.CreateDiv("body-type-icon"); let bodyTypeIconImage = BIG.CreateImg("", `https://bigimages.azureedge.net/iconimages/body-type-${slugValue}.svg`, + " icon"); bodyTypeIconContainer.appendChild(bodyTypeIconImage); wrapper.appendChild(bodyTypeIconContainer); } if (checkedSpec.some(o => o == slugValue)) { input.checked = true; } specContainer.appendChild(wrapper); }); }); document.querySelectorAll('#body-checkboxes-container .body-type-icon img').forEach(img => { let isImageLoaded = CheckIfImageIsLoaded(img); if (isImageLoaded == false) { img.src = 'https://bigimages.azureedge.net/iconimages/body-type-estate.svg'; //bodyTypeIconImage.remove(); } }); } let count = 0; let max = cars.length; let appliedTrigger = false; let trigger = max * 0.7; // load each car? let stocknumberArray = []; let carResults = document.querySelector("#search-wrapper .car-results"); var resultsClone = document.createDocumentFragment(); const areAllCarsElectric = AreAllCarsElectric(cars); const allCarsSiteID = GetAllCarsLocation(cars); for (let car of cars) { count++; stocknumberArray.push(car.StockNumber); if (count == 4) { let financeExample = document.getElementById("finance-example").cloneNode(true); financeExample.removeAttribute('id'); resultsClone.appendChild(financeExample); hasAddedFinanceExample = true; } if (count == 5) { if (areAllCarsElectric) { insertEVChargeMapCard(allCarsSiteID, resultsClone); } } if (reqPage > 0) hasAddedFinanceExample = true; let carCard = BuildCarCard(car, count); if (count >= trigger && appliedTrigger == false) { appliedTrigger = true; carCard.classList.add("terrfl"); } if (count == 1) { triggerPageUrlUpdate.observe(carCard); carCard.setAttribute("page", searchManager.CurrentPage); } if (searchManager.CurrentPage == 0) searchManager.CurrentPage = 1; let countForAds = count; if (countForAds > inlineAdsMaxEveryXCards) { let divisor = Math.floor(countForAds / inlineAdsMaxEveryXCards); countForAds = countForAds - (divisor * inlineAdsMaxEveryXCards) + 1; } let carsAddedOffsetForAds = ((searchManager.CurrentPage - 1) * req.Count); if (carsAddedOffsetForAds > inlineAdsMaxEveryXCards) { let divisor = Math.floor(carsAddedOffsetForAds / inlineAdsMaxEveryXCards); carsAddedOffsetForAds = carsAddedOffsetForAds - (divisor * inlineAdsMaxEveryXCards) + 1; } let adPositionOffset = carsAddedOffsetForAds + countForAds; // offset should be in 7th position every 10 cars, so after car 10 it should reset back to 0, not count up let adPositionEveryXCars = 9; if (adPositionOffset % adPositionEveryXCars == 0) { // insert advert let adCard = document.createElement("a"); adCard.className = "car-card car-info-card"; let iconHref, adTextContent, buttonHref, buttonText, disclaimerText; disclaimerText = ""; switch (adCount) { case 0: // finance offer { iconHref = "/statics/images/icons/clipboard2.svg"; adTextContent = "Car finance eligibility checker, no impact on your credit score."; buttonHref = "/finance/"; buttonText = "Start now"; adCard.classList.add("blue"); let secondImg = document.createElement("img"); secondImg.classList.add("ad-car-image"); secondImg.src = "/statics/images/finance/car.png"; adCard.appendChild(secondImg); break; } case 1: // EV £500 contribution { iconHref = "/statics/images/icons/searchpops/Buy Online.svg"; adTextContent = "Go Electric with £500 towards your finance deposit*
offer ends 25 September 2025
"; buttonHref = "/news/make-the-jump-and-go-electric-with-this-limited-time-offer/"; disclaimerText = " Bapchild Motoring World (Kent) Limited is a credit broker (FCA No. 686118), not a lender. We offer select finance/insurance products and will earn commission. APR rate offered will depend on your personal circumstances. *Full T&Cs: Terms And Conditions | Big Motoring World"; buttonText = "Find out more"; adCard.classList.add("blue"); adCard.classList.add("green"); adCard.classList.add("ev-contribution"); //adCard.classList.add("extra-space-top"); let secondImgl = document.createElement("img"); secondImgl.classList.add("ad-car-image"); secondImgl.src = "/statics/images/icons/searchpops/FDC_Car_Image.svg"; adCard.appendChild(secondImgl); break; } case 2: // below market prices { iconHref = "/statics/images/icons/searchpops/Below Market Prices2.svg"; adTextContent = "95% of our cars are below market value."; buttonHref = "/big-value/"; buttonText = "Learn More"; adCard.classList.add("blue"); adCard.classList.add("green"); adCard.classList.add("extra-space-top"); let secondImgl = document.createElement("img"); secondImgl.classList.add("ad-car-image"); secondImgl.src = "/statics/images/icons/searchpops/silver-car.png"; adCard.appendChild(secondImgl); break; } case 3: // 200 point inspection iconHref = "/statics/images/icons/searchpops/200-point Inspection.svg"; adTextContent = "All of our cars undergo a 200-multipoint inspection."; buttonHref = "/big-quality/"; buttonText = "Learn More"; break; default: case 4: // finance iconHref = "/statics/images/icons/clipboard.svg"; adTextContent = "Finance available today."; buttonHref = "/finance/"; buttonText = "Check Your Affordability"; break; } let adImgWrapper = BIG.CreateDiv("img-container"); let adImg = adImgWrapper.AddImage(iconHref, true, iconHref); adImg.classList.add("car-img"); adCard.appendChild(adImgWrapper); // body let adBody = BIG.CreateDiv("card-body"); adCard.appendChild(adBody); // h4 let adText = BIG.CreateDiv("pseudo-h4"); adText.innerHTML = adTextContent; adBody.appendChild(adText); // button let adButton = document.createElement("a"); adButton.className = "btn primary rounded"; adButton.setAttribute("href", buttonHref); adButton.textContent = buttonText; adBody.appendChild(adButton); let disclaimerTextDiv = BIG.CreateDiv("disclaimer-text"); disclaimerTextDiv.innerHTML = disclaimerText; adBody.appendChild(disclaimerTextDiv); resultsClone.appendChild(adCard); adCount++; if (adCount >= 5) adCount = 0; } resultsClone.appendChild(carCard); } document.querySelector("#search-wrapper .car-results").append(resultsClone); document.querySelectorAll('.car-image-container-slider.lazy-slider').forEach(elem => { BuildCarCardImagesSlider(elem); }); try { fbq('track', 'Search', { content_type: 'vehicle', content_ids: stocknumberArray }, { eventID: generateUUID() }); } catch { } // todo: if SEO section exists, show/hide that instead if (document.querySelector(".specific-search-section")) document.querySelector(".specific-search-section").classList.remove("hidden"); // if no results, show no results section if (count == 0 && document.getElementById("car-results").childNodes.length == 0) { document.querySelector(".no-result-section").classList.remove("hidden"); } else { if (document.querySelector(".no-result-section").classList.contains("hidden") == false) document.querySelector(".no-result-section").classList.add("hidden"); } lozadObserver.observe(); // Load lazy images let oldPlaceholders = document.querySelectorAll('.car-results .ph-item'); if (oldPlaceholders) { oldPlaceholders.forEach(el => carResults.removeChild(el)); } buildPagination(carsResponse.TotalCount, carsPerPageCount); previousUrlParams = latestUrlParams; }); }, loadSearchTimeoutTime); if (loadSearchTimeoutTime == 0) loadSearchTimeoutTime = 1000; // 500ms after first load to allow scrolling.. if (hasAddedFinanceExample == false && req.TotalCount < 3) { let carResults = document.querySelector("#search-wrapper .car-results"); let financeExample = document.getElementById("finance-example").cloneNode(true); financeExample.removeAttribute('id'); carResults.appendChild(financeExample); } } const AreAllCarsElectric = (cars) => { return cars.every(car => car.Fuel === "Electric"); }; const GetAllCarsLocation = (cars) => { const firstCarSiteID = cars[0].SiteID; if (cars.every(car => car.SiteID === firstCarSiteID)) return firstCarSiteID; return 1;// Default to BBH }; function insertEVChargeMapCard(allCarsSiteID, resultsClone) { const evCard = document.createElement('div'); evCard.classList.add('info-card', 'ev-charging-points-map'); evCard.classList.add('car-card'); const title = document.createElement('h2'); title.textContent = 'EV Charge Map'; evCard.appendChild(title); title.classList.add('ev-map-heading'); const tableWrapper = document.createElement('div'); tableWrapper.classList.add('ev-charging-points-map-iframe'); evCard.appendChild(tableWrapper); // Container for iframe const iframeWrapper = document.createElement('div'); iframeWrapper.classList.add('iframe'); tableWrapper.appendChild(iframeWrapper); // Load site profile and embed iframe API.Get("sites/get-site-profile", { siteid: allCarsSiteID }) .then(siteProfile => { const iframe = document.createElement('iframe'); iframe.setAttribute('src', `https://map.openchargemap.io/?mode=embedded&latitude=${siteProfile.Latitude}&longitude=${siteProfile.Longitude}`); iframe.setAttribute('height', '500'); iframe.setAttribute('width', '100%'); iframe.setAttribute('style', 'border:0; display:block; margin: 0 auto; color-scheme: dark;'); iframeWrapper.appendChild(iframe); }) .catch(err => { }); resultsClone.appendChild(evCard); } let loadSearchTimeoutTime = 0; const CheckIfImageIsLoaded = (image) => { if (image.onerror) return false; return true; } const insertUrlParam = (key, value) => { if (history.pushState) { let searchParams = new URLSearchParams(window.location.search); searchParams.set(key, value); let newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + searchParams.toString(); window.history.pushState({ path: newurl }, '', newurl); } } const removeUrlParam = (paramKey) => { var url = new URL(window.location.href); url.searchParams.delete(paramKey); const newUrl = url.href; window.history.pushState({ path: newUrl }, '', newUrl); } const buildPagination = (carCount, carsPerPage) => { let paginationContainer = document.getElementById('pagination-container'); paginationContainer.innerHTML = ''; if (carCount <= 0) { removeUrlParam('page'); return; } if (carsPerPage == undefined || carsPerPage == null || carsPerPage == 0) carsPerPage = 10; let totalPages = Math.ceil(carCount / carsPerPage) if (totalPages == 0) totalPages = 1; let maxPages = 8; if (window.innerWidth <= 1000) { maxPages = 4; } let startPage = 1; let endPage = maxPages; if (searchManager.CurrentPage < 1) searchManager.CurrentPage = 1; if (searchManager.CurrentPage > totalPages) searchManager.CurrentPage = totalPages; let currentPage = +searchManager.CurrentPage; const addPage = (page) => { let pageNumberDiv = BIG.CreateDiv('page-number', page); if (page === currentPage) { pageNumberDiv.classList.add('selected'); } else { if (page !== '...') { pageNumberDiv.addEventListener('click', () => { searchManager.CurrentPage = page; const searchStart = document.getElementById('search-start'); searchStart.scrollIntoView({ behavior: "instant" }); paginationContainer.classList.add('hidden'); insertUrlParam('page', searchManager.CurrentPage); TriggerNextSearchResults(); }); } else { pageNumberDiv.classList.add('dot'); } } paginationContainer.appendChild(pageNumberDiv); } let previousPageButton = BIG.CreateDiv('page-number arrow'); previousPageButton.innerHTML = '‹'; paginationContainer.appendChild(previousPageButton); if (currentPage == 1) { previousPageButton.classList.add('disabled'); } else { previousPageButton.addEventListener('click', () => { searchManager.CurrentPage--; const searchStart = document.getElementById('search-start'); searchStart.scrollIntoView({ behavior: "instant" }); insertUrlParam('page', searchManager.CurrentPage); paginationContainer.classList.add('hidden'); TriggerNextSearchResults(); }); } if (totalPages <= maxPages) { endPage = totalPages; } else { let maxPagesBeforeCurrentPage = Math.floor(maxPages / 2) - 1; let maxPagesAfterCurrentPage = Math.ceil(maxPages / 2) - 1; if (currentPage <= maxPagesBeforeCurrentPage) { // current page near the start startPage = 1; endPage = maxPages; } else if (currentPage + maxPagesAfterCurrentPage >= totalPages) { // current page near the end startPage = totalPages - maxPages + 1; endPage = totalPages; } else { // current page somewhere in the middle startPage = currentPage - maxPagesBeforeCurrentPage; endPage = currentPage + maxPagesAfterCurrentPage; } } let pages = Array.from(Array((endPage + 1) - startPage).keys()).map(i => startPage + i); if (pages.length > 2 && pages[pages.length - 1] != totalPages) { if (pages[pages.length - 1] != totalPages - 1) { pages.push('...'); pages.push(totalPages); } else { pages.push(totalPages); } } if (pages[0] != 1) { pages.unshift('...'); pages.unshift(1); } pages.forEach(page => { addPage(page); }); let nextPageButton = BIG.CreateDiv('page-number arrow'); nextPageButton.innerHTML = '›'; paginationContainer.appendChild(nextPageButton); if (currentPage == totalPages) { nextPageButton.classList.add('disabled'); } else { nextPageButton.addEventListener('click', () => { searchManager.CurrentPage++; const searchStart = document.getElementById('search-start'); searchStart.scrollIntoView({ behavior: "instant" }); insertUrlParam('page', searchManager.CurrentPage); paginationContainer.classList.add('hidden'); TriggerNextSearchResults(); }); } paginationContainer.classList.remove('hidden'); } const GetLabelsFromCheckboxes = (parentElementID) => { let others = document.getElementById(parentElementID).querySelectorAll(`input[type='checkbox']`); let text = []; for (let other of others) { if (other.checked == false) continue; text.push(other.parentElement.getAttribute("search-slug")); } return text; } const ChangeMakeSearch = (event) => { let selectedMake = event.currentTarget.value; if (selectedMake.includes('mercedes')) { selectedMake = 'mercedes-benz'; } let modelSelect = document.getElementById('model-select'); modelSelect.removeAttribute('disabled'); modelSelect.innerHTML = ''; let anyModelOpt = document.createElement('option'); anyModelOpt.innerText = 'Any'; anyModelOpt.setAttribute('value', '-1'); modelSelect.appendChild(anyModelOpt); let modelOpts; if (selectedMake != -1) { modelOpts = makeModelManager.AllModels[selectedMake.toLowerCase()]; modelSelect.closest('.input-wrapper').classList.remove('hidden'); } else { modelSelect.setAttribute('disabled', 'disabled'); modelSelect.closest('.input-wrapper').classList.add('hidden'); // hide model select if make is not selected (i.e 'Any') return; } modelOpts.forEach(modelOpt => { modelSelect.append(modelOpt); modelOpt.classList.remove('hidden'); }); } const SetupMakeModelSearch = () => { let models = document.querySelectorAll('#hidden-all-models-select option'); models.forEach(modelOpt => { let make = modelOpt.getAttribute('make'); if (!make) return; let obj = makeModelManager.AllModels[make.toLowerCase()]; if (!obj) makeModelManager.AllModels[make.toLowerCase()] = []; makeModelManager.AllModels[make.toLowerCase()].push(modelOpt); }); }