PageSourceSearch

https://svetzavod.ru/bitrix/cache/js/ag_hc_s1/build/af192170c4586451fb5cc39f3eed2f24.js

js svetzavod.ru collected 2026-10-07 16:42:06 UTC 46,121 bytes, 1,035 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function () {
2const utils = {
3lockScroll: () => (document.body.style.overflow = "hidden"),unlockScroll: () => (document.body.style.overflow = ""),
4findTextNode: (element) =>
5Array.from(element.childNodes).find(
6(node) => node.nodeType === Node.TEXT_NODE && node.textContent.trim(),),
7parseNumber: (str) => {
8const cleaned = str.replace(/\s/g, "").replace(",", ".");
9const number = parseFloat(cleaned);
10return isNaN(number) ? 0 : number;},formatPrice: (price) => {
11return new Intl.NumberFormat("ru-RU").format(price);},};const cookieUtils = {
12set: function (name, value, days){let expires = "";
13if(days){const date = new Date();
14date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
15expires = "; expires=" + date.toUTCString();}
16document.cookie = name + "=" + (value || "") + expires + "; path=/";
17},get: function (name){const nameEQ = name + "=";
18const ca = document.cookie.split(";");
19for (let i = 0; i < ca.length; i++){let c = ca[i];
20while (c.charAt(0) === " ") c = c.substring(1, c.length);
21if(c.indexOf(nameEQ) === 0)
22return c.substring(nameEQ.length, c.length);}
23return null;},remove: function (name){document.cookie = name + "=; Max-Age=-99999999; path=/";
24},};function safeJQuery(callback){if(typeof jQuery !== "undefined") {
25callback(jQuery);}else{console.warn("jQuery не доступен, пропускаем выполнение");}
26}
27function updateBasketCounter(basketData){if(!basketData) return;
28const cartSpan = document.querySelector(".top_header_cart span");
29if(cartSpan){cartSpan.textContent = " — " + (basketData.total_quantity || 0);}
30// У нас нет items в basketData, поэтому делаем отдельный запрос
31loadBasketState();
32if(typeof BX !== "undefined"){BX.onCustomEvent("OnBasketChange");}
33}
34function updateFavoritesLine(){fetch("/ajax/get_favorites.php")
35.then((response) => response.json())
36.then((data) => {
37if(data.success){const favSpan = document.querySelector(".favor-list-wrap span");
38if(favSpan){favSpan.textContent = " — " + (data.count || 0);}
39safeJQuery(function ($){$.ajax("?AJAX_CALL_FAVORITES_LINE=Y", {
40type: "POST",success: function (data){var obj = $("<div />").html(data);
41$(".favor-list-wrap").html(obj.find(".favor-list-wrap").html());},dataType: "html",
42});});
43}
44});}
45const modals = [
46{
47buttons: ".product-card__btn, .product-detail__btn-basket",modal: ".pp-modal-basket",
48close: ".pp-modal-basket__btn-close, .pp-modal-basket__btn",onOpen: function (btn, modal){fillBasketModal(btn, modal);},
49addToCart: true,checkInBasket: true,
50},{
51buttons: ".product-detail__buy-one-click",modal: ".pp-quick-order",
52close: ".pp-quick-order__btn-close",onOpen: function (btn, modal){fillQuickOrderModal(btn, modal);},
53addToCart: false,},
54];
55modals.forEach(
56({
57buttons: btnSel,modal: modalSel,
58close: closeSel,onOpen,
59addToCart,}) => {
60const modal = document.querySelector(modalSel);
61if(!modal) return;
62const openButtons = document.querySelectorAll(btnSel);
63openButtons.forEach((btn) => {
64const newBtn = btn.cloneNode(true);
65btn.parentNode.replaceChild(newBtn, btn);
66newBtn.addEventListener("click", (e) => {
67e.preventDefault();
68const productId = newBtn.dataset.id;
69if(!productId){console.error("ID товара не найден");
70return;}
71if(newBtn.classList.contains("in-basket")){window.location.href = "/personal/cart/";
72return;}
73const originalText = newBtn.innerHTML;
74if(onOpen){onOpen(newBtn, modal);}
75if(addToCart){newBtn.classList.add("loading");
76const textSpan = newBtn.querySelector(
77".product-card__btn-text, .product-detail__btn-basket-text",);
78if(textSpan){textSpan.textContent = "Добавление...";}
79let quantity = 1;
80if(document.querySelector(".product-detail__counter-input")){quantity = getCurrentQuantity();}
81fetch("/ajax/addToBasket.php", {
82method: "POST",headers: {
83"Content-Type": "application/x-www-form-urlencoded",
84},body: "product_id=" + productId + "&quantity=" + quantity,
85})
86.then((response) => response.json())
87.then((data) => {
88if(data.success){if(typeof updateBasketCounter === "function") {
89updateBasketCounter(data.basket);}
90updateButtonToInBasket(newBtn);
91modal.classList.add("open");
92utils.lockScroll();
93newBtn.classList.remove("loading");}else{console.error("Ошибка:", data.error);
94alert(
95"Ошибка при добавлении товара: " +
96(data.error || "Неизвестная ошибка"),);
97newBtn.classList.remove("loading");
98newBtn.innerHTML = originalText;}
99})
100.catch((error) => {
101console.error("Ошибка запроса:", error);
102alert("Ошибка при добавлении товара");
103newBtn.classList.remove("loading");
104newBtn.innerHTML = originalText;});}else{modal.classList.add("open");
105utils.lockScroll();}
106});});
107const closeButtons = closeSel ? modal.querySelectorAll(closeSel) : [];
108closeButtons.forEach((btn) => {
109btn.addEventListener("click", () => {
110modal.classList.remove("open");
111utils.unlockScroll();});
112});
113modal.addEventListener("click", (e) => {
114if(e.target === modal){modal.classList.remove("open");
115utils.unlockScroll();}
116});},);
117function fillBasketModal(btn, modal){if(!modal) return;
118let productName = "Товар";
119let productPrice = "0 ₽";
120let productId = btn.dataset.id;
121const isDetailPage =
122document.querySelector(".product-detail__counter-input") !== null;
123const currentQty = isDetailPage ? getCurrentQuantity() : 1;
124const productCard = btn.closest('[data-entity="item"], .product-card');
125if(productCard){const titleEl = productCard.querySelector(".product-card__title");
126const priceEl = productCard.querySelector(".product-card__price");
127if(titleEl) productName = titleEl.textContent || productName;
128if(priceEl) productPrice = priceEl.textContent?.trim() || productPrice;}else{const titleEl = document.querySelector(".product-detail__info-title");
129const priceEl = document.querySelector(".product-detail__info-price");
130if(titleEl) productName = titleEl.textContent || productName;
131if(priceEl) productPrice = priceEl.textContent?.trim() || productPrice;}
132const priceNumber = utils.parseNumber(productPrice);
133const totalPrice = priceNumber * currentQty;
134const titleElement = modal.querySelector(".js-modal-product-title");
135if(titleElement) titleElement.textContent = productName;
136const priceElement = modal.querySelector(".js-modal-product-price");
137if(priceElement) priceElement.textContent = productPrice;
138const priceMobileElement = modal.querySelector(
139".js-modal-product-price-mobile",);
140if(priceMobileElement)
141priceMobileElement.textContent = productPrice + "/шт.";
142const totalElement = modal.querySelector(".js-modal-product-total");
143if(totalElement)
144totalElement.textContent = utils.formatPrice(totalPrice) + " ₽";
145const quantityElement = modal.querySelector(".js-modal-product-quantity");
146if(quantityElement) quantityElement.textContent = currentQty;}
147function fillQuickOrderModal(btn, modal){if(!modal) return;
148const isDetailPage =
149document.querySelector(".product-detail__counter-input") !== null;
150const productName =
151document.querySelector(".product-detail__info-title")?.textContent ||
152btn.closest(".product-card")?.querySelector(".product-card__title")
153?.textContent ||
154"Товар";
155const productPrice =
156document
157.querySelector(".product-detail__info-price")
158?.textContent?.trim() ||
159btn
160.closest(".product-card")
161?.querySelector(".product-card__price")
162?.textContent?.trim() ||
163"0 ₽";
164const priceNumber = utils.parseNumber(productPrice);
165const productId = btn.dataset.id;
166const productUrl = window.location.href;
167const currentQty = isDetailPage ? getCurrentQuantity() : 1;
168const totalPrice = priceNumber * currentQty;
169const titleElement = modal.querySelector(".js-quick-product-title");
170if(titleElement) titleElement.textContent = productName;
171const priceElement = modal.querySelector(".js-quick-product-price");
172if(priceElement)
173priceElement.textContent = utils.formatPrice(priceNumber) + " ₽";
174const priceMobileElement = modal.querySelector(
175".js-quick-product-price-mobile",);
176if(priceMobileElement){priceMobileElement.textContent =
177utils.formatPrice(priceNumber) + " ₽/шт.";
178}
179const totalElement = modal.querySelector(".js-quick-product-total");
180if(totalElement) totalElement.textContent = utils.formatPrice(totalPrice);
181const quantityElement = modal.querySelector(".js-quick-product-quantity");
182if(quantityElement) quantityElement.textContent = currentQty;
183const commentInput = modal.querySelector(".js-quick-product-comment");
184if(commentInput){commentInput.value =
185productUrl + (isDetailPage ? " | Количество: " + currentQty + " шт." : "");}
186modal.dataset.productId = productId;
187modal.dataset.productPrice = priceNumber;
188modal.dataset.productName = productName;
189modal.dataset.productQuantity = currentQty;
190modal.dataset.productUrl = productUrl;}
191function initQuickOrderForm(){const quickForm = document.querySelector(".pp-quick-order__form");
192if(!quickForm){console.log("Форма быстрого заказа не найдена");
193return;}
194const submitBtn = quickForm.querySelector(
195".js-quick-order-submit, .pp-quick-order__form-btn-submit",);
196if(!submitBtn){console.log("Кнопка отправки не найдена");
197return;}
198const newSubmitBtn = submitBtn.cloneNode(true);
199submitBtn.parentNode.replaceChild(newSubmitBtn, submitBtn);
200newSubmitBtn.addEventListener("click", function (e){e.preventDefault();
201const modalElement = this.closest(".pp-quick-order");
202const formBlock = modalElement.querySelector(
203".pp-quick-order__form-block",);
204const successBlock = modalElement.querySelector(
205".pp-quick-order__success-block",);
206const nameInput = quickForm.querySelector('input[name="fio"]');
207const phoneInput = quickForm.querySelector('input[name="phone"]');
208const commentInput = quickForm.querySelector('input[name="comment"]');
209const agreeCheckbox = quickForm.querySelector('input[name="agree"]');
210if(!nameInput){console.error("Поле fio не найдено");
211alert("Ошибка: поле имени не найдено");
212return;}
213if(!phoneInput){console.error("Поле phone не найдено");
214alert("Ошибка: поле телефона не найдено");
215return;}
216if(!nameInput.value.trim()){alert("Введите имя");
217nameInput.focus();
218return;}
219const phone = phoneInput.value.replace(/\D/g, "") || "";
220if(phone.length < 11){alert("Введите корректный номер телефона");
221phoneInput.focus();
222return;}
223if(agreeCheckbox && !agreeCheckbox.checked){alert("Необходимо согласие на обработку данных");
224agreeCheckbox.focus();
225return;}
226const formData = new URLSearchParams();
227formData.append("fio", nameInput.value.trim());
228formData.append("phone", phoneInput.value);
229formData.append("comment", commentInput?.value || window.location.href);
230formData.append("source", "Быстрая покупка");
231const quickOrderModal = document.querySelector(".pp-quick-order");
232formData.append("page_url", quickOrderModal?.dataset.productUrl || window.location.href);
233if(quickOrderModal && quickOrderModal.dataset.productQuantity){formData.append("quantity", quickOrderModal.dataset.productQuantity);}
234const originalText = this.textContent;
235this.disabled = true;
236this.textContent = "Отправка...";
237fetch("/ajax/quick_order.php", {
238method: "POST",headers: {
239"Content-Type": "application/x-www-form-urlencoded",
240},body: formData.toString(),
241})
242.then((response) => response.text())
243.then((data) => {
244if(
245data.indexOf("success-form") !== -1 ||
246data.indexOf("успешно отправлена") !== -1
247){if(formBlock) formBlock.style.display = "none";
248if(successBlock) successBlock.style.display = "block";}else{alert("Ошибка при отправке: " + data);}
249})
250.catch((error) => {
251console.error("Ошибка:", error);
252alert("Произошла ошибка при отправке. Попробуйте позже.");})
253.finally(() => {
254this.disabled = false;
255this.textContent = originalText;});
256});
257const quickOrderModal = document.querySelector(".pp-quick-order");
258if(quickOrderModal){const continueBtn = quickOrderModal.querySelector(
259".pp-quick-order__success-btn",);
260if(continueBtn){continueBtn.addEventListener("click", function () {
261quickOrderModal.classList.remove("open");
262utils.unlockScroll();
263setTimeout(() => {
264const formBlock = quickOrderModal.querySelector(
265".pp-quick-order__form-block",);
266const successBlock = quickOrderModal.querySelector(
267".pp-quick-order__success-block",);
268if(formBlock) formBlock.style.display = "block";
269if(successBlock) successBlock.style.display = "none";
270quickForm.reset();
271if(agreeCheckbox) agreeCheckbox.checked = true;}, 300);
272});}
273}
274}
275initQuickOrderForm();
276const goToCartBtn = document.querySelector(
277".pp-modal-basket__btn-basket, .js-go-to-cart",);
278if(goToCartBtn){goToCartBtn.addEventListener("click", function () {
279window.location.href = "/personal/cart/";
280});}
281document
282.querySelectorAll(".pp-modal-basket__btn, .js-close-modal")
283.forEach((btn) => {
284btn.addEventListener("click", function (){const modal = this.closest(".pp-modal-section");
285if(modal){modal.classList.remove("open");
286utils.unlockScroll();}
287});});
288const thumbsEl = document.querySelector(".product-detail__slider-thumbs");
289if(thumbsEl && typeof Swiper !== "undefined"){const thumbsSwiper = new Swiper(thumbsEl, {
290spaceBetween: 0,slidesPerView: "auto",
291freeMode: true,updateOnWindowResize: true,
292speed: 500,});
293const mainSliderEl = document.querySelector(".product-detail__slider");
294if(mainSliderEl){new Swiper(mainSliderEl, {
295spaceBetween: 10,slidesPerView: 1,
296thumbs: { swiper: thumbsSwiper },});}
297}
298const reviewsSlider = document.querySelector(
299".product-detail__reviews-slider",);
300if(reviewsSlider && typeof Swiper !== "undefined"){new Swiper(reviewsSlider, {
301slidesPerView: 3,spaceBetween: 16,
302speed: 600,grabCursor: true,
303loop: reviewsSlider.querySelectorAll('.swiper-slide').length > 3,navigation: {
304nextEl: ".reviews-btn-next",prevEl: ".reviews-btn-prev",
305},breakpoints: {
3060: { slidesPerView: 1, spaceBetween: 10 },768: { slidesPerView: 2, spaceBetween: 16 },
307992: { slidesPerView: 2.5, spaceBetween: 16 },1340: { slidesPerView: 3, spaceBetween: 16 },
308},});}
309const ppReviewsSlider = document.querySelector(".pp-modal-review__slider");
310if(ppReviewsSlider && typeof Swiper !== "undefined"){new Swiper(ppReviewsSlider, {
311slidesPerView: 1,spaceBetween: 10,
312speed: 600,loop: ppReviewsSlider.querySelectorAll('.swiper-slide').length > 1,
313grabCursor: true,navigation: {
314nextEl: ".pp-modal-review__slider-btn-next",prevEl: ".pp-modal-review__slider-btn-prev",
315},});}
316if(typeof Fancybox !== "undefined"){Fancybox.bind("[data-fancybox='product-gallery']", {
317caption: (fancybox, slide) =>
318slide.triggerEl?.getAttribute("data-caption") || "",});}
319const copyContainer = document.querySelector(".product-detail__info-sku");
320if(copyContainer){const textElem = copyContainer.querySelector(
321".product-detail__info-sku-text",);
322const iconElem = copyContainer.querySelector(
323".product-detail__info-sku-icon",);
324if(textElem){const originalFullText = textElem.textContent;
325let isCopying = false;
326copyContainer.addEventListener("click", async () => {
327if(isCopying) return;
328const skuToCopy = textElem.textContent
329.replace(/Артикул:\s*/i, "")
330.trim();
331try {
332await navigator.clipboard.writeText(skuToCopy);
333isCopying = true;
334copyContainer.classList.add("is-copied");
335textElem.textContent = "Скопировано!";
336setTimeout(() => {
337textElem.textContent = originalFullText;
338copyContainer.classList.remove("is-copied");
339isCopying = false;}, 2000);
340} catch (err){console.error("Ошибка при копировании:", err);}
341});}
342}
343document.querySelectorAll(".review-card__block").forEach((block) => {
344const pics = block.querySelectorAll(".review-card__pic");
345if(pics.length <= 3) return;
346pics.forEach((pic, i) => {
347if(i >= 3) pic.style.display = "none";});
348const indicator = document.createElement("div");
349indicator.className = "review-card__more-indicator";
350indicator.innerHTML = `<span>+${pics.length - 3}</span>`;
351block.appendChild(indicator);});
352document.addEventListener("click", (e) => {
353const cardBtn = e.target.closest(".product-card__btn-fav");
354if(cardBtn){e.preventDefault();
355handleFavoriteClick(cardBtn);
356return;}
357const detailBtn = e.target.closest(".product-detail__info-fav-btn");
358if(detailBtn){e.preventDefault();
359handleFavoriteClick(detailBtn);
360return;}
361});
362function handleFavoriteClick(btn){const productId = btn.dataset.id;
363if(!productId){console.error("ID товара не найден");
364return;}
365const isActive = btn.classList.contains("active");
366const action = isActive ? "remove" : "add";
367fetch("/ajax/favorite_handler.php", {
368method: "POST",headers: {
369"Content-Type": "application/x-www-form-urlencoded",
370},body: "product_id=" + productId + "&action=" + action,
371})
372.then((response) => response.json())
373.then((data) => {
374if(data.success){btn.classList.toggle("active");
375if(btn.classList.contains("product-detail__info-fav-btn")){const textSpan = btn.querySelector(
376".product-detail__info-benefit-text",);
377if(textSpan){textSpan.textContent = btn.classList.contains("active")
378? "В избранном"
379: "В избранное";}
380}
381updateFavoritesLine();} else if(data.error === "Необходима авторизация"){alert("Для добавления в избранное необходимо авторизоваться");
382window.location.href = "/auth/";
383} else {
384console.error("Ошибка:", data.error);
385alert("Ошибка: " + data.error);}
386})
387.catch((error) => {
388console.error("Ошибка запроса:", error);});
389}
390function loadFavoritesStatus(){fetch("/ajax/get_favorites.php")
391.then((response) => response.json())
392.then((data) => {
393if(data.success && data.favorites){const favoritesIds = data.favorites.map((id) => parseInt(id));
394document.querySelectorAll(".product-card__btn-fav").forEach((btn) => {
395const productId = parseInt(btn.dataset.id);
396if(favoritesIds.includes(productId)){btn.classList.add("active");}
397});
398document
399.querySelectorAll(".product-detail__info-fav-btn")
400.forEach((btn) => {
401const productId = parseInt(btn.dataset.id);
402if(favoritesIds.includes(productId)){btn.classList.add("active");
403const textSpan = btn.querySelector(
404".product-detail__info-benefit-text",);
405if(textSpan){textSpan.textContent = "В избранном";}}else{btn.classList.remove("active");
406const textSpan = btn.querySelector(
407".product-detail__info-benefit-text",);
408if(textSpan){textSpan.textContent = "В избранное";}
409}
410});
411const favSpan = document.querySelector(".favor-list-wrap span");
412if(favSpan && data.count !== undefined){favSpan.textContent = " — " + data.count;}
413}
414})
415.catch((error) => console.error("Ошибка загрузки избранного:", error));}
416loadFavoritesStatus();
417const navLinks = document.querySelectorAll(".product-detail__nav-link");
418if(navLinks.length){const menuItems = Array.from(navLinks)
419.map((link) => ({
420link,section: document.querySelector(link.getAttribute("href")),
421}))
422.filter((item) => item.section);
423const getOffsetTop = (el) => {
424let top = 0;
425while (el){top += el.offsetTop;
426el = el.offsetParent;}
427return top;};const updateActiveNav = () => {
428const scrollPos = window.scrollY + 180;
429let activeItem = menuItems[0];
430menuItems.forEach((item) => {
431if(scrollPos >= getOffsetTop(item.section)) activeItem = item;});
432const isBottom =
433window.innerHeight + window.scrollY >=
434document.documentElement.scrollHeight - 50;
435if(isBottom) activeItem = menuItems[menuItems.length - 1];
436menuItems.forEach((item) => {
437item.link.classList.toggle("active", item === activeItem);});
438};let ticking = false;
439window.addEventListener("scroll", () => {
440if(!ticking){requestAnimationFrame(() => {
441updateActiveNav();
442ticking = false;});
443ticking = true;}
444});
445setTimeout(updateActiveNav, 100);
446navLinks.forEach((link) => {
447link.addEventListener("click", (e) => {
448const targetId = link.getAttribute("href");
449if(!targetId?.startsWith("#")) return;
450const target = document.querySelector(targetId);
451if(target){e.preventDefault();
452window.scrollTo({
453top: getOffsetTop(target) - 150,behavior: "smooth",
454});}
455});});
456}
457function initPriceCalculator(){const priceEl = document.querySelector(".product-detail__info-price");
458const qtyInput = document.querySelector(".product-detail__counter-input");
459const totalEl = document.querySelector(".product-detail__total-price");
460const minusBtn = document.querySelector(
461".product-detail__counter-btn--minus",);
462const plusBtn = document.querySelector(
463".product-detail__counter-btn--plus",);
464if(!priceEl || !qtyInput || !totalEl) return;
465const pricePerUnit = utils.parseNumber(priceEl.textContent);
466if(pricePerUnit === 0) return;
467function updateTotal(){let qty = parseInt(qtyInput.value) || 1;
468if(qty < 1) qty = 1;
469qtyInput.value = qty;
470const total = qty * pricePerUnit;
471totalEl.textContent = `на сумму ${total.toLocaleString("ru-RU", {
472minimumFractionDigits: 2,maximumFractionDigits: 2,
473})} ₽`;}
474if(minusBtn){const newMinusBtn = minusBtn.cloneNode(true);
475minusBtn.parentNode.replaceChild(newMinusBtn, minusBtn);
476newMinusBtn.addEventListener("click", function (e){e.preventDefault();
477let qty = parseInt(qtyInput.value) || 1;
478if(qty > 1){qtyInput.value = qty - 1;
479updateTotal();
480if(typeof BX !== "undefined"){BX.onCustomEvent("onQuantityChange", [{ quantity: qty - 1 }]);}
481}
482});}
483if(plusBtn){const newPlusBtn = plusBtn.cloneNode(true);
484plusBtn.parentNode.replaceChild(newPlusBtn, plusBtn);
485newPlusBtn.addEventListener("click", function (e){e.preventDefault();
486let qty = parseInt(qtyInput.value) || 1;
487qtyInput.value = qty + 1;
488updateTotal();
489if(typeof BX !== "undefined"){BX.onCustomEvent("onQuantityChange", [{ quantity: qty + 1 }]);}
490});}
491qtyInput.addEventListener("input", function (e){this.value = this.value.replace(/[^\d]/g, "");
492if(this.value === "" || this.value === "0"){this.value = "1";}
493updateTotal();});
494qtyInput.addEventListener("blur", function (){if(!this.value || parseInt(this.value) < 1) {
495this.value = "1";}
496updateTotal();});
497updateTotal();}
498if(document.readyState === "loading"){document.addEventListener("DOMContentLoaded", initPriceCalculator);}else{initPriceCalculator();}
499const quickForm = document.querySelector(".pp-quick-order__form");
500if(quickForm){const formRules = {
501phone: {
502validate: (v) =>
503/^[\d\s+()-]+$/.test(v) && v.replace(/\D/g, "").length >= 11,
504msg: "Введите корректный номер",},
505name: { validate: (v) => v.trim().length >= 2, msg: "Введите имя" },agree: { validate: (c) => c, msg: "Необходимо согласие" },
506};const addError = (field, msg) => {
507const errorDiv = document.createElement("div");
508errorDiv.className = "form-error-msg";
509errorDiv.textContent = msg;
510errorDiv.style.display = "none";
511field.parentNode.insertBefore(errorDiv, field.nextSibling);};["name", "phone"].forEach((name) => {
512const field = quickForm.querySelector(`[name="${name}"]`);
513if(field && !field.parentNode.querySelector(".form-error-msg")){addError(field, formRules[name].msg);}
514});
515const agree = quickForm.querySelector('[name="agree"]');
516if(agree){const container =
517agree.closest(".pp-quick-order__form-checkbox") || agree.parentNode;
518if(!container.parentNode.querySelector(".form-error-msg")){const error = document.createElement("div");
519error.className = "form-error-msg";
520error.textContent = "Необходимо согласие";
521error.style.display = "none";
522container.parentNode.insertBefore(error, container.nextSibling);}
523}
524const validateField = (field) => {
525const rule = formRules[field.name];
526if(!rule) return true;
527const isValid = !field.required
528? true
529: rule.validate(
530field.type === "checkbox" ? field.checked : field.value,);
531const error =
532field.type === "checkbox"
533? (
534field.closest(".pp-quick-order__form-checkbox") ||
535field.parentNode
536).parentNode.querySelector(".form-error-msg")
537: field.parentNode.querySelector(".form-error-msg");
538if(error){error.style.display = isValid ? "none" : "block";
539field.classList.toggle("error", !isValid);}
540return isValid;};quickForm.querySelectorAll("input, select, textarea").forEach((field) => {
541if(field.type === "submit" || field.type === "hidden") return;
542field.addEventListener("blur", () => validateField(field));
543field.addEventListener(
544"input",() => field.classList.contains("error") && validateField(field),
545);});
546quickForm
547.querySelector('.feedback-btn[type="button"]')
548?.addEventListener("click", function (){const isValid = [
549...quickForm.querySelectorAll("input, select, textarea"),]
550.filter((f) => f.type !== "submit" && f.type !== "hidden")
551.every((f) => validateField(f));
552if(isValid) console.log("Форма валидна");});
553quickForm
554.querySelectorAll(".pp-quick-order__form-input")
555.forEach((input) => {
556const original = input.placeholder;
557input.addEventListener("focus", function (){this.placeholder = "";
558this.classList.add("focused");});
559input.addEventListener("blur", function (){if(!this.value) {
560this.placeholder = original;
561this.classList.remove("focused");}
562});});
563}
564function initPhoneMasks(){const phoneInputs = document.querySelectorAll(
565'input[name="phone"], .form_phone',);
566phoneInputs.forEach((input) => {
567const newInput = input.cloneNode(true);
568input.parentNode.replaceChild(newInput, input);
569newInput.addEventListener("input", function (e){let numbers = e.target.value.replace(/\D/g, "");
570if(numbers.startsWith("7") || numbers.startsWith("8")){numbers = numbers.substring(1);}
571numbers = numbers.substring(0, 10);
572let result = "+7";
573if(numbers.length > 0) result += ` (${numbers.substring(0, 3)}`;
574if(numbers.length > 3) result += `) ${numbers.substring(3, 6)}`;
575if(numbers.length > 6) result += `-${numbers.substring(6, 8)}`;
576if(numbers.length > 8) result += `-${numbers.substring(8, 10)}`;
577e.target.value = result;});
578newInput.addEventListener("keydown", function (e){const allowedKeys = [
579"Backspace","Delete",
580"Tab","ArrowLeft",
581"ArrowRight",];
582if(!/\d/.test(e.key) && !allowedKeys.includes(e.key)) {
583e.preventDefault();}
584});});
585}
586if(document.readyState === "loading"){document.addEventListener("DOMContentLoaded", initPhoneMasks);}else{initPhoneMasks();}
587document.addEventListener("click", function (e){if(e.target.closest(".product-detail__buy-one-click")) {
588setTimeout(initPhoneMasks, 100);}
589});
590// window.updateLike = function (reviewId, type, button) {
591// method: "POST",
592// "Content-Type": "application/x-www-form-urlencoded",
593// body:
594// reviewId +
595// type +
596// (isActive ? "0" : "1"),
597// .then((response) => response.json())
598// if(data.success) {
599// if(counter) {
600// }
601// if(container) {
602// ? container.querySelector(".review-card__btn-disl")
603// otherBtn.classList.remove("active");
604// ".review-card__counter",
605// if(otherCounter) {
606// }
607// }
608// saveVoteToCookie(reviewId, type);
609// saveVoteToCookie(reviewId, null);
610// } else {
611// }
612// .catch((error) => {
613// });
614window.updateLike = function (reviewId, type, button){if(event) event.stopPropagation();
615if(button.disabled) return;
616button.disabled = true;
617const isActive = button.classList.contains("active");
618fetch("/ajax/review_handler.php", {
619method: "POST",headers: {
620"Content-Type": "application/x-www-form-urlencoded",
621},body: "action=update_like&review_id=" + reviewId +
622"&type=" + type +
623"&active=" + (isActive ? "0" : "1")
624})
625.then((response) => response.json())
626.then((data) => {
627if(data.success){const counter = button.querySelector(".review-card__counter");
628if(counter){counter.textContent = data.new_value;}
629button.classList.toggle("active");
630const container = button.closest(".review-card__actions");
631if(container){const otherBtn = button.classList.contains("review-card__btn-like")
632? container.querySelector(".review-card__btn-disl")
633: container.querySelector(".review-card__btn-like");
634if(otherBtn && data.other_value !== undefined){otherBtn.classList.remove("active");
635const otherCounter = otherBtn.querySelector(".review-card__counter");
636if(otherCounter){otherCounter.textContent = data.other_value;}
637}
638}
639const modal = document.getElementById("review-modal");
640if(modal && modal.classList.contains("open") && modal.dataset.reviewId == reviewId){const likeCounter = document.getElementById("modal-like-count");
641const dislCounter = document.getElementById("modal-disl-count");
642if(likeCounter && dislCounter){if(type === "like") {
643likeCounter.textContent = data.new_value;
644if(data.other_value !== undefined){dislCounter.textContent = data.other_value;}}else{dislCounter.textContent = data.new_value;
645if(data.other_value !== undefined){likeCounter.textContent = data.other_value;}
646}
647}
648const modalLikeBtn = document.getElementById("modal-like-btn");
649const modalDislBtn = document.getElementById("modal-disl-btn");
650if(modalLikeBtn && modalDislBtn){if(type === "like") {
651modalLikeBtn.classList.toggle("active");
652if(modalDislBtn.classList.contains("active")){modalDislBtn.classList.remove("active");}}else{modalDislBtn.classList.toggle("active");
653if(modalLikeBtn.classList.contains("active")){modalLikeBtn.classList.remove("active");}
654}
655}
656}
657if(!isActive){saveVoteToCookie(reviewId, type);}else{saveVoteToCookie(reviewId, null);}
658} else {
659console.error("Ошибка:", data.error);
660if(data.error === "Необходима авторизация"){alert("Для оценки отзыва необходимо авторизоваться");}else{alert("Ошибка: " + data.error);}
661}
662})
663.catch((error) => {
664console.error("Ошибка запроса:", error);
665alert("Произошла ошибка при отправке");})
666.finally(() => {
667button.disabled = false;});
668};// console.log(review);
669// if(!modal) return;
670// if(userNameEl) userNameEl.textContent = review.USER_NAME || "Аноним";
671// if(dateEl) dateEl.textContent = review.DATE ? formatDate(review.DATE) : "";
672// if(textEl) textEl.textContent = review.TEXT || "";
673// if(likeEl) likeEl.textContent = review.LIKE_COUNT || 0;
674// if(dislEl) dislEl.textContent = review.DISLIKE_COUNT || 0;
675// if(starsContainer) {
676// if(isNaN(rating) || rating < 1 || rating > 5) {
677// }
678// const star = document.createElement("div");
679// i < rating ? "pp-modal-review__star active" : "pp-modal-review__star";
680// }
681// if(imagesContainer) {
682// review.FILES.forEach((file) => {
683// slide.className = "swiper-slide pp-modal-review__slider-card";
684// file.SRC || file.src || file.THUMBNAIL || file.thumbnail || "";
685// <picture class="pp-modal-review__slider-pic">
686// </picture>
687// imagesContainer.appendChild(slide);
688// } else {
689// slide.className = "swiper-slide pp-modal-review__slider-card";
690// <picture class="pp-modal-review__slider-pic">
691// </picture>
692// imagesContainer.appendChild(slide);
693// }
694// const responseText = document.getElementById("modal-response-text");
695// if(review.RESPONSE) {
696// responseBlock.style.display = "block";
697// responseBlock.style.display = "none";
698// }
699// const dislBtn = document.getElementById("modal-disl-btn");
700// const newLikeBtn = likeBtn.cloneNode(true);
701// likeBtn.parentNode.replaceChild(newLikeBtn, likeBtn);
702// const voteType = window.userReviewVotes[review.ID];
703// newLikeBtn.classList.add("active");
704// newDislBtn.classList.add("active");
705// }
706// e.stopPropagation();
707// });
708// e.stopPropagation();
709// });
710// if(window.reviewModalSlider) {
711// }
712// window.reviewModalSlider = new Swiper(".pp-modal-review__slider", {
713// spaceBetween: 10,
714// loop: imagesContainer.children.length > 1,
715// navigation: {
716// prevEl: ".pp-modal-review__slider-btn-prev",
717// });
718// }, 100);
719// utils.lockScroll();
720window.openReviewModal = function (review){console.log('Opening review modal for:', review);
721const modal = document.getElementById("review-modal");
722if(!modal) return;
723modal.classList.add("open");
724utils.lockScroll();
725const userNameEl = document.getElementById("modal-user-name");
726if(userNameEl) userNameEl.textContent = review.USER_NAME || "Аноним";
727const dateEl = document.getElementById("modal-review-date");
728if(dateEl) dateEl.textContent = review.DATE ? formatDate(review.DATE) : "";
729const textEl = document.getElementById("modal-review-text");
730if(textEl) textEl.textContent = review.TEXT || "";
731const likeEl = document.getElementById("modal-like-count");
732if(likeEl) likeEl.textContent = review.LIKE_COUNT || 0;
733const dislEl = document.getElementById("modal-disl-count");
734if(dislEl) dislEl.textContent = review.DISLIKE_COUNT || 0;
735const starsContainer = document.getElementById("modal-review-stars");
736if(starsContainer){starsContainer.innerHTML = "";
737let rating = parseInt(review.RATING);
738if(isNaN(rating) || rating < 1 || rating > 5){rating = 5;}
739for (let i = 0; i < 5; i++){const star = document.createElement("div");
740star.className = i < rating ? "pp-modal-review__star active" : "pp-modal-review__star";
741starsContainer.appendChild(star);}
742}
743const imagesContainer = document.getElementById("modal-images-container");
744if(imagesContainer){imagesContainer.innerHTML = "";
745if(review.FILES && review.FILES.length > 0){review.FILES.forEach((file) => {
746const slide = document.createElement("div");
747slide.className = "swiper-slide pp-modal-review__slider-card";
748const imageUrl = file.SRC || file.src || file.THUMBNAIL || file.thumbnail || "";
749slide.innerHTML = `
750<picture class="pp-modal-review__slider-pic">
751<img src="${imageUrl}" alt="" loading="lazy" />
752</picture>
753`;
754imagesContainer.appendChild(slide);});}else{const slide = document.createElement("div");
755slide.className = "swiper-slide pp-modal-review__slider-card";
756slide.innerHTML = `
757<picture class="pp-modal-review__slider-pic">
758<img src="/local/img/nf.jpg" alt="" />
759</picture>
760`;
761imagesContainer.appendChild(slide);}
762}
763const responseBlock = document.getElementById("modal-shop-response");
764const responseText = document.getElementById("modal-response-text");
765if(responseBlock && responseText){if(review.RESPONSE) {
766responseText.textContent = review.RESPONSE;
767responseBlock.style.display = "block";}else{responseBlock.style.display = "none";}
768}
769modal.dataset.reviewId = review.ID || review.id;
770const likeBtn = document.getElementById("modal-like-btn");
771const dislBtn = document.getElementById("modal-disl-btn");
772if(likeBtn && dislBtn){const newLikeBtn = likeBtn.cloneNode(true);
773const newDislBtn = dislBtn.cloneNode(true);
774likeBtn.parentNode.replaceChild(newLikeBtn, likeBtn);
775dislBtn.parentNode.replaceChild(newDislBtn, dislBtn);
776newLikeBtn.addEventListener("click", function (e){e.stopPropagation();
777window.updateLike(review.ID || review.id, "like", this);});
778newDislBtn.addEventListener("click", function (e){e.stopPropagation();
779window.updateLike(review.ID || review.id, "dislike", this);});
780}
781fetch("/ajax/get_review_data.php?review_id=" + (review.ID || review.id))
782.then(response => response.json())
783.then(actualData => {
784if(actualData.success){console.log('Actual review data received:', actualData);
785const likeCounter = document.getElementById("modal-like-count");
786if(likeCounter){likeCounter.textContent = actualData.like_count;}
787const dislCounter = document.getElementById("modal-disl-count");
788if(dislCounter){dislCounter.textContent = actualData.dislike_count;}
789const currentLikeBtn = document.getElementById("modal-like-btn");
790const currentDislBtn = document.getElementById("modal-disl-btn");
791if(currentLikeBtn && currentDislBtn){currentLikeBtn.classList.remove("active");
792currentDislBtn.classList.remove("active");
793if(actualData.user_vote === "like"){currentLikeBtn.classList.add("active");} else if(actualData.user_vote === "dislike"){currentDislBtn.classList.add("active");
794}
795}}else{console.error('Error fetching actual review data:', actualData.error);}
796})
797.catch(error => {
798console.error('Error fetching actual review data:', error);});
799setTimeout(() => {
800if(window.reviewModalSlider){window.reviewModalSlider.destroy();}
801if(typeof Swiper !== "undefined" && imagesContainer){window.reviewModalSlider = new Swiper(".pp-modal-review__slider", {
802slidesPerView: 1,spaceBetween: 10,
803speed: 600,loop: imagesContainer.children.length > 1,
804grabCursor: true,navigation: {
805nextEl: ".pp-modal-review__slider-btn-next",prevEl: ".pp-modal-review__slider-btn-prev",
806},});}
807}, 100);};window.closeReviewModal = function (){const modal = document.getElementById("review-modal");
808if(modal){modal.classList.remove("open");
809utils.unlockScroll();}
810};window.openAddReviewModal = function (productId){const modal = document.getElementById("add-review-modal");
811if(!modal) return;
812document.getElementById("review-product-id").value = productId;
813const form = document.getElementById("review-form");
814form.reset();
815document.getElementById("rating-value").value = "5";
816document.querySelectorAll(".rating-star").forEach((star, index) => {
817if(index < 5){star.classList.add("active");}else{star.classList.remove("active");}
818});
819document.getElementById("file-preview").innerHTML = "";
820document.querySelector(".pp-modal-add-review__form").style.display =
821"block";
822document.querySelector(".pp-modal-add-review__success").style.display =
823"none";
824modal.classList.add("open");
825utils.lockScroll();};window.closeAddReviewModal = function (){const modal = document.getElementById("add-review-modal");
826if(modal){modal.classList.remove("open");
827utils.unlockScroll();}
828};function formatDate(dateString){// return date.toLocaleDateString("ru-RU");
829if(!dateString) return '';
830return dateString.split(' ')[0];}
831function saveVoteToCookie(reviewId, type){let votes = window.userReviewVotes || {};if(type){votes[reviewId] = type;}else{delete votes[reviewId];}
832cookieUtils.set(
833"review_votes",encodeURIComponent(JSON.stringify(votes)),
834365,);
835window.userReviewVotes = votes;}
836function loadUserVotesFromCookie(){const votesCookie = cookieUtils.get("review_votes");
837let votes = {};if(votesCookie){try {
838votes = JSON.parse(decodeURIComponent(votesCookie));} catch (e){console.error("Ошибка парсинга куки:", e);
839}
840}
841window.userReviewVotes = votes;
842document.querySelectorAll(".review-card").forEach((card) => {
843const reviewId = card.dataset.reviewId;
844if(reviewId && votes[reviewId]){const likeBtn = card.querySelector(".review-card__btn-like");
845const dislBtn = card.querySelector(".review-card__btn-disl");
846if(likeBtn && votes[reviewId] === "like"){likeBtn.classList.add("active");}
847if(dislBtn && votes[reviewId] === "dislike"){dislBtn.classList.add("active");}
848}
849});
850return votes;}
851function initReviewForm(){const form = document.getElementById("review-form");
852if(!form) return;
853const stars = document.querySelectorAll(".rating-star");
854const ratingInput = document.getElementById("rating-value");
855stars.forEach((star, index) => {
856star.addEventListener("mouseover", function (){stars.forEach((s, i) => {
857if(i <= index){s.classList.add("active");}else{s.classList.remove("active");}
858});});
859star.addEventListener("mouseout", function (){const currentRating = parseInt(ratingInput.value) || 5;
860stars.forEach((s, i) => {
861if(i < currentRating){s.classList.add("active");}else{s.classList.remove("active");}
862});});
863star.addEventListener("click", function (){const value = index + 1;
864ratingInput.value = value;
865stars.forEach((s, i) => {
866if(i < value){s.classList.add("active");}else{s.classList.remove("active");}
867});});
868});
869const fileInput = document.getElementById("review-files");
870const preview = document.getElementById("file-preview");
871fileInput.addEventListener("change", function (){preview.innerHTML = "";
872Array.from(this.files).forEach((file, index) => {
873if(index >= 5) return;
874const reader = new FileReader();
875reader.onload = function (e){const item = document.createElement("div");
876item.className = "file-preview-item";
877item.innerHTML = `
878<img src="${e.target.result}" alt="">
879<div class="file-preview-remove" data-index="${index}">×</div>
880`;
881item
882.querySelector(".file-preview-remove")
883.addEventListener("click", function (){item.remove();});
884preview.appendChild(item);};reader.readAsDataURL(file);});
885});
886form.addEventListener("submit", function (e){e.preventDefault();
887const formData = new FormData(this);
888const submitBtn = this.querySelector(".pp-modal-add-review__submit");
889const originalText = submitBtn.textContent;
890submitBtn.disabled = true;
891submitBtn.textContent = "Отправка...";
892fetch("/ajax/review_handler.php", {
893method: "POST",body: formData,
894})
895.then((response) => response.json())
896.then((data) => {
897if(data.success){document.querySelector(".pp-modal-add-review__form").style.display =
898"none";
899document.querySelector(
900".pp-modal-add-review__success",).style.display = "block";
901setTimeout(() => {
902location.reload();}, 2000);}else{alert("Ошибка: " + data.error);}
903})
904.catch((error) => {
905console.error("Ошибка:", error);
906alert("Произошла ошибка при отправке");})
907.finally(() => {
908submitBtn.disabled = false;
909submitBtn.textContent = originalText;});
910});}
911initReviewForm();
912function getCurrentQuantity(){const qtyInput = document.querySelector(".product-detail__counter-input");
913if(qtyInput){let qty = parseInt(qtyInput.value);
914return !isNaN(qty) && qty > 0 ? qty : 1;}
915return 1;}
916const tabCont = document.querySelector(".tab_cont");
917if(tabCont){const row = tabCont.querySelector(".row");
918if(row){const rowContent = row.innerHTML.replace(/<br\s*\/?>/gi, "").trim();
919if(!rowContent){tabCont.style.display = "none";}}else{tabCont.style.display = "none";}
920}
921loadUserVotesFromCookie();
922function updateButtonsFromBasket(basketData){let basketItemsIds = new Set();
923if(basketData.items){if(
924Array.isArray(basketData.items) &&
925typeof basketData.items[0] === "number"
926){basketItemsIds = new Set(basketData.items);}
927else if(
928Array.isArray(basketData.items) &&
929basketData.items[0] &&
930basketData.items[0].id
931){basketItemsIds = new Set(
932basketData.items.map((item) => parseInt(item.id)),);}
933}
934else if(Array.isArray(basketData)){basketItemsIds = new Set(basketData);}
935// ВАЖНО: Ищем кнопки по классу .product-card__btn и data-id
936document.querySelectorAll(".product-card__btn").forEach((btn) => {
937const productId = parseInt(btn.dataset.id);
938if(basketItemsIds.has(productId)){updateButtonToInBasket(btn);}else{resetButtonToAddToCart(btn);}
939});
940const detailBtn = document.querySelector(".product-detail__btn-basket");
941if(detailBtn){const productId = parseInt(detailBtn.dataset.id);
942if(basketItemsIds.has(productId)){updateButtonToInBasket(detailBtn);}else{resetButtonToAddToCart(detailBtn);}
943}
944}
945function loadBasketState(){fetch("/ajax/get_basket_items.php")
946.then((response) => response.json())
947.then((data) => {
948if(data.success){updateButtonsFromBasket(data);
949const cartSpan = document.querySelector(".top_header_cart span");
950if(cartSpan){cartSpan.textContent = " — " + (data.total_quantity || 0);}
951}
952})
953.catch((error) =>
953 console.error("Ошибка загрузки корзины:", error));}
954function updateButtonToInBasket(btn){if(!btn) return;
955if(btn.classList.contains("in-basket")) return;
956btn.classList.add("in-basket");
957btn.dataset.inBasket = "true";
958if(btn.classList.contains("product-card__btn")){const textSpan = btn.querySelector(".product-card__btn-text");
959if(textSpan){textSpan.textContent = "Уже в корзине";}else{const icon = btn.querySelector(".product-card__btn-icon");
960if(icon){btn.innerHTML = "";
961btn.appendChild(icon);
962const newSpan = document.createElement("span");
963newSpan.className = "product-card__btn-text";
964newSpan.textContent = "Уже в корзине";
965btn.appendChild(newSpan);}else{btn.innerHTML = "Уже в корзине";}
966}
967btn.disabled = false;}
968else if(btn.classList.contains("product-detail__btn-basket")){const textSpan = btn.querySelector(".product-detail__btn-basket-text");
969if(textSpan){textSpan.textContent = "Уже в корзине";}
970const oneClickBtn = document.querySelector(
971".product-detail__buy-one-click",);
972if(oneClickBtn){oneClickBtn.disabled = false;}
973}
974}
975function resetButtonToAddToCart(btn){if(!btn) return;
976if(!btn.classList.contains("in-basket")) return;
977btn.classList.remove("in-basket");
978btn.dataset.inBasket = "false";
979if(btn.classList.contains("product-card__btn")){const textSpan = btn.querySelector(".product-card__btn-text");
980if(textSpan){textSpan.textContent = "В корзину";}else{const icon = btn.querySelector(".product-card__btn-icon");
981if(icon){btn.innerHTML = "";
982btn.appendChild(icon);
983const newSpan = document.createElement("span");
984newSpan.className = "product-card__btn-text";
985newSpan.textContent = "В корзину";
986btn.appendChild(newSpan);}else{btn.innerHTML = "В корзину";}
987}
988btn.disabled = false;}
989else if(btn.classList.contains("product-detail__btn-basket")){const textSpan = btn.querySelector(".product-detail__btn-basket-text");
990if(textSpan){textSpan.textContent = "В корзину";}
991}
992}
993setTimeout(() => {
994loadBasketState();}, 500);
995if(typeof BX !== "undefined"){BX.addCustomEvent("OnBasketChange", function () {
996loadBasketState();});
997}
998function refreshAllLikeCounters(){const productId = getCurrentProductId();
999if(!productId) return;
1000fetch("/ajax/get_reviews_actual.php?product_id=" + productId + "&_=" + Date.now())
1001.then(response => response.json())
1002.then(data => {
1003if(data.success && data.reviews){const actualCounts = {};data.reviews.forEach(review => {
1004actualCounts[review.ID] = {
1005likes: review.LIKE_COUNT,dislikes: review.DISLIKE_COUNT
1006};});
1007document.querySelectorAll('.review-card').forEach(card => {
1008const reviewId = card.dataset.reviewId;
1009if(reviewId && actualCounts[reviewId]){const likeSpan = card.querySelector('.review-card__btn-like .review-card__counter');
1010const dislSpan = card.querySelector('.review-card__btn-disl .review-card__counter');
1011if(likeSpan) likeSpan.textContent = actualCounts[reviewId].likes;
1012if(dislSpan) dislSpan.textContent = actualCounts[reviewId].dislikes;}
1013});}
1014})
1015.catch(error => console.error('Error refreshing like counters:', error));}
1016function getCurrentProductId(){const urlMatch = window.location.pathname.match(/\/catalog\/[^/]+\/(\d+)\//);
1017if(urlMatch) return urlMatch[1];
1018const metaTag = document.querySelector('meta[name="product-id"]');
1019if(metaTag) return metaTag.content;
1020const basketBtn = document.querySelector('.product-detail__btn-basket');
1021if(basketBtn && basketBtn.dataset.id) return basketBtn.dataset.id;
1022return null;}
1023setTimeout(() => {
1024refreshAllLikeCounters();}, 500);
1025window.addEventListener('pageshow', function (event){if(event.persisted) {
1026setTimeout(refreshAllLikeCounters, 100);}
1027});
1028document.addEventListener('visibilitychange', function (){if(!document.hidden) {
1029refreshAllLikeCounters();}
1030});});
1031window.addEventListener("pageshow", function (event){if(event.persisted) {
1032setTimeout(() => {
1033loadBasketState();}, 100);
1034}
1035});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.