PageSourceSearch

https://xn-----dlccfbfdksbbn6ccdrcazo.xn--p1ai/local/templates/main/js/ajax.js?v=20260805-4

js xn-----dlccfbfdksbbn6ccdrcazo.xn--p1ai collected 2026-10-04 23:53:27 UTC 75,006 bytes, 1,937 lines download raw bytes

1(function () {
2    function isUnavailableProductCard(card) {
3        if (!card) return false;
4        const availabilityText = (card.querySelector('.card__availability')?.textContent || '').trim().toLowerCase();
5        return card.classList.contains('not-available')
6            || /нет в наличии|отсутств|под заказ/.test(availabilityText);
7    }
8
9    function Ajax(module, method, callback) {
10        return function (data) {
11            if (method === 'catalog.basket.add' && data?.id) {
12                const card = document.querySelector(`.card[data-product-id="${data.id}"]`);
13                if (isUnavailableProductCard(card)) {
14                    showUnavailableProductNotice(card);
15                    return Promise.resolve({status: 'unavailable'});
16                }
17            }
18
19            return new Promise(function (resolve) {
20
21                BX.ajax.runAction(module + '.' + method, {
22                    data: data
23                }).then(function (response) {
24                    //костыль
25                    if (method == 'catalog.basket.update') {
26                        if (data['id'] && data['quantity'] == 0) {
27                            $("[data-product-id=" + data['id'] + "]").removeClass('added-to-cart');
28                        }
29                    }
30
31                    if (callback) callback(response);
32                    resolve(response);
33                });
34            })
35        }
36    }
37
38    let bu_core = function (method, callback) {
39        return Ajax('bu:tools.api', method, callback);
40    }
41
42    window.bu_ajax = {
43
44        discount: {
45            rent: bu_core('catalog.discount.rent')
46        },
47        reviews: {
48            add: bu_core('reviews.add'),
49        },
50        rent: {
51            create: bu_core('rent.create')
52        },
53        oneclick: {
54            create: bu_core('oneclick.create')
55        },
56        basket: {
57            quick: bu_core('catalog.basket.add', function () {
58                location.href = '/quick-order/';
59            }),
60            add: bu_core('catalog.basket.add', function (response) {
61                bu_livecircle.update_small_basket(response);
62                if (response && response.status === 'success' && window.SiteMetrika) {
63                    window.SiteMetrika.reach('cart_add', {}, {dedupeMs: 1000, dedupeKey: 'cart_add'});
64                }
65            }),
66            update: bu_core('catalog.basket.update', bu_livecircle.update_small_basket),
67            clear: bu_core('catalog.basket.clear', bu_livecircle.update_small_basket),
68            property: bu_core('catalog.basket.property')
69        },
70        faq: {
71            add: bu_core('faq.add')
72        },
73        subscribe: {
74            add: bu_core('subscription.add')
75        },
76        user: {
77            auth: bu_core('user.auth'),
78            register: bu_core('user.register'),
79            registerCompany: bu_core('user.registerCompany'),
80            registerFast: bu_core('user.registerFast'),
81            requestPassword: bu_core('user.requestPassword'),
82            changePassword: bu_core('user.changePassword')
83        }
84    }
85
86})();
87
88// Общая проверка доступности для обработчиков карточек и inline-кнопок.
89function isUnavailableProductCard(card) {
90    if (!card) return false;
91    const availabilityText = (card.querySelector('.card__availability')?.textContent || '').trim().toLowerCase();
92    return card.classList.contains('not-available')
93        || /нет в наличии|отсутств|под заказ/.test(availabilityText);
94}
95
96//Избранное
97
98document.addEventListener('DOMContentLoaded', function () {
99    BX.ajax({
100        url: '/local/ajax/favorites_handler.php',
101        data: { 'action': 'getFavorites' },
102        method: 'POST',
103        dataType: 'json',
104        onsuccess: function (response) {
105            if (response.status === 'success') {
106                const favorites = response.favorites;
107                updateFavoriteIcons(favorites);
108                updateCounter(response.favorites.length);
109            }
110        },
111        onfailure: function () {
112            console.error("Произошла ошибка при получении избранных");
113        }
114    });
115});
116
117function updateFavoriteIcons(favorites) {
118    updateButtons('.card__save', 'data-favid', favorites);
119    updateButtons('.product-buttons__item_favorites', 'data-compid', favorites);
120    updateButtons('.catalog-label.liked', 'data-favid', favorites, true);
121}
122
123function updateButtons(selector, attrName, favorites, isCatalogLabel = false) {
124    document.querySelectorAll(selector).forEach((button) => {
125        const productId = button.getAttribute(attrName);
126        const svgUse = button.querySelector('use');
127        const textElement = button.querySelector('.hide');
128
129        if (favorites.includes(parseInt(productId, 10))) {
130            button.classList.add('in-favorite');
131            svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#saved-icon');
132            if (textElement) {
133                textElement.innerHTML = 'Убрать из Ð¸Ð·Ð±Ñ€Ð°Ð½Ð½Ð¾Ð³Ð¾';
134            }
135
136            if (isCatalogLabel) {
137                const pElement = button.querySelector('p');
138                if (pElement) {
139                    pElement.textContent = 'Убрать из';
140                }
141            }
142        } else {
143            button.classList.remove('in-favorite');
144            svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#liked-icon');
145            if (textElement) {
146                textElement.innerHTML = 'Добавить в Ð¸Ð·Ð±Ñ€Ð°Ð½Ð½Ð¾Ðµ';
147            }
148
149            if (isCatalogLabel) {
150                const pElement = button.querySelector('p');
151                if (pElement) {
152                    pElement.textContent = 'В избранное';
153                }
154            }
155        }
156    });
157}
158
159const favoriteButtons = document.querySelectorAll('.catalog-label.liked');
160
161favoriteButtons.forEach((favoriteButton) => {
162    favoriteButton.addEventListener('click', function () {
163        const productId = this.getAttribute('data-favid');
164        toggleFavorite(productId, this);
165    });
166});
167
168function toggleFavorite(productId, buttonElement) {
169    const svgUse = buttonElement.querySelector('use');
170    const isCatalogLabel = buttonElement.classList.contains('catalog-label');
171
172    BX.ajax({
173        url: '/local/ajax/favorites_handler.php',
174        data: {
175            'action': 'toggle',
176            'product_id': productId
177        },
178        method: 'POST',
179        dataType: 'json',
180        onsuccess: function (response) {
181            if (response.status === 'success') {
182                const pElement = buttonElement.querySelector('p');
183
184                if (response.action === 'added') {
185                    window.SiteMetrika?.reach('favorite_add', {product_id: productId}, {
186                        dedupeMs: 1000,
187                        dedupeKey: 'favorite_add:' + productId
188                    });
189                    buttonElement.classList.add('in-favorite');
190                    if (svgUse) {
191                        svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#saved-icon');
192                    }
193
194                    if (isCatalogLabel && pElement) {
195                        pElement.textContent = 'Убрать из';
196                    }
197
198                    let addedBlockFav = document.querySelector('.addedFav');
199                    addedBlockFav.classList.add('show');
200                    setTimeout(function () { addedBlockFav.classList.remove('show') }, 3000);
201
202
203                } else {
204                    buttonElement.classList.remove('in-favorite');
205                    if (svgUse) {
206                        svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#liked-icon');
207                    }
208
209                    if (isCatalogLabel && pElement) {
210                        pElement.textContent = 'В избранное';
211                    }
212
213
214                    let removedBlockFav = document.querySelector('.removedFav');
215                    removedBlockFav.classList.add('show');
216                    setTimeout(function () { removedBlockFav.classList.remove('show') }, 3000);
217
218                }
219
220                updateCounter(response.favorites.length);
221
222            } else {
223                console.error('Ошибка при добавлении/удалении товара из избранного: ', response.message);
224            }
225        },
226        onfailure: function (error) {
227            console.error('Произошла ошибка: ', error);
228        }
229    });
230}
231
232
233function updateCounter(count) {
234    const counterElements = document.querySelectorAll('.count-saved');
235
236    if (counterElements.length === 0) {
237        return;
238    }
239
240    counterElements.forEach((element) => {
241        element.textContent = count;
242        if (count > 0) {
243            element.style.opacity = '1';
244        } else {
245            element.style.opacity = '0';
246        }
247    });
248}
249
250function addClickListener(selector, attrName) {
251    document.querySelectorAll(selector).forEach((button) => {
252        button.addEventListener('click', function (e) {
253            const productId = this.getAttribute(attrName);
254            const svgUse = this.querySelector('use');
255            const textElement = this.querySelector('.hide');
256            const isFavorited = svgUse.getAttribute('xlink:href') === '/local/templates/main/img/sprite.svg#liked-icon';
257
258            BX.ajax({
259                url: '/local/ajax/favorites_handler.php',
260                data: {
261                    'action': 'toggle',
262                    'product_id': productId
263                },
264                method: 'POST',
265                dataType: 'json',
266                onsuccess: function (response) {
267                    if (response.status === 'success') {
268                        if (response.action === 'added') {
269                            window.SiteMetrika?.reach('favorite_add', {product_id: productId}, {
270                                dedupeMs: 1000,
271                                dedupeKey: 'favorite_add:' + productId
272                            });
273                            button.classList.add('in-favorite');
274                            svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#saved-icon');
275                            if (textElement) {
276                                textElement.innerHTML = 'Убрать из Ð¸Ð·Ð±Ñ€Ð°Ð½Ð½Ð¾Ð³Ð¾';
277                            }
278                            let addedBlockFav = document.querySelector('.addedFav');
279                            addedBlockFav.classList.add('show');
280                            setTimeout(function () { addedBlockFav.classList.remove('show') }, 3000);
281                        } else {
282                            button.classList.remove('in-favorite');
283                            svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#liked-icon');
284                            if (textElement) {
285                                textElement.innerHTML = 'Добавить в Ð¸Ð·Ð±Ñ€Ð°Ð½Ð½Ð¾Ðµ';
286                            }
287                            let removedBlockFav = document.querySelector('.removedFav');
288                            removedBlockFav.classList.add('show');
289                            setTimeout(function () { removedBlockFav.classList.remove('show') }, 3000);
290                        }
291
292                        if (document.getElementById('favorite')) {
293                            const cardElement = document.querySelector(`[data-
293product-id="${productId}"]`);
294                            console.log(productId);
295                            if (cardElement) {
296                                cardElement.remove();
297                            }
298                        }
299                        updateCounter(response.favorites.length);
300
301                        if (response.favorites.length === 0) {
302                            const savedSection = document.querySelector('.saved');
303                            if (savedSection) {
304                                savedSection.innerHTML = `
305                            <div class="saved-empty">
306                                <p class="saved-empty__desc">Вы&nbsp;еще не&nbsp;добавили ни&nbsp;одного товара в&nbsp;избранное</p>
307                                <a href="/catalog/mezhkomnatnye_dveri/massiv/" class="saved-empty__link btn">Перейти в&nbsp;каталог</a>
308                            </div>
309                        `;
310                            }
311                        }
312                    } else {
313                        if (isFavorited) {
314                            svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#liked-icon');
315                        } else {
316                            svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#saved-icon');
317                        }
318                        updateCounter(response.favorites.length);
319                    }
320                },
321                onfailure: function () {
322                    alert("Произошла ошибка");
323                    if (isFavorited) {
324                        svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#liked-icon');
325                    } else {
326                        svgUse.setAttribute('xlink:href', '/local/templates/main/img/sprite.svg#saved-icon');
327                    }
328                }
329            });
330        });
331    });
332}
333
334addClickListener('.card__save', 'data-favid');
335addClickListener('.product-buttons__item_favorites', 'data-compid');
336
337
338
339
340
341//Сравнение
342
343document.addEventListener('DOMContentLoaded', function () {
344    BX.ajax({
345        url: '/local/ajax/compare_handler.php',
346        data: { 'action': 'init' },
347        method: 'POST',
348        dataType: 'json',
349        onsuccess: function (response) {
350            if (response.status === 'success') {
351                const compareList = Object.values(response.compare).map(Number);
352                updateCompareIcons(compareList);
353                updateCounterCompare(compareList.length);
354            }
355        },
356        onfailure: function () {
357            console.error("Произошла ошибка при работе с сравнением");
358        }
359    });
360
361
362    const clearButton = document.querySelector('.catalog-label.delete');
363
364    if (clearButton) {
365        clearButton.addEventListener('click', function () {
366            BX.ajax({
367                url: '/local/ajax/compare_handler.php',
368                data: {
369                    'action': 'clear'
370                },
371                method: 'POST',
372                dataType: 'json',
373                onsuccess: function (response) {
374                    if (response.status === 'success') {
375                        const compareSection = document.querySelector('.compare');
376
377                        if (compareSection) {
378                            compareSection.innerHTML = `
379                                    <div class="saved-empty"><p class="saved-empty__desc">Вы&nbsp;еще не&nbsp;добавили ни&nbsp;одного товара в&nbsp;сравнение</p>
380                                    <a href="/catalog/mezhkomnatnye_dveri/massiv/" class="saved-empty__link btn">Перейти в&nbsp;каталог</a></div>`;
381                        }
382                        let removedBlockCompare = document.querySelector('.removedCompare');
383                        removedBlockCompare.classList.add('show');
384                        setTimeout(function () { removedBlockCompare.classList.remove('show') }, 3000);
385
386                        updateCompareIcons([]);
387                        updateCounterCompare(0);
388                    } else {
389                        console.error('Ошибка при очистке списка: ', response.message);
390                    }
391                },
392                onfailure: function (error) {
393                    console.error('Произошла ошибка: ', error);
394                }
395            });
396        });
397    }
398});
399
400const deleteButtons = document.querySelectorAll('.del-item');
401
402deleteButtons.forEach((deleteButton) => {
403    deleteButton.addEventListener('click', function () {
404        const productId = this.getAttribute('data-id');
405        removeProductFromFavorites(productId, this);
406    });
407});
408
409function removeProductFromFavorites(productId, buttonElement) {
410    BX.ajax({
411        url: '/local/ajax/compare_handler.php',
412        data: {
413            'action': 'remove',
414            'product_id': productId
415        },
416        method: 'POST',
417        dataType: 'json',
418        onsuccess: function (response) {
419            if (response.status === 'success') {
420                window.location.reload();
421            } else {
422                console.error('Ошибка при удалении товара: ', response.message);
423            }
424        },
425        onfailure: function (error) {
426            console.error('Произошла ошибка: ', error);
427        }
428    });
429}
430
431
432function updateCompareIcons(compareList) {
433    if (!Array.isArray(compareList)) {
434        console.error('compareList должен быть массивом. Получено:', compareList);
435        return;
436    }
437
438    document.querySelectorAll('.card__compare').forEach((button) => {
439        updateButton(button, compareList, 'data-compid');
440    });
441
442    document.querySelectorAll('.product-buttons__item-compare').forEach((button) => {
443        updateButton(button, compareList, 'data-favid');
444    });
445}
446
447function updateButton(button, compareList, dataAttribute) {
448    const productId = parseInt(button.getAttribute(dataAttribute), 10);
449    const textElement = button.querySelector('span');
450
451    if (compareList.includes(productId)) {
452        button.classList.add('in-compare');
453        if (textElement) {
454            textElement.textContent = 'Убрать из сравнения';
455        }
456    } else {
457        button.classList.remove('in-compare');
458        if (textElement) {
459            textElement.textContent = 'Добавить в сравнение';
460        }
461    }
462}
463
464function updateCounterCompare(count) {
465    const counterElement = document.getElementById('count-compare');
466    const counterElementMob = document.querySelector('.count-compare');
467
468    if (!counterElement) {
469        return;
470    }
471
472    counterElement.textContent = count;
473    counterElement.style.opacity = count > 0 ? '1' : '0';
474    counterElementMob.textContent = count;
475    counterElementMob.style.opacity = count > 0 ? '1' : '0';
476}
477
478document.querySelectorAll('.card__compare, .product-buttons__item-compare').forEach((button) => {
479    button.addEventListener('click', function (e) {
480        const productId = this.getAttribute('data-favid') || this.getAttribute('data-compid');
481
482        BX.ajax({
483            url: '/local/ajax/compare_handler.php',
484            data: {
485                'action': 'toggle',
486                'product_id': productId
487            },
488            method: 'POST',
489            dataType: 'json',
490            onsuccess: function (response) {
491                if (response.status === 'success') {
492                    const compareList = Object.values(response.compare).map(Number);
493                    if (compareList.includes(parseInt(productId, 10))) {
494                        window.SiteMetrika?.reach('compare_add', {product_id: productId}, {
495                            dedupeMs: 1000,
496                            dedupeKey: 'compare_add:' + productId
497                        });
498                    }
499                    updateCompareIcons(compareList);
500                    updateCounterCompare(compareList.length);
501                    if (!button.classList.contains('in-compare')) {
502                        let removedBlockCompare = document.querySelector('.removedCompare');
503                        removedBlockCompare.classList.add('show');
504                        setTimeout(function () { removedBlockCompare.classList.remove('show') }, 3000);
505                    }
506                    else {
507                        let addedBlockCompare = document.querySelector('.addedCompare');
508                        addedBlockCompare.classList.add('show');
509                        setTimeout(function () { addedBlockCompare.classList.remove('show') }, 3000);
510                    }
511                }
512            },
513            onfailure: function (error) {
514                console.error("Произошла ошибка: ", JSON.stringify(error));
515                alert("Произошла ошибка: " + JSON.stringify(error));
516            }
517        });
518    });
519});
520
521
522
523
524
525//Работа с корзиной
526
527
528//Выделение и удаление товаров
529document.addEventListener('DOMContentLoaded', function () {
530    const selectAllCheckbox = document.querySelector('input[name="basketAll"]');
531    const itemCheckboxes = document.querySelectorAll('input[name="basketChooseItem"]');
532
533    if (selectAllCheckbox) {
534        selectAllCheckbox.addEventListener('change', function () {
535            itemCheckboxes.forEach(function (checkbox) {
536                checkbox.checked = selectAllCheckbox.checked;
537            });
538        });
539    }
540});
541
542
543// Удаление выбранных товаров
544const deleteButton = document.querySelector('.basket-content__delete');
545
546if (deleteButton) {
547    deleteButton.addEventListener('click', function () {
548        const selectedIds = Array.from(document.querySelectorAll('input[name="basketChooseItem"]:checked')).map(input => input.getAttribute('data-id'));
549        if (selectedIds.length === 0) return;
550
551        BX.ajax({
552            url: '/local/ajax/dell_cart.php',
553            data: {
554                'action': 'delete',
555                'idsToDelete': selectedIds
556            },
557            method: 'POST',
558            dataType: 'json',
559            onsuccess: function (response) {
560                if (response.status === 'success') {
561                    selectedIds.forEach(id => {
562                        const item = document.getElementById(`basket-item-${id}`);
563                        if (response.deletedCount === response.remainingCount) {
564                            replaceBasketBlock();
565                        }
566                        if (item) {
567                            item.remove();
568                        }
569                        recalculateBasket();
570                    });
571                } else {
572                    console.error('Ошибка удаления: ', response.message);
573                }
574            },
575            onfailure: function (error) {
576                console.error('Произошла ошибка: ', error);
577            }
578        });
579    });
580}
581
582
583function recalculateBasket() {
584    const basketItems = document.querySelectorAll('.basket-card[data-entity="basket-item"]');
585    let totalItemsSum = 0;
586
587    basketItems.forEach(item => {
588        // Товар установки остается в заказе как отметка клиента, но никогда
589        // не участвует в визуальном пересчете корзины.
590        if (item.getAttribute('data-product-id') === '11542') {
591            return;
592        }
593        const priceElement = item.querySelector('.basket-item-price-current-text');
594        const price = priceElement ? parseFloat(priceElement.textContent.replace(/\s+/g, '').replace('₽', '')) : 0;
595
596        totalItemsSum += price;
597    });
598
599    const totalItemsElement = document.querySelector('.basket-total__item-right');
600    if (totalItemsElement && totalItemsSum !== 0) {
601        totalItemsElement.textContent = totalItemsSum.toLocaleString('ru-RU') + ' ₽';
602    }
603
604    const totalPriceElement = document.querySelector('.basket-total__price');
605    if (totalPriceElement && totalItemsSum !== 0) {
606        totalPriceElement.textContent = totalItemsSum.toLocaleString('ru-RU') + ' ₽';
607    }
608}
609
610document.addEventListener('DOMContentLoaded', recalculateBasket);
611
612
613//Карточка товара//
614
615
616//Добавление в корзину
617
618function getProductPrice() {
619    function parsePriceText(el) {
620        const t = (el?.textContent || el?.innerText || '')
621        const parsed = parseFloat(t.replace(/\u00A0/g, ' ').replace(/\s+/g, '').replace(/[^\d.,]/g, '').replace(',', '.')) || 0
622        return parsed
623    }
624
625    const checked = document.querySelector('input[name="productType"]:checked')
626
627    if (checked) {
628        if (checked.id === 'productType1') {
629            const checkedSetItems = document.querySelectorAll(
630                '.product-set__blocks input[type="checkbox"]:checked'
631            );
632            const setTotal = Array.from(checkedSetItems).reduce((sum, input) => {
633                return sum + parsePriceText(input.dataset.price);
634            }, 0);
635
636            if (setTotal > 0) {
637                return setTotal;
638            }
639        }
640
641        const wrap = checked.closest('.product-sides__item')
642        const priceEl = wrap ? wrap.querySelector('.product-choose__price, .product-sides__price') : null
643        if (priceEl) {
644            const val = parsePriceText(priceEl)
645            return val
646        }
647    }
648
649    const single = document.querySelector('.product-sides .product-choose__price, .product-sides__price, .product-choose__prices .product-choose__price')
650
651    if (single) {
652        const val = parsePriceText(single)
653        return val
654    }
655
656    return 0
657}
658
659function parseProductPrice(value) {
660    return parseFloat(String(value || '')
661        .replace(/\u00A0/g, ' ')
662        .replace(/\s+/g, '')
663        .replace(/[^\d,.-]/g, '')
664        .replace(',', '.')) || 0;
665}
666
667function formatProductPrice(price) {
668    return new Intl.NumberFormat('ru-RU', {
669        maximumFractionDigits: 0
670    }).format(price) + ' ₽';
671}
672
673function updateSetPrice(fallbackPrice) {
674    const setPrice = document.getElementById('set_price');
675    if (!setPrice) {
676        return;
677    }
678
679    const offerPrice = Number(fallbackPrice) || 0;
680    const mainSetCheckbox = document.getElementById('setParams_main');
681    const mainSetBlock = mainSetCheckbox?.closest('.product-set__block');
682    const mainSetTotal = mainSetBlock?.querySelector('.total');
683
684    if (offerPrice > 0 && mainSetCheckbox) {
685        mainSetCheckbox.dataset.price = String(offerPrice);
686
687        // Старый обработчик комплекта читает значение через jQuery.data(),
688        // поэтому обновляем и DOM-атрибут, и внутренний кеш jQuery.
689        if (window.jQuery) {
690            window.jQuery(mainSetCheckbox).data('price', offerPrice);
691        }
692    }
693
694    if (offerPrice > 0 && mainSetTotal) {
695        mainSetTotal.textContent = formatProductPrice(offerPrice);
696    }
697
698    const basicTotalElement = document.getElementById('total_price_tele');
699    const basicSetContent = basicTotalElement?.closest('.product-set__content');
700    const itemPrices = Array.from(
701        basicSetContent?.querySelectorAll('.product-set__item .right') || []
702    );
703    const calculatedPrice = itemPrices.reduce((sum, item) => sum + parseProductPrice(item.textContent), 0);
704    const basicTotal = calculatedPrice > 0 ? calculatedPrice : offerPrice;
705
706    const customTotal = Array.from(
707        document.querySelectorAll('.product-set__blocks input[type="checkbox"]:checked')
708    ).reduce((sum, input) => sum + parseProductPrice(input.dataset.price), 0);
709    const activeSetTotal = customTotal > 0 ? customTotal : basicTotal;
710
711    if (activeSetTotal > 0) {
712        setPrice.textContent = formatProductPrice(activeSetTotal);
713        setPrice.classList.add('is-price-ready');
714    }
715
716    if (basicTotalElement && basicTotal > 0) {
717        basicTotalElement.textContent = formatProductPrice(basicTotal);
718    }
719
720    const customTotalElement = document.getElementById('costum_summ');
721    if (customTotalElement && activeSetTotal > 0) {
722        customTotalElement.textContent = formatProductPrice(activeSetTotal);
723    }
724
725    ['pricebase', 'pbase'].forEach(id => {
726        const input = document.getElementById(id);
727        if (input && activeSetTotal > 0) {
728            input.value = String(activeSetTotal);
729        }
730    });
731}
732
733function setProductCartAvailability(isUnavailable) {
734    const cartButton = document.querySelector('.product-sides__cart');
735    if (!cartButton) {
736        return;
737    }
738
739    cartButton.classList.toggle('is-cart-price-unavailable', isUnavailable);
740    cartButton.dataset.priceUnavailable = isUnavailable ? 'Y' : 'N';
741    cartButton.setAttribute('aria-disabled', isUnavailable ? 'true' : 'false');
742}
743
744function updateOfferPrice() {
745    const selectedOffer = document.querySelector('.sizesku input[name="productSize"]:checked');
746    const canvasPrice = document.getElementById('polotno');
747    const telescopicPrice = document.getElementById('price_teles');
748
749    if (!selectedOffer || (!canvasPrice && !telescopicPrice)) {
750        return;
751    }
752
753    const offerId = selectedOffer.value;
754    if (!offerId) {
755        return;
756    }
757
758    [canvasPrice, telescopicPrice].filter(Boolean).forEach(priceElement => {
759        priceElement.classList.add('is-loading-price');
760        priceElement.classList.remove('is-price-ready', 'is-price-unavailable');
761    });
762    setProductCartAvailability(true);
763
764    fetch('/local/ajax/get_offer_price.php', {
765        method: 'POST',
766        headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
767        body: 'offerId=' + encodeURIComponent(offerId)
768    })
769        .then(response => {
770            if (!response.ok) {
771                throw new Error('HTTP ' + response.status);
772            }
773
774            return response.json();
775        })
776        .then(data => {
777            if (!data.success) {
778                throw new Error(data.message || 'Не удалось получить цену товара.');
779            }
780
781            selectedOffer.dataset.price = String(data.price);
782            selectedOffer.dataset.printPrice = data.formattedPrice;
783            [canvasPrice, telescopicPrice].filter(Boolean).forEach(priceElement => {
784                priceElement.textContent = data.formattedPrice;
785                priceElement.classList.add('is-price-ready');
786            });
787            updateSetPrice(Number(data.price));
788            setProductCartAvailability(false);
789        })
790        .catch(error => {
791            console.error('Не удалось обновить цену товара.', error);
792            [canvasPrice, telescopicPrice].filter(Boolean).forEach(priceElement => {
793                priceElement.textContent = 'Цена уточняется';
794                priceElement.classList.add('is-price-ready', 'is-price-unavailable');
795            });
796            setProductCartAvailability(true);
797        })
798        .finally(() => {
799            [canvasPrice, telescopicPrice].filter(Boolean).forEach(priceElement => {
800                priceElement.classList.remove('is-loading-price');
801            });
802        });
803}
804
805document.addEventListener('change', function (event) {
806    if (event.target.matches('.sizesku input[name="productSize"]')) {
807        updateOfferPrice();
808    }
809});
810
811document.addEventListener('DOMContentLoaded', updateOfferPrice);
812
813
814
815function parseQty(v) {
816    const m = String(v).match(/[\d]+(?:[.,]\d+)?/);
817    return m ? parseFloat(m[0].replace(',', '.')) : 1;
818}
819
820
821let cartButton = document.querySelector('.product-sides__cart');
822
823if (cartButton) {
824    cartButton.addEventListener('click', function () {
825        if (cartButton.dataset.priceUnavailable === 'Y') {
826            return;
827        }
828
829        let selectedItems = [];
830        let productID, productName;
831
832        let activeSize = document.querySelector('.sizesku input[type="radio"]:checked');
833        if (activeSize) {
834            productID = activeSize.value;
835            productName = activeSize.getAttribute('data-name');
836        } else {
837            productID = cartButton.dataset.id;
838            let productNameElement = document.querySelector('.product-title, .product-sides__title, .card__name');
839            productName = productNameElement ? productNameElement.innerText.trim() : "Товар";
840        }
841
842        let productPrice = getProductPrice(activeSize);
843
844        selectedItems.push({
845            name: productName,
846            price: productPrice,
847            id: productID
848        });
849
850        let productTypeRadio = document.querySelector('input[name="productType"]:checked');
851        if (productTypeRadio && productTypeRadio.id === "productType1") {
852            let productSetContainer = document.querySelector('.product-set__blocks');
853            let checkboxes = productSetContainer.querySelectorAll('input[type="checkbox"]:checked');
854            checkboxes.forEach(function (checkbox) {
855                let block = checkbox.closest('.product-set__block');
856                let itemName = block.querySelector('.name span').innerText;
857                let itemPrice = parseFloat(block.querySelector('.total').innerHTML.replace(' ₽', '').replace(/&nbsp;/g, '').trim());
858                const isMoulding = block.classList.contains('long-block');
859                let itemCount = isMoulding
860                    ? parseQty(block.querySelector('.basket-card-total__complect')?.innerText)
861                    : parseQty(block.querySelector('.count')?.innerText);
862
863                let item = {
864                    NAME: itemName,
865                    PRICE: itemPrice,
866                    COUNT: itemCount,
867                    ID: checkbox.getAttribute('data-id'),
868                    SPEC: block.classList.contains('long-block') ? "" : "Y"
869                };
870                selectedItems.push(item);
871            });
872        }
873
874
875        let productType = productTypeRadio
876            ? (productTypeRadio.id === "productType2" ? "canvas" : "set")
877            : "catalog";
878
879        addToCart(selectedItems, productType, productID, 'add');
880    });
881}
882
883
884
885
886
887let additionalItems = [];
888
889document.querySelectorAll('.access-block__add').forEach(button => {
890    button.addEventListener('click', function () {
891        const id = this.closest('.access-block__item').getAttribute('data-id');
892        const name = this.closest('.access-block__item').getAttribute('data-name');
893        const price = this.closest('.access-block__item').getAttribute('data-price');
894
895        this.classList.add('show', 'active');
896
897        addToCart([{ id: id, name: name, price: price }], 'additional', id);
898    });
899});
900
901
902document.addEventListener('click', function (event) {
903    const button = event.target.closest('.card__to-cart, .card__plus, .card__minus');
904    if (!button) return;
905
906
907
908    let card, productID, productName, productPriceElement, productPriceText, productPrice, cardActions, cardTotalElement, cardTotal;
909
910    card = button.closest('.card');
911
912    if (card) {
913        if (isUnavailableProductCard(card) && (button.classList.contains('card__to-cart') || button.classList.contains('card__plus'))) {
914            event.preventDefault();
915            showUnavailableProductNotice(card);
916            return;
917        }
918
919        productID = card.getAttribute('data-product-id');
920        productName = card.querySelector('.card__name').textContent.trim();
921        productPriceElement = card.querySelector('.card__price');
922        productPriceText = productPriceElement.textContent.replace('₽', '').replace(/\s/g, '').trim();
923        productPrice = parseFloat(productPriceText);
924        cardActions = card.querySelector('.card__actions');
925        cardTotalElement = card.querySelector('.card__total');
926        cardTotal = parseInt(cardTotalElement.textContent, 10);
927    } else {
928        card = button.closest('.product-nav__container');
929        if (!card) return;
930
931        productID = card.querySelector('[data-id]').getAttribute('data-id');
932        productName = card.querySelector('.product-nav__title h3').textContent.trim();
933        productPriceElement = card.querySelector('.card__price');
934        productPriceText = productPriceElement.textContent.replace('₽', '').replace(/\s/g, '').trim();
935        productPrice = parseFloat(productPriceText);
936    }
937
938    let items = [{
939        id: productID,
940        name: productName,
941        price: productPrice
942    }];
943
944    if (button.classList.contains('card__to-cart')) {
945        addToCart(items, 'catalog', productID);
946        card.classList.add('added-to-cart');
947        if (cardActions) cardActions.classList.add('up');
948    } else if (button.classList.contains('card__plus')) {
949        cardTotal++;
950        addToCart(items, 'catalog', productID, 'add');
951        cardTotalElement.textContent = cardTotal;
952    } else if (button.classList.contains('card__minus')) {
953        if (cardTotal > 1) {
954            cardTotal--;
955            addToCart(items, 'catalog', productID, 'remove');
956            cardTotalElement.textContent = cardTotal;
957        } else {
958            addToCart(items, 'catalog', productID, 'remove');
959            card.classList.remove('added-to-cart');
960            if (cardActions) cardActions.classList.remove('up');
961        }
962    }
963});
964
965function showUnavailableProductNotice(card) {
966    const message = 'Данный товар временно отсутствует. Оставьте заявку, и мы подберём для вас аналогичную модель';
967    let notice = document.querySelector('.unavailable-product-notice');
968
969    if (!notice) {
970        notice = document.createElement('div');
971        notice.className = 'unavailable-product-notice';
972        document.body.appendChild(notice);
973    }
974
975    notice.textContent = message;
976    notice.classList.add('show');
977    clearTimeout(notice.hideTimer);
978    notice.hideTimer = setTimeout(() => notice.classList.remove('show'), 5000);
979
980    openUnavailableRequestModal(card);
981}
982
983function openUnavailableRequestModal(card) {
984    const productId = card?.getAttribute('data-product-id') || '';
985    const productName = card?.querySelector('.card__name')?.textContent.trim() || '';
986    let modal = document.querySelector('.unavailable-request-modal');
987
988    if (!modal) {
989        modal = document.createElement('div');
990        modal.className = 'modal unavailable-request-modal';
991        document.body.appendChild(modal);
992    }
993
994    const escapeHtml = (value) => String(value).replace(/[&<>'"]/g, (char) => ({
995        '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#039;', '"': '&quot;'
996    }[char]));
997
998    modal.innerHTML = `
999        <div class="modal__container container">
1000            <form class="modal-form unavailable-request-form">
1001                <button class="modal__close btn-reset" data-modal-close type="button" aria-label="Закрыть">
1002                    <svg><use xlink:href="/local/templates/main/img/sprite.svg#reset-icon"></use></svg>
1003                </button>
1004                <span class="h2 modal-form__title">Оставьте заявку</span>
1005                <p class="modal-form__subtitle">Подберём аналогичную модель вместо товара:</p>
1006                <p class="unavailable-request-product">${escapeHtml(productName)}</p>
1007                <div class="modal-form__inputs">
1008                    <input class="input modal-form__input" name="name" type="text" placeholder="Ваше имя*" required>
1009                    <input class="input modal-form__input" name="email" type="email" placeholder="E-mail*" required>
1010                    <input class="input modal-form__input" name="phone" type="tel" placeholder="Телефон">
1011                </div>
1012                <label class="order-form__checkbox checkbox-block">
1013                    <span>Соглашаюсь на обработку персональных данных</span>
1014                    <input type="checkbox" name="terms" value="Y" required>
1015                    <span class="checkmark"></span>
1016                </label>
1017                <input type="hidden" name="product_id" value="${escapeHtml(productId)}">
1018                <input type="hidden" name="product_name" value="${escapeHtml(productName)}">
1019                <input type="hidden" name="sessid" value="${escapeHtml(window.BX?.bitrix_sessid?.() || '')}">
1020                <button class="modal-form__submit btn btn-reset" type="submit">Отправить заявку</button>
1021                <p class="unavailable-request-error" role="alert"></p>
1022            </form>
1023        </div>`;
1024
1025    modal.classList.add('show');
1026    document.body.classList.add('no-scrolling');
1027
1028    const close = () => {
1029        modal.classList.remove('show');
1030        document.body.classList.remove('no-scrolling');
1031    };
1032
1033    modal.querySelector('[data-modal-close]').addEventListener('click', close);
1034    modal.onclick = (event) => {
1035        if (event.target === modal || event.target.classList.contains('modal__container')) close();
1036    };
1037
1038    modal.querySelector('form').addEventListener('submit', (event) => {
1039        event.preventDefault();
1040        const form = event.currentTarget;
1041        const submit = form.querySelector('button[type="submit"]');
1042        const error = form.querySelector('.unavailable-request-error');
1043        submit.disabled = true;
1044        error.textContent = '';
1045
1046        fetch('/local/ajax/unavailable_request.php', {
1047            method: 'POST',
1048            body: new FormData(form)
1049        })
1050            .then(response => response.json())
1051            .then(data => {
1052                if (data.status !== 'success') throw new Error(data.message || 'Не удалось отправить заявку');
1053                form.innerHTML = `
1054                    <button class="modal__close btn-reset" data-modal-close type="button" aria-label="Закрыть">
1055                        <svg><use xlink:href="/local/templates/main/img/sprite.svg#reset-icon"></use></svg>
1056                    </button>
1057                    <span class="h2 modal-form__title">Спасибо за заявку!</span>
1058                    <p class="modal-form__subtitle">Мы подберём аналогичную модель и отправим подтверждение на вашу почту.</p>`;
1059                form.querySelector('[data-modal-close]').addEventListener('click', close);
1060            })
1061            .catch((requestError) => {
1062                error.textContent = requestError.message;
1063                submit.disabled = false;
1064            });
1065    }, {once: true});
1066}
1067
1068
1069document.addEventListener('click', function (event) {
1070    const button = event.target.closest('.basket-card__plus, .basket-card__minus, .basket-card__delete, .basket-card__delete-mobile');
1071    if (!button) return;
1072
1073    if ((button.classList.contains('basket-card__plus') && button.classList.contains('basket-card-plus__complect')) || (button.classList.contains('basket-card__minus') && button.classList.contains('basket-card-minus__complect'))) {
1074        return;
1075    }
1076
1077    if (button.classList.contains('basket-card__delete') || button.classList.contains('basket-card__delete-mobile')) {
1078        // Составляющая комплекта удаляется своим отдельным обработчиком.
1079        if (button.closest('.basket-card__block')) {
1080            return;
1081        }
1082
1083        const basketCard = button.closest('.basket-card');
1084        const basketItemId = basketCard?.getAttribute('data-id');
1085        if (!basketItemId) return;
1086
1087        event.preventDefault();
1088        BX.ajax({
1089            url: '/local/ajax/dell_cart.php',
1090            data: {
1091                action: 'delete',
1092                idsToDelete: [basketItemId]
1093            },
1094            method: 'POST',
1095            dataType: 'json',
1096            onsuccess: function (response) {
1097                if (response.status === 'success') {
1098                    // Перерисовываем корзин
1098у, чтобы синхронно обновились составы и итоги.
1099                    window.location.reload();
1100                } else {
1101                    console.error('Ошибка удаления: ', response.message);
1102                }
1103            },
1104            onfailure: function (error) {
1105                console.error('Произошла ошибка: ', error);
1106            }
1107        });
1108        return;
1109    }
1110
1111    if (button.classList.contains('basket-card__plus') || button.classList.contains('basket-card__minus')) {
1112        const basketCard = button.closest('.basket-card');
1113
1114        if (!basketCard) return;
1115        const productId = basketCard.getAttribute('data-product-id');
1116        const productName = basketCard.querySelector('.basket-card__name').textContent.trim();
1117        const productPrice = parseFloat(basketCard.getAttribute('data-product-price'));
1118        const quantityField1 = basketCard.querySelector('.costum_total');
1119        const quantityField2 = basketCard.querySelector('.costum_total2');
1120
1121        if (!quantityField1 && !quantityField2) return;
1122
1123        let quantity1 = quantityField1 ? parseInt(quantityField1.value, 10) : 0;
1124        let quantity2 = quantityField2 ? parseInt(quantityField2.value, 10) : 0;
1125
1126
1127        let action;
1128        if (button.classList.contains('basket-card__plus')) {
1129            quantity1++;
1130            quantity2++;
1131            action = 'add';
1132        } else if (button.classList.contains('basket-card__minus')) {
1133            if (quantity1 > 1) {
1134                quantity1--;
1135            } else {
1136                removeFromCart(productId);
1137            }
1138            if (quantity2 > 1) {
1139                quantity2--;
1140            }
1141            action = 'remove';
1142        }
1143
1144        const priceElement = basketCard.querySelector('.basket-card__price');
1145        if (priceElement) {
1146            const currentPriceText = priceElement.textContent.trim();
1147            const currentPrice = parseFloat(currentPriceText.replace(/\s+/g, '').replace('₽', ''));
1148            let newPrice;
1149
1150            if (action === 'add') {
1151                newPrice = currentPrice + productPrice;
1152            } else if (action === 'remove') {
1153                newPrice = currentPrice - productPrice;
1154            }
1155
1156            priceElement.textContent = newPrice.toLocaleString('ru-RU') + ' ₽';
1157        }
1158
1159
1160
1161        addToCart([{ id: productId, name: productName, price: productPrice }], 'catalog', productId, action, quantity1, quantity2);
1162
1163
1164
1165        if (quantityField1) {
1166            quantityField1.value = quantity1;
1167        }
1168
1169        if (quantityField2) {
1170            quantityField2.value = quantity2;
1171        }
1172        recalculateBasket();
1173    } else if (button.classList.contains('basket-card__delete')) {
1174        const basketCard = button.closest('.basket-card');
1175        if (basketCard) {
1176            const productId = basketCard.getAttribute('data-product-id');
1177            const productName = basketCard.querySelector('.basket-card__name').textContent.trim();
1178            const productPrice = parseFloat(basketCard.getAttribute('data-product-price'));
1179            const isDveriProduct = basketCard.getAttribute('data-dveri') === 'Y';
1180
1181
1182            const itemPriceElement = document.getElementById('basket-item-sum-price-3118');
1183            if (itemPriceElement) {
1184                const currentItemPriceText = itemPriceElement.textContent.trim();
1185                const currentItemPrice = parseFloat(currentItemPriceText.replace(/\s+/g, '').replace('₽', ''));
1186                const newItemPrice = currentItemPrice - productPrice;
1187                itemPriceElement.textContent = newItemPrice.toLocaleString('ru-RU') + ' ₽';
1188            }
1189
1190            const totalElement = document.querySelector('.basket-total__price');
1191            if (totalElement) {
1192                const currentTotalText = totalElement.textContent.trim();
1193                let currentTotal = parseFloat(currentTotalText.replace(/\s+/g, '').replace('₽', ''));
1194                if (isDveriProduct) {
1195                    currentTotal -= 1700;
1196                }
1197                const newTotal = currentTotal - productPrice;
1198                totalElement.textContent = newTotal.toLocaleString('ru-RU') + ' ₽';
1199            }
1200
1201            const totalItemRightElement = document.querySelector('.basket-total__item-right');
1202            if (totalItemRightElement) {
1203                const currentTotalItemRightText = totalItemRightElement.textContent.trim();
1204                const currentTotalItemRight = parseFloat(currentTotalItemRightText.replace(/\s+/g, '').replace('₽', ''));
1205                const newTotalItemRight = currentTotalItemRight - productPrice;
1206                totalItemRightElement.textContent = newTotalItemRight.toLocaleString('ru-RU') + ' ₽';
1207            }
1208
1209            recalculateBasket();
1210
1211            addToCart([{ id: productId, name: productName, price: productPrice }], 'catalog', productId, 'remove_all', 10);
1212            removeFromCart(productId);
1213        }
1214    } else if (button.classList.contains('basket-card__delete-mobile')) {
1215        const basketCard = button.closest('.basket-card');
1216        if (basketCard) {
1217            const productId = basketCard.getAttribute('data-product-id');
1218            const productName = basketCard.querySelector('.basket-card__name').textContent.trim();
1219            const productPrice = parseFloat(basketCard.getAttribute('data-product-price'));
1220
1221
1222            const itemPriceElement = document.getElementById('basket-item-sum-price-3118');
1223            if (itemPriceElement) {
1224                const currentItemPriceText = itemPriceElement.textContent.trim();
1225                const currentItemPrice = parseFloat(currentItemPriceText.replace(/\s+/g, '').replace('₽', ''));
1226                const newItemPrice = currentItemPrice - productPrice;
1227                itemPriceElement.textContent = newItemPrice.toLocaleString('ru-RU') + ' ₽';
1228            }
1229
1230            const totalElement = document.querySelector('.basket-total__price');
1231            if (totalElement) {
1232                const currentTotalText = totalElement.textContent.trim();
1233                const currentTotal = parseFloat(currentTotalText.replace(/\s+/g, '').replace('₽', ''));
1234                const newTotal = currentTotal - productPrice;
1235                totalElement.textContent = newTotal.toLocaleString('ru-RU') + ' ₽';
1236            }
1237
1238            const totalItemRightElement = document.querySelector('.basket-total__item-right');
1239            if (totalItemRightElement) {
1240                const currentTotalItemRightText = totalItemRightElement.textContent.trim();
1241                const currentTotalItemRight = parseFloat(currentTotalItemRightText.replace(/\s+/g, '').replace('₽', ''));
1242                const newTotalItemRight = currentTotalItemRight - productPrice;
1243                totalItemRightElement.textContent = newTotalItemRight.toLocaleString('ru-RU') + ' ₽';
1244            }
1245
1246            addToCart([{ id: productId, name: productName, price: productPrice }], 'catalog', productId, 'remove_all', 10);
1247            removeFromCart(productId);
1248        }
1249    }
1250});
1251
1252function removeFromCart(productId) {
1253    const basketCard = document.querySelector(`.basket-card[data-product-id="${productId}"]`);
1254    if (basketCard) {
1255        basketCard.remove();
1256    }
1257}
1258
1259
1260function addToCart(items, type, mainProductId, action = 'add') {
1261    let formData = new FormData();
1262
1263    formData.append('mainProduct', mainProductId);
1264
1265    if (type === 'catalog') {
1266        formData.append('action', action);
1267        formData.append('productType', 'catalog');
1268        formData.append('notes', JSON.stringify(items));
1269    } else {
1270        if (type !== 'additional') {
1271            let customProductID = document.querySelector('input[name="productSize"]:checked')?.value || '';
1272            let customProductName = document.querySelector('input[name="productSize"]:checked')?.getAttribute('data-name') || '';
1273            let customProductPriceEl;
1274
1275            const checkedType = document.querySelector('input[name="productType"]:checked');
1276
1277            if (checkedType) {
1278                const wrap = checkedType.closest('.product-sides__item');
1279                customProductPriceEl = wrap
1280                    ? wrap.querySelector('.product-choose__price, .product-sides__price')
1281                    : null;
1282            }
1283
1284            let customProductPrice = customProductPriceEl
1285                ? customProductPriceEl.textContent.replace(/\D/g, '')
1286                : 0;
1287
1288            formData.append('customProductPrice', customProductPrice);
1289            formData.append('customProductID', customProductID);
1290            formData.append('customProductName', customProductName);
1291        }
1292
1293        let setProducts = items.map(item => item.id);
1294
1295        formData.append('setProducts', JSON.stringify(setProducts));
1296        formData.append('productType', type);
1297        formData.append('action', action);
1298
1299        if (type !== 'additional') {
1300            items = items.slice(1);
1301        }
1302
1303        formData.append('notes', JSON.stringify(items));
1304    }
1305
1306    fetch('/local/ajax/add_cart.php', {
1307        method: 'POST',
1308        body: formData
1309    })
1310        .then(response => {
1311            if (!response.ok) {
1312                throw new Error('Ошибка сервера: ' + response.statusText);
1313            }
1314
1315            return response.json();
1316        })
1317        .then(data => {
1318            if (data.status !== 'success') {
1319                throw new Error(data.message || 'Ошибка при изменении корзин
1319ы.');
1320            }
1321
1322            if (action === 'add') {
1323                window.SiteMetrika?.reach('cart_add', {product_id: mainProductId, product_type: type}, {
1324                    dedupeMs: 1000,
1325                    dedupeKey: 'cart_add:' + mainProductId
1326                });
1327            }
1328
1329            const notification = action === 'remove'
1330                ? document.querySelector('.removedCart')
1331                : document.querySelector('.addedCart');
1332
1333            notification?.classList.add('show');
1334
1335            $('#cart').load('/local/ajax/mini_basket.php', { ajax: 'Y' }, function () {
1336                checkHiddenProduct().catch(function (error) {
1337                    console.error('Не удалось проверить служебную позицию корзины:', error);
1338                });
1339            });
1340
1341            setTimeout(() => {
1342                notification?.classList.remove('show');
1343            }, 3000);
1344        })
1345        .catch(error => {
1346            console.error('Ошибка:', error);
1347            alert(error.message || 'Произошла ошибка при изменении корзины.');
1348        });
1349}
1350
1351
1352
1353
1354document.addEventListener("DOMContentLoaded", function () {
1355    let deleteButtons = document.querySelectorAll('.basket-card__block-delete');
1356
1357    deleteButtons.forEach(function (button) {
1358        button.addEventListener('click', function () {
1359            let card = button.closest('.basket-card');
1360            let productId = card.dataset.id;
1361
1362            let partId = button.closest('.basket-card__block').dataset.id;
1363
1364            let partPrice = parseFloat(button.closest('.basket-card__block').querySelector('.basket-card__block-total').textContent.replace(/\D/g, ''));
1365
1366            fetch('/local/ajax/delete_complect.php', {
1367                method: 'POST',
1368                body: JSON.stringify({
1369                    action: 'delete_product_part',
1370                    product_id: productId,
1371                    part_id: partId,
1372                    partPrice: partPrice
1373                }),
1374                headers: {
1375                    'Content-Type': 'application/json'
1376                }
1377            })
1378                .then(response => response.json())
1379                .then(data => {
1380                    if (data.status === 'success') {
1381                        button.closest('.basket-card__block').remove();
1382
1383                        let totalSpan = card.querySelector('.basket-item-price-current-text');
1384                        let currentTotal = parseFloat(totalSpan.textContent.replace(/\D/g, ''));
1385                        totalSpan.textContent = formatPrice(currentTotal - partPrice) + ' ₽';
1386
1387                        let overallTotalSpans = document.querySelectorAll('.basket-total__price, .basket-total__item-right');
1388                        overallTotalSpans.forEach(span => {
1389                            let overallTotal = parseFloat(span.textContent.replace(/\D/g, ''));
1390                            span.textContent = formatPrice(overallTotal - partPrice) + ' ₽';
1391                        });
1392                    } else {
1393                        alert('Ошибка при удалении части товара.');
1394                    }
1395                })
1396                .catch(error => {
1397                    console.error('Ошибка:', error);
1398                    alert('Произошла ошибка при удалении части товара.');
1399                });
1400        });
1401    });
1402
1403
1404
1405    document.querySelectorAll('.basket-card__block-actions button').forEach(function (button) {
1406        button.addEventListener('click', function () {
1407            let actionContainer = button.closest('.basket-card__block-actions');
1408            let totalSpan = actionContainer.querySelector('.basket-card-total__complect');
1409            let currentQuantity = parseInt(totalSpan.textContent);
1410            let priceSpan = button.closest('.basket-card__block').querySelector('.basket-card__block-total');
1411            let unitPrice = parseFloat(priceSpan.textContent.replace(/\D/g, '')) / currentQuantity;
1412
1413            if (button.classList.contains('card__minus') && currentQuantity > 1) {
1414                currentQuantity -= 1;
1415            } else if (button.classList.contains('card__plus')) {
1416                currentQuantity += 1;
1417            } else {
1418                return;
1419            }
1420
1421            totalSpan.textContent = currentQuantity;
1422            priceSpan.textContent = formatPrice(unitPrice * currentQuantity) + ' ₽';
1423
1424            let card = button.closest('.basket-card');
1425            let totalCardSpan = card.querySelector('.basket-item-price-current-text');
1426            let currentTotalCard = parseFloat(totalCardSpan.textContent.replace(/\D/g, ''));
1427            totalCardSpan.textContent = formatPrice(currentTotalCard + (button.classList.contains('card__minus') ? -unitPrice : unitPrice)) + ' ₽';
1428
1429            let overallTotalSpans = document.querySelectorAll('.basket-total__price, .basket-total__item-right');
1430            overallTotalSpans.forEach(span => {
1431                let overallTotal = parseFloat(span.textContent.replace(/\D/g, ''));
1432                span.textContent = formatPrice(overallTotal + (button.classList.contains('card__minus') ? -unitPrice : unitPrice)) + ' ₽';
1433            });
1434
1435            let productId = card.dataset.id;
1436            let partId = button.closest('.basket-card__block').dataset.id;
1437            let updatedQuantity = currentQuantity;
1438
1439            fetch('/local/ajax/delete_complect.php', {
1440                method: 'POST',
1441                body: JSON.stringify({
1442                    action: 'update_product_part_quantity',
1443                    product_id: productId,
1444                    part_id: partId,
1445                    quantity: updatedQuantity
1446                }),
1447                headers: {
1448                    'Content-Type': 'application/json'
1449                }
1450            })
1451                .then(response => response.json())
1452                .then(data => {
1453                    if (data.status !== 'success') {
1454                        alert('Ошибка при обновлении количества товара на сервере.');
1455                    }
1456                })
1457                .catch(error => {
1458                    console.error('Ошибка:', error);
1459                    alert('Произошла ошибка при обновлении количества товара.');
1460                });
1461        });
1462    });
1463
1464
1465
1466
1467
1468});
1469
1470function formatPrice(price) {
1471    return price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
1472}
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483//Наценка в корзине
1484
1485let setAllDoorsCheckbox = $("input[name='setAllDoors']");
1486let ustanovkaBlock = $('.ustanovka');
1487let hiddenProductSum = 0;
1488
1489let requestInProgress = false;
1490let hiddenProductCheckPromise = null;
1491
1492// Функция для управления скрытым товаром
1493function manageHiddenProduct(action) {
1494    return new Promise((resolve, reject) => {
1495        if (requestInProgress) {
1496            return reject('Request is already in progress');
1497        }
1498
1499        requestInProgress = true;
1500
1501        $.ajax({
1502            url: "/local/ajax/pr.php",
1503            type: "POST",
1504            data: { action: action },
1505            dataType: "json",
1506            success: function (response) {
1507                resolve(response);
1508            },
1509            error: function (xhr) {
1510                reject(xhr.responseText);
1511            },
1512            complete: function () {
1513                requestInProgress = false;
1514            }
1515        });
1516    });
1517}
1518
1519
1520function checkHiddenProduct() {
1521    if (hiddenProductCheckPromise) {
1522        return hiddenProductCheckPromise;
1523    }
1524
1525    hiddenProductCheckPromise = new Promise((resolve, reject) => {
1526        $.ajax({
1527            url: "/local/ajax/check_hidden_product.php",
1528            type: "POST",
1529            dataType: "json",
1530            success: function (response) {
1531                if (!response || response.success === false) {
1532                    reject(response?.error || 'Не удалось проверить служебную позицию корзины');
1533                    return;
1534                }
1535
1536                hiddenProductSum = 0;
1537                updateUstanovkaBlock('0 ₽');
1538
1539                updateBasketCount(response.positionsCountAll);
1540
1541                setAllDoorsCheckbox.prop('checked', response.hasHiddenProduct);
1542                response.hasHiddenProduct ? ustanovkaBlock.addClass('show') : ustanovkaBlock.removeClass('show');
1543                resolve(hiddenProductSum);
1544                recalculateBasket();
1545            },
1546            error: function (xhr) {
1547                const responseMessage = xhr.responseJSON?.error || xhr.responseText || 'Ошибка проверки корзины';
1548                console.error("Произошла ошибка: " + responseMessage);
1549                reject(responseMessage);
1550            },
1551            complete: function () {
1552                hiddenProductCheckPromise = null;
1553            }
1554        });
1555    });
1556
1557    return hiddenProductCheckPromise;
1558}
1559
1560
1561function updateBasketCount(count) {
1562    let basketCountElements = document.querySelectorAll('.actions__label.basket-head-count');
1563
1564    basketCountElements.forEach(function (basketCountElement) {
1565        basketCountElement.textContent = count;
1566
1567        if (count === 0) {
1568            replaceBasketBlock();
1569        }
1570    });
1571}
1572
1573function replaceBasketBlock() {
1574    const newBasketBlock = document.createElement('section');
1575    newBasketBlock.className = 'basket';
1576    newBasketBlock.innerHTML = `
1577    <div class="basket__container">
1578            <div class="breadcrumbs">
1579                <ul class="breadcrumbs__list list-reset">
1580                    <li><a href="/">Главная</a></li>
1581                    <li>Корзина</li>
1582                </ul>
1583            </div>
1584            <div class="basket__head" id="basket-items-list-wrapper" data-entity="basket-items-list-header">
1585                <h1 class="basket__title section-title to-left">Корзина</h1>
1586            </div>
1587            <section class="compare compare-page">
1588                <div class="saved-empty cart-empty">
1589                    <svg>
1590                        <use xlink:href="/local/templates/main/img/sprite.svg#cart-icon"></use>
1591                    </svg>
1592                    <p class="saved-empty__desc">Вы&nbsp;еще не&nbsp;добавили ни&nbsp;одного товара в&nbsp;корзину</p>
1593                    <a href="/product-category/mezhkomnatnye-dveri/" class="saved-empty__link btn">Перейти в&nbsp;каталог</a>
1594                </div>
1595            </section>
1596    </div>
1597    `;
1598
1599    const oldBasketBlock = document.querySelector('.basket');
1600
1601    if (oldBasketBlock) {
1602        oldBasketBlock.parentNode.replaceChild(newBasketBlock, oldBasketBlock);
1603    }
1604}
1605
1606
1607function updateUstanovkaBlock(sum) {
1608    if (!sum || sum === "0 ₽") {
1609        ustanovkaBlock.removeClass('show');
1610        return;
1611    }
1612    ustanovkaBlock.find('.basket-total__item-right').html(sum);
1613}
1614
1615
1616setAllDoorsCheckbox.on("change", function () {
1617    let isChecked = $(this).is(":checked");
1618
1619    if (isChecked) {
1620        manageHiddenProduct('add')
1621            .then(() => checkHiddenProduct())
1622            .then((sum) => {
1623                ustanovkaBlock.addClass('show');
1624                recalculateBasket();
1625            })
1626            .catch(error => {
1627                console.error("Произошла ошибка:", error);
1628            });
1629    } else {
1630        manageHiddenProduct('remove')
1631            .then(() => {
1632                ustanovkaBlock.removeClass('show');
1633                recalculateBasket();
1634            })
1635            .catch(error => {
1636                console.error("Произошла ошибка:", error);
1637            });
1638    }
1639});
1640
1641
1642
1643
1644$(document).ready(function () {
1645    checkHiddenProduct().catch(function (error) {
1646        console.error('Не удалось проверить служебную позицию корзин
1646ы:', error);
1647    });
1648});
1649
1650
1651
1652
1653//Маля корзина
1654
1655
1656
1657
1658
1659
1660
1661
1662//Покупка в 1 клик
1663let buyOneClickButton = $('.basket-total__one, .product-sides__click');
1664let modalByOneClick = $('#modalByOneClick');
1665
1666buyOneClickButton.on('click', function () {
1667    modalByOneClick.addClass('show');
1668
1669    modalByOneClick.attr('aria-hidden', 'false').focus();
1670});
1671
1672$('.card__one-click').on('click', function () {
1673    let $this = $(this);
1674    modalByOneClick.addClass('show');
1675    modalByOneClick.attr('aria-hidden', 'false').focus();
1676
1677    modalByOneClick.data('productID', $this.data('id'));
1678    modalByOneClick.data('productType', $this.data('product_type'));
1679});
1680
1681$(document).on('click', '.js-onelick-submit', function (e) {
1682    e.preventDefault();
1683
1684    let $button = $(this);
1685    let form = document.getElementById('modal-form3');
1686
1687    if (!form) {
1688        alert('Форма не найдена');
1689        return;
1690    }
1691
1692    if (!form.checkValidity()) {
1693        form.reportValidity();
1694        return;
1695    }
1696
1697    if ($button.data('requestRunning')) {
1698        return;
1699    }
1700
1701    $button.data('requestRunning', true);
1702    $button.prop('disabled', true);
1703
1704    let productID;
1705    let productType;
1706
1707    let selectedProductSize = $('input[name="productSize"]:checked').val();
1708    let selectedProductType = $('input[name="productType"]:checked').val();
1709
1710    if (selectedProductSize && selectedProductType) {
1711        productID = selectedProductSize;
1712        productType = selectedProductType;
1713    } else {
1714        productID = modalByOneClick.data('productID');
1715        productType = modalByOneClick.data('productType');
1716    }
1717
1718    let formData = $('#modal-form3').serialize();
1719
1720    formData += '&PRODUCT_TYPE=' + encodeURIComponent(productType || '');
1721    formData += '&PRODUCT_ID=' + encodeURIComponent(productID || '');
1722
1723    let basketExists = $('.basket__container').length > 0 ? 'Y' : 'N';
1724
1725    formData += '&BASKET=' + basketExists;
1726
1727    $.ajax({
1728        url: '/local/ajax/one_order.php',
1729        type: 'POST',
1730        data: formData,
1731        dataType: 'json',
1732        success: function (response) {
1733            if (response.success) {
1734                window.SiteMetrika?.formSuccess('oneclick_ok', {
1735                    product_id: productID,
1736                    order_id: response.orderID || ''
1737                });
1738                $('#modalByOneClick').removeClass('show');
1739                $('#thanksOrderProduct').addClass('show');
1740
1741                $('#thanksOrderProduct .modal-form__subtitle').text(
1742                    '№ заказа ' + response.orderID + ' выслан на вашу электронную почту'
1743                );
1744
1745                form.reset();
1746            } else {
1747                alert(
1748                    'Ошибка при создании заказа: ' +
1749                    (response.error || 'Неизвестная ошибка')
1750                );
1751            }
1752        },
1753        error: function (jqXHR, textStatus) {
1754            alert('Ошибка AJAX-запроса: ' + textStatus);
1755        },
1756        complete: function () {
1757            $button.data('requestRunning', false);
1758            $button.prop('disabled', false);
1759        }
1760    });
1761});
1762
1763
1764//Всякое
1765const editButton = document.querySelector('.checkout-basket__edit');
1766
1767if (editButton) {
1768    editButton.addEventListener('click', function () {
1769        window.location.href = '/personal/basket/';
1770    });
1771}
1772
1773$('.product-sides__cart.btn.btn-reset.close-bt-n').on('click', function () {
1774    $('#productMore').removeClass('show');
1775    $('input[type="radio"][name="productType"][id="productType1"]').prop("checked", true);
1776});
1777
1778
1779function transferErrorMessage() {
1780
1781    let userAuth = document.getElementById('userAuth');
1782
1783    if (userAuth.innerHTML.includes('Неверный логин или пароль')) {
1784        let registerError = document.getElementById('bx_register_error');
1785        registerError.style.display = '';
1786        registerError.querySelector('font.errortext').textContent = 'Неверный логин или пароль.';
1787    }
1788}
1789
1790document.addEventListener('DOMContentLoaded', transferErrorMessage);
1791
1792ymaps.ready(initMapForPickup);
1793
1794function initMapForPickup() {
1795    var mapElement = document.getElementById('mapNoDev2');
1796    if (mapElement) {
1797        let myMap = new ymaps.Map('mapNoDev2', {
1798            center: [56.318276, 43.920090],
1799            zoom: 16,
1800            controls: []
1801        });
1802
1803        let myPlacemark = new ymaps.Placemark([56.318276, 43.920090], {}, {
1804            iconLayout: 'default#image',
1805            iconImageHref: '/local/templates/main/img/marker.png',
1806            iconImageSize: [24, 40]
1807        });
1808        myMap.geoObjects.add(myPlacemark);
1809
1810        var CustomControlClass = function (options) {
1811            CustomControlClass.superclass.constructor.call(this, options);
1812            this._$content = null;
1813            this._geocoderDeferred = null;
1814        };
1815
1816        ymaps.util.augment(CustomControlClass, ymaps.collection.Item, {
1817            onAddToMap: function (map) {
1818                CustomControlClass.superclass.onAddToMap.call(this, map);
1819                this._lastCenter = null;
1820                this.getParent().getChildElement(this).then(this._onGetChildElement, this);
1821            },
1822
1823            onRemoveFromMap: function (oldMap) {
1824                if (this._$content) {
1825                    this._$content.remove();
1826                    this._mapEventGroup.removeAll();
1827                }
1828                CustomControlClass.superclass.onRemoveFromMap.call(this, oldMap);
1829            },
1830
1831            _onGetChildElement: function (parentDomContainer) {
1832                this._$content = $(
1833                    '<div class="info-card">' +
1834                    '<div class="info-card-header">' +
1835                    '<span>Московское шоссе, 83</span>' +
1836                    '</div>' +
1837                    '<div class="info-card-body">' +
1838                    '<div class="hz">' +
1839                    '<div>Время работы:</div>' +
1840                    '<div class="mapcont">Пн–Пт:<br>9:00 — 18:00<br><br>Сб:<br>10:00 — 15:00</div>' +
1841                    '</div>' +
1842                    '<div class="hz">' +
1843                    '<div>Телефон:</div>' +
1844                    '<div class="mapcont">8 (831) 2915763</div>' +
1845                    '</div>' +
1846                    '</div>' +
1847                    '</div>'
1848                ).appendTo(parentDomContainer);
1849                this._mapEventGroup = this.getMap().events.group();
1850                this._$content.find('.close-button').on('click', this._onCloseClick.bind(this));
1851            },
1852
1853        });
1854
1855
1856        var customControl = new CustomControlClass();
1857        myMap.controls.add(customControl, {
1858            float: 'none',
1859            position: {
1860                top: 10,
1861                left: 10
1862            }
1863        });
1864    }
1865}
1866document.addEventListener("DOMContentLoaded", function () {
1867    let phoneInputs = document.getElementsByClassName('phoneNum');
1868
1869    Array.from(phoneInputs).forEach(function (phoneInput) {
1870        phoneInput.addEventListener('input', function (e) {
1871            let currentValue = e.target.value.replace(/\D/g, '');
1872            let formattedValue = '+7 ';
1873
1874            if (e.inputType !== 'deleteContentBackward') {
1875                if (currentValue.length > 1) {
1876                    formattedValue += '(' + currentValue.slice(1, 4);
1877                }
1878                if (currentValue.length >= 4) {
1879                    formattedValue += ') ';
1880                }
1881                if (currentValue.length > 4) {
1882                    formattedValue += currentValue.slice(4, 7);
1883                }
1884                if (currentValue.length >= 7) {
1885                    formattedValue += '-';
1886                }
1887                if (currentValue.length > 7) {
1888                    formattedValue += currentValue.slice(7, 9);
1889                }
1890                if (currentValue.length >= 9) {
1891                    formattedValue += '-';
1892                }
1893                if (currentValue.length > 9) {
1894                    formattedValue += currentValue.slice(9, 11);
1895                }
1896            } else {
1897                formattedValue = '+7 ' + currentValue.slice(1).replace(/(\d{3})(\d{3})(\d{2})(\d{2})/, '($1) $2-$3-$4').trim();
1898            }
1899
1900            e.target.value = formattedValue;
1901        });
1902    });
1903});
1904
1905
1906document.addEventListener('DOMContentLoaded', function () {
1907    const buttons = document.querySelectorAll('.card__btn[data-modal="custom-product"]');
1908    buttons.forEach(function (button) {
1909        button.addEventListener('click', function () {
1910            const cardElement = this.closest('.card');
1911
1912            const productName = cardElement.querySelector('.card__name').textContent;
1913
1914            const inputComment = document.querySelector('input[name="COMMNT"]');
1915            if (inputComment) {
1916                inputComment.value = productName;
1917            }
1918        });
1919    });
1920
1921    const fileInput = document.getElementById('modalFile');
1922    const fileLabel = fileInput.nextElementSibling;
1923
1924    fileInput.addEventListener('change', function () {
1925        let fileName = this.files[0] ? this.files[0].name : '';
1926        fileName = truncateString(fileName, 30);
1927        fileLabel.textContent = fileName;
1928    });
1929});
1930
1931
1932function truncateString(str, num) {
1933    if (str.length > num) {
1934        return str.slice(0, num > 3 ? num - 3 : num) + '...';
1935    }
1936    return str;
1937}

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.