PageSourceSearch

https://www.mirkrasok.ru/bitrix/cache/js/s1/mirkrasok_2026v2/page_…f6f125e53851146c20bba_v1.js?178976626551930

js mirkrasok.ru collected 2026-10-03 20:56:47 UTC 51,930 bytes, 1,308 lines download raw bytes

1
2; /* Start:"a:4:{s:4:"full";s:113:"/local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/mainbanner_index_new/script.js?17869993714147";s:6:"source";s:98:"/local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/mainbanner_index_new/script.js";s:3:"min";s:0:"";s:3:"map";s:0:"";}"*/
3function loadMainBannerSlide(slide) {
4    if (!slide) {
5        return null;
6    }
7
8    // Узкий макет баннера — отдельная картинка в <source media>, поэтому
9    // набор заполняем раньше самой картинки: выбор внутри <picture> браузер
10    // делает в момент, когда у <img> появляется src, и к этой секунде
11    // варианты уже должны быть на месте. Иначе на телефоне подхватится
12    // широкий файл.
13    var sources = slide.querySelectorAll('source[data-srcset]');
14    for (var i = 0; i < sources.length; i++) {
15        sources[i].srcset = sources[i].dataset.srcset;
16        sources[i].removeAttribute('data-srcset');
17    }
18
19    var image = slide.querySelector('img[data-src]');
20    if (image) {
21        image.src = image.dataset.src;
22        image.removeAttribute('data-src');
23    }
24
25    return slide.querySelector('img');
26}
27
28function loadVisibleMainBannerSlides(swiper) {
29    if (!swiper.slides || swiper.slides.length < 2) {
30        return;
31    }
32
33    var nextIndex = (swiper.activeIndex + 1) % swiper.slides.length;
34    loadMainBannerSlide(swiper.slides[swiper.activeIndex]);
35    loadMainBannerSlide(swiper.slides[nextIndex]);
36}
37
38function scheduleFollowingMainBannerSlide(swiper) {
39    var loadFollowing = function() {
40        if (!swiper.slides || swiper.slides.length < 3) {
41            return;
42        }
43
44        var followingIndex = (swiper.activeIndex + 2) % swiper.slides.length;
45        loadMainBannerSlide(swiper.slides[followingIndex]);
46    };
47
48    if ('requestIdleCallback' in window) {
49        window.requestIdleCallback(loadFollowing, {timeout: 2000});
50    } else {
51        window.setTimeout(loadFollowing, 300);
52    }
53}
54
55var mainBannerAutoplayDelay = window.matchMedia('(max-width: 600px)').matches ? 10000 : 5000;
56
57var swipermain = new Swiper(".mainBanner", {
58    spaceBetween: 30,
59    speed: 1000,
60    centeredSlides: true,
61    // Слайд сам является ссылкой. Стандартный role="group" от Swiper для
62    // элемента <a> недопустим и ломает accessibility tree в Lighthouse.
63    a11y: {
64        slideRole: null,
65    },
66    autoplay: {
67        // Avoid replacing the largest first-screen image during initial mobile rendering.
68        delay: mainBannerAutoplayDelay,
69        disableOnInteraction: false,
70    },
71    navigation: {
72        nextEl: ".swiper-button-next",
73        prevEl: ".swiper-button-prev",
74    },
75    // Прогресс-бар настраивается через pagination с типом progressbar
76    pagination: {
77        el: ".mainBanner .swiper-progressbar",
78        type: "progressbar",
79    },
80    // Добавляем наблюдателей
81    observer: true,
82    observeParents: true,
83    on: {
84        init: function() {
85            // Убираем класс lock принудительно
86            const progressbar = document.querySelector('.mainBanner .swiper-progressbar');
87            if (progressbar) {
88                progressbar.classList.remove('swiper-pagination-lock');
89            }
90            loadVisibleMainBannerSlides(this);
91            // Do not download the third banner during the first screen load.
92            // It is scheduled after the first transition below.
93        },
94        slideChangeTransitionStart: function() {
95            loadVisibleMainBannerSlides(this);
96        },
97        slideChangeTransitionEnd: function() {
98            scheduleFollowingMainBannerSlide(this);
99        }
100    },
101    breakpoints: {
102        0: {
103            slidesPerView: 1.08,
104            spaceBetween: 8,
105            centeredSlides: false
106        },
107        360: {
108            slidesPerView: 1.08,
109            spaceBetween: 8,
110            centeredSlides: false
111        },
112        601: {
113            slidesPerView: 1,
114            spaceBetween: 0,
115            centeredSlides: true
116        },
117        1024: {
118            slidesPerView: 1,
119            spaceBetween: 0,
120            centeredSlides: true
121        }
122    }
123});
124
125/* End */
126;
127; /* Start:"a:4:{s:4:"full";
127s:105:"/local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/brands-index/script.js?17829140649879";s:6:"source";s:90:"/local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/brands-index/script.js";s:3:"min";s:0:"";s:3:"map";s:0:"";}"*/
128document.addEventListener('DOMContentLoaded', function() {
129  const marquee = document.querySelector('.brands-marque');
130
131  if (marquee) {
132    const clone = marquee.innerHTML;
133    marquee.innerHTML = clone + clone;
134  }
135});
136
137var swiper2 = new Swiper(".brands-slider", {
138  observer: true,
139  observeParents: true,
140  observeSlideChildren: true,
141  slidesPerView: "auto",
142  watchSlidesProgress: true,
143  breakpoints: {
144    0: {
145      spaceBetween: 10,
146    },
147    601: {
148      spaceBetween: 20,
149    },
150    1024: {
151      spaceBetween: 20,
152    },
153  },
154  pagination: {
155    el: ".brands-slider .swiper-progressbar",
156    type: "progressbar",
157  },
158});
159
160
161document.addEventListener('DOMContentLoaded', function() {
162    const marque = document.querySelector('.brands-marque');
163    if (!marque) return;
164
165    let position = 0;
166    let isDragging = false;
167    let startX = 0;
168    let startPos = 0;
169    let velocity = 0;
170    let lastX = 0;
171    let lastTime = 0;
172    let animId = null;
173    let isMoving = true;
174    let hasDragged = false; // Флаг перетаскивания
175    
176    const SPEED = 1.0; // Увеличена скорость в 2 раза
177
178    function getContentWidth() {
179        return marque.scrollWidth / 2;
180    }
181
182    function setPosition(x) {
183        position = x;
184        marque.style.transform = 'translateX(' + x + 'px)';
185    }
186
187    function normalize(x) {
188        const w = getContentWidth();
189        if (x > 0) return x - w;
190        if (x < -w) return x + w;
191        return x;
192    }
193
194    function animate() {
195        if (!isMoving || isDragging) {
196            animId = requestAnimationFrame(animate);
197            return;
198        }
199        
200        let newPos = position - SPEED;
201        newPos = normalize(newPos);
202        setPosition(newPos);
203        animId = requestAnimationFrame(animate);
204    }
205
206    function startAnimation() {
207        if (animId) cancelAnimationFrame(animId);
208        isMoving = true;
209        animId = requestAnimationFrame(animate);
210    }
211
212    function stopAnimation() {
213        isMoving = false;
214        if (animId) {
215            cancelAnimationFrame(animId);
216            animId = null;
217        }
218    }
219
220    // ===== TOUCH =====
221    marque.addEventListener('touchstart', function(e) {
222        stopAnimation();
223        isDragging = true;
224        hasDragged = false;
225        const t = e.touches[0];
226        startX = t.clientX;
227        startPos = position;
228        lastX = startX;
229        lastTime = Date.now();
230        velocity = 0;
231    }, { passive: true });
232
233    marque.addEventListener('touchmove', function(e) {
234        if (!isDragging) return;
235        e.preventDefault();
236        
237        hasDragged = true; // Отмечаем, что было перетаскивание
238        
239        const t = e.touches[0];
240        const now = Date.now();
241        const dx = t.clientX - lastX;
242        const dt = now - lastTime;
243        
244        if (dt > 0) velocity = (dx / dt) * 10;
245        
246        lastX = t.clientX;
247        lastTime = now;
248        
249        let newPos = startPos + (t.clientX - startX);
250        const w = getContentWidth();
251        const containerWidth = marque.parentElement.offsetWidth;
252        const min = -w + containerWidth;
253        const max = 0;
254        
255        if (newPos > max) newPos = max + (newPos - max) * 0.2;
256        if (newPos < min) newPos = min + (newPos - min) * 0.2;
257        
258        setPosition(newPos);
259    }, { passive: false });
260
261    marque.addEventListener('touchend', function(e) {
262        if (!isDragging) return;
263        isDragging = false;
264        
265        const w = getContentWidth();
266        const containerWidth = marque.parentElement.offsetWidth;
267        const min = -w + containerWidth;
268        const max = 0;
269        
270        // Если было перетаскивание - отменяем клик
271        if (hasDragged) {
272            e.preventDefault();
273            // Сбрасываем флаг через 300ms после окончания касания
274            setTimeout(() => {
275                hasDragged = false;
276            }, 300);
277        }
278        
279        // Инерция
280        if (Math.abs(velocity) > 0.3) {
281            let v = velocity;
282            const friction = 0.97;
283            
284            function decelerate() {
285                v *= friction;
286                let newPos = position + v;
287                
288                if (newPos > max) {
289                    newPos = max + (newPos - max) * 0.1;
290                    v *= 0.8;
291                }
292                if (newPos < min) {
293                    newPos = min + (newPos - min) * 0.1;
294                    v *= 0.8;
295                }
296                
297                newPos = normalize(newPos);
298                setPosition(newPos);
299                
300                if (Math.abs(v) > 0.1) {
301                    requestAnimationFrame(decelerate);
302                } else {
303                    setPosition(normalize(position));
304                    startAnimation();
305                }
306            }
307            decelerate();
308        } else {
309            setPosition(normalize(position));
310            startAnimation();
311        }
312    }, { passive: false });
313
314    marque.addEventListener('touchcancel', function() {
315        if (isDragging) {
316            isDragging = false;
317            hasDragged = false;
318            setPosition(normalize(position));
319            startAnimation();
320        }
321    }, { passive: true });
322
323    // ===== MOUSE =====
324    marque.addEventListener('mousedown', function(e) {
325        stopAnimation();
326        isDragging = true;
327        hasDragged = false;
328        startX = e.clientX;
329        startPos = position;
330        lastX = startX;
331        lastTime = Date.now();
332        velocity = 0;
333        e.preventDefault();
334    }, { passive: false });
335
336    document.addEventListener('mousemove', function(e) {
337        if (!isDragging) return;
338        
339        hasDragged = true; // Отмечаем, что было перетаскивание
340        
341        const now = Date.now();
342        const dx = e.clientX - lastX;
343        const dt = now - lastTime;
344        
345        if (dt > 0) velocity = (dx / dt) * 10;
346        
347        lastX = e.clientX;
348        lastTime = now;
349        
350        let newPos = startPos + (e.clientX - startX);
351        const w = getContentWidth();
352        const containerWidth = marque.parentElement.offsetWidth;
353        const min = -w + containerWidth;
354        const max = 0;
355        
356        if (newPos > max) newPos = max + (newPos - max) * 0.2;
357        if (newPos < min) newPos = min + (newPos - min) * 0.2;
358        
359        setPosition(newPos);
360    }, { passive: false });
361
362    document.addEventListener('mouseup', function(e) {
363        if (!isDragging) return;
364        isDragging = false;
365        
366        const w = getContentWidth();
367        const containerWidth = marque.parentElement.offsetWidth;
368        const min = -w + containerWidth;
369        const max = 0;
370        
371        // Если было перетаскивание - отменяем клик
372        if (hasDragged) {
373            e.preventDefault();
374            // Сбрасываем флаг через 300ms
375            setTimeout(() => {
376                hasDragged = false;
377            }, 300);
378        }
379        
380        if (Math.abs(velocity) > 0.3) {
381            let v = velocity;
382            const friction = 0.97;
383            
384            function decelerate() {
385                v *= friction;
386                let newPos = position + v;
387                
388                if (newPos > max) {
389                    newPos = max + (newPos - max) * 0.1;
390                    v *= 0.8;
391                }
392                if (newPos < min) {
393                    newPos = min + (newPos - min) * 0.1;
394                    v *= 0.8;
395                }
396                
397                newPos = normalize(newPos);
398                setPosition(newPos);
399                
400                if (Math.abs(v) > 0.1) {
401                    requestAnimationFrame(decelerate);
402                } else {
403                    setPosition(normalize(position));
404                    startAnimation();
405                }
406            }
407            decelerate();
408        } else {
409            setPosition(normalize(position));
410            startAnimation();
411        }
412    }, { passive: false });
413
414    // Остановка при наведении (только для десктопа)
415    let isHovering = false;
416    
417    marque.addEventListener('mouseenter', function() {
418        isHovering = true;
419        if (!isDragging) {
420            stopAnimation();
421        }
422    });
423
424    marque.addEventListener('mouseleave', function() {
425        isHovering = false;
426        // Возобновляем анимацию, если не перетаскиваем и не наведены
427        if (!isDragging && !isMoving) {
428            startAnimation();
429        }
430    });
431
432    // ===== ОБРАБОТЧИК КЛИКОВ ПО ССЫЛКАМ =====
433    const links = marque.querySelectorAll('.brands-group');
434    links.forEach(link => {
435        link.addEventListener('click', function(e) {
436            // Если было перетаскивание - отменяем клик
437            if (hasDragged) {
438                e.preventDefault();
439                e.stopPropagation();
440                return false;
441            }
442            // Иначе разрешаем переход по ссылке
443            return true;
444        });
445    });
446
447    // ===== ОБРАБОТКА РЕСАЙЗА =====
448    let resizeTimeout;
449    window.addEventListener('resize', function() {
450        clearTimeout(resizeTimeout);
451        resizeTimeout = setTimeout(function() {
452            let newPos = normalize(position);
453            setPosition(newPos);
454        }, 200);
455    });
456
457    // ===== ЗАПУСК =====
458    startAnimation();
459
460    console.log('✅ Бесконечная прокрутка с инерцией запущена');
461});
462/* End */
463;
464; /* Start:"a:4:{s:4:"full";s:105:"/local/templates/mirkrasok_2026v2/components/mk-v2/catalog.element.sku2/.default/script.js?17884338734317";s:6:"source";s:90:"/local/templates/mirkrasok_2026v2/components/mk-v2/catalog.element.sku2/.default/script.js";s:3:"min";s:0:"";s:3:"map";s:0:"";}"*/
465(function() {
466    'use strict';
467
468    if (window.__mkCatalogQuickViewInitialized) return;
469    window.__mkCatalogQuickViewInitialized = true;
470
471    var modal = null;
472    var frame = null;
473
474    function ensureModal() {
475        if (modal) return modal;
476        modal = document.createElement('div');
477        modal.className = 'mk-quick-view-modal';
478        modal.hidden = true;
479        modal.innerHTML =
480            '<div class="mk-quick-view-modal__backdrop" data-mk-quick-view-close></div>' +
481            '<div class="mk-quick-view-modal__dialog" role="dialog" aria-modal="true" aria-label="Быстрый просмотр товара">' +
482                '<button type="button" class="mk-quick-view-modal__close" data-mk-quick-view-close aria-label="Закрыть">&times;</button>' +
483                '<iframe class="mk-quick-view-modal__frame" title="Быстрый просмотр товара"></iframe>' +
484            '</div>';
485        document.body.appendChild(modal);
486        frame = modal.querySelector('.mk-quick-view-modal__frame');
487        return modal;
488    }
489
490    // Открытие и закрытие попапа умеют утаскивать каталог наверх, к первому
491    // товару. Причин две: focus() на кнопке внутри position:fixed заставляет
492    // браузер «доскроллить» до элемента, а от overflow: hidden на html телефон
493    // обнуляет прокрутку — и вернуть её тем же scrollTo уже нельзя, страница в
494    // этот момент не прокручивается. Поэтому фокусируем без прокрутки, а
495    // страницу «замораживаем»: запоминаем прокрутку, сдвигаем body вверх на ту
496    // же величину (сам body делает fixed стиль по классу) и при закрытии
497    // возвращаем прокрутку на место.
498    var lockedScrollY = 0;
499
500    function currentScrollY() {
501        return window.pageYOffset || document.documentElement.scrollTop || 0;
502    }
503
504    function lockScroll() {
505        lockedScrollY = currentScrollY();
506        document.body.style.top = (-lockedScrollY) + 'px';
507        document.documentElement.classList.add('mk-quick-view-open');
508        document.body.classList.add('mk-quick-view-open');
509    }
510
511    function unlockScroll() {
512        document.documentElement.classList.remove('mk-quick-view-open');
513        document.body.classList.remove('mk-quick-view-open');
514        document.body.style.top = '';
515        // У страницы включена плавная прокрутка — возврат на место она бы
516        // анимировала, поэтому на один вызов её выключаем.
517        var html = document.documentElement;
518        var smooth = html.style.scrollBehavior;
519        html.style.scrollBehavior = 'auto';
520        window.scrollTo(0, lockedScrollY);
521        html.style.scrollBehavior = smooth;
522    }
523
524    function focusWithoutScroll(el) {
525        if (!el) return;
526        try {
527            el.focus({preventScroll: true});
528        } catch (e) {
529            el.focus();
530        }
531    }
532
533    function openModal(url) {
534        ensureModal();
535        frame.src = url;
536        modal.hidden = false;
537        lockScroll();
538        focusWithoutScroll(modal.querySelector('.mk-quick-view-modal__close'));
539    }
540
541    function closeModal() {
542        if (!modal) return;
543        modal.hidden = true;
544        frame.src = 'about:blank';
545        unlockScroll();
546    }
547
548    document.addEventListener('click', function(event) {
549        var trigger = event.target.closest && event.target.closest('[data-mk-quick-view]');
550        if (trigger) {
551            event.preventDefault();
552            event.stopPropagation();
553            openModal(trigger.getAttribute('data-mk-quick-view'));
554            return;
555        }
556        if (event.target.closest && event.target.closest('[data-mk-quick-view-close]')) {
557            event.preventDefault();
558            closeModal();
559        }
560    });
561
562    document.addEventListener('keydown', function(event) {
563        if (event.key === 'Escape' && modal && !modal.hidden) closeModal();
564    });
565})();
566
567/* End */
568;
569; /* Start:"a:4:{s:4:"full";s:39:"/local/js/tint-cards.js?178872632028285";s:6:"source";s:23:"/local/js/tint-cards.js";s:3:"min";s:0:"";s:3:"map";s:0:"";}"*/
570/**
571 * Выбор цвета прямо на плитке товара в списках каталога.
572 *
573 * Разметку кружка ставит MkTintSwatch (local/php_interface/classes), а здесь —
574 * то же поведение, что у виджета поиска sside на /search/: клик открывает окно
575 * колеровки (робот-колеровщик или палитру Dominanta), выбранный цвет
576 * закрашивает кружок, рядом с ценой появляется надбавка за колеровку, а товар
577 * уезжает в корзину уже с цветом.
578 *
579 * Робот-колеровщик (js/s.js, ~280 КБ) на страницах списков не подключён —
580 * шапка кладёт его только в карточку товара, поиск и корзину. Поэтому грузим
581 * его сами и только по первому клику: до клика колеровщик не нужен.
582 */
583(function ($) {
584    'use strict';
585
586    if (!$) {
587        return;
588    }
589
590    var TINT_ATTR = 'data-tint-card';
591    var PRICES_URL = '/local/ajax/sside_robotint_prices.php';
592    var DOMINANTA_STYLE = '/local/components/sside/tinting/templates/.default/style.css';
593    var DOMINANTA_SCRIPT = '/local/components/sside/tinting_ajax/templates/.default/script.js';
594
595    /** Выбранный цвет по каждой плитке: {id ску: {colorId, hex, name, pricePerLiter}} */
596    var selected = {};
597    /** Плитка, для которой сейчас открыто окно колеровки. */
598    var activeId = '';
599    var robotintState = 'idle';
600    var robotintQueue = [];
601
602    function settings() {
603        return window.MK_TINT_CARDS || {};
604    }
605
606    function cardData(trigger) {
607        try {
608            return JSON.parse(trigger.getAttribute(TINT_ATTR) || '{}');
609        } catch (error) {
610            console.error('[Колеровка] Не разобрать данные плитки', error);
611            return {};
612        }
613    }
614
615    function cardNode(id) {
616        return $('[data-product-id="' + id + '"]').filter(function () {
617            return $(this).find('[' + TINT_ATTR + ']').length > 0;
618        }).first();
619    }
620
621    // --- Робот-колеровщик ----------------------------------------------------
622
623    /**
624     * Догружает s.js, правила цен колеровки и настройки клиента — ровно то же,
625     * что шапка и подвал делают на карточке товара.
626     */
627    function withRobotint(callback) {
628        if (robotintState === 'ready') {
629            callback(true);
630            return;
631        }
632        robotintQueue.push(callback);
633        if (robotintState === 'loading') {
634            return;
635        }
636        robotintState = 'loading';
637
638        var done = function (ok) {
639            robotintState = ok ? 'ready' : 'idle';
640            var queue = robotintQueue.splice(0);
641            for (var i = 0; i < queue.length; i++) {
642                queue[i](ok);
643            }
644        };
645
646        var ready = function () {
647            if (!window.obColorer || typeof window.obColorer.show !== 'function') {
648                console.error('[Колеровка] Робот-колеровщик не загрузился');
649                done(false);
650                return;
651            }
652            if (!window.selectedColor) {
653                window.selectedColor = [];
654            }
655            if (!window.selectedColorPRICE) {
656                window.selectedColorPRICE = [];
657            }
658            loadPrices(function () {
659                setOptions();
660                bindRobotintSubmit();
661                // Свои кнопки колеровщик проверяет при готовности страницы,
662                // когда настроек клиента ещё нет. Повторяем проверку теперь.
663                if (typeof window.obColorer.enableButtons === 'function') {
664                    window.obColorer.enableButtons();
665                }
666                done(true);
667            });
668        };
669
670        if (window.obColorer && typeof window.obColorer.show === 'function') {
671            ready();
672            return;
673        }
674
675        var url = settings().robotintUrl;
676        if (!url) {
677            console.error('[Колеровка] Не указан путь к роботу-колеровщику');
678            done(false);
679            return;
680        }
681        loadRobotintScript(url, function (ok) {
682            if (!ok) {
683                console.error('[Колеровка] Не удалось загрузить ' + url);
684                done(false);
685                return;
686            }
687            ready();
688        });
689    }
690
691    /**
692     * Колеровщик рассчитан на подключение в <head>: свой $(function(){...}) он
693     * запускает прямо во время сборки объекта obColorer. На уже загруженной
694     * странице jQuery 1.11 выполняет такой обработчик сразу же — объекта ещё
695     * нет, и файл падает на середине (obColorer остаётся пустым). Поэтому на
696     * время загрузки откладываем обработчики готовности на следующий такт: к
697     * нему объект уже собран.
698     */
699    function loadRobotintScript(url, callback) {
700        var originalReady = $.fn.ready;
701        $.fn.ready = function (handler) {
702            if (typeof handler === 'function') {
703                window.setTimeout(function () {
704                    handler($);
705                }, 0);
706            }
707            return this;
708        };
709
710        loadScript(url, function (ok) {
711            $.fn.ready = originalReady;
712            // Отложенный обработчик колеровщика ещё в очереди — пропускаем его
713            // вперёд, иначе настройки лягут на недостроенный объект.
714            window.setTimeout(function () {
715                callback(ok);
716            }, 0);
717        });
718    }
719
720    /**
721     * Обычный тег <script>, а не $.ajax({dataType:"script"}): колеровщик
722     * объявляет свои глобальные переменные через var, и при выполнении через
723     * jQuery.globalEval часть из них теряется, а сам запрос считается неудачным.
724     */
725    function loadScript(url, callback) {
726        var script = document.createElement('script');
727        script.src = url;
728        script.async = true;
729        script.onload = function () {
730            callback(true);
731        };
732        script.onerror = function () {
733            callback(false);
734        };
735        document.head.appendChild(script);
736    }
737
738    /** Ценовые уровни колеровки: без них obRobotint.processPrice не считает. */
739    function loadPrices(callback) {
740        if (window.obRobotint && $.isArray(window.obRobotint.prices) && window.obRobotint.prices.length) {
741            callback();
742            return;
743        }
744        $.getJSON(PRICES_URL)
745            .done(function (response) {
746                if (!window.obRobotint) {
747                    window.obRobotint = {};
748                }
749                if (response && $.isArray(response.prices)) {
750                    window.obRobotint.prices = response.prices;
751                }
752            })
753            .always(callback);
754    }
755
756    /**
757     * Находит серверное правило тарифа по уже рассчитанной Robotint цене.
758     * Оно нужно корзине как COLOR_SKU_ID. Не запрашиваем Robotint повторно:
759     * к моменту выбора оттенка весь прайс уже загружен в obRobotint.prices.
760     */
761    function findPriceRule(systemId, rawPrice) {
762        var prices = window.obRobotint && window.obRobotint.prices;
763        systemId = parseInt(systemId, 10);
764        rawPrice = parseFloat(rawPrice);
765        if (!$.isArray(prices) || !isFinite(systemId) || !isFinite(rawPrice)) {
766            return null;
767        }
768
769        for (var i = 0; i < prices.length; i++) {
770            var row = prices[i] || {};
771            var from = parseFloat(row.FROM);
772            var to = parseFloat(row.TO);
773            if (parseInt(row.TINT_SYSTEM_ID, 10) === systemId
774                && (!isFinite(from) || rawPrice >= from)
775                && (!isFinite(to) || rawPrice <= to)) {
776                return row;
777            }
778        }
779        return null;
780    }
781
782    function setOptions() {
783        if (typeof window.obColorer.setOptions !== 'function') {
784            return;
785        }
786        var site = {bgZindex: 10000};
787        if (window.obRobotint) {
788            site.colorSelect = window.obRobotint.select;
789            site.processPrice = window.obRobotint.processPrice;
790        }
791        window.obColorer.setOptions({
792            tintSettings: {
793                CLIENT_ID: 4,
794                SECRET: 'mka-s12431_ASd',
795                DOMAIN: 'https://core.robotint.ru',
796                WITH_PRICE: 1,
797                USE_SWITCHER: 0
798            },
799            siteSettings: site
800        });
801    }
802
803    /**
804     * Колеровщик не сообщает наружу о выборе цвета, поэтому подслушиваем его
805     * submit — так же поступает виджет поиска.
806     */
807    function bindRobotintSubmit() {
808        if (!window.obColorer || window.obColorer.__mkTintBound || typeof window.obColorer.submit !== 'function') {
809            return;
810        }
811        var original = window.obColorer.submit;
812        window.obColorer.submit = function () {
813            var result = original.apply(this, arguments);
814            if (result !== false) {
815                window.setTimeout(captureRobotint, 0);
816            }
817            return result;
818        };
819        window.obColorer.__mkTintBound = true;
820    }
821
822    function openRobotint(trigger, data) {
823        var tint = {
824            PRODUCT_ID: data.id,
825            PRODUCT_PRICE: data.price,
826            VOLUME: data.volume,
827            BASE_TYPE_INT: data.baseTypeInt,
828            FROM: 'Item',
829            BARCODE: data.barcode
830        };
831        trigger.setAttribute('data-tint-data', JSON.stringify(tint));
832        trigger.setAttribute('data-tint-bt', String(data.baseTypeInt || 0
832));
833
834        withRobotint(function (ok) {
835            if (!ok || activeId !== String(data.id)) {
836                return;
837            }
838            trigger.removeAttribute('disabled');
839            window.obColorer.show(trigger);
840        });
841    }
842
843    function captureRobotint() {
844        if (!activeId || !window.obColorer || typeof window.obColorer.getSelectedData !== 'function') {
845            return;
846        }
847        var data = window.obColorer.getSelectedData() || {};
848        if (!data.COLOR_ID) {
849            return;
850        }
851
852        // Цену за литр колеровщик держит на кнопке своей корзины: в data она
853        // приезжает только со второго клика.
854        var root = (window.obColorer.parent && window.obColorer.parent[0]) || document;
855        var priceNode = root.querySelector('.tint-color-price[data-price-shop], [data-color-price][data-price-shop]')
856            || document.querySelector('.tint-color-price[data-price-shop], [data-color-price][data-price-shop]');
857        var shopPrice = priceNode ? parseFloat(priceNode.getAttribute('data-price-shop')) : NaN;
858        var refVolume = priceNode ? parseFloat(priceNode.getAttribute('data-price-ref_can_volume')) : NaN;
859        var rawPrice = priceNode ? parseFloat(priceNode.getAttribute('data-price-per-litr')) : NaN;
860        var perLiter = isFinite(shopPrice) && shopPrice > 0
861            ? shopPrice / (isFinite(refVolume) && refVolume > 0 ? refVolume : 1)
862            : parseFloat(data.PRICE_PER_LITER);
863        var selectedNode = root.querySelector('[data-id="' + String(data.COLOR_ID) + '"][data-collection-vendor-id]');
864        var systemId = data.COLLECTION_VENDOR_ID
865            || (selectedNode && selectedNode.getAttribute('data-collection-vendor-id'));
866        var collectionId = data.COLLECTION_ID
867            || (selectedNode && selectedNode.getAttribute('data-collection-id'));
868        var priceRule = findPriceRule(systemId, isFinite(rawPrice) ? rawPrice : perLiter);
869
870        applyColor(activeId, {
871            colorId: String(data.COLOR_ID),
872            hex: data.HEX || '#ffffff',
873            name: data.COLOR_NAME || '',
874            collectionName: data.COLLECTION_NAME || '',
875            collectionId: collectionId || '',
876            collectionVendorId: systemId || '',
877            pricePerLiter: perLiter,
878            rawPricePerLiter: isFinite(rawPrice) ? rawPrice : perLiter,
879            colorSkuId: priceRule ? String(priceRule.ID || priceRule.SKU_ID || '') : '',
880            dominanta: false,
881            // В корзину товар кладёт кнопка плитки, а не колеровщик: свой
882            // обработчик colorSelect (obRobotint.select) он зовёт только на
883            // странице корзины — см. submit() в js/s.js.
884            inBasket: false
885        });
886
887        if (typeof window.obColorer.close === 'function') {
888            window.obColorer.close();
889        }
890        activeId = '';
891    }
892
893    // --- Палитра Dominanta ---------------------------------------------------
894
895    /**
896     * Разметку палитры отдаёт /local/ajax/tinting.php, но выбор оттенка в ней
897     * держат choose_color() и del_color() из компонента sside:tinting_ajax. На
898     * страницах списков компонента нет — подкладываем его скрипт сами.
899     */
900    function withDominanta(callback) {
901        if (typeof window.choose_color === 'function') {
902            callback(true);
903            return;
904        }
905        loadScript(DOMINANTA_SCRIPT, function (ok) {
906            if (!ok) {
907                console.error('[Колеровка] Не удалось загрузить ' + DOMINANTA_SCRIPT);
908            }
909            callback(ok);
910        });
911    }
912
913    function openDominanta(trigger, data) {
914        var payload = {
915            PRODUCT_ID: data.id,
916            BASE_TYPE_INT: data.baseTypeInt,
917            COLLECTION_ID: 10000,
918            COLLECTION_NAME: 'Dominanta',
919            COLLECTION_VENDOR_ID: 5,
920            COLOR_ID: 127802,
921            COLOR_SKU_ID: '163486',
922            COLOR_NAME: 'DM_С0008',
923            COLOR_PRICE: '100',
924            BASKET_ID: 0,
925            VOLUME: data.volume,
926            BASE: String(data.base),
927            BARCODE: data.barcode
928        };
929
930        // Форма ищет данные товара по классу productcard__coler — он должен
931        // остаться ровно на одном кружке, иначе она возьмёт чужой цвет.
932        $('.sside-tint-trigger.productcard__coler').removeClass('productcard__coler');
933        $(trigger).addClass('productcard__coler').attr('data-dom-data', JSON.stringify(payload));
934
935        if (!document.getElementById('color-form')) {
936            $('<div id="color-form"></div>').appendTo(document.body);
937        }
938        if (!document.querySelector('link[data-mk-tint-style]')) {
939            $('<link rel="stylesheet" data-mk-tint-style="1">').attr('href', DOMINANTA_STYLE).appendTo('head');
940        }
941
942        var $form = $('#color-form');
943        $form.attr('data-dom-data', JSON.stringify(payload)).data('trigger', $(trigger));
944
945        withDominanta(function (ok) {
946            if (!ok || activeId !== String(data.id)) {
947                return;
948            }
949            $.ajax({
950                type: 'POST',
951                url: '/local/ajax/tinting.php',
952                data: {json: JSON.stringify(payload)},
953                success: function (html) {
954                    $form.html(html);
955                    $('.bg-color').show();
956                    $('.color-form').show();
957                    if (typeof window.del_color === 'function') {
958                        window.del_color();
959                    }
960                },
961                error: function () {
962                    console.error('[Колеровка] Палитра Dominanta не открылась');
963                }
964            });
965        });
966    }
967
968    function captureDominanta() {
969        if (!activeId) {
970            return;
971        }
972        var $active = $('.color-form .colors .color.active').first();
973        var stored = (window.selectedColor && window.MF_COLOR !== undefined)
974            ? window.selectedColor[window.MF_COLOR]
975            : null;
976        if (!$active.length && !stored) {
977            return;
978        }
979        stored = stored || {};
980
981        // Сначала берём то, что выбрано в самой палитре: если на странице есть
982        // ещё и карточка товара со своим выбором цвета (например, в блоке
983        // «с этим товаром покупают»), в selectedColor лежат её данные.
984        applyColor(activeId, {
985            colorId: String(stored.COLOR_ID || 127802),
986            hex: $active.attr('data-hex') || stored.HEX || '#ffffff',
987            name: $.trim($active.text()) || stored.COLOR_NAME || '',
988            collectionName: stored.COLLECTION_NAME || 'Dominanta',
989            texture: $active.attr('data-back') || '',
990            pricePerLiter: parseFloat($active.attr('data-price') || stored.PRICE_PER_LITER),
991            dominanta: true,
992            inBasket: false
993        });
994
995        $('.bg-color').hide();
996        $('.color-form').hide();
997        activeId = '';
998    }
999
1000    // --- Плитка --------------------------------------------------------------
1001
1002    /** Закрашивает кружок и дописывает надбавку за колеровку рядом с ценой. */
1003    function applyColor(id, color) {
1004        selected[id] = color;
1005
1006        // Наружу сообщаем сразу: аналитике (local/js/mk-analytics.js) важен
1007        // сам факт выбора цвета, даже если плитки на странице уже нет.
1008        try {
1009            document.dispatchEvent(new CustomEvent("mk:tint-color", {
1010                detail: {id: String(id), color: color}
1011            }));
1012        } catch (error) {}
1013
1014        var $card = cardNode(id);
1015        if (!$card.length) {
1016            return;
1017        }
1018
1019        var $trigger = $card.find('[' + TINT_ATTR + ']').first();
1020        $trigger.css({
1021            backgroundColor: color.hex,
1022            backgroundImage: color.texture ? 'url("' + color.texture + '")' : 'none',
1023            backgroundSize: 'cover'
1024        }).attr('title', $.trim(color.name || '') || 'Выбранный цвет');
1025
1026        var data = cardData($trigger[0]);
1027        var volume = parseFloat(data.volume) || 1;
1028        var perLiter = parseFloat(color.pricePerLiter);
1029        var $price = $card.find('.price-current, .price-value').not('.mk-tint-price').first();
1030        var $extra = $card.find('.mk-tint-price');
1031
1032        if (!isFinite(perLiter) || perLiter <= 0 || !$price.length) {
1033            $extra.remove();
1034            return;
1035        }
1036        if (!$extra.length) {
1037            // Классы те же, что у виджета поиска, — оформление берётся из
1038            // local/css/tint-cards.css и совпадает с карточкой в поиске.
1039            $extra = $('<div class="price-current tinted mk-tint-price"></div>')
1040                .attr('title', 'Стоимость колеровки')
1041                // Последним в строке цены — после старой цены и скидки: иначе
1042                // надбавка вклинивается между текущей и старой ценой.
1043                .appendTo($price.parent());
1044            $price.parent().addClass('mk-tint-price-row');
1045        }
1046        $extra.text('+' + new Intl.NumberFormat('ru-RU').format(Math.round(perLiter * volume)) + ' ₽');
1047    }
1048
1049    /**
1050     * Корзина. Цвет приезжает в неё отдельным запросом уже после того, как
1051     * товар добавлен: addcolor.php ищет свежую строку корзин
1051ы по товару.
1052     */
1053    function attachColorToBasket(id) {
1054        var color = selected[id];
1055        if (!color || color.inBasket) {
1056            return;
1057        }
1058        var $trigger = cardNode(id).find('[' + TINT_ATTR + ']').first();
1059        if (!$trigger.length) {
1060            return;
1061        }
1062        var data = cardData($trigger[0]);
1063        var attempts = 0;
1064
1065        var send = function () {
1066            attempts++;
1067            var volume = Math.ceil(parseFloat(data.volume) || 1);
1068            var perLiter = parseFloat(color.pricePerLiter);
1069            $.ajax({
1070                url: '/local/ajax/addcolor.php',
1071                method: 'POST',
1072                data: {
1073                    PRODUCT_ID: id,
1074                    COLOR_ID: color.colorId,
1075                    BARCODE: data.barcode,
1076                    HEX: color.hex || '',
1077                    COLOR_NAME: color.name || '',
1078                    COLLECTION_ID: color.collectionId || '',
1079                    COLLECTION_VENDOR_ID: color.collectionVendorId || '',
1080                    COLLECTION_NAME: color.collectionName || '',
1081                    BASE_TYPE_INT: data.baseTypeInt,
1082                    VOLUME: volume,
1083                    COLOR_PRICE: isFinite(perLiter) ? perLiter * volume : '',
1084                    PRICE_PER_CAN: isFinite(perLiter) ? perLiter * volume : '',
1085                    PRICE_PER_LITER: isFinite(perLiter) ? perLiter : '',
1086                    RAW_PRICE_PER_LITER: color.rawPricePerLiter || '',
1087                    COLOR_SKU_ID: color.colorSkuId || ''
1088                },
1089                success: function (response) {
1090                    var payload = response;
1091                    if (typeof payload === 'string') {
1092                        try {
1093                            payload = JSON.parse(payload);
1094                        } catch (error) {
1095                            console.error('[Колеровка] Непонятный ответ addcolor.php', error);
1096                            return;
1097                        }
1098                    }
1099                    if (!payload || !payload.BASKET_ID) {
1100                        // Строка корзины ещё не создана — подождём и повторим.
1101                        if (attempts < 5) {
1102                            window.setTimeout(send, 500);
1103                        }
1104                        return;
1105                    }
1106
1107                    payload.PRODUCT_ID = id;
1108                    payload.COLOR_ID = color.colorId;
1109                    payload.COLOR_NAME = color.name || '';
1110                    payload.HEX = color.hex;
1111                    payload.VOLUME = volume;
1112                    if (color.dominanta) {
1113                        payload.COLLECTION_ID = 10000;
1114                        payload.COLLECTION_VENDOR_ID = 5;
1115                        payload.COLOR_SKU_ID = '163486';
1116                        payload.COLLECTION_NAME = /^dominanta\b/i.test(color.collectionName || '')
1117                            ? color.collectionName
1118                            : $.trim('Dominanta ' + (color.collectionName || ''));
1119                    }
1120                    if (isFinite(perLiter) && perLiter >= 0) {
1121                        payload.COLOR_PRICE = perLiter * volume;
1122                        payload.PRICE_PER_CAN = perLiter * volume;
1123                        payload.PRICE_PER_LITER = perLiter;
1124                    }
1125                    // Услуга колеровки и коллекция, известные заранее. По
1126                    // штрихкоду конкретного товара робоколер отвечает не всегда,
1127                    // и тогда addcolor.php возвращает пустой COLOR_SKU_ID —
1128                    // корзина молча кладёт товар без цвета. Виджет поиска
1129                    // страхуется так же: услуга у цвета одна на все товары.
1130                    if (!payload.COLOR_SKU_ID && color.colorSkuId) {
1131                        payload.COLOR_SKU_ID = color.colorSkuId;
1132                    }
1133                    if (!payload.COLLECTION_NAME && color.collectionName) {
1134                        payload.COLLECTION_NAME = color.collectionName;
1135                    }
1136                    payload.action = 'colorate-change';
1137                    payload.BIG_BASKET = 1;
1138                    payload.COLORATE_USE = 'Y';
1139                    payload.basket_new = 'Y';
1140
1141                    $.ajax({
1142                        url: '/local/components/art-fresh/sale.basket/ajax.php',
1143                        method: 'POST',
1144                        data: payload,
1145                        success: function (html) {
1146                            if (window.obBasket && typeof window.obBasket.changeBasket === 'function') {
1147                                window.obBasket.changeBasket(html);
1148                            }
1149                        }
1150                    });
1151                },
1152                error: function () {
1153                    if (attempts < 5) {
1154                        window.setTimeout(send, 500);
1155                    }
1156                }
1157            });
1158        };
1159
1160        window.setTimeout(send, 750);
1161    }
1162
1163    function open(trigger) {
1164        var data = cardData(trigger);
1165        if (!data.id) {
1166            return;
1167        }
1168        activeId = String(data.id);
1169        if (data.dominanta) {
1170            openDominanta(trigger, data);
1171        } else {
1172            openRobotint(trigger, data);
1173        }
1174    }
1175
1176    $(document).on('click', '[' + TINT_ATTR + ']', function (event) {
1177        event.preventDefault();
1178        event.stopPropagation();
1179        open(this);
1180    });
1181
1182    $(document).on('keydown', '[' + TINT_ATTR + ']', function (event) {
1183        if (event.key === 'Enter' || event.key === ' ' || event.keyCode === 13 || event.keyCode === 32) {
1184            event.preventDefault();
1185            open(this);
1186        }
1187    });
1188
1189    // Кнопка «выбрать оттенок» в палитре Dominanta своего события не шлёт.
1190    // Слушаем на перехвате: собственный обработчик формы написан под карточку
1191    // товара, и если он споткнётся, до всплытия дело может не дойти.
1192    document.addEventListener('click', function (event) {
1193        var target = event.target;
1194        if (target && target.closest && target.closest('.color-form .submit.active')) {
1195            window.setTimeout(captureDominanta, 0);
1196        }
1197    }, true);
1198
1199    // Обычная кнопка «В корзину» плитки: цвет дописываем следом за товаром.
1200    $(document).on('click', '.add-to-cart, [data-add-to-basket]', function () {
1201        var $card = $(this).closest('[data-product-id]');
1202        var id = $card.attr('data-product-id');
1203        if (id && selected[id]) {
1204            attachColorToBasket(id);
1205        }
1206    });
1207    // Страница цвета знает нужный оттенок заранее и проставляет его на все
1208    // плитки списка — см. products.js компонента mirkrasok:color.collection2.
1209    window.MkTintCards = {applyColor: applyColor};
1210})(window.jQuery);
1211
1212/* End */
1213;
1214; /* Start:"a:4:{s:4:"full";s:108:"/local/templates/mirkrasok_2026v2/components/bitrix/system.pagenavigation/show_more/script.js?17859373281779";s:6:"source";s:93:"/local/templates/mirkrasok_2026v2/components/bitrix/system.pagenavigation/show_more/script.js";s:3:"min";s:0:"";s:3:"map";s:0:"";}"*/
1215$(() => {
1216    $(document).on('click', '[data-js="load_more"]', function () {
1217        let _this = $(this),
1218            mainBlock = $('[data-block-pagination]'),
1219            container = mainBlock.data('block-pagination'),
1220            targetContainer = $(`[data-block-pagination=${container}]`),
1221            url = _this.data('url'),
1222            divide =  mainBlock.data('divide') === "Y",
1223            useMas = mainBlock.data('masonry') === "Y";
1224
1225
1226        if (url !== undefined) {
1227            $.ajax({
1228                type: 'POST',
1229                url: url,
1230                dataType: 'html',
1231                success: function (data) {
1232                    _this.remove();
1233
1234                    let elements = $(data).find('[data-block="element"]'),
1235                        pagination = $(data).find('[data-js="load_more"]');
1236
1237
1238                    if (divide) {
1239                        $.each(elements, function(index, value) {
1240                            if (index % 2 === 0) {
1241                                targetContainer.find('[data-col="1"]').append(value);
1242                            } else {
1243                                targetContainer.find('[data-col="2"]').append(value);
1244                            }
1245                        });
1246                    } else {
1247                        targetContainer.append(elements);
1248                    }
1249
1250                    if (typeof $.fn.masonry === 'function') {
1251                        $('.rev__items').masonry('reloadItems')
1252                        $('.rev__items').masonry('layout')
1253                    }
1254
1255                    $('[data-block="pagination"]').append(pagination);
1256                },
1257                error: function (err) {
1258                    console.log(err);
1259                }
1260            });
1261        }
1262    })
1263
1264})
1265/* End */
1266;
1267; /* Start:"a:4:{s:4:"full";
1267s:106:"/local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/articles-index/script.js?1679485855384";s:6:"source";s:92:"/local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/articles-index/script.js";s:3:"min";s:0:"";s:3:"map";s:0:"";}"*/
1268$(document).ready(function(){
1269	
1270	if($(window).width()<=980){	
1271		$('.js-mobile-slider').slick({
1272		  dots: false,
1273		  infinite: true,
1274		  speed: 300,
1275		  autoplay: false,
1276		  slidesToShow: 1,
1277		  slidesToScroll: 1,
1278		  nextArrow: '<div class="slick-next"><div class="up-line"></div></div>',
1279          prevArrow: '<div class="slick-prev"><div class="up-line"></div></div>',
1280		});
1281	}
1282});	
1283/* End */
1284;
1285; /* Start:"a:4:{s:4:"full";s:102:"/local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/news-index/script.js?1637067475384";s:6:"source";s:88:"/local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/news-index/script.js";s:3:"min";s:0:"";s:3:"map";s:0:"";}"*/
1286$(document).ready(function(){
1287	
1288	if($(window).width()<=980){	
1289		$('.js-mobile-slider').slick({
1290		  dots: false,
1291		  infinite: true,
1292		  speed: 300,
1293		  autoplay: false,
1294		  slidesToShow: 1,
1295		  slidesToScroll: 1,
1296		  nextArrow: '<div class="slick-next"><div class="up-line"></div></div>',
1297          prevArrow: '<div class="slick-prev"><div class="up-line"></div></div>',
1298		});
1299	}
1300});	
1301/* End */
1302;; /* /local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/mainbanner_index_new/script.js?17869993714147*/
1303; /* /local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/brands-index/script.
1303js?17829140649879*/
1304; /* /local/templates/mirkrasok_2026v2/components/mk-v2/catalog.element.sku2/.default/script.js?17884338734317*/
1305; /* /local/js/tint-cards.js?178872632028285*/
1306; /* /local/templates/mirkrasok_2026v2/components/bitrix/system.pagenavigation/show_more/script.js?17859373281779*/
1307; /* /local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/articles-index/script.js?1679485855384*/
1308; /* /local/templates/mirkrasok_2026v2/components/art-fresh-v2/flat.list/news-index/script.js?1637067475384*/

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.