`;
}
function animateValue(element, start, end, duration) {
return new Promise(resolve => {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
const val = (progress * (end - start) + start);
element.textContent = formatNumber(val);
if (progress < 1) window.requestAnimationFrame(step);
else resolve();
};
window.requestAnimationFrame(step);
});
}
function setupViewer(viewer) {
if (!viewer || viewer.__priceRangeInited) return; // جلوگیری از init دوباره
viewer.__priceRangeInited = true;
const productId = viewer.dataset.productId ? String(viewer.dataset.productId) : "";
const contentWrapper = viewer.querySelector(".price-range-content");
const columnsContainer = contentWrapper ? contentWrapper.querySelector(".price-range-columns-v2") : null;
if (!columnsContainer) return;
(async () => {
if (!productId) {
showError(columnsContainer, "خطا: کلید محصول تعریف نشده.");
return;
}
let allPrices;
try {
allPrices = await getPrices();
} catch (e) {
const details = e && e.message ? e.message : withBust(PRICES_URL);
console.error("خطا در دریافت فایل قیمت:", e);
showError(columnsContainer, "خطا در ارتباط با سرور قیمت.", details);
return;
}
const basePrice = getProductPriceToman(allPrices, productId);
if (!isFinite(basePrice)) {
showError(columnsContainer, `قیمت محصول با کلید "${productId}" یافت نشد.`);
return;
}
const priceFrom = basePrice - 10000000;
const priceTo = basePrice + 10000000;
// رندر امن (productId فقط برای id استفاده میشود؛ سادهسازی میکنیم)
const safePid = productId.replace(/[^a-zA-Z0-9_-]/g, "");
columnsContainer.innerHTML = `
`;
const fromElement = document.getElementById(`price-from-${safePid}`);
const toElement = document.getElementById(`price-to-${safePid}`);
if (!fromElement || !toElement) {
showError(columnsContainer, "خطا: عناصر نمایش قیمت ساخته نشدند.");
return;
}
await new Promise(r => setTimeout(r, 80));
viewer.classList.add("loaded");
await Promise.all([
animateValue(fromElement, 0, priceFrom, 1500),
animateValue(toElement, 0, priceTo, 1700),
]);
// Shimmer (فقط یک بار برای هر viewer)
if (!viewer.__shimmerTimer) {
const priceWrappers = viewer.querySelectorAll(".price-value-wrapper");
viewer.__shimmerTimer = setInterval(() => {
priceWrappers.forEach(wrapper => {
wrapper.classList.add("shimmer-effect-wrapper");
setTimeout(() => wrapper.classList.remove("shimmer-effect-wrapper"), 1200);
});
}, 5000);
}
// Gyroscope: یک listener سراسری (نه برای هر ویجت)
if (window.DeviceOrientationEvent && "ontouchstart" in window) {
if (!window.__virazhGyroListeners) window.__virazhGyroListeners = new Set();
if (!window.__virazhGyroHandlerInstalled) {
window.__virazhGyroHandlerInstalled = true;
window.addEventListener("deviceorientation", (event) => {
const beta = event && typeof event.beta === "number" ? event.beta : 0;
const gamma = event && typeof event.gamma === "number" ? event.gamma : 0;
const rotateX = beta / 15;
const rotateY = gamma / -15;
const clampedRotateX = Math.max(-8, Math.min(8, rotateX));
const clampedRotateY = Math.max(-8, Math.min(8, rotateY));
window.requestAnimationFrame(() => {
window.__virazhGyroListeners.forEach(v => {
if (v && v.style) {
v.style.transform = `perspective(1000px) rotateX(${clampedRotateX}deg) rotateY(${clampedRotateY}deg)`;
}
});
});
});
}
// این viewer را به لیست کنترل ژیروسکوپ اضافه میکنیم
window.__virazhGyroListeners.add(viewer);
}
})();
}
function boot() {
const viewers = document.querySelectorAll(".price-range-container-v2");
if (!viewers.length) return;
viewers.forEach(setupViewer);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot);
} else {
boot();
}
})();