");
}
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 ``;
}
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);
})();