PageSourceSearch

https://gdekomfort.ru/templates/komfort/resource/js/rscomponent/theme.js

js gdekomfort.ru collected 2026-10-05 02:26:01 UTC 17,580 bytes, 473 lines download raw bytes

1/**
2 * Инициализирует работу базовых возможностей темы оформления
3 */
4new class Theme extends RsJsCore.classes.component {
5    /**
6     * Делает кликабельными ссылки, у которых URL записан в data-href атрибуте.
7     */
8    bindDataHrefLinks() {
9        this.utils.on('click', '[data-redirect]:not(.rs-no-redirect)', (event) => {
10            if (!event.target.classList.contains('rs-no-redirect')) {
11                event.rsTarget.classList.add('rs-in-loading');
12                location.href = event.rsTarget.dataset.redirect;
13            }
14        });
15    }
16
17    /**
18     * Активирует корректную работу переключателя Юридическое/Физическое лицо в форме регистрации
19     */
20    bindRegisterFields() {
21        this.utils.on('change', 'input[name="is_company"]', (event) => {
22            let context = event.rsTarget.closest('form');
23            let companyFields = context && context.querySelector('.user-profile__company');
24            if (companyFields) {
25                if (event.rsTarget.value == 1) {
26                    companyFields.classList.add('show');
27                } else {
28                    companyFields.classList.remove('show');
29                }
30            }
31        });
32
33        this.utils.on('change', 'input[name="changepass"]', (event) => {
34            let context = event.rsTarget.closest('form');
35            let passwordFields = context && context.querySelector('.user-profile__password');
36            if (passwordFields) {
37                if (event.rsTarget.checked) {
38                    passwordFields.classList.add('show');
39                } else {
40                    passwordFields.classList.remove('show');
41                }
42            }
43        });
44    }
45
46    /**
47     * Инициализирует числовые инпуты с +/- кнопками рядом
48     */
49    initNumberInputs(event) {
50        event.target.querySelectorAll('.rs-number-input').forEach(element => {
51            if (!element.rsInitialized) {
52                let input = element.querySelector('input[type="number"]');
53                let buttonUp = element.querySelector('.rs-number-up');
54                let buttonDown = element.querySelector('.rs-number-down');
55
56                buttonUp.disabled = input.disabled;
57                buttonDown.disabled = input.disabled;
58
59                buttonUp.addEventListener('click', () => {
60                    let oldValue = parseFloat(input.value);
61                    let step = parseFloat(input.step);
62                    let min = parseFloat(input.min);
63                    let max = parseFloat(input.max);
64                    let breakPoint = parseFloat(input.dataset.breakPoint);
65
66                    let newValue = Math.round((oldValue + step) * 1000) / 1000;
67                    if (newValue < min) {
68                        newValue = min;
69                    }
70                    if (oldValue < breakPoint && newValue > breakPoint) {
71                        newValue = breakPoint;
72                    }
73                    if (max !== null && newValue > max) {
74                        newValue = max;
75                        element.dispatchEvent(new CustomEvent('max-limit', { bubbles: true }));
76                    } else {
77                        element.dispatchEvent(new CustomEvent('increase-amount', { bubbles: true }));
78                    }
79                    input.value = newValue;
80                    input.dispatchEvent(new Event('change', { bubbles: true }));
81                });
82                buttonDown.addEventListener('click', () => {
83                    let oldValue = parseFloat(input.value);
84                    let step = parseFloat(input.step);
85                    let min = parseFloat(input.min);
86                    let breakPoint = parseFloat(input.dataset.breakPoint);
87
88                    let newValue = Math.round((oldValue - step) * 1000) / 1000;
89                    if (newValue < min) {
90                        newValue = 0;
91                    }
92                    if (oldValue > breakPoint && newValue < breakPoint) {
93                        newValue = breakPoint;
94                    }
95                    if (newValue != 0) {
96                        element.dispatchEvent(new CustomEvent('decrease-amount', { bubbles: true }));
97                        input.value = newValue;
98                        input.dispatchEvent(new Event('change', { bubbles: true }));
99                    }
100                });
101
102                element.rsInitialized = true;
103            }
104
105        });
106    }
107
108    /**
109     * Активирует кнопки "Вернуться назад" на разных страницах
110     */
111    initBackButton() {
112        this.utils.on('click', '.rs-back-button', (event) => {
113            if (window.history.length) {
114                window.history.back();
115            } else {
116                location.href = event.rsTarget.dataset.rootUrl;
117            }
118        });
119    }
120
121    initTabs() {
122        this.utils.on('click', '[data-tab-header-item]:not(.active)', (event) => {
123            const target = event.target.closest('[data-tab-header-item]');
124            const siblings = target.parentElement.children;
125
126            for (let i = 0; i < siblings.length; i++) {
127                siblings[i].classList.remove('active');
128            }
129
130            target.classList.add('active');
131
132            const tabsContainer = target.closest('[data-tabs]');
133
134            if (tabsContainer) {
135                const tabContents = tabsContainer.querySelectorAll('[data-tab-item]');
136                const index = Array.prototype.indexOf.call(target.parentElement.children, target);
137
138                tabContents.forEach(content => content.classList.remove('active'));
139
140                if (index !== -1) {
141                    tabContents[index].classList.add('active');
142                }
143            }
144        });
145    }
146
147    initSpoiler() {
148        this.utils.on('click', '[data-spoiler-header]', (event) => {
149            const spoilerHeader = event.target.closest('[data-spoiler-header]');
150            const spoiler = spoilerHeader.closest('[data-spoiler]').classList.toggle('active');
151        });
152
153        document.querySelectorAll('.faq-question').forEach(button => {
154            button.addEventListener('click', () => {
155                const answer = button.nextElementSibling;
156                const isActive = button.classList.contains('active');
157                
158                // Закрыть все открытые ответы (опционально) 
159                document.querySelectorAll('.faq-question.active').forEach(activeBtn => {
160                    if (activeBtn !== button) {
161                        activeBtn.classList.remove('active');
162                        activeBtn.nextElementSibling.style.display = 'none';
163                    }
164                });
165
166                // Переключить текущий 
167                button.classList.toggle('active'); if (!isActive) { answer.style.display = 'block'; } else {
168                    answer.style.display = 'none';
169                }
170            });
171        });
172    }
173
174    initHeader() {
175        this.utils.on('click', '.rs-header-burger', (event) => {
176            const burgerElement = event.target.closest('.rs-header-burger');
177
178            if (burgerElement) {
179                const loadUrl = burgerElement.getAttribute('data-load-url');
180
181                if (loadUrl) {
182                    if (!document.querySelector('.header-mobile')) {
183                        fetch(loadUrl)
184                            .then(response => response.text())
185                            .then(htmlContent => {
186                                document.querySelector('.header').insertAdjacentHTML('beforeend', htmlContent);
187                                document.body.classList.toggle('mobile-header-toggled');
188
189                                this.initMobileCatalog();
190                            })
191                            .catch(error => {
192                                console.error('Ошибка при загрузке контента:', error);
193                            });
194                    } else {
195                        document.body.classList.toggle('mobile-header-toggled');
196                    }
197                }
198            }
199        });
200
201        let header = document.querySelector('.header');
202        let headerOffsetTop = header.offsetTop;
203        let lastScrollTop = 0;
204        let headerHeight = document.querySelector('.header').offsetHeight;
205        let headerTopHeight = document.querySelector('.header-top').offsetHeight;
206
207        document.addEventListener('scroll', function () {
208            let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
209            // let headerHeight = document.querySelector('.header').offsetHeight;
210            let isScrollDown = scrollTop > lastScrollTop;
211
212
213            if (isScrollDown) {
214                document.body.classList.add('scroll-down');
215            } else {
216                document.body.classList.remove('scroll-down');
217            }
218
219            lastScrollTop = scrollTop;
220
221            let isSticky = scrollTop > (headerOffsetTop + headerHeight);
222
223            if (!isScrollDown && scrollTop != headerOffsetTop) {
224                isSticky = true;
225            }
226
227            if (window.innerWidth > 991) { 
228                if (!isScrollDown && scrollTop <= headerOffsetTop + headerTopHeight) {
229                    isSticky = false;
230                }
231            }
232
233            if (isSticky) {
234                let headerPillow = document.querySelector('.header-pillow');
235                if (headerPillow) {
236                    headerPillow.style.height = headerHeight + 'px';
237                }
238            }
239
240            document.body.classList.toggle('header-sticky', isSticky);
241        });
242    }
243
244    initMobileCatalog() {
245        this.utils.on('click', '.mobile-catalog__first-item-name', (event) => {
246            const element = event.target.closest('.mobile-catalog__first-item');
247            element.classList.toggle('active');
248        });
249
250        this.utils.on('click', '.mobile-catalog__btn', (event) => {
251            const element = event.target.closest('.mobile-catalog');
252            element.classList.toggle('active');
253            document.body.classList.toggle('mobile-catalog-toggled');
254
255            element.querySelectorAll('.mobile-catalog__first-item.active').forEach(function (element) {
256                element.classList.remove('active');
257            });
258        });
259
260        this.utils.on('click', '.header-mobile-navigation__subtoogle', (event) => {
261            event.preventDefault();
262            event.target.closest('li').classList.toggle('active');
263        });
264    }
265
266    initFileInput() {
267        this.utils.on('change', '.input-file input[type="file"]', (event) => {
268            const input = event.target;
269            let fileName = input.value.split('\\').pop();
270
271            if (!fileName) {
272                const placeholder = input.dataset.placeholder;
273                fileName = placeholder;
274            } else {
275                if (fileName.length > 15) {
276                    fileName = fileName.substring(0, 15) + '...';
277                }
278            }
279
280            input.closest('.input-file').querySelector('.input-file__btn').querySelector('span').textContent = fileName;
281        });
282    }
283
284    initTextCopy() {
285        this.utils.on('click', '[data-copy-target]', (event) => {
286            const em = event.target.closest('[data-copy-target]');
287            const target = em.getAttribute('data-copy-target');
288            const textElement = document.querySelector('[data-copy="' + target + '"]');
289
290            if (textElement) {
291                const textToCopy = textElement.innerText;
292                navigator.clipboard.writeText(textToCopy);
293            }
294        });
295    }
296
297    initUrlCopy() {
298        this.utils.on('click', '[data-copy-url]', () => {
299            navigator.clipboard.writeText(window.location.href);
300        });
301    }
302
303    initFancybox() {
304        Fancybox.bind("[data-fancybox]", {});
305    }
306
307    initCatalogViewMore() {
308        this.utils.on('click', '.catalog-other__view-all--desktop', (event) => {
309            const element = event.target;
310            const oldText = element.innerText;
311            const newText = element.getAttribute('data-text');
312
313            const wrapper = element.closest('.catalog-other');
314
315            if (wrapper) {
316                wrapper.classList.toggle('catalog-other--desktop-toggled');
317
318                element.innerText = newText;
319                element.setAttribute('data-text', oldText);
320            }
321        });
322
323        this.utils.on('click', '.catalog-other__view-all--mobile', (event) => {
324            const element = event.target;
325            const oldText = element.innerText;
326            const newText = element.getAttribute('data-text');
327
328            const wrapper = element.closest('.catalog-other');
329
330            if (wrapper) {
331                wrapper.classList.toggle('catalog-other--mobile-toggled');
332
333                element.innerText = newText;
334                element.setAttribute('data-text', oldText);
335            }
336        });
337    }
338
339    initOtherModelCarousel() {
340        const carousels = document.querySelectorAll('.swiper-other-model');
341
342        if (!carousels) return;
343
344        carousels.forEach((em) => {
345            const wrapper = em.closest('.catalog-other');
346
347            const init = function () {
348                return new Swiper(em, {
349                    slidesPerView: "auto",
350                    spaceBetween: 4,
351                    navigation: {
352                        nextEl: wrapper.querySelector('.carousel__arrow--next'),
353                        prevEl: wrapper.querySelector('.carousel__arrow--prev'),
354                    },
355                });
356            }
357
358            let swiper = init();
359
360
361            const btnMore = wrapper.querySelector('.catalog-other__view-all');
362
363            if (btnMore) {
364                btnMore.addEventListener('click', () => {
365                    const oldText = btnMore.innerText;
366                    const newText = btnMore.dataset.text;
367
368                    btnMore.innerText = newText;
369                    btnMore.dataset.text = oldText;
370
371                    if (wrapper.classList.contains('catalog-other--destroy')) {
372                        swiper = init();
373                        wrapper.classList.remove('catalog-other--destroy');
374                    } else {
375                        swiper.destroy();
376                        wrapper.classList.add('catalog-other--destroy');
377                    }
378                });
379            }
380        });
381    }
382
383    initUserOrders() {
384        this.utils.on('click', '.user-orders-group__header', (event) => {
385            event.rsTarget.closest('.user-orders-group').classList.toggle('active');
386        });
387    }
388
389    initGallery() {
390        if (document.querySelector('.swiper-gallery')) {
391            document.querySelectorAll('.swiper-gallery').forEach(element => {
392                let params = {
393                    slidesPerView: 1,
394                    spaceBetween: 8,
395                    
396                    navigation: {
397                        nextEl: '.carousel__arrow--next',
398                        prevEl: '.carousel__arrow--prev',
399                    },
400
401                    breakpoints: {
402                        1200: {
403                            spaceBetween: 24,
404                            slidesPerView: 4
405                        },
406                        992: {
407                            spaceBetween: 24,
408                            slidesPerView: 3
409                        },
410                        768: {
411                            spaceBetween: 24,
412                            slidesPerView: 2
413                        },
414                    }
415                };
416
417                const swiper = new Swiper(element, params);
418            });
419        }
420    }
421
422    initTolltips() {
423        tippy('[data-tippy-content]', {
424            allowHTML: true,
425        });
426    }
427
428    initExtendedBreadcrumbs() {
429        this.utils.on('click', '.breadcrumbs__item-icon', (event) => {
430            const currentItem = event.rsTarget.closest('.breadcrumbs__item');
431            const isActive = currentItem.classList.contains('active');
432
433            event.rsTarget.closest('.breadcrumbs__list')
434                .querySelectorAll('.breadcrumbs__item')
435                .forEach((em) => em.classList.remove('active'));
436
437            if (!isActive) {
438                currentItem.classList.add('active');
439            }
440        });
441    }
442
443    /**
444     * Выполняется, когда весь DOM загружен
445     */
446    onDocumentReady() {
447        this.bindDataHrefLinks();
448        this.bindRegisterFields();
449        this.initBackButton();
450        this.initTabs();
451        this.initSpoiler();
452        this.initHeader();
453        this.initFileInput();
454        this.initTextCopy();
455        this.initUrlCopy();
456        this.initFancybox();
457        this.initCatalogViewMore();
458        this.initOtherModelCarousel();
459        this.initUserOrders();
460        this.initGallery();
461        this.initTolltips();
462        this.initExtendedBreadcrumbs();
463    }
464
465    /**
466     * Выполняется, когда на странице пояился новый контент
467     *
468     * @param event
469     */
470    onContentReady(event) {
471        this.initNumberInputs(event);
472    }
473};

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.