PageSourceSearch

https://kubanskoe-pole.ru/wp-content/plugins/chat-widget-pro/assets/js/frontend.js

js kubanskoe-pole.ru collected 2026-10-08 14:18:25 UTC 21,395 bytes, 509 lines download raw bytes

1(function($) {
2    'use strict';
3
4    class ChatWidgetPro {
5        constructor() {
6            this.config = window.cwp_config || {};
7            this.currentQrIndex = 0;
8            this.messengerKeys = Object.keys(this.config.messengers);
9            this.isModalOpen = false;
10            this.isMobileSheetOpen = false;
11            this.widgetInterval = null;
12            this.touchStartY = 0;
13            this.touchCurrentY = 0;
14            this.currentMessengerIndex = 0;
15            this.widgetVisible = false;
16            this.firstChangeDone = false;
17            this.scanInterval = null;
18            this.isScanActive = false;
19
20            this.init();
21        }
22
23        init() {
24            this.setupWidget();
25            this.setupModal();
26            this.setupMobileSheet();
27            this.setupQrNavigation();
28            this.setupScanAnimation();
29            this.applyDesignSettings();
30
31            this.$widget.hide();
32
33            setTimeout(() => {
34                this.showWidget();
35            }, this.config.design.delay);
36        }
37
38        setupWidget() {
39            this.$widget = $('#cwp-widget');
40            this.$widgetContainer = this.$widget.find('.cwp-widget-container');
41            this.$widgetBubble = this.$widget.find('.cwp-widget-bubble');
42            this.$widgetIcon = this.$widget.find('.cwp-widget-icon');
43
44            this.$widget.addClass(this.config.design.position);
45    
46            const marginRight = this.config.design.margin_right || 20;
47            const marginLeft = this.config.design.margin_left || 20;
48            const marginBottom = this.config.design.margin_bottom || 20;
49
50            if (this.config.design.position === 'bottom-right') {
51                this.$widget.css({
52                    'right': marginRight + 'px',
53                    'left': 'auto',
54                    'bottom': marginBottom + 'px'
55                });
56            } else {
57                this.$widget.css({
58                    'left': marginLeft + 'px',
59                    'right': 'auto',
60                    'bottom': marginBottom + 'px'
61                });
62            }
63
64            this.setInitialIcon();
65
66            this.$widget.on('click', () => {
67                if (this.config.is_mobile) {
68                    this.openMobileSheet();
69                } else {
70                    this.openModal();
71                }
72            });
73
74            if (!this.config.is_mobile) {
75                this.$widget.on('mouseenter', () => {
76                    this.$widget.addClass('pulse');
77                }).on('mouseleave', () => {
78                    this.$widget.removeClass('pulse');
79                });
80            }
81        }
82
83        setInitialIcon() {
84            if (this.messengerKeys.length > 0) {
85                const firstMessengerKey = this.messengerKeys[this.currentMessengerIndex];
86                const messenger = this.config.messengers[firstMessengerKey];
87                
88                this.$widgetIcon.html(this.getMessengerSvg(firstMessengerKey, true));
89                
90                if (firstMessengerKey === 'max') {
91                    this.createMaxGradientBubble();
92                } else {
93                    this.updateBubbleColor(messenger.color);
94                }
95            }
96        }
97
98        setupModal() {
99            this.$modal = $('#cwp-modal');
100            this.$modalOverlay = this.$modal.find('.cwp-modal-overlay');
101            this.$modalClose = this.$modal.find('.cwp-modal-close');
102            this.$modalContent = this.$modal.find('.cwp-modal-content');
103
104            this.$modalClose.on('click', () => this.closeModal());
105            this.$modalOverlay.on('click', () => this.closeModal());
106
107            this.$modalContent.on('click', (e) => e.stopPropagation());
108
109            $(document).on('keydown', (e) => {
110                if (e.keyCode === 27 && this.isModalOpen) {
111                    this.closeModal();
112                }
113            });
114        }
115
116        setupMobileSheet() {
117            this.$mobileSheet = $('#cwp-mobile-sheet');
118            this.$mobileOverlay = this.$mobileSheet.find('.cwp-mobile-overlay');
119            this.$mobileContent = this.$mobileSheet.find('.cwp-mobile-content');
120            this.$mobileDragHandle = this.$mobileSheet.find('.cwp-mobile-drag-handle');
121
122            this.$mobileOverlay.on('click', () => this.closeMobileSheet());
123
124            this.setupSwipeGestures();
125
126            this.$mobileContent.on('click', (e) => e.stopPropagation());
127        }
128
129        setupSwipeGestures() {
130            const content = this.$mobileContent[0];
131            
132            content.addEventListener('touchstart', (e) => {
133                this.touchStartY = e.touches[0].clientY;
134            }, { passive: true });
135
136            content.addEventListener('touchmove', (e) => {
137                if (this.touchStartY === 0) return;
138                
139                this.touchCurrentY = e.touches[0].clientY;
140                const diff = this.touchCurrentY - this.touchStartY;
141                
142                if (diff > 0) {
143                    e.preventDefault();
144                    this.$mobileContent.css('transform', `translateY(${diff}px)`);
145                }
146            }, { passive: false });
147
148            content.addEventListener('touchend', () => {
149                const diff = this.touchCurrentY - this.touchStartY;
150                const threshold = 100;
151                
152                if (diff > threshold) {
153                    this.closeMobileSheet();
154                } else {
155                    this.$mobileContent.css('transform', 'translateY(0)');
156                }
157                
158                this.touchStartY = 0;
159                this.touchCurrentY = 0;
160            });
161        }
162
163        setupQrNavigation() {
164            this.$qrImage = this.$modal.find('.cwp-qr-image');
165            this.$qrTitle = this.$modal.find('.cwp-qr-title');
166            this.$qrPrev = this.$modal.find('.cwp-qr-prev');
167            this.$qrNext = this.$modal.find('.cwp-qr-next');
168
169            this.$qrPrev.on('click', () => this.prevQr());
170            this.$qrNext.on('click', () => this.nextQr());
171            
172            this.$qrPrev.css({
173                'opacity': '1',
174                'visibility': 'visible'
175            });
176            this.$qrNext.css({
177                'opacity': '1', 
178                'visibility': 'visible'
179            });
180
181            this.updateQrDisplay();
182        }
183
184        setupScanAnimation() {
185            this.$scanAnimation = this.$modal.find('.cwp-qr-scan-animation');
186            this.$scanLine = this.$modal.find('.cwp-scan-line');
187            
188            this.startScanAnimation();
189        }
190
191        startScanAnimation() {
192            if (this.scanInterval) {
193                clearInterval(this.scanInterval);
194            }
195    
196            this.scanInterval = setInterval(() => {
197                if (this.isModalOpen && this.$scanAnimation.is(':visible')) {
198                    this.showScanAnimation();
199                }
200            }, 7000);
201        }
202
203        showScanAnimation() {
204            if (this.isScanActive) return;
205            
206            this.isScanActive = true;
207            
208            this.$scanAnimation.addClass('active');
209            
210            this.$scanLine.css('animation', 'none');
211            setTimeout(() => {
212                this.$scanLine.css('animation', 'cwp-scan-line 2s ease-in-out');
213            }, 10);
214            
215            setTimeout(() => {
216                this.$scanAnimation.removeClass('active');
217                this.isScanActive = false;
218            }, 2000);
219        }
220
221        stopScanAnimation() {
222            if (this.scanInterval) {
223                clearInterval(this.scanInterval);
224                this.scanInterval = null;
225            }
226            this.$scanAnimation.removeClass('active');
227            this.isScanActive = false;
228        }
229
230        applyDesignSettings() {
231            if (this.config.design.modal_bg_color) {
232                this.$modalContent.css('background-color', this.config.design.modal_bg_color);
233                this.$mobileContent.css('background-color', this.config.design.modal_bg_color);
234            }
235
236            if (this.config.texts.title_font_size) {
237                this.$modal.find('.cwp-modal-title').css('font-size', this.config.texts.title_font_size + 'px');
238            }
239            if (this.config.texts.title_color) {
240                this.$modal.find('.cwp-modal-title').css('color', this.config.texts.title_color);
241            }
242            if (this.config.texts.subtitle_font_size) {
243                this.$modal.find('.cwp-modal-subtitle').css('font-size', this.config.texts.subtitle_font_size + 'px');
244            }
245            if (this.config.texts.subtitle_color) {
246                this.$modal.find('.cwp-modal-subtitle').css('color', this.config.texts.subtitle_color);
247            }
248
249            if (this.config.texts.title_font_size) {
250                this.$mobileSheet.find('.cwp-mobile-title').css('font-size', this.config.texts.title_font_size + 'px');
251            }
252            if (this.config.texts.title_color) {
253                this.$mobileSheet.find('.cwp-mobile-title').css('color', this.config.texts.title_color);
254            }
255            if (this.config.texts.subtitle_font_size) {
256                this.$mobileSheet.find('.cwp-mobile-subtitle').css('font-size', this.config.texts.subtitle_font_size + 'px');
257            }
258            if (this.config.texts.subtitle_color) {
259                this.$mobileSheet.find('.cwp-mobile-subtitle').css('color', this.config.texts.subtitle_color);
260            }
261
262            if (this.config.texts.qr_hint_font_size) {
263                this.$modal.find('.cwp-qr-hint').css('font-size', this.config.texts.qr_hint_font_size + 'px');
264            }
265            if (this.config.texts.qr_hint_color) {
266                this.$modal.find('.cwp-qr-hint').css('color', this.config.texts.qr_hint_color);
267            }
268
269            if (this.config.texts.footer_text_font_size) {
270                this.$modal.find('.cwp-modal-footer').css('font-size', this.config.texts.footer_text_font_size + 'px');
271                this.$mobileSheet.find('.cwp-mobile-footer').css('font-size', this.config.texts.footer_text_font_size + 'px');
272            }
273            if (this.config.texts.footer_text_color) {
274                this.$modal.find('.cwp-modal-footer').css('color', this.config.texts.footer_text_color);
275                this.$mobileSheet.find('.cwp-mobile-footer').css('color', this.config.texts.footer_text_color);
276            }
277        }
278
279        startWidgetAnimation() {
280            if (this.messengerKeys.length <= 1) {
281                this.$widgetIcon.addClass('float');
282                return;
283            }
284
285            setTimeout(() => {
286                this.animateIconChange();
287            }, 2000);
288            
289            this.widgetInterval = setInterval(() => {
290                this.animateIconChange();
291            }, 3000);
292        }
293
294        animateIconChange() {
295            if (this.messengerKeys.length <= 1) return;
296
297            if (!this.firstChangeDone) {
298                this.currentMessengerIndex = (this.currentMessengerIndex + 1) % this.messengerKeys.length;
299                this.firstChangeDone = true;
300            }
301
302            const currentMessengerKey = this.messengerKeys[this.currentMessengerIndex];
303            const messenger = this.config.messengers[currentMessengerKey];
304            
305            this.$widgetIcon.addClass('animate-out');
306            this.rotateBubble();
307            
308            setTimeout(() => {
309                this.$widgetIcon.html(this.getMessengerSvg(currentMessengerKey, true));
310                
311                if (currentMessengerKey === 'max') {
312                    this.createMaxGradientBubble();
313                } else {
314                    this.updateBubbleColor(messenger.color);
315                }
316                
317                this.$widgetIcon.removeClass('animate-out').addClass('animate-in');
318                
319                this.currentMessengerIndex = (this.currentMessengerIndex + 1) % this.messengerKeys.length;
320                
321                setTimeout(() => {
322                    this.$widgetIcon.removeClass('animate-in');
323                }, 600);
324                
325            }, 400);
326        }
327
328        rotateBubble() {
329            this.$widgetBubble.css('transform', 'rotate(-10deg)');
330            
331            setTimeout(() => {
332                this.$widgetBubble.css('transform', 'rotate(0deg)');
333            }, 200);
334        }
335
336        createMaxGradientBubble() {
337            const newBubbleSvg = `
338                <svg class="cwp-widget-bubble" viewBox="0 0 16 16" width="60" height="60">
339                    <defs>
340                        <linearGradient id="max-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
341                            <stop offset="0%" style="stop-color:#4bc2fd;stop-opacity:1" />
342                            <stop offset="100%" style="stop-color:#8E44AD;stop-opacity:1" />
343                        </linearGradient>
344                    </defs>
345                    <path d="M8 15c4.418 0 8-3.134 8-7s-3.582-7-8-7-8 3.134-8 7c0 1.76.743 3.37 1.97 4.6-.097 1.016-.417 2.13-.771 2.966-.079.186.074.394.273.362 2.256-.37 3.597-.938 4.18-1.234A9.06 9.06 0 0 0 8 15z" fill="url(#max-gradient)"/>
346                </svg>
347            `;
348            this.$widgetBubble.replaceWith(newBubbleSvg);
349            this.$widgetBubble = this.$widget.find('.cwp-widget-bubble');
350        }
351
352        updateBubbleColor(color) {
353            const newBubbleSvg = `
354                <svg class="cwp-widget-bubble" viewBox="0 0 16 16" width="60" height="60" style="fill: ${color}">
355                    <path d="M8 15c4.418 0 8-3.134 8-7s-3.582-7-8-7-8 3.134-8 7c0 1.76.743 3.37 1.97 4.6-.097 1.016-.417 2.13-.771 2.966-.079.186.074.394.273.362 2.256-.37 3.597-.938 4.18-1.234A9.06 9.06 0 0 0 8 15z"/>
356                </svg>
357            `;
358            this.$widgetBubble.replaceWith(newBubbleSvg);
359            this.$widgetBubble = this.$widget.find('.cwp-widget-bubble');
360        }
361
362        stopWidgetAnimation() {
363            if (this.widgetInterval) {
364                clearInterval(this.widgetInterval);
365                this.widgetInterval = null;
366            }
367            this.$widgetIcon.removeClass('animate-in animate-out float');
368            this.firstChangeDone = false;
369        }
370
371        showWidget() {
372            if (this.widgetVisible) return;
373            
374            this.widgetVisible = true;
375            this.$widget.addClass('animate').show();
376            
377            setTimeout(() => {
378                this.$widget.addClass('pulse');
379                this.startWidgetAnimation();
380            }, 1000);
381        }
382
383        hideWidget() {
384            this.$widget.removeClass('pulse animate');
385            this.$widget.hide();
386            this.stopWidgetAnimation();
387            this.widgetVisible = false;
388        }
389
390        showWidgetAgain() {
391            if (this.widgetVisible) return;
392            
393            this.widgetVisible = true;
394            this.$widget.addClass('animate').show();
395            setTimeout(() => {
396                this.$widget.addClass('pulse');
397                this.startWidgetAnimation();
398            }, 1000);
399        }
400
401        openModal() {
402            this.isModalOpen = true;
403            this.$modal.show();
404            this.$modalContent.hide().fadeIn(300);
405
406            this.updateQrDisplay();
407            
408            this.startScanAnimation();
409        }
410
411        closeModal() {
412            this.isModalOpen = false;
413            this.stopScanAnimation();
414            this.$modalContent.fadeOut(300, () => {
415                this.$modal.hide();
416                this.showWidgetAgain();
417            });
418        }
419
420        openMobileSheet() {
421            this.isMobileSheetOpen = true;
422            this.$mobileSheet.show();
423            
424            setTimeout(() => {
425                this.$mobileSheet.addClass('open');
426            }, 10);
427            
428            $('body').css('overflow', 'hidden');
429        }
430
431        closeMobileSheet() {
432            this.isMobileSheetOpen = false;
433            this.$mobileSheet.removeClass('open');
434            
435            setTimeout(() => {
436                this.$mobileSheet.hide();
437                this.showWidgetAgain();
438            }, 300);
439            
440            $('body').css('overflow', '');
441        }
442
443        updateQrDisplay() {
444            if (this.messengerKeys.length === 0) return;
445
446            const currentMessengerKey = this.messengerKeys[this.currentQrIndex];
447            const messenger = this.config.messengers[currentMessengerKey];
448            
449            if (messenger && messenger.qr_url) {
450                this.$qrImage.attr('src', messenger.qr_url)
451                    .attr('alt', `QR код для ${messenger.name}`);
452                
453                this.$qrTitle.html(`
454                    <span class="cwp-qr-title-icon">
455                        ${this.getMessengerSvg(currentMessengerKey, false)}
456                    </span>
457                    ${messenger.name}
458                `);
459            }
460
461            const showNavigation = this.messengerKeys.length > 1 && !this.config.is_mobile;
462    
463            if (showNavigation) {
464                this.$qrPrev.css({
465                    'opacity': '1',
466                    'visibility': 'visible',
467                    'display': 'flex'
468                });
469                this.$qrNext.css({
470                    'opacity': '1', 
471                    'visibility': 'visible',
472                    'display': 'flex'
473                });
474            } else {
475                this.$qrPrev.hide();
476                this.$qrNext.hide();
477            }
478        }
479
480        nextQr() {
481            this.currentQrIndex = (this.currentQrIndex + 1) % this.messengerKeys.length;
482            this.updateQrDisplay();
483        }
484
485        prevQr() {
486            this.currentQrIndex = this.currentQrIndex === 0 ? 
487                this.messengerKeys.length - 1 : 
488                this.currentQrIndex - 1;
489            this.updateQrDisplay();
490        }
491
492        getMessengerSvg(messengerKey, forWidget = true) {
493            let fillColor = forWidget ? 'white' : 'currentColor';
494            
495            const svgMap = {
496                'whatsapp': `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="${fillColor}" d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893-.001-3.189-1.262-6.209-3.553-8.503"/></svg>`,
497                'telegram': `<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"><path fill="${fillColor}" d="M41.4193 7.30899C41.4193 7.30899 45.3046 5.79399 44.9808 9.47328C44.8729 10.9883 43.9016 16.2908 43.1461 22.0262L40.5559 39.0159C40.5559 39.0159 40.3401 41.5048 38.3974 41.9377C36.4547 42.3705 33.5408 40.4227 33.0011 39.9898C32.5694 39.6652 24.9068 34.7955 22.2086 32.4148C21.4531 31.7655 20.5897 30.4669 22.3165 28.9519L33.6487 18.1305C34.9438 16.8319 36.2389 13.8019 30.8426 17.4812L15.7331 27.7616C15.7331 27.7616 14.0063 28.8437 10.7686 27.8698L3.75342 25.7055C3.75342 25.7055 1.16321 24.0823 5.58815 22.459C16.3807 17.3729 29.6555 12.1786 41.4193 7.30899Z"/></svg>`,
498                'max': `<svg viewBox="0 0 42 42" xmlns="http://www.w3.org/2000/svg"><path fill="${fillColor}" fill-rule="evenodd" d="M21.47 41.88c-4.11 0-6.02-.6-9.34-3-2.1 2.7-8.75 4.81-9.04 1.2 0-2.71-.6-5-1.28-7.5C1 29.5.08 26.07.08 21.1.08 9.23 9.82.3 21.36.3c11.55 0 20.6 9.37 20.6 20.91a20.6 20.6 0 0 1-20.49 20.67Zm.17-31.32c-5.62-.29-10 3.6-10.97 9.7-.8 5.05.62 11.2 1.83 11.52.58.14 2.04-1.04 2.95-1.95a10.4 10.4 0 0 0 5.08 1.81 10.7 10.7 0 0 0 11.
49819-9.97 10.7 10.7 0 0 0-10.08-11.1Z" clip-rule="evenodd"></path></svg>`
499            };
500            
501            return svgMap[messengerKey] || '';
502        }
503    }
504
505    $(document).ready(() => {
506        new ChatWidgetPro();
507    });
508
509})(jQuery);

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.