PageSourceSearch

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

js gdekomfort.ru collected 2026-10-05 02:26:22 UTC 8,689 bytes, 235 lines download raw bytes

1/**
2 * Скрипт обеспечивает корректную работу карточки товара
3 */
4new class Product extends RsJsCore.classes.component {
5
6    constructor(settings) {
7        super();
8        let defaults = {
9            galleryThumbs: '.product-gallery__thumbs',
10            galleryThumbsPrev: '.product-gallery-thumbs-wrap .swiper-button-prev',
11            galleryThumbsNext: '.product-gallery-thumbs-wrap .swiper-button-next',
12            galleryImages: '.product-gallery__main',
13            galleryLightboxSelector: '.swiper-zoom-container',
14
15            swiperAccessories: '.swiper-accessories',
16            swiperAccessoriesNext: '.product-accessories .swiper-button-next',
17            swiperAccessoriesPrev: '.product-accessories .swiper-button-prev',
18            product: '.rs-product',
19
20            concomitantBlock: '.rs-product-concomitant',
21            concomitantInput: '[name^="concomitant"]',
22            concomitantPrice: '.rs-concomitant-price',
23            concomitantPriceValue: '.rs-concomitant-price .rs-value',
24
25            hiddenClass: 'd-none'
26        };
27
28        this.settings = {...defaults, ...this.getExtendsSettings(), ...settings};
29        this.galleryThumbs = null;
30        this.galleryThumbsSlides = null;
31        this.galleryTop = null;
32        this.galleryTopSlides = null;
33    }
34
35    initGallerySliders() {
36        let swiperThumbs = document.querySelector(this.settings.galleryThumbs);
37        let product = document.querySelector(this.settings.product);
38        let zoom = product && product.classList.contains('rs-zoom');
39        let lightboxEnabled =  product && product.classList.contains('rs-lightbox');
40
41        let direction;
42        if (swiperThumbs && swiperThumbs.dataset.swiperDirection) {
43            direction = {
44                breakpoints: {
45                    992: {
46                        direction: 'vertical',
47                        spaceBetween: 16,
48                    }
49                }
50            };
51        } else {
52            direction = {};
53        }
54
55        let p1 = new Promise((resolve) => {
56            this.galleryThumbs = new Swiper(this.settings.galleryThumbs, {
57                spaceBetween: 8,
58                // slidesPerView: 'auto',
59                slidesPerView: 4,
60                freeMode: true,
61                watchSlidesVisibility: true,
62                watchSlidesProgress: true,
63                navigation: {
64                    nextEl: this.settings.galleryThumbsNext,
65                    prevEl: this.settings.galleryThumbsPrev,
66                },
67                on: {
68                    imagesReady: (e) => {
69                        this.galleryThumbsSlides = Object.assign({}, e.slides);
70                        resolve();
71                    }
72                },
73                ...direction
74            });
75        });
76
77        let p2 = new Promise((resolve) => {
78            this.galleryTop = new Swiper(this.settings.galleryImages, {
79                spaceBetween: 16,
80                zoom: !!zoom,
81                thumbs: {
82                    swiper: this.galleryThumbs
83                },
84                breakpoints: {
85                    992: {
86                        allowTouchMove: false,
87                    }
88                },
89                on: {
90                    imagesReady: (e) => {
91                        this.galleryTopSlides = Object.assign({}, e.slides);
92                        resolve();
93                    }
94                }
95            });
96        });
97
98        this.whenImagesReady = Promise.all([p1, p2]);
99
100        if (lightboxEnabled && GLightbox) {
101            this.glightbox = GLightbox({
102                selector: this.settings.galleryImages + ' ' + this.settings.galleryLightboxSelector
103            });
104        }
105    }
106
107    initEvents() {
108        this.utils.on( 'click', '.rs-go-to-tab', (event) => {
109            let tab = event.rsTarget.getAttribute('data-target');
110
111
112            if (tab) {
113                let someTab = document.querySelector('[data-tab-header-name="' + tab +'"]');
114                someTab.click();
115
116                let tabWrapper = document.querySelector('.product-bottom');
117                tabWrapper.scrollIntoView();
118            }
119
120            event.preventDefault();
121        });
122
123        //Изменяем состав отображаемых изображений товаров
124        this.utils.on('offer-photo-changed', this.settings.product, (event) => {
125            this.whenImagesReady.then(() => {
126                this.galleryThumbs.removeAllSlides();
127                this.galleryTop.removeAllSlides();
128
129                let offerImages = event.detail.images.map((value) => {
130                    return parseInt(value);
131                });
132
133                for (let [index, element] of Object.entries(this.galleryThumbsSlides)) {
134                    if (!offerImages.length || offerImages.indexOf(parseInt(element.dataset.imageId)) > -1) {
135                        this.galleryThumbs.appendSlide(element);
136                    }
137                }
138
139                for (let [index, element] of Object.entries(this.galleryTopSlides)) {
140                    if (!offerImages.length || offerImages.indexOf(parseInt(element.dataset.imageId)) > -1) {
141                        this.galleryTop.appendSlide(element);
142                    }
143                }
144
145                this.galleryThumbs.update();
146                this.galleryTop.update();
147
148                if (this.glightbox) {
149                    this.glightbox.reload();
150                }
151            });
152        });
153    }
154
155    /**
156     * Инициирует запуск и остановку видео YouTube, RuTube, VK
157     */
158    initVideoModal() {
159        this.utils.on('shown.bs.modal', '#modal-video', (event) => {
160            let element = event.rsTarget.querySelector('iframe.youtube-player');
161            if (element) {
162                element.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
163                element.contentWindow.postMessage('{"type":"player:play","data":{}}', '*');
164                element.contentWindow.postMessage({"method": "play"}, '*');
165            }
166        });
167
168        this.utils.on('hidden.bs.modal', '#modal-video', (event) => {
169            event.rsTarget.querySelectorAll('iframe.youtube-player').forEach(element => {
170                element.contentWindow.postMessage('{"event":"command","func":"stopVideo","args":""}', '*');
171                element.contentWindow.postMessage('{"type":"player:pause","data":{}}', '*');
172                element.contentWindow.postMessage({"method": "pause"}, '*');
173            });
174        });
175    }
176
177    /**
178     * Устанавливает комплектацию по умолчанию
179     */
180    setDefaultOffer() {
181        let product = document.querySelector(this.settings.product);
182        if (product && product.changeOffer) {
183            let offerIdMatch = location.hash.match(/#(\d+)/);
184            if (offerIdMatch) {
185                product.changeOffer.setOffer(offerIdMatch[1]);
186            }
187        }
188    }
189
190    /**
191     * Инициализирует подсказку итоговой стоимости сопутствующих товаров
192     */
193    initConcomitantCalculate() {
194        let recalculate = () => {
195            let concomitantBlock = document.querySelector(this.settings.concomitantBlock);
196            if (concomitantBlock) {
197                let sum = 0;
198                document.querySelectorAll(this.settings.concomitantInput + ':checked').forEach((element) => {
199                    sum += parseFloat(element.dataset.price);
200                });
201
202                let Format = wNumb({
203                    thousand: ' '
204                });
205
206                let currency = concomitantBlock && concomitantBlock.dataset.currency;
207                let element = document.querySelector(this.settings.concomitantPrice);
208                let value = element.querySelector(this.settings.concomitantPriceValue);
209                value.innerHTML = '+' + Format.to(sum) + ' ' + currency;
210                if (sum > 0) {
211                    element.classList.remove(this.settings.hiddenClass);
212                } else {
213                    element.classList.add(this.settings.hiddenClass);
214                }
215            }
216        };
217
218        this.utils.on('change', '[name^="concomitant"]', (element) => {
219            recalculate();
220        });
221
222        recalculate();
223    }
224
225    onDocumentReady() {
226        this.initEvents();
227        this.initVideoModal();
228        this.initGallerySliders();
229        this.initConcomitantCalculate();
230    }
231
232    onAfterDocumentReady() {
233        this.setDefaultOffer();
234    }
235};

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.