TOURMAX

CERCA LA TUA MOTO

Modifica marca, modello e anno senza tornare alla home.

"); } function containsWholeNormalizedTerm(query, term) { const normalized = normalizeSearch(term); return normalized && new RegExp(`(^| )${escapeRegex(normalized)}( |$)`).test(query); } function inferFromSearch(rows, marche) { const params = new URLSearchParams(window.location.search); const query = normalizeSearch(params.get("q") || params.get("query") || ""); if (!query) return {}; const marca = marche .slice() .sort((a, b) => b.length - a.length) .find((value) => { return containsWholeNormalizedTerm(query, value); }) || ""; if (!marca) return {}; const modelli = Array.from(new Set(rows .filter((row) => row[0] === marca) .map((row) => row[1]) .filter(Boolean))); const compactQuery = query.replace(/\s+/g, ""); const modello = modelli .map((value) => { const normalized = normalizeSearch(value); const compact = normalized.replace(/\s+/g, ""); const core = normalized .replace(/\([^)]*\)/g, " ") .replace(/\b(E[0-9]|ABS|ADVENTURE|ADVENTUR|ADV|LC|RALLY|RACING|SPORT|ABS)\b/g, " ") .replace(/\b[0-9]{2}[-/][0-9]{2}\b/g, " ") .replace(/\s+/g, " ") .trim(); const coreWithoutSeries = core.replace(/^[A-Z]\s+(?=\d)/, "").trim(); let score = 0; if (containsWholeNormalizedTerm(query, normalized)) score = 10000 + normalized.length; else if (compact && compactQuery.includes(compact)) score = 9000 + compact.length; else if (core && containsWholeNormalizedTerm(query, core)) score = 5000 - Math.max(0, normalized.length - core.length); else if (coreWithoutSeries && containsWholeNormalizedTerm(query, coreWithoutSeries)) score = 4600 - Math.max(0, normalized.length - coreWithoutSeries.length); return { value, score, length: normalized.length }; }) .filter((item) => item.score > 0) .sort((a, b) => b.score - a.score || a.length - b.length) .map((item) => item.value)[0] || ""; const years = Array.from(new Set(rows .filter((row) => row[0] === marca && (!modello || row[1] === modello)) .map((row) => row[2]) .filter(Boolean))); const yearMatch = query.match(/\b(19[5-9]\d|20[0-3]\d)\b/); const anno = yearMatch && years.includes(yearMatch[1]) ? yearMatch[1] : ""; return { marca, modello, anno }; } function selectedFitmentValues() { const rows = window.FP_FITMENT_FILTERS?.rows || []; const params = new URLSearchParams(window.location.search); const marche = Array.from(new Set(rows.map((row) => row[0]).filter(Boolean))); const marca = firstExisting(marche, [ valueFromTag("fp-marca-", marche), params.get("fp_marca"), params.get("filter.p.m.custom.marca"), valueFromSelect("MARCA"), ]); const modelli = Array.from(new Set(rows .filter((row) => !marca || row[0] === marca) .map((row) => row[1]) .filter(Boolean))); const modello = firstExisting(modelli, [ valueFromTag("fp-modello-", modelli), params.get("fp_modello"), params.get("filter.p.m.custom.modello"), valueFromSelect("MODELLO"), ]); const anni = Array.from(new Set(rows .filter((row) => (!marca || row[0] === marca) && (!modello || row[1] === modello)) .map((row) => row[2]) .filter(Boolean))); const anno = firstExisting(anni, [ valueFromTag("fp-anno-", anni), params.get("fp_anno"), params.get("filter.p.m.custom.anno"), valueFromSelect("ANNO"), ]); return { marca, modello, anno }; } function fitmentTagsForSelected() { const selected = selectedFitmentValues(); return fitmentTagsForValues(selected); } function fitmentTagsForValues(selected = {}) { const tags = []; if (selected.marca) tags.push(`fp-marca-${handleize(selected.marca)}`); if (selected.modello) tags.push(`fp-modello-${handleize(selected.modello)}`); if (selected.anno) tags.push(`fp-anno-${handleize(selected.anno)}`); return tags; } function buildUrl({ add = {}, remove = {} } = {}) { const url = new URL(window.location.href); const params = new URLSearchParams(url.search); params.delete("page"); for (const [key, values] of Object.entries(remove)) { const removeSet = new Set(values); const kept = params.getAll(key).filter((value) => !removeSet.has(value)); params.delete(key); kept.forEach((value) => params.append(key, value)); } for (const [key, values] of Object.entries(add)) { const existing = new Set(params.getAll(key)); values.forEach((value) => { if (!existing.has(value)) params.append(key, value); }); } const tags = fitmentTagsOnly().length ? fitmentTagsOnly() : fitmentTagsForSelected(); const path = tags.length ? `${baseCollectionPath()}/${tags.map(encodeURIComponent).join("+")}` : baseCollectionPath(); const query = params.toString(); return query ? `${path}?${query}` : path; } function cleanLegacyParams(params) { params.delete(LEGACY_CATEGORY_PARAM); params.delete(LEGACY_SUBCATEGORY_PARAM); params.delete("page"); } function buildTagUrl({ type, value, active, selectedCategories = [] } = {}) { const url = new URL(window.location.href); const params = new URLSearchParams(url.search); cleanLegacyParams(params); params.delete("page"); const tags = fitmentTagsOnly().length ? fitmentTagsOnly() : fitmentTagsForSelected(); const categoryKey = "fp_cat"; const subcategoryKey = "fp_subcat"; let categoryTags = selectedQueryHandles(categoryKey); let subcategoryTags = selectedQueryHandles(subcategoryKey); const hasFitmentTags = tags.length > 0; if (type === "category") { const categoryHandle = handleize(value); categoryTags = hasFitmentTags ? toggleHandle(categoryTags, categoryHandle, active) : (active ? [] : [categoryHandle]); subcategoryTags = []; } if (type === "subcategory") { const subcategoryHandle = handleize(value); if (!selectedCategories.length) { subcategoryTags = []; } else { subcategoryTags = hasFitmentTags ? toggleHandle(subcategoryTags, subcategoryHandle, active) : (active ? [] : [subcategoryHandle]); } } if (!type) { categoryTags = []; subcategoryTags = []; } params.delete(categoryKey); params.delete(subcategoryKey); const categoryPathTags = []; if (categoryTags.length === 1) categoryPathTags.push(`${CATEGORY_TAG_PREFIX}${categoryTags[0]}`); if (subcategoryTags.length === 1) categoryPathTags.push(`${SUBCATEGORY_TAG_PREFIX}${subcategoryTags[0]}`); const canUseShopifyTagFilter = categoryPathTags.length === categoryTags.length + subcategoryTags.length; if (!canUseShopifyTagFilter) { categoryTags.forEach((tag) => params.append(categoryKey, tag)); subcategoryTags.forEach((tag) => params.append(subcategoryKey, tag)); } const pathTags = canUseShopifyTagFilter ? [...tags, ...categoryPathTags] : [...tags]; const path = pathTags.length ? `${baseCollectionPath()}/${pathTags.map(encodeURIComponent).join("+")}` : baseCollectionPath(); const query = params.toString(); return query ? `${path}?${query}` : path; } function cleanSearchQuery() { const params = new URLSearchParams(window.location.search); return String(params.get("q") || "") .replace(/\s+/g, " ") .trim(); } function buildSearchUrl({ type, value, active, selectedCategories = [], selectedFitment = {} } = {}) { const url = new URL(window.location.href); const params = new URLSearchParams(url.search); cleanLegacyParams(params); params.delete("page"); params.delete("query"); params.delete("fp_cat"); params.delete("fp_subcat"); params.set("fp_from_search", "1"); const originalQuery = cleanSearchQuery(); if (originalQuery) { params.set("q", originalQuery); params.set("options[prefix]", "last"); } else { params.delete("q"); params.delete("options[prefix]"); } const categoryKey = "fp_cat"; const subcategoryKey = "fp_subcat"; let categoryTags = selectedQueryHandles(categoryKey); let subcategoryTags = selectedQueryHandles(subcategoryKey); if (type === "category") { const categoryHandle = handleize(value); categoryTags = toggleHandle(categoryTags, categoryHandle, active); subcategoryTags = []; } if (type === "subcategory") { const subcategoryHandle = handleize(value); if (!selectedCategories.length || active) { subcategoryTags = toggleHandle(subcategoryTags, subcategoryHandle, active); } else { categoryTags = selectedCategories.map((category) => handleize(category)); subcategoryTags = toggleHandle(subcategoryTags, subcategoryHandle, false); } } if (!type) { categoryTags = []; subcategoryTags = []; } params.delete(categoryKey); params.delete(subcategoryKey); categoryTags.forEach((tag) => params.append(categoryKey, tag)); subcategoryTags.forEach((tag) => params.append(subcategoryKey, tag)); const query = params.toString(); return query ? `/search?${query}` : "/search"; } function selectedQueryHandles(key) { return Array.from(new Set(new URLSearchParams(window.location.search).getAll(key).map(handleize).filter(Boolean))); } function toggleHandle(handles, handle, active) { const next = new Set(handles); if (active) next.delete(handle); else next.add(handle); return Array.from(next); } function selectedValuesFromTags(prefix, values, legacyParamName) { const tags = currentTags(); const selected = values.filter((value) => tags.includes(`${prefix}${handleize(value)}`)); if (selected.length) return selected; return new URLSearchParams(window.location.search).getAll(legacyParamName).filter((value) => values.includes(value)); } function selectedValuesFromSearch(prefix, values) { const key = prefix === CATEGORY_TAG_PREFIX ? "fp_cat" : "fp_subcat"; const selected = selectedQueryHandles(key); return values.filter((value) => selected.includes(handleize(value))); } function selectedValuesFromCollectionQuery(prefix, values) { const key = prefix === CATEGORY_TAG_PREFIX ? "fp_cat" : "fp_subcat"; const selected = selectedQueryHandles(key); const queryValues = values.filter((value) => selected.includes(handleize(value))); if (queryValues.length) return queryValues; return selectedValuesFromTags(prefix, values, prefix === CATEGORY_TAG_PREFIX ? LEGACY_CATEGORY_PARAM : LEGACY_SUBCATEGORY_PARAM); } function resultBaseUrl() { const url = new URL(window.location.href); const params = new URLSearchParams(url.search); cleanLegacyParams(params); params.delete("fp_cat"); params.delete("fp_subcat"); params.delete("page"); const query = params.toString(); return query ? `${url.pathname}?${query}` : url.pathname; } async function fetchResultPage(page) { const base = new URL(resultBaseUrl(), window.location.origin); base.searchParams.set("page", String(page)); const response = await fetch(base.toString(), { credentials: "same-origin" }); if (!response.ok) return null; const html = await response.text(); return new DOMParser().parseFromString(html, "text/html"); } function matchesCategoryFilter(card, selectedCategories, selectedSubcategories) { const categories = selectedCategories.map(handleize); const subcategories = selectedSubcategories.map(handleize); const cardCategory = card.dataset.fpCategoria || ""; const cardSubcategory = card.dataset.fpSottocategoria || ""; const okCategory = !categories.length || categories.includes(cardCategory); const okSubcategory = !subcategories.length || subcategories.includes(cardSubcategory); return okCategory && okSubcategory; } async function applyFullResultFilter(selectedCategories, selectedSubcategories) { const grid = document.querySelector("#product-grid"); if (!grid || (!selectedCategories.length && !selectedSubcategories.length)) return; const container = document.querySelector("#ProductGridContainer") || grid.closest(".collection") || grid; container.dataset.fpFiltering = "loading"; const firstCards = Array.from(document.querySelectorAll("#product-grid [data-fp-product-card]")); const allCards = []; const seen = new Set(); const addCards = (cards) => { cards.forEach((card) => { const key = card.querySelector("a[href*='/products/']")?.getAttribute("href") || card.innerHTML.slice(0, 120); if (seen.has(key)) return; seen.add(key); allCards.push(card); }); }; addCards(firstCards); const paginationLinks = Array.from(document.querySelectorAll("a[href*='page=']")); const pages = paginationLinks .map((link) => Number(new URL(link.href, window.location.origin).searchParams.get("page") || "0")) .filter((page) => Number.isFinite(page) && page > 1); const maxPage = pages.length ? Math.max(...pages) : 1; for (let page = 2; page <= maxPage; page += 1) { const doc = await fetchResultPage(page); if (!doc) continue; addCards(Array.from(doc.querySelectorAll("#product-grid [data-fp-product-card]"))); } const matching = allCards.filter((card) => matchesCategoryFilter(card, selectedCategories, selectedSubcategories)); grid.innerHTML = ""; matching.forEach((card) => grid.appendChild(card.cloneNode(true))); document.querySelectorAll("ajax-paginate, .pagination, nav[role='navigation']").forEach((element) => { if (element.closest("#ProductGridContainer") || element.parentElement?.tagName === "AJAX-PAGINATE") element.hidden = true; }); if (!matching.length) { grid.insertAdjacentHTML("beforebegin", '
Nessun articolo trovato per le categorie selezionate.
'); } container.dataset.fpFiltering = "done"; } function checkboxLink({ label, type, value, active, selectedCategories = [], selectedFitment = {}, mode = "collection" }) { const href = mode === "search" ? buildSearchUrl({ type, value, active, selectedCategories, selectedFitment }) : buildTagUrl({ type, value, active, selectedCategories }); const activeClass = active ? "fp-category-check is-active" : "fp-category-check"; return `
  • ${label}
  • `; } function facetHtml(title, rows) { if (!rows.length) return ""; return `
      ${rows.join("")}
    `; } function render() { const container = document.querySelector("[data-fp-fitment-categories]"); if (!container || !Array.isArray(window.FP_FITMENT_FILTERS?.categories)) return; const fallbackSidebar = container.closest("[data-fp-fitment-sidebar-fallback]"); const rows = window.FP_FITMENT_FILTERS?.rows || []; const marche = Array.from(new Set(rows.map((row) => row[0]).filter(Boolean))); const strictSelected = selectedFitmentValues(); const inferredSelected = isSearchPage() ? inferFromSearch(rows, marche) : {}; const mode = isSearchPage() ? "search" : "collection"; const selected = inferredSelected.marca && inferredSelected.modello ? inferredSelected : strictSelected; const categories = new Map(); const subcategoriesByCategory = new Map(); window.FP_FITMENT_FILTERS.categories.forEach(([marca, modello, anno, categoria, sottocategoria]) => { if (selected.marca && marca !== selected.marca) return; if (selected.modello && modello !== selected.modello) return; if (selected.anno && anno !== selected.anno) return; if (categoria) categories.set(categoria, categoria); if (categoria && sottocategoria) { if (!subcategoriesByCategory.has(categoria)) subcategoriesByCategory.set(categoria, new Map()); subcategoriesByCategory.get(categoria).set(sottocategoria, sottocategoria); } }); if (!categories.size) { container.hidden = true; if (fallbackSidebar) fallbackSidebar.hidden = true; return; } const categoryValues = Array.from(categories.keys()).sort((a, b) => a.localeCompare(b, "it", { numeric: true })); const selectedCategories = (mode === "search" ? selectedValuesFromSearch(CATEGORY_TAG_PREFIX, categoryValues) : selectedValuesFromCollectionQuery(CATEGORY_TAG_PREFIX, categoryValues)) .filter((value) => categories.has(value)); const selectedCategorySet = selectedCategories.length ? selectedCategories : []; const subcategorySource = selectedCategorySet.length ? selectedCategorySet.reduce((map, category) => { (subcategoriesByCategory.get(category) || new Map()).forEach((item) => map.set(item, item)); return map; }, new Map()) : new Map(); const subcategoryValues = subcategorySource.size ? Array.from(subcategorySource.keys()).sort((a, b) => a.localeCompare(b, "it", { numeric: true })) : []; const selectedSubcategories = (mode === "search" ? selectedValuesFromSearch(SUBCATEGORY_TAG_PREFIX, subcategoryValues) : selectedValuesFromCollectionQuery(SUBCATEGORY_TAG_PREFIX, subcategoryValues)) .filter((value) => subcategoryValues.includes(value)); const allClass = selectedCategories.length || selectedSubcategories.length ? "fp-category-check" : "fp-category-check is-active"; const allHref = mode === "search" ? buildSearchUrl({ selectedFitment: selected }) : buildTagUrl(); const allRow = `
  • Tutte le categorie
  • `; const categoryRows = categoryValues .map((value) => checkboxLink({ label: value, type: "category", value, active: selectedCategories.includes(value), selectedFitment: selected, mode, })); const subcategoryRows = subcategoryValues .map((value) => checkboxLink({ label: value, type: "subcategory", value, active: selectedSubcategories.includes(value), selectedCategories, selectedFitment: selected, mode, })); container.innerHTML = `
    Filtra i ricambi compatibili
    ` + facetHtml("Categorie", [allRow, ...categoryRows]) + facetHtml("Sottocategorie", subcategoryRows); container.hidden = false; if (fallbackSidebar) { fallbackSidebar.hidden = false; const facetsWrap = fallbackSidebar.closest(".facets-wrap"); if (facetsWrap) { facetsWrap.classList.add("page-layout-with-sidebar", "fp-has-fitment-sidebar"); facetsWrap.classList.remove("page-layout-fullwidth"); } } applyFullResultFilter(selectedCategories, selectedSubcategories); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", render); } else { render(); } window.addEventListener("pageshow", render); window.addEventListener("hashchange", render); setTimeout(render, 400); })();