PageSourceSearch

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

js gdekomfort.ru collected 2026-10-05 02:26:09 UTC 8,172 bytes, 220 lines download raw bytes

1/**
2 * Активирует работу избранного на всех страницах
3 */
4new class Favorite extends RsJsCore.classes.component {
5
6    constructor(settings) {
7        super();
8        let defaults = {
9            favorite: '.rs-favorite-page',            //Селектор блока, в котором отображаются товары на странице избранного
10            favoriteBlock: '.rs-favorite-block',  //Блок "избранное"
11            favoriteLink: '.rs-favorite-link',    //Ссылка на front контроллер
12            favoriteCount: '.rs-favorite-items-count',  //Количество избранных товаров
13            favoriteButton: '.rs-favorite',       //Кнопка добавить/удалить избранное
14            inFavoriteClass: 'rs-in-favorite',    //Класс указывающий, что товар в избранном
15            activeClass: 'active',            //Класс, указывающий, что в избранном есть товары
16            context: '[data-id]'
17        };
18        this.settings = {...defaults, ...this.getExtendsSettings(), ...settings};
19    }
20
21    /**
22     * Инициализирует подписку на события
23     */
24    init() {
25        this.blocks = document.querySelectorAll(this.settings.favoriteBlock);
26        let favUrlElement = document.querySelector('[data-favorite-url]');
27        this.addUrl = favUrlElement && favUrlElement.dataset.favoriteUrl;
28        if (this.addUrl) {
29            this.favoriteCounterElement = document.querySelector(this.settings.favoriteCount);
30            this.utils.on('click', this.settings.favoriteButton, event => this.toggleFavorite(event))
31            this.utils.on('click', this.settings.favoriteLink, event => {
32                location.href = event.rsTarget.dataset.href;
33            });
34
35            this.initFirstState();
36            this.initUpdateTitle();
37        } else {
38            console.error(lang.t('Не найден элемент [data-favorite-url], содержащий ссылку на контроллер управления избранным'));
39        }
40    }
41
42    /**
43     * Переключает состояние товара. Помещает/Исключает товар из избранного
44     *
45     * @param event
46     */
47    toggleFavorite(event) {
48        let productId = event.rsTarget.closest('[data-id]').dataset.id;
49
50        if (event.rsTarget.classList.contains(this.settings.inFavoriteClass)) {
51            this.remove(productId);
52        } else {
53            this.add(productId);
54        }
55
56        event.preventDefault();
57    }
58
59    /**
60     * Добавляет товар в избранное
61     *
62     * @param productId
63     */
64    add(productId) {
65        this.toggleIcons(productId, true);
66
67        let data = new FormData();
68        data.append('Act', 'add');
69        data.append('product_id', productId);
70
71        this.utils.fetchJSON(this.addUrl, {
72            method:'POST',
73            body: data
74        }).then((response) => {
75            this.checkActive(response.count, productId, true);
76        });
77    }
78
79    /**
80     * Переключает состояние кнопок в избранное
81     *
82     * @param productId
83     * @param active
84     */
85    toggleIcons(productId, active) {
86        let items = document.querySelectorAll('[data-id="' + productId + '"] ' + this.settings.favoriteButton);
87        items.forEach((element) => {
88            if (active) {
89                element.classList.add(this.settings.inFavoriteClass);
90            } else {
91                element.classList.remove(this.settings.inFavoriteClass);
92            }
93            this.updateTitle(element);
94        });
95
96        return items;
97    }
98
99    /**
100     * Удаление товара из избранного
101     *
102     * @param productId
103     */
104    remove(productId) {
105        let isFavoritePage;
106        let items = this.toggleIcons(productId, false);
107        items.forEach((element) => {
108                isFavoritePage = element.closest(this.settings.favorite);
109                if (isFavoritePage) {
110                    element.closest(this.settings.context).style.opacity = 0.5;
111                }
112            });
113
114        if (items.length) {
115            let data = new FormData();
116            data.append('Act', 'remove');
117            data.append('product_id', productId);
118
119            this.utils.fetchJSON(this.addUrl, {
120                method:'POST',
121                body: data
122            }).then((response) => {
123                if (response.success) {
124                    this.checkActive(response.count, productId, false);
125
126                    if (isFavoritePage) {
127                        this.updateBody();
128                    }
129                }
130
131            });
132        }
133    }
134
135    /**
136     * Обновляет страницу с помощью ajax
137     */
138    updateBody() {
139        this.utils.fetchJSON(window.location.href)
140            .then((response) => {
141                let element = document.querySelector(this.settings.favorite);
142                if (element) {
143                    let parent = element.parentNode;
144                    element.insertAdjacentHTML('afterend', response.html);
145                    element.remove();
146                    parent.dispatchEvent(new CustomEvent('new-content', {bubbles: true}));
147                    this.initUpdateTitle();
148                }
149            });
150    }
151
152    /**
153     * Устанавливает нужный title, в зависимости от того, добавлен ли в избранное товар
154     *
155     * @param element
156     */
157    updateTitle(element) {
158        let title = element.classList.contains(this.settings.inFavoriteClass)
159            ? element.dataset.alreadyTitle : element.dataset.title;
160        if (typeof(title) != 'undefined') {
161            element.title = title;
162        }
163    }
164
165    /**
166     * Инициализирует начальное состояние кнопки В избранное с учетом кэша HTML блока
167     */
168    initFirstState() {
169        if (global.favoriteProducts) {
170            document.querySelectorAll(this.settings.favoriteButton).forEac
170h((element) => {
171                let productId = element.closest(this.settings.context).dataset.id;
172                if (productId) {
173                    let isActive = global.favoriteProducts.indexOf(parseInt(productId)) > -1;
174                    isActive ? element.classList.add(this.settings.inFavoriteClass)
175                        : element.classList.remove(this.settings.inFavoriteClass);
176                }
177            });
178        }
179    }
180
181    /**
182     * Инициализирует всплывающие подсказки у кнопок В Избранное
183     */
184    initUpdateTitle() {
185        document.querySelectorAll(this.settings.favoriteButton + '[data-title]')
186            .forEach((element) => this.updateTitle(element));
187    }
188
189    /**
190     * Добавляет класс activeClass к блоку, если есть хоть один товар в избранном
191     *
192     * @param count
193     */
194    checkActive(count, productId, productState, noFireOpener) {
195        this.blocks.forEach((block) => {
196            let counter = block.querySelector(this.settings.favoriteCount);
197            counter && (counter.innerHTML = count);
198
199            if (count > 0) {
200                block.classList.add(this.settings.activeClass);
201            } else {
202                block.classList.remove(this.settings.activeClass);
203            }
204        });
205
206        if (!noFireOpener) {
207            try {
208                //Обновляем количество товаров в избранном в главном окне
209                window.opener.RsJsCore.components.Favorite.checkActive(count, null, null, true);
210                if (productId) {
211                    window.opener.RsJsCore.components.Favorite.toggleIcons(productId, productState);
212                }
213            } catch(e) {}
214        }
215    }
216
217    onDocumentReady() {
218        this.init();
219    }
220};

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.