Wyniki pojawią się tutaj.
Wybierz przeznaczenie, aby rozpocząć.
"']/g, function (character) {
return {
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'"
}[character];
});
}
function unique(values) {
return values.filter(function (value, index, array) {
return array.indexOf(value) === index;
});
}
function formatNumber(value, maxDecimals) {
return new Intl.NumberFormat("id-ID", {
maximumFractionDigits: maxDecimals,
minimumFractionDigits: 0
}).format(value);
}
function mmToInch(mm) {
return Math.round((mm / 25.4) / 0.05) * 0.05;
}
function tireDisplay(mm) {
return formatNumber(mm, 2) + " mm / " + formatNumber(mmToInch(mm), 2) + "\"";
}
function formatHoleOption(value) {
var text = String(value).trim().toUpperCase();
return /H$/.test(text) ? text : text + "H";
}
function fillSelect(select, values, placeholder, labels, order) {
var current = select.value;
var sorted = values.slice();
if (order) {
sorted.sort(function (a, b) { return order.indexOf(a) - order.indexOf(b); });
}
select.innerHTML = "";
if (sorted.length !== 1) {
var placeholderOption = document.createElement("option");
placeholderOption.value = "";
placeholderOption.textContent = placeholder;
select.appendChild(placeholderOption);
}
sorted.forEach(function (value) {
var option = document.createElement("option");
option.value = value;
option.textContent = labels[value] || value;
select.appendChild(option);
});
if (sorted.length === 1) {
select.value = sorted[0];
} else if (sorted.indexOf(current) !== -1) {
select.value = current;
} else {
select.value = "";
}
select.disabled = sorted.length <= 1;
select.setAttribute("data-locked", sorted.length === 1 ? "true" : "false");
return sorted.length;
}
function init(root) {
if (root.getAttribute("data-rjm-ready") === "true") return;
root.setAttribute("data-rjm-ready", "true");
var form = root.querySelector("[data-rjm-form]");
var resultsNode = root.querySelector("[data-results]");
var tireHint = root.querySelector("[data-tire-hint]");
var tireOutput = root.querySelector("[data-tire-output]");
var tireMinLabel = root.querySelector("[data-tire-min]");
var tireMaxLabel = root.querySelector("[data-tire-max]");
var sizeHelp = root.querySelector("[data-size-help]");
var brakeHelp = root.querySelector("[data-brake-help]");
var fields = {
use: root.querySelector('[data-field="use"]'),
size: root.querySelector('[data-field="size"]'),
brake: root.querySelector('[data-field="brake"]'),
tire: root.querySelector('[data-field="tire"]')
};
var sliderSignature = "";
function availableRims() {
return RIMS.slice();
}
function rimsForUse(use) {
return availableRims().filter(function (rim) {
return rim.uses.indexOf(use) !== -1;
});
}
function candidatesBeforeTire() {
if (!fields.use.value) return [];
return rimsForUse(fields.use.value).filter(function (rim) {
if (fields.size.value && rim.size !== fields.size.value) return false;
if (fields.brake.value && rim.brake !== fields.brake.value) return false;
return true;
});
}
function updateSliderVisual() {
if (fields.tire.disabled) return;
var value = Number(fields.tire.value);
var min = Number(fields.tire.min);
var max = Number(fields.tire.max);
var progress = max === min ? 100 : ((value - min) / (max - min)) * 100;
fields.tire.style.setProperty("--rjm-range-progress", Math.max(0, Math.min(100, progress)) + "%");
tireOutput.textContent = tireDisplay(value);
}
function compatibilityScore(rim, tireMm) {
var span = Math.max(1, rim.maxMm - rim.minMm);
var margin = Math.min(tireMm - rim.minMm, rim.maxMm - tireMm);
return margin / span;
}
function findResults(tireMm) {
return candidatesBeforeTire().filter(function (rim) {
return tireMm >= rim.minMm && tireMm <= rim.maxMm;
}).sort(function (a, b) {
if (a.active !== b.active) return a.active === false ? 1 : -1;
var scoreDifference = compatibilityScore(b, tireMm) - compatibilityScore(a, tireMm);
if (scoreDifference !== 0) return scoreDifference;
return (a.brand + " " + a.model).localeCompare(b.brand + " " + b.model, "id");
});
}
function productCard(rim, tireMm) {
var holeKnown = Array.isArray(rim.holes) && rim.holes.length > 0;
var discontinued = rim.active === false;
var edgeMargin = Math.min(tireMm - rim.minMm, rim.maxMm - tireMm);
var edgeBadge = edgeMargin <= 1 ? 'Blisko granicy zakresu' : "";
var statusBadge = discontinued
? 'Wycofany'
: "";
var holeBadge = holeKnown
? 'Otwory na szprychy: ' + escapeHtml(rim.holes.map(formatHoleOption).join(", ")) + ""
: 'Liczbę otworów należy potwierdzić';
var holeText = holeKnown ? "" : " Potwierdź liczbę otworów na szprychy z używaną piastą.";
var link = rim.url
? 'Zobacz produkt →'
: "";
return '' +
'' +
'' +
'
' + escapeHtml(rim.brand) + "
" +
'
' + escapeHtml(rim.model) + "
" +
'
' +
'' + escapeHtml(LABELS.materials[rim.material]) + "" +
'' + escapeHtml(LABELS.brakes[rim.brake]) + "" +
statusBadge + edgeBadge + holeBadge +
"
" +
"
" +
'' +
'
' +
'- Rozmiar
- ' + escapeHtml(LABELS.sizes[rim.size]) + "
" +
'- Szerokość opony
- ' + escapeHtml(tireDisplay(rim.minMm)) + " – " + escapeHtml(tireDisplay(rim.maxMm)) + "
" +
'- Szerokość wewnętrzna
- ' + escapeHtml(formatNumber(rim.innerMm, 2)) + " mm
" +
'- Szerokość zewnętrzna
- ' + escapeHtml(formatNumber(rim.outerMm, 2)) + " mm
" +
'- Wysokość profilu
- ' + escapeHtml(formatNumber(rim.heightMm, 2)) + " mm
" +
'- System opony
- ' + escapeHtml(LABELS.systems[rim.system]) + "
" +
"
" +
'
Opona ' + escapeHtml(tireDisplay(tireMm)) + " mieści się w zakresie danych tego rimu." + escapeHtml(holeText) + "
" +
link +
"
" +
"";
}
function renderResults(rims, tireMm) {
if (rims.length === 0) {
resultsNode.innerHTML = '' +
'
' +
'Brak kompatybilnych rimów dla tej szerokości opony.' +
'Nie używaj rozmiaru spoza zakresu. Zmień szerokość opony lub ponownie sprawdź rozmiar koła i system hamulcowy.' +
"
";
return;
}
var unknownHoleCount = rims.filter(function (rim) { return !Array.isArray(rim.holes) || rim.holes.length === 0; }).length;
var holeNotice = unknownHoleCount > 0
? '
Niepełne dane dotyczące otworów na szprychy. W przypadku ' + unknownHoleCount + " z " + rims.length + " wyników należy potwierdzić liczbę otworów na szprychy przed montażem.
"
: "";
var hooklessNotice = rims.some(function (rim) { return rim.system === "TR_HOOKLESS"; })
? '
Uwaga dotycząca systemu hookless: używaj opony wyraźnie kompatybilnej z systemem hookless i przestrzegaj limitów ciśnienia podanych przez producenta rimu i opony.
'
: "";
var discontinuedCount = rims.filter(function (rim) { return rim.active === false; }).length;
var discontinuedNotice = discontinuedCount > 0
? '
Status wycofania: ' + discontinuedCount + " wyników dotyczy produktów, które nie są już aktywne. Dostępność magazynowa może być ograniczona; przed zakupem sprawdź stronę produktu lub skontaktuj się z dealerem.
"
: "";
resultsNode.innerHTML = '' +
'
' +
'
Kompatybilne rimy
Wyniki zmieniają się automatycznie podczas przesuwania suwaka. Kolejność zależy od położenia szerokości opony w zakresie, a nie od jakości ani ceny.
' +
'
' + rims.length + " wyników" +
"
" +
'
' + rims.map(function (rim) { return productCard(rim, tireMm); }).join("") + "
" +
holeNotice + hooklessNotice + discontinuedNotice;
}
function configureSlider() {
var candidates = candidatesBeforeTire();
var ready = fields.use.value && fields.size.value && fields.brake.value && candidates.length > 0;
if (!ready) {
fields.tire.disabled = true;
fields.tire.style.setProperty("--rjm-range-progress", "0%");
tireOutput.textContent = "—";
tireMinLabel.textContent = "—";
tireMaxLabel.textContent = "—";
tireHint.textContent = "Najpierw wybierz przeznaczenie, rozmiar koła i system hamulcowy.";
resultsNode.innerHTML = INITIAL_RESULT_HTML;
sliderSignature = "";
return;
}
var minMm = Math.min.apply(null, candidates.map(function (rim) { return rim.minMm; }));
var maxMm = Math.max.apply(null, candidates.map(function (rim) { return rim.maxMm; }));
var signature = fields.use.value + "|" + fields.size.value + "|" + fields.brake.value;
fields.tire.min = minMm;
fields.tire.max = maxMm;
fields.tire.step = "1";
fields.tire.disabled = false;
if (signature !== sliderSignature) {
var preferred = DEFAULT_TIRE_MM[fields.use.value] || Math.round((minMm + maxMm) / 2);
fields.tire.value = Math.max(minMm, Math.min(maxMm, preferred));
sliderSignature = signature;
} else {
fields.tire.value = Math.max(minMm, Math.min(maxMm, Number(fields.tire.value)));
}
tireMinLabel.textContent = tireDisplay(minMm);
tireMaxLabel.textContent = tireDisplay(maxMm);
tireHint.textContent = "Ustaw szerokość podaną na swojej oponie.";
updateSliderVisual();
renderResults(findResults(Number(fields.tire.value)), Number(fields.tire.value));
}
function refreshDependencies() {
var selectedUse = fields.use.value;
if (!selectedUse) {
fillSelect(fields.size, [], "Najpierw wybierz przeznaczenie", LABELS.sizes, SIZE_ORDER);
fillSelect(fields.brake, [], "Najpierw wybierz rozmiar koła", LABELS.brakes, ["DISC", "RIM"]);
sizeHelp.textContent = "Wartość ETRTO pomaga rozróżnić rozmiary 20-calowe.";
brakeHelp.textContent = "Hamulce tarczowe i szczękowe nie są zamienne.";
configureSlider();
return;
}
var useRims = rimsForUse(selectedUse);
var sizeCount = fillSelect(
fields.size,
unique(useRims.map(function (rim) { return rim.size; })),
"Wybierz rozmiar koła",
LABELS.sizes,
SIZE_ORDER
);
sizeHelp.textContent = sizeCount === 1
? "Wybrano automatycznie, ponieważ dostępny jest tylko jeden rozmiar."
: "Wybierz rozmiar koła używanego w rowerze.";
if (!fields.size.value) {
fillSelect(fields.brake, [], "Najpierw wybierz rozmiar koła", LABELS.brakes, ["DISC", "RIM"]);
brakeHelp.textContent = "Najpierw wybierz rozmiar koła.";
configureSlider();
return;
}
var sizeRims = useRims.filter(function (rim) { return rim.size === fields.size.value; });
var brakeCount = fillSelect(
fields.brake,
unique(sizeRims.map(function (rim) { return rim.brake; })),
"Wybierz system hamulcowy",
LABELS.brakes,
["DISC", "RIM"]
);
brakeHelp.textContent = brakeCount === 1
? "Wybrano automatycznie, ponieważ dostępny jest tylko jeden system hamulcowy."
: "Wybierz system hamulcowy w swoim rowerze.";
configureSlider();
}
form.addEventListener("submit", function (event) { event.preventDefault(); });
fields.use.addEventListener("change", function () {
fields.size.value = "";
fields.brake.value = "";
sliderSignature = "";
refreshDependencies();
});
fields.size.addEventListener("change", function () {
fields.brake.value = "";
sliderSignature = "";
refreshDependencies();
});
fields.brake.addEventListener("change", function () {
sliderSignature = "";
refreshDependencies();
});
fields.tire.addEventListener("input", function () {
updateSliderVisual();
renderResults(findResults(Number(fields.tire.value)), Number(fields.tire.value));
});
refreshDependencies();
}
function initAll(scope) {
var base = scope || document;
if (base.matches && base.matches("[data-rjm-rim-selector]")) init(base);
base.querySelectorAll("[data-rjm-rim-selector]").forEach(init);
}
initAll(document);
document.addEventListener("shopify:section:load", function (event) { initAll(event.target); });
})();