'); let product_id = product_node.attr("data-product-id"); let is_cur_product_instance = $(data.action.product[0]) .parents(".editable-widget") .data("widgetId") == $(el).parents(".editable-widget").data("widgetId"); if ( data.action && data.action.product && data.first_image.url && data.product_id == product_id && is_cur_product_instance ) { let variant_photos = []; let product_img_ids = []; let product_imgs = data.action.productJSON.images; let product_variants = data.action.productJSON.variants; if (data.image_id) { if (data.image_ids && data.image_ids.length > 0) { variant_photos.push.apply(variant_photos, data.image_ids); } product_imgs.forEach(function(item) { product_img_ids.push(item.id); }); product_img_ids.forEach(function(imageId) { let is_image_for_all_variants = true; product_variants.forEach(function(variantItem) { let variant_imgs = variantItem.image_ids; if (variant_imgs.indexOf(imageId) != -1) { is_image_for_all_variants = false; } }); if (is_image_for_all_variants) { variant_photos.push(imageId); } }); } product_node.attr("data-variant-photos", variant_photos); product_node.attr("data-variant-id", data.id); product_node.attr("data-variant-first-img-id", data.first_image.id); let gallery_type = product_node.attr("data-gallery-type"); if (gallery_type == "variant-photos") { configurateVariantsPhoto(product_node); } else { if (product_node.is("[data-is-gallery-type-all-photos]")) { goToCurrentVariantPhoto(product_node); configurateVariantsPhoto(product_node); } else { configurateVariantsPhoto(product_node); } } } }); }); EventBus.subscribe("reviews-open:insales:site", function() { let reviews_block = $widget.find(".reviews"); if (reviews_block.length) { $("html, body").animate( { scrollTop: reviews_block.offset().top - 20 }, 500 ); } }); }); // === СЛАЙДЕРЫ / ГАЛЕРЕЯ === function initProductGallerySlider(galleryBlock) { let mainSliderBlock = galleryBlock.find(".js-product-gallery-main"); let tumbsSliderBlock = galleryBlock.find(".js-product-gallery-tumbs"); mainSliderBlock.find('.product__slide-main.play-video').each(function(index, el) { let $splideVideoItem = $(el) $(el).find('iframe[src*="rutube"]').each(function(index, iframe) { let $controlPlay = $splideVideoItem.find('.control.play'); $controlPlay[0].dataset.iframeSrc = iframe.src; iframe.src = ''; $controlPlay.on('click', function(e) { let targetSrc = e.target.parentNode.dataset.iframeSrc; if (targetSrc === $controlPlay[0].dataset.iframeSrc) { $(.fslightbox-absoluted .fslightbox-fade-in-strong #${iframe.id}).attr('src', targetSrc); } }) }) }); let img_id = mainSliderBlock.parents('[data-variant-first-img-id]:first').attr("data-variant-first-img-id"); let startIndex = 0; let currentImage = mainSliderBlock.find([data-product-img-id="${img_id}"]); if (currentImage.length) { startIndex = currentImage.index() } mainSliderBlock[0].splide = new Splide(mainSliderBlock[0], { gap: 1, start: startIndex }); mainSliderBlock[0].splide.on("move", function(newIndex) { let slide_index = newIndex; let slider_tumbs_node = mainSliderBlock .parents(".js-product-gallery") .find(".js-product-gallery-tumbs"); let slider_tumbs_inst = slider_tumbs_node[0].splide; if (slider_tumbs_inst) { slider_tumbs_inst.go(Number(slide_index)); slider_tumbs_node .find(".splide__slide.is-current") .removeClass("is-current"); slider_tumbs_node .find(".splide__slide[data-product-img-index=" + slide_index + "]") .addClass("is-current"); } }); mainSliderBlock[0].splide.mount(); tumbsSliderBlock[0].splide = new Splide(tumbsSliderBlock[0], { perPage: 5, perMove: 1, gap: "1rem", pagination: false, start: startIndex }); tumbsSliderBlock .find(".splide__slide.is-current") .removeClass("is-current"); tumbsSliderBlock .find(".splide__slide[data-product-img-index=" + startIndex + "]") .addClass("is-current"); tumbsSliderBlock[0].splide.mount(); } function configurateVariantsPhoto(productNode) { let product_gallery_block = productNode.find(".js-product-gallery"); let videoFirst = productNode.find(".js-product-all-images.video-first"); if (product_gallery_block.length > 0) { let gallery_type = productNode.attr("data-gallery-type"); let variant_id = productNode.attr("data-variant-id"); let variant_photos_ids = productNode .attr("data-variant-photos") .split(",") .filter((element) => element !== ""); let sizeVideo = productNode.data('video-size') if (variant_photos_ids.length > 0 && sizeVideo && sizeVideo > 0) { for (let i = sizeVideo - 1; i >= 0; i--) { if (videoFirst.length > 0) { variant_photos_ids.unshift(i); } else { variant_photos_ids.push(i); } } } let slider_main_inst = productNode.find(".js-product-gallery-main")[0] .splide; let slider_tumbs_inst = productNode.find(".js-product-gallery-tumbs")[0] .splide; let showVariantPhotos = () => { for (let i = 0; i < variant_photos_ids.length; i++) { let result_main_slide = productNode.find( '.js-product-all-images .product__slide-main[data-product-img-id="' + variant_photos_ids[i] + '"]' ); let result_tumbs_slide = productNode.find( '.js-product-all-images .product__slide-tumbs[data-product-img-id="' + variant_photos_ids[i] + '"]' ); if (result_main_slide.length > 0) { let main_slide_clone = result_main_slide .clone() .attr("data-product-img-index", i); main_slide_clone .find(".product__photo") .attr("data-fslightbox", "product-photos-lightbox-" + variant_id); main_slide_clone.appendTo( $(slider_main_inst.Components.Elements.list) ); } if (result_tumbs_slide.length > 0) { let tumbs_slide_clone = result_tumbs_slide .clone() .attr("data-product-img-index", i); if (i == 0) { tumbs_slide_clone.addClass("is-current"); } tumbs_slide_clone.appendTo( $(slider_tumbs_inst.Components.Elements.list) ); } } if (variant_photos_ids.length > 1) { product_gallery_block.removeClass("is-shown-one-photo"); } else { product_gallery_block.addClass("is-shown-one-photo"); } }; let showAllProductPhotos = () => { let all_main_photos = productNode.find( ".js-product-all-images .product__slide-main" ); let all_tumbs_photos = productNode.find( ".js-product-all-images .product__slide-tumbs" ); all_main_photos.each(function(index, el) { let main_slide_clone = $(el) .clone() .attr("data-product-img-index", index); main_slide_clone .find(".product__photo") .attr("data-fslightbox", "product-photos-lightbox-" + variant_id); main_slide_clone.appendTo($(slider_main_inst.Components.Elements.list)); }); all_tumbs_photos.each(function(index, el) { let tumbs_slide_clone = $(el) .clone() .attr("data-product-img-index", index); if (index == 0) { tumbs_slide_clone.addClass("is-current"); } tumbs_slide_clone.appendTo( $(slider_tumbs_inst.Components.Elements.list) ); }); if (all_main_photos.length > 1) { product_gallery_block.removeClass("is-shown-one-photo"); } else { product_gallery_block.addClass("is-shown-one-photo"); } }; $(slider_main_inst.Components.Elements.list).html(""); $(slider_tumbs_inst.Components.Elements.list).html(""); slider_main_inst.destroy(); slider_tumbs_inst.destroy(); if (gallery_type == "variant-photos") { if (variant_photos_ids.length > 0) { showVariantPhotos(); } else { showAllProductPhotos(); } initProductGallerySlider(product_gallery_block); productNode.removeAttr("data-is-gallery-type-all-photos"); } else { showAllProductPhotos(); initProductGallerySlider(product_gallery_block); goToCurrentVariantPhoto(productNode); productNode.attr("data-is-gallery-type-all-photos", ""); } refreshFsLightbox(); } } function updateProductGallerySlider(data) { let widget_slider_main_node = $( '[data-widget-id="' + data.widget_id + '"] .js-product-gallery-main' ); let widget_slider_tumbs_node = $( '[data-widget-id="' + data.widget_id + '"] .js-product-gallery-tumbs' ); if (widget_slider_main_node.length) { let sliderMainInst = widget_slider_main_node[0].splide; setTimeout(function() { sliderMainInst.refresh(); }, 0); } if (widget_slider_tumbs_node.length) { let sliderTumbsInst = widget_slider_tumbs_node[0].splide; setTimeout(function() { sliderTumbsInst.refresh(); }, 0); } } function goToCurrentVariantPhoto(productNode) { let img_id = productNode.attr("data-variant-first-img-id"); let videoFirst = productNode.find(".js-product-all-images.video-first").not('.after-inited'); let result_slide_elem_first_video = productNode.find( '.js-product-gallery-main [data-product-img-id="0"]' ); let result_slide_elem = productNode.find( '.js-product-gallery-main [data-product-img-id="' + img_id + '"]' ); if (result_slide_elem.length > 0) { let sliderMainInst = productNode.find(".js-product-gallery-main")[0].splide; if (sliderMainInst) { if (videoFirst.length > 0) { sliderMainInst.go( Number(result_slide_elem_first_video.attr("data-product-img-index")) ); } else { sliderMainInst.go( Number(result_slide_elem.attr("data-product-img-index")) ); } let slider_tumbs_node = productNode.find(".js-product-gallery-tumbs"); slider_tumbs_node .find(".splide__slide.is-current") .removeClass("is-current"); if (videoFirst.length > 0) { slider_tumbs_node .find( ".splide__slide[data-product-img-index=" + result_slide_elem_first_video.attr("data-product-img-index") + "]" ) .addClass("is-current"); } else { slider_tumbs_node .find( ".splide__slide[data-product-img-index=" + result_slide_elem.attr("data-product-img-index") + "]" ) .addClass("is-current"); } } } if (videoFirst.length > 0) { videoFirst.addClass('after-inited') } } $widget.find(".js-load-review-image").on("change", function() { let str = $(this).val(); let i = str.lastIndexOf("/") + 1; if (str.lastIndexOf("\\")) { i = str.lastIndexOf("\\") + 1; } let filename = str.slice(i); $widget.find(".load-review-image-name").html(filename); }); function fixedBuyBtnOnMobile(widgetLayout) { if (widgetLayout.find("#product-detail-buy-area").length === 0) { return false; } configureBuyBtn(); $(window).on("scroll resize", configureBuyBtn); function configureBuyBtn() { if (widgetLayout.find('.hide-all-buttons').length || widgetLayout.find('.is-show-marketplace-button').length) { return; // Если один из классов найден, выходим из функции } let buy_area = widgetLayout.find("#product-detail-buy-area"); let buy_area_height = buy_area.innerHeight(); let fixed_bottom_panel = $('[data-fixed-panels="bottom"]'); if ($(window).width() < 768) { let fixed_bottom_panel_height = 0; if ( fixed_bottom_panel.length && !fixed_bottom_panel.is(".is-no-layouts") ) { fixed_bottom_panel_height = fixed_bottom_panel.innerHeight(); } let btn_area_height = buy_area .find(".product__buy-btn-area-inner") .innerHeight(); let new_bottom_offset = ${ fixed_bottom_panel_height + btn_area_height }px; buy_area .css("height", buy_area_height) .addClass("is-fixed-state") .css("--product-buy-fixed-position", ${fixed_bottom_panel_height}px); if (widgetLayout.find(".product__not-available").is(":hidden")) { buy_area.parent().css("height", "0"); } $("html").css("--fixed-panels-bottom-offset", new_bottom_offset); } else { let fixed_bottom_panel_height = 0; if ( fixed_bottom_panel.length && !fixed_bottom_panel.is(".is-no-layouts") ) { fixed_bottom_panel_height = fixed_bottom_panel.innerHeight(); } buy_area.css("height", "auto").removeClass("is-fixed-state"); buy_area.parent().css("height", "auto"); $("html").css( "--fixed-panels-bottom-offset", ${fixed_bottom_panel_height}px ); } } } // Отключаем увеличение картинок в галерее let fs_gallery = document.querySelector(".product__area-photo"); if (fs_gallery) { fs_gallery.addEventListener("click", (event) => { $(".fslightbox-absoluted video").each(function(i) { $(this).get(0).play(); $(this).get(0).controls = false; $(this).get(0).autoplay = true; $(this).get(0).muted = true; $(this).get(0).playsinline = true; $(this).get(0).loop = true; }); if ( event.target.nodeName === "IMG" && event.target.closest(".product__gallery-main") ) { let items = document.querySelectorAll(".fslightbox-absoluted"); items.forEach(function(item) { item.addEventListener( "touchmove", (event) => { event.preventDefault(); }, { passive: false } ); }); } }); } // ===================================================================== // ОКРУГЛЁННЫЕ ОСТАТКИ (шт / метры) // ===================================================================== // Бины для штук function mapStockByPieces(q) { if (q <= 10) return { text: 'до 10 шт', mod: 'stock--critical' }; if (q <= 50) return { text: '10–50 шт', mod: 'stock--low' }; if (q <= 250) return { text: '50–250 шт', mod: 'stock--many' }; if (q <= 1000) return { text: '250–1 000 шт', mod: 'stock--many' }; if (q <= 2500) return { text: '1–2.5 тыс. шт', mod: 'stock--many' }; if (q <= 5000) return { text: '2.5–5 тыс. шт', mod: 'stock--many' }; if (q <= 10000) return { text: '5–10 тыс. шт', mod: 'stock--many' }; return { text: '10 тыс.+', mod: 'stock--many' }; } // Бины для метров function mapStockByMeters(q) { if (q <= 25) return { text: 'до 25 м', mod: 'stock--critical' }; if (q <= 99) return { text: '25–99 м', mod: 'stock--low' }; if (q <= 249) return { text: '100–249 м', mod: 'stock--many' }; if (q <= 499) return { text: '250–499 м', mod: 'stock--many' }; if (q <= 999) return { text: '500–999 м', mod: 'stock--many' }; if (q <= 2499) return { text: '1–2.4 км', mod: 'stock--many' }; if (q <= 4999) return { text: '2.5–4.9 км', mod: 'stock--many' }; if (q <= 9999) return { text: '5–9.9 км', mod: 'stock--many' }; return { text: '10 км+', mod: 'stock--many' }; } // Универсальная маршрутизация по unit function mapStockLabel(q, unit) { if (q === 0) { return { text: 'Нет в наличии', mod: null, none: true }; } if (q == null || isNaN(Number(q))) { // Если платформа не даёт точное число — просто «В наличии» return { text: 'В наличии', mod: 'stock--many', plain: true }; } const n = Number(q); const u = (unit || '').toString().toLowerCase(); // Любой unit, содержащий «м», трактуем как метры if (u.includes('м')) return mapStockByMeters(n); return mapStockByPieces(n); } // Применение ярлыка к бейджу function applyStockToBadge($product, qty, unit) { const $badge = $product.find('.data-quantity-unit[data-stock-indicator]'); const $label = $badge.find('[data-product-quantity-label]'); if (!$badge.length || !$label.length) return; const mapped = mapStockLabel(qty, unit); if (mapped.none) { $badge.hide(); return; } $badge.removeClass('stock--many stock--low stock--critical'); if (mapped.mod) $badge.addClass(mapped.mod); const text = (mapped.plain || mapped.text === 'В наличии') ? mapped.text : В наличии: ${mapped.text}; $label.text(text); $badge.show(); } // Инициализация при загрузке (используем data-initial-qty и unit) $widget.each(function () { const $product = $(this).find('[data-product-id]:first'); const $badge = $product.find('.data-quantity-unit[data-stock-indicator]'); if (!$badge.length) return; const unit = $badge.data('product-unit') || $product.attr('data-product-unit') || ''; const initialQtyRaw = $badge.data('initial-qty'); const initialQty = (initialQtyRaw === '' || initialQtyRaw === undefined) ? null : Number(initialQtyRaw); applyStockToBadge($product, initialQty, unit); }); // Обновляем ярлык при смене варианта EventBus.subscribe('change_variant:insales:product', function (data) { let product_node = $widget.find('[data-product-id]:first'); let product_id = product_node.attr('data-product-id'); if (!(data && data.action && data.product_id == product_id)) return; const $badge = product_node.find('.data-quantity-unit[data-stock-indicator]'); const unit = $badge.data('product-unit') || product_node.attr('data-product-unit') || ''; const q = (data.quantity === undefined) ? null : data.quantity; applyStockToBadge(product_node, q, unit); }); // === ВАШЕ ИМЕЮЩЕЕСЯ JS ОСТАВЛЯЕМ КАК ЕСТЬ ================================== // (все ваши существующие функции/инициализации ниже не трогаем) // --- БЛОК «Артикул»: кликабельная копия + обновление при смене варианта ---- (function () { var ROOT = document.querySelector('[data-product-id]'); if (!ROOT) return; var skuLink = ROOT.querySelector('.sku-inline'); if (!skuLink) return; var skuValueEl = skuLink.querySelector('[data-sku-value]'); function extractSkuLabelText(raw) { if (!raw) return ''; return raw.replace(/^Артикул:\s*/i,'').replace(/^SKU:\s*/i,'').trim(); } function readSkuFromPlatform() { var node = ROOT.querySelector('.product-form__area-sku [data-product-card-sku]'); if (!node) return ''; return extractSkuLabelText((node.textContent || '').trim()); } function readSkuFromJSON() { try { var json = ROOT.getAttribute('data-product-json'); if (!json) return ''; var pj = JSON.parse(json); var currentVariantId = ROOT.getAttribute('data-variant-id'); var v = null; if (currentVariantId && pj.variants) { v = pj.variants.find(function (x) { return String(x.id) === String(currentVariantId); }); } if (!v && pj.variants && pj.variants.length) v = pj.variants[0]; return (v && v.sku) || pj.sku || ''; } catch (e) { return ''; } } function getCurrentSku() { return readSkuFromPlatform() || readSkuFromJSON() || ''; } function updateSkuLine() { var sku = getCurrentSku(); if (!sku) return; skuLink.setAttribute('data-sku', sku); if (skuValueEl) skuValueEl.textContent = sku; } // Копирование в буфер function copyText(text) { if (navigator.clipboard && navigator.clipboard.writeText) { return navigator.clipboard.writeText(text); } return new Promise(function (resolve) { var ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); } catch(e) {} document.body.removeChild(ta); resolve(); }); } var toast = document.querySelector('.copy-toast'); function showToast(msg) { if (!toast) return; toast.textContent = msg || 'Скопировано в буфер обмена'; toast.classList.add('is-shown'); setTimeout(function () { toast.classList.remove('is-shown'); }, 1600); } // Делегирование клика document.addEventListener('click', function (e) { var a = e.target.closest('[data-copy-sku]'); if (!a || !ROOT.contains(a)) return; e.preventDefault(); var sku = a.getAttribute('data-sku') || (a.querySelector('.sku-value') || {}).textContent || ''; sku = (sku || '').trim(); if (!sku) return; copyText(sku).then(function () { showToast('Скопировано в буфер обмена'); }); }); // Первичное заполнение updateSkuLine(); // Обновление при смене варианта от платформы if (window.EventBus && typeof EventBus.subscribe === 'function') { EventBus.subscribe('change_variant:insales:product', function (data) { if (!data || String(data.product_id) !== String(ROOT.getAttribute('data-product-id'))) return; // даём платформе перерисовать значение в штатном блоке setTimeout(updateSkuLine, 0); }); } })(); $(function(){ function killNativeTitles(ctx){ $(ctx).find('[title]').removeAttr('title'); } // сразу убрать: killNativeTitles(document); // и убирать при наведении/ре-рендерах: $(document).on('mouseenter', '.sku-copy-line', function(){ killNativeTitles(this); }); }); // Сбрасываем делегаты на случай повторной инициализации $widget.off('click', '[data-quantity] [data-quantity-change]'); $widget.off('input blur change', '.counter-input[name="quantity"]'); $widget.off('click', '[data-add-cart-counter-plus],[data-add-cart-counter-minus]'); $widget.off('click', '[data-add-cart-counter-btn]'); // === ЕДИНЫЙ СИНХРОНИЗАТОР КОЛИЧЕСТВА (фикс рассинхронизации 6 vs 7) === $(function () { var MIN = 1, STEP = 1; function normInt(v) { v = String(v == null ? '' : v).replace(',', '.').replace(/[^\d.]/g, ''); var n = parseFloat(v); if (isNaN(n) || n < MIN) n = MIN; return String(Math.round(n / STEP) * STEP); } function $root(el){ return $(el).closest('[data-product-id]'); } function $acc($r){ return $r.find('[data-add-cart-counter]').first(); } function $ext($r){ return $r.find('.counter-input[name="quantity"]'); } function getCart($a){ var t = ($a.find('[data-add-cart-counter-count]').text()||'').replace(/[^\d]/g,''); var n = parseInt(t,10); return isNaN(n) ? MIN : n; } // Клик по кнопке и ожидание фактического изменения числа function clickAndWait($a, btnSel, prevVal, done){ $a.find(btnSel).trigger('click'); var tries = 0; var timer = setInterval(function(){ var now = getCart($a); if (now !== prevVal || tries > 40) { // ~3.2 c максимум clearInterval(timer); done(now); } tries++; }, 80); } // Последовательно доводим внутренний счётчик до нужного target function requestAdjust($a, target){ target = parseInt(normInt(target), 10); $a.data('target', target); if ($a.data('adjusting')) return; // уже идёт доведение — просто обновили цель $a.data('adjusting', true); (function loop(){ var cur = getCart($a); var tgt = parseInt($a.data('target'),10) || MIN; if (cur === tgt) { $a.removeData('adjusting'); return; } var btn = (tgt > cur) ? '[data-add-cart-counter-plus]' : '[data-add-cart-counter-minus]'; clickAndWait($a, btn, cur, function(){ setTimeout(loop, 10); }); })(); } // Нормализуем стартовое значение поля $widget.find('.counter-input[name="quantity"]').each(function(){ this.value = normInt(this.value || 1); }); // Внешние +/- возле «окошка» $widget.on('click', '[data-quantity] [data-quantity-change]', function(){ var $r = $root(this), $a = $acc($r); setTimeout(function(){ var val = parseInt(normInt($ext($r).val()),10); $ext($r).val(val); if ($a.hasClass('is-add-cart')){ if ($a.data('adjusting')) { $a.data('target', val); return; } requestAdjust($a, val); } }, 0); }); // Ручной ввод в «окошке» $widget.on('input', '.counter-input[name="quantity"]', function(){ this.value = this.value.replace(/[^\d]/g,''); }); $widget.on('blur change', '.counter-input[name="quantity"]', function(){ var $r = $root(this), $a = $acc($r); var val = parseInt(normInt(this.value),10); this.value = val; if ($a.hasClass('is-add-cart')){ if ($a.data('adjusting')) { $a.data('target', val); return; } requestAdjust($a, val); } }); // Первый клик «В корзину»: ждём is-add-cart и доводим до выбранного количества $widget.on('click', '[data-add-cart-counter-btn]', function(){ var $r = $root(this), $a = $acc($r), want = parseInt(normInt($ext($r).val()),10); var tries = 0, timer = setInterval(function(){ tries++; if ($a.hasClass('is-add-cart')){ clearInterval(timer); if ($a.data('adjusting')) { $a.data('target', want); } else { requestAdjust($a, want); } } if (tries > 60) clearInterval(timer); // ~3 сек таймаут }, 50); }); // Внутренние +/- блока корзины — зеркалим в «окошко» $widget.on('click', '[data-add-cart-counter-plus],[data-add-cart-counter-minus]', function(){ var $r = $root(this), $a = $acc($r); setTimeout(function(){ $ext($r).val(getCart($a)); }, 120); }); }); (function(){ // Берём только цифры, поддерживаем пробелы/₽/запятые function parsePrice(text){ if (!text) return NaN; var t = String(text).replace(/\s+/g,'').replace(',', '.'); var m = t.match(/-?\d+(\.\d+)?/); return m ? parseFloat(m[0]) : NaN; } function updateDiscount($root){ var $cur = $root.find('.product__price-cur'); var $old = $root.find('.product__price-old'); var $badge = $root.find('[data-discount-badge]'); if (!$cur.length || !$old.length || !$badge.length){ return; } var cur = parsePrice($cur.text()); var old = parsePrice($old.text()); // если нет старой цены или скидки нет — прячем бейдж if (!isFinite(cur) || !isFinite(old) || old <= cur){ $badge.hide().text(''); return; } var pct = Math.round((old - cur) / old * 100); // размер скидки, % $badge.text('−' + pct + '%').show(); // Хочешь ещё и сумму скидки — раскомментируй следующую строку: // $badge.text('−' + (old - cur).toLocaleString('ru-RU') + ' ₽ (−' + pct + '%)').show(); } var $product = $('.product').first(); if (!$product.length) return; // первичный расчёт updateDiscount($product); // Обновляем при смене варианта платформой if (window.EventBus && typeof EventBus.subscribe === 'function'){ EventBus.subscribe('change_variant:insales:product', function(){ setTimeout(function(){ updateDiscount($product); }, 0); }); } // И на любые изменения текста цен (когда их дорисовывает виджет) var targets = [ $product.find('.product__price-cur')[0], $product.find('.product__price-old')[0] ].filter(Boolean); var obs = new MutationObserver(function(){ updateDiscount($product); }); targets.forEach(function(node){ obs.observe(node, {childList:true, subtree:true, characterData:true}); }); })(); (function(){ var MOVE_DELAY = 0; (function () { // Ищем только контейнеры Яндекс Сплита (узкие селекторы) var splitSel = '.ya-split, [data-ya-split], [data-module="ya-split"], iframe[src*="yandex.ru"][src*="split"]'; function findSplit($root){ var $c = $root.find(splitSel); // если поймали iframe, поднимаем до ближайшей видимой обёртки $c = $c.map(function(){ var n = this; if (n.tagName === 'IFRAME') { var p = n.closest('.widget, .block, .payment, .payment-method, div'); return p || n; } return n; }); return $($c); } function move(){ var $root = $('.product').first(); var $holder = $root.find(holderSel).first(); if (!$root.length || !$holder.length) return; findSplit($root).each(function(){ var $n = $(this); if ($n.closest(holderSel).length) return; // уже на месте if ($n.closest('.product__controls').length) return; // не трогаем блок кнопок $holder.append($n.detach().show()); }); } // сразу, при смене варианта, при мутациях и с «страховкой» $(move); if (window.EventBus && EventBus.subscribe){ EventBus.subscribe('change_variant:insales:product', function(){ setTimeout(move,0); }); } var prod = document.querySelector('.product'); if (prod){ var obs = new MutationObserver(function(){ setTimeout(move,0); }); obs.observe(prod, {childList:true, subtree:true}); } var tries = 40; // ~10 сек var timer = setInterval(function(){ move(); if (--tries<=0) clearInterval(timer); }, 250); })(); (function () { var holderSel = '.product__area-calculate-delivery [data-ya-split-holder]'; var splitSel = '.ya-split, [data-ya-split], [data-module="ya-split"], iframe[src*="yandex.ru"][src*="split"]'; function findSplit($root){ var $c = $root.find(splitSel); // если попался iframe — берем ближайшую обертку, иначе сам узел $c = $c.map(function(){ var n = this; if (n.tagName === 'IFRAME') { var p = n.closest('.widget, .block, .payment, .payment-method, div'); return p || n; } return n; }); return $($c); } function ensureHolder($root){ var $holder = $root.find(holderSel).last(); if ($holder.length) return $holder; var $area = $root.find('.product__area-calculate-delivery').first(); if (!$area.length) return $(); return $('
').appendTo($area); } function move(){ var $root = $('.product').first(); if (!$root.length) return; var $holder = ensureHolder($root); if (!$holder.length) return; findSplit($root).each(function(){ var $n = $(this); // уже стоит на месте — пропускаем if ($n.closest(holderSel).length) return; // ни при каких условиях не тащим его в блок кнопок if ($n.closest('.product__controls').length) return; $holder.append($n.detach().show()); }); } $(move); if (window.EventBus && EventBus.subscribe){ EventBus.subscribe('change_variant:insales:product', function(){ setTimeout(move,0); }); } // следим именно за зоной доставки — когда она дорисуется, перетащим Сплит var area = document.querySelector('.product__area-calculate-delivery'); if (area){ var obs = new MutationObserver(function(){ setTimeout(move,0); }); obs.observe(area, {childList:true, subtree:true}); } // страховочные попытки ~15 сек var tries = 60, timer = setInterval(function(){ move(); if(--tries<=0) clearInterval(timer); }, 250); })(); (function(){ function parsePrice(text){ if (!text) return NaN; var t = String(text).replace(/\s+/g,'').replace(',', '.'); var m = t.match(/-?\d+(\.\d+)?/); return m ? parseFloat(m[0]) : NaN; } function updateDiscount($root){ var $cur = $root.find('.product__price-cur'); var $old = $root.find('.product__price-old'); var $badge = $root.find('[data-discount-badge]'); if (!$cur.length || !$old.length || !$badge.length){ return; } var cur = parsePrice($cur.text()); var old = parsePrice($old.text()); if (!isFinite(cur) || !isFinite(old) || old <= cur){ $badge.text('').css('display','none'); return; } var pct = Math.round((old - cur) / old * 100); $badge.text('−' + pct + '%').css('display','inline-flex'); // ← гарантированно перебиваем inline "display:none" } var $product = $('.product').first(); if (!$product.length) return; // первичная попытка + небольшие «догонялки» на случай поздней дорисовки updateDiscount($product); setTimeout(function(){ updateDiscount($product); }, 200); setTimeout(function(){ updateDiscount($product); }, 600); // вместо подписки на 2 конкретные
— слушаем ВЕСЬ контейнер цены var priceBox = $product.find('.product__price')[0]; if (priceBox){ var obs = new MutationObserver(function(){ updateDiscount($product); }); obs.observe(priceBox, {childList:true, subtree:true, characterData:true}); } // и при смене варианта if (window.EventBus && typeof EventBus.subscribe === 'function'){ EventBus.subscribe('change_variant:insales:product', function(){ setTimeout(function(){ updateDiscount($product); }, 0); }); } })();
} catch(error) {
console.error('Widget "widget-type_widget_v4_product_7_0e0e1076d5087c4b49b40beb13ab5a70"', error)
}
;try {
let widget = '.widget-type_widget_v4_header_4_0e472dad113e2262bdbf7e1e057ed450';
let $widget = $('.widget-type_widget_v4_header_4_0e472dad113e2262bdbf7e1e057ed450');
$(widget).find(".header__collections").css('display', 'none');
/* eslint-disable linebreak-style */
$(function() {
var mobilePoint = 768;
function getWidget(item) {
return item.closest('.layout');
}
$(widget).find(".header__collections").css('display', '');
$(document).ready(function() {
$widget.each(function(index, el) {
new LazyLoad({
container: $(el).get(0),
elements_selector: '.lazyload'
});
});
function initCutList() {
$(widget).find(".js-cut-list").cutList({
moreBtnTitle: ' ',
alwaysVisibleElem: '.is-current'
});
}
if ($(window).width() >= mobilePoint) {
initCutList()
}
$(window).on('resize', function() {
if ($(window).width() >= mobilePoint && $(widget).find(".icon-ellipsis-h").length < 1) {
initCutList()
}
});
$(window).on('load', function() {
$(widget).find(".js-cut-list").resize();
});
$(window).on('resize', function() {
let collections_menu = $(widget).find(".header__collections");
let collections_menu_content = collections_menu.find(".header__collections-content");
if (collections_menu.is(".is-show")) {
let max_height = $(window).height() - collections_menu.offset().top - 20;
collections_menu_content.css("maxHeight", max_height);
}
});
if ($(widget).find(".header__collections .is-current").length) {
$(widget).find(".header__collections .is-current").addClass("is-show-mobile");
$(widget).find(".header__collections > .header__collections-item.is-current").addClass("is-show");
}
$(widget).find(".js-show-header-collections").on("click", function() {
let thisWidget = getWidget($(this));
thisWidget.each(function(index, el) {
let lazyLoadCollectionList = new LazyLoad({
container: $(el).get(0),
elements_selector: '.lazyload'
});
try {
lazyLoadCollectionList.loadAll()
} catch (e) {
console.log(e)
}
});
let collections_menu = thisWidget.find(".header__collections");
let collections_menu_content = collections_menu.find(".header__collections-content");
if (collections_menu.is(".is-show")) {
collections_menu.removeClass("is-show");
bodyScrollLock.clearAllBodyScrollLocks();
} else {
collections_menu.addClass("is-show");
if ($(this).is(".header-mobile-panel__show-menu-btn")) {
let targetElement = $(widget).find(".header__collections.is-show").get(0);
if (targetElement) {
bodyScrollLock.disableBodyScroll(targetElement);
}
}
let max_height = $(window).height() - collections_menu.offset().top - 20;
collections_menu_content.css("maxHeight", max_height);
}
$(this).toggleClass("is-active");
thisWidget.find(".header__collections-overlay").toggleClass("is-show");
});
$(document).on("click", function(event) {
let thisWidget = getWidget($(event.target).closest('.layout'));
if ($(event.target).closest(".js-show-header-collections").length || $(event.target).closest(".header__collections-content").length) {
return;
}
thisWidget.find(".header__collections.is-show").removeClass("is-show");
thisWidget.find(".header__collections-overlay.is-show").removeClass("is-show");
thisWidget.find(".js-show-header-collections").removeClass("is-active");
bodyScrollLock.clearAllBodyScrollLocks();
});
$(widget).find(".js-hide-header-collections").on("click", function() {
let thisWidget = getWidget($(this));
thisWidget.find(".header__collections.is-show").removeClass("is-show");
thisWidget.find(".js-show-header-collections").removeClass("is-active");
thisWidget.find(".header__collections-overlay.is-show").removeClass("is-show");
bodyScrollLock.clearAllBodyScrollLocks();
});
$(widget).find(".js-show-more-subcollections").on("click", function() {
let thisWidget = getWidget($(this));
let collections_menu = thisWidget.find(".header__collections-menu");
let limit = collections_menu.attr("data-subcollections-items-limit");
let collection_elem = $(this).parents(".header__collections-item.is-level-1");
if ($(this).is(".is-active")) {
$(this).removeClass("is-active");
collection_elem.find('.header__collections-submenu .header__collections-item:nth-child(n+' + (parseInt(limit) + 1) + ')').addClass("is-hide");
} else {
$(this).addClass("is-active");
collection_elem.find(".header__collections-submenu .header__collections-item").removeClass("is-hide");
}
});
});
$(function() {
EventBus.subscribe(['widget:input-setting:insales:system:editor', 'widget:change-setting:insales:system:editor'], (data) => {
$(widget).find(".js-cut-list").resize();
if (data.setting_name == 'subcollections-items-limit') {
configureSubcollectionsItemsLimit(data.value);
}
});
});
function configureSubcollectionsItemsLimit(limit) {
let collections_menu = $(widget).find(".header__collections-menu");
collections_menu.attr("data-subcollections-items-limit", limit);
$(widget).find(".header__collections-submenu").each(function() {
let collection_elem = $(this).parents(".header__collections-item.is-level-1");
let collection_elem_more_controls = collection_elem.find(".header__collections-show-more");
$(this).find(".header__collections-item").removeClass("is-hide");
$(this).find('.header__collections-item:nth-child(n+' + (parseInt(limit) + 1) + ')').addClass("is-hide");
collection_elem_more_controls.find(".js-show-more-subcollections").removeClass("is-active");
if ($(this).find(".header__collections-item").length > parseInt(limit)) {
collection_elem_more_controls.addClass("is-show");
} else {
collection_elem_more_controls.removeClass("is-show");
}
});
}
});
} catch(error) {
console.error('Widget "widget-type_widget_v4_header_4_0e472dad113e2262bdbf7e1e057ed450"', error)
}
;try {
let widget = '.widget-type_widget_v4_footer_18_1_f1a5f40fa79138dfe9ec0a773a5979a4';
let $widget = $('.widget-type_widget_v4_footer_18_1_f1a5f40fa79138dfe9ec0a773a5979a4');
$(function() {
$(widget).find(".js-show-mobile-submenu").on("click", function() {
$(this).parents(".menu-item:first").toggleClass("is-show-mobile");
});
});
} catch(error) {
console.error('Widget "widget-type_widget_v4_footer_18_1_f1a5f40fa79138dfe9ec0a773a5979a4"', error)
}
;try {
let widget = '.widget-type_widget_v4_products_similar_1_b490de5034feb79e7590a450fb0b3fea';
let $widget = $('.widget-type_widget_v4_products_similar_1_b490de5034feb79e7590a450fb0b3fea');
$(function () {
// ========= вспомогательные =========
var mobile_point = 767;
function isMobileWidth() { return $(window).width() <= mobile_point; }
// ====== МАППИНГ ОСТАТКОВ ДЛЯ КАРУСЕЛИ (PLP-грубость) ======
// Штуки:
function mapStockByPieces(q) {
if (q <= 10) return { text: 'до 10 шт', mod: 'stock--critical' };
if (q <= 50) return { text: '10–50 шт', mod: 'stock--low' };
if (q <= 250) return { text: '50–250 шт', mod: 'stock--many' };
if (q <= 1000) return { text: '250–1 000 шт', mod: 'stock--many' };
if (q <= 2500) return { text: '1–2.5 тыс. шт', mod: 'stock--many' };
if (q <= 5000) return { text: '2.5–5 тыс. шт', mod: 'stock--many' };
if (q <= 10000) return { text: '5–10 тыс. шт', mod: 'stock--many' };
return { text: '10 тыс.+', mod: 'stock--many' };
}
// Метры:
function mapStockByMeters(q) {
if (q <= 25) return { text: 'до 25 м', mod: 'stock--critical' };
if (q <= 99) return { text: '25–99 м', mod: 'stock--low' };
if (q <= 249) return { text: '100–249 м', mod: 'stock--many' };
if (q <= 499) return { text: '250–499 м', mod: 'stock--many' };
if (q <= 999) return { text: '500–999 м', mod: 'stock--many' };
if (q <= 2499) return { text: '1–2.4 км', mod: 'stock--many' };
if (q <= 4999) return { text: '2.5–4.9 км', mod: 'stock--many' };
if (q <= 9999) return { text: '5–9.9 км', mod: 'stock--many' };
return { text: '10 км+', mod: 'stock--many' };
}
// Выбор маппинга по unit
function mapStockLabel(q, unit) {
if (q === 0) return { text: 'Под заказ', mod: 'out-of-stock', none: false };
if (q == null || isNaN(Number(q))) return { text: 'В наличии', mod: 'stock--many', plain: true };
const n = Number(q);
const u = (unit || '').toString().toLowerCase();
if (u.includes('м')) return mapStockByMeters(n);
return mapStockByPieces(n);
}
function applyStockToBadge($scope) {
$scope.find('.quantity-badge[data-stock-indicator]').each(function () {
const $badge = $(this);
const raw = $badge.data('initial-qty');
const unit = $badge.data('product-unit') || '';
const qty = (raw === '' || typeof raw === 'undefined') ? null : Number(raw);
const mapped = mapStockLabel(qty, unit);
$badge.removeClass('stock--many stock--low stock--critical out-of-stock');
if (mapped.mod) $badge.addClass(mapped.mod);
const txt = (mapped.plain) ? mapped.text : `В наличии: ${mapped.text}`;
$badge.text(mapped.text === 'Под заказ' ? 'Под заказ' : txt).show();
});
}
function initStockBadges(ctx) { applyStockToBadge(ctx || $widget); }
// ========= ИНИЦИАЛИЗАЦИЯ СЛАЙДЕРА =========
$(document).ready(function () {
// тексты отзывов (оставил как в твоём коде)
function declinationText(number, txt) {
var cases = [2, 0, 1, 1, 1, 2];
return txt[number % 100 > 4 && number % 100 < 20 ? 2 : cases[number % 10 < 5 ? number % 10 : 5]];
}
$widget.find('.product-preview').each(function () {
const reviewsCount = parseInt($(this).find('.product-preview__review-count').text(), 10);
let s = $(this).data('review-singular');
let g = $(this).data('review-singular-gen');
let p = $(this).data('review-plural');
$(this).find('.product-preview__review-text').text(declinationText(reviewsCount, [s, g, p]));
});
let $blocks = $widget.find(".js-special-products");
$blocks.each(function () {
let $wrap = $(this);
let $slider = $wrap.find(".js-special-products-slider");
let slide_min_width = $slider.is("[data-slide-min-width]") ? parseInt($slider.data("slideMinWidth")) : 220;
let slide_min_width_mobile = $slider.is("[data-slide-min-width-mobile]") ? parseInt($slider.data("slideMinWidthMobile")) : 150;
let slide_gap = $slider.is("[data-slide-gap]") ? parseInt($slider.data("slideGap")) : 30;
$slider.data("slideMinWidth", slide_min_width);
$slider.data("slideMinWidthMobile", slide_min_width_mobile);
$slider.data("slideGap", slide_gap);
$slider[0].splide = new Splide($slider[0], {
perPage: Math.floor(($slider.width() + slide_gap) / ((isMobileWidth() ? slide_min_width_mobile : slide_min_width) + slide_gap)),
gap: slide_gap,
perMove: 1
});
$(window).on("resize", function () {
let sw = parseInt($slider[0].dataset.slideMinWidth);
let swm = parseInt($slider[0].dataset.slideMinWidthMobile);
let g = parseInt($slider[0].dataset.slideGap);
$slider[0].splide.options = { perPage: Math.floor(($slider.width() + g) / ((isMobileWidth() ? swm : sw) + g)) };
if ($slider[0].splide.length <= $slider[0].splide.options.perPage) {
$slider[0].splide.options = { drag: false };
} else {
$slider[0].splide.options = { drag: true };
}
});
$slider[0].splide.on('mounted updated', function () {
// показать/скрыть стрелки
let prev_btn = $wrap.find(".special-products__slider-arrow-prev");
let next_btn = $wrap.find(".special-products__slider-arrow-next");
let inst = $slider[0].splide;
if (inst.length <= inst.options.perPage) {
prev_btn.addClass("is-hide"); next_btn.addClass("is-hide"); $slider.addClass("is-hide-paging");
} else {
prev_btn.removeClass("is-hide"); next_btn.removeClass("is-hide"); $slider.removeClass("is-hide-paging");
}
});
$slider[0].splide.on('mounted', function () {
// инициализируем бейджи после монтирования слайдера
initStockBadges($wrap);
// drag on/off
if ($slider[0].splide.length <= $slider[0].splide.options.perPage) {
$slider[0].splide.options = { drag: false };
} else {
$slider[0].splide.options = { drag: true };
}
});
$slider[0].splide.on('arrows:updated', function () {
let prev_btn = $wrap.find(".special-products__slider-arrow-prev");
let next_btn = $wrap.find(".special-products__slider-arrow-next");
prev_btn.toggleClass("is-disabled", $slider.find(".splide__arrow--prev").prop("disabled") === true);
next_btn.toggleClass("is-disabled", $slider.find(".splide__arrow--next").prop("disabled") === true);
});
$slider[0].splide.mount();
$widget.find(".js-move-slide").on("click", function () {
let slider_node = $(this).parents(".js-special-products").find(".js-special-products-slider");
if (slider_node.length) {
let s = slider_node[0].splide;
if ($(this).is(".special-products__slider-arrow-prev")) s.go('-');
if ($(this).is(".special-products__slider-arrow-next")) s.go('+');
}
});
});
});
// Обновление фото варианта и бейджа при смене варианта в карточке из карусели
EventBus.subscribe("change_variant:insales:product", function (data) {
let $product = $(data.action.product[0]);
// 1) Фото варианта (как было)
let $imagesContainer = $product.find(".product-preview__photo .product-preview__photo-variant");
if ($imagesContainer.length) {
let images = [];
if (data.image_ids.length <= 1) {
let defaultImage = data.images[0];
if (defaultImage) {
images = [`
`];
}
} else {
let variantImages = data.action.productJSON.images.filter(img => data.image_ids.includes(img.id));
let firstImage = variantImages[0] || data.action.productJSON.images[0];
if (firstImage) {
images.push(`
`);
}
}
$imagesContainer.html(images.join(''));
}
// 2) Бейдж остатков
const $badge = $product.find('.quantity-badge[data-stock-indicator]');
if ($badge.length) {
const unit = $badge.data('product-unit') || '';
const q = (typeof data.quantity === 'undefined') ? null : data.quantity;
const mapped = mapStockLabel(q, unit);
$badge.removeClass('stock--many stock--low stock--critical out-of-stock');
if (mapped.mod) $badge.addClass(mapped.mod);
const txt = (mapped.plain) ? mapped.text : `В наличии: ${mapped.text}`;
$badge.text(mapped.text === 'Под заказ' ? 'Под заказ' : txt).show();
}
});
});
} catch(error) {
console.error('Widget "widget-type_widget_v4_products_similar_1_b490de5034feb79e7590a450fb0b3fea"', error)
}
;try {
let widget = '.widget-type_widget_v4_products_related_1_d74a193ddafb7a3f2877e7a153e4d12c';
let $widget = $('.widget-type_widget_v4_products_related_1_d74a193ddafb7a3f2877e7a153e4d12c');
(function ($, window, document) {
if (!$) return;
// ====== STOCK BADGE ======
var StockBadge = (function () {
function mapPieces(q) {
if (q <= 10) return { text: 'В наличии: до 10 шт', mod: 'stock--critical' };
if (q <= 50) return { text: 'В наличии: 10–50 шт', mod: 'stock--low' };
if (q <= 250) return { text: 'В наличии: 50–250 шт', mod: 'stock--many' };
if (q <= 1000) return { text: 'В наличии: 250–1 000 шт', mod: 'stock--many' };
if (q <= 2500) return { text: 'В наличии: 1–2.5 тыс. шт', mod: 'stock--many' };
if (q <= 5000) return { text: 'В наличии: 2.5–5 тыс. шт', mod: 'stock--many' };
if (q <= 10000) return { text: 'В наличии: 5–10 тыс. шт', mod: 'stock--many' };
return { text: 'В наличии: 10 тыс.+', mod: 'stock--many' };
}
function mapMeters(q) {
if (q <= 25) return { text: 'В наличии: до 25 м', mod: 'stock--critical' };
if (q <= 99) return { text: 'В наличии: 25–99 м', mod: 'stock--low' };
if (q <= 249) return { text: 'В наличии: 100–249 м',mod: 'stock--many' };
if (q <= 499) return { text: 'В наличии: 250–499 м',mod: 'stock--many' };
if (q <= 999) return { text: 'В наличии: 500–999 м',mod: 'stock--many' };
if (q <= 2499) return { text: 'В наличии: 1–2.4 км', mod: 'stock--many' };
if (q <= 4999) return { text: 'В наличии: 2.5–4.9 км',mod: 'stock--many' };
if (q <= 9999) return { text: 'В наличии: 5–9.9 км', mod: 'stock--many' };
return { text: 'В наличии: 10 км+', mod: 'stock--many' };
}
function compute(qty, unit) {
if (qty === 0) return { text: 'Под заказ', mod: 'out-of-stock' };
if (qty == null || isNaN(Number(qty))) return { text: 'В наличии', mod: 'stock--many' };
var u = (unit || '').toString().toLowerCase();
return u.includes('м') ? mapMeters(Number(qty)) : mapPieces(Number(qty));
}
function applyOne($badge, qty, unit) {
var res = compute(qty, unit);
$badge.removeClass('stock--many stock--low stock--critical out-of-stock')
.addClass(res.mod)
.text(res.text)
.show();
}
function init(scope) {
$(scope || document).find('.quantity-badge[data-stock-indicator]').each(function () {
var $b = $(this);
var raw = $b.attr('data-initial-qty');
var qty = (raw === '' || typeof raw === 'undefined') ? null : Number(raw);
var unit = $b.attr('data-product-unit') || '';
applyOne($b, qty, unit);
});
}
return { init: init, applyOne: applyOne };
})();
// ====== SPLIDE ======
function perView($slider, minW, gap) {
var w = $slider.width() || 1;
return Math.max(1, Math.floor((w + gap) / (minW + gap)));
}
function initSlider($wrap) {
$wrap.find('.js-special-products-slider').each(function () {
var el = this;
var $el = $(el);
var minW = parseInt($el.data('slideMinWidth')) || 220;
var minWm = parseInt($el.data('slideMinWidthMobile')) || 150;
var gap = parseInt($el.data('slideGap')) || 30;
var isMobile = $(window).width() <= 767;
if (typeof Splide === 'undefined') return; // нет библиотеки — тихо выходим
var inst = new Splide(el, {
perPage: perView($el, isMobile ? minWm : minW, gap),
gap: gap,
perMove: 1,
arrows: true,
pagination: $(window).width() <= 767
});
inst.on('mounted move updated', function () {
toggleControls($wrap, inst);
});
inst.mount();
// кнопки
$wrap.find('.js-move-slide').off('click.splideNav').on('click.splideNav', function () {
if ($(this).hasClass('special-products__slider-arrow-prev')) inst.go('-');
if ($(this).hasClass('special-products__slider-arrow-next')) inst.go('+');
});
// пересчёт при ресайзе
$(window).on('resize.relatedSplide', function () {
var mobile = $(window).width() <= 767;
inst.options = { perPage: perView($el, mobile ? minWm : minW, gap) };
toggleControls($wrap, inst);
});
});
}
function toggleControls($wrap, inst) {
var $prev = $wrap.find('.special-products__slider-arrow-prev');
var $next = $wrap.find('.special-products__slider-arrow-next');
var slides = inst.Components.Elements.list.children.length;
var perPage = inst.options.perPage || 1;
if (slides <= perPage) {
$prev.addClass('is-hide'); $next.addClass('is-hide');
} else {
$prev.removeClass('is-hide'); $next.removeClass('is-hide');
// блокируем крайние состояния
var idx = inst.index;
$prev.toggleClass('is-disabled', idx === 0);
$next.toggleClass('is-disabled', idx >= slides - perPage);
}
}
// ====== VARIANT CHANGE ======
if (window.EventBus && typeof window.EventBus.subscribe === 'function') {
EventBus.subscribe('change_variant:insales:product', function (data) {
try {
var $card = $(data.action.product[0]);
// Обновление фото (если используется клик по .product-preview__photo-variant)
var $imgWrap = $card.find('.product-preview__photo .product-preview__photo-variant');
if ($imgWrap.length) {
var html = '';
if (data.image_ids.length <= 1) {
var def = data.images && data.images[0];
if (def) {
html = ''
+ ''
+ ''
+ ' '
+ ' ';
}
} else {
var arr = data.action.productJSON.images.filter(function (img) { return data.image_ids.indexOf(img.id) > -1; });
var first = arr[0] || data.action.productJSON.images[0];
if (first) {
html = ''
+ ''
+ ''
+ ' '
+ ' ';
}
}
$imgWrap.html(html);
}
// Обновление бейджа
var $badge = $card.find('.quantity-badge[data-stock-indicator]');
if ($badge.length) {
var unit = $badge.attr('data-product-unit') || '';
var qty = (typeof data.quantity === 'undefined') ? null : data.quantity;
StockBadge.applyOne($badge, qty, unit);
}
} catch (e) {
// тихо
if (window.console && console.warn) console.warn('Variant/badge update error:', e);
}
});
}
// ====== INIT ======
$(function () {
$('.js-special-products').each(function () {
var $wrap = $(this);
initSlider($wrap);
});
// Бейджи после отрисовки
StockBadge.init(document);
});
})(window.jQuery, window, document);
} catch(error) {
console.error('Widget "widget-type_widget_v4_products_related_1_d74a193ddafb7a3f2877e7a153e4d12c"', error)
}