PageSourceSearch

https://www.grandline.ru/js/ad-views-tracker.js?v=1765974235

js grandline.ru collected 2026-10-03 19:49:22 UTC 18,457 bytes, 373 lines download raw bytes

1/**
2 * Система отслеживания просмотров рекламных баннеров с ERID
3 * Поддерживает:
4 * - Swiper слайдеры (главная страница)
5 * - Slick слайдеры (интернет-магазин)
6 * - Статические блоки (список новостей)
7 * - AJAX подгрузку контента
8 */
9
10(function() {
11    'use strict';
12
13    // Функция для отправки просмотра на сервер
14    function trackAdView(erid) {
15        console.log('[AdViewsTracker] trackAdView вызван с erid:', erid);
16        
17        if (!erid || erid.trim() === '') {
18            console.warn('[AdViewsTracker] ERID пустой или не указан:', erid);
19            return;
20        }
21
22        // Проверяем, не был ли уже засчитан просмотр для этого erid в текущей сессии
23        const sessionKey = 'tracked_erid_' + erid;
24        if (sessionStorage.getItem(sessionKey)) {
25            console.log('[AdViewsTracker] Просмотр для ERID уже засчитан в этой сессии:', erid);
26            return;
27        }
28
29        console.log('[AdViewsTracker] Отправка запроса на сервер для ERID:', erid);
30        
31        // Отправляем запрос на сервер
32        fetch('/forms/track-ad-view/', {
33            method: 'POST',
34            headers: {
35                'Content-Type': 'application/json',
36                'X-Requested-With': 'XMLHttpRequest'
37            },
38            body: JSON.stringify({
39                erid: erid
40            })
41        })
42        .then(response => {
43            console.log('[AdViewsTracker] Ответ сервера получен:', response.status, response.statusText);
44            return response.json();
45        })
46        .then(data => {
47            console.log('[AdViewsTracker] Данные от сервера:', data);
48            if (data.success) {
49                // Сохраняем в sessionStorage, чтобы не считать повторные просмотры
50                sessionStorage.setItem(sessionKey, '1');
51                console.log('[AdViewsTracker] Просмотр успешно засчитан для ERID:', erid);
52            } else {
53                console.error('[AdViewsTracker] Сервер вернул ошибку:', data.message || 'Неизвестная ошибка');
54            }
55        })
56        .catch(error => {
57            console.error('[AdViewsTracker] Ошибка при отправке просмотра:', error);
58        });
59    }
60
61    // Глобальный объект для хранения observer
62    let adObserver = null;
63
64    // Функция для инициализации отслеживания для всех рекламных блоков
65    function initAdTracking(container) {
66        // Если контейнер не указан, используем document
67        const searchContainer = container || document;
68        
69        // Находим все рекламные блоки, которые еще не отслеживаются
70        const adBlocks = searchContainer.querySelectorAll('.reklama-block:not([data-tracked])');
71        console.log('[AdViewsTracker] Найдено .reklama-block элементов:', adBlocks.length);
72        
73        // Также находим блоки с классом .tooltip.advert (интернет-магазин)
74        const tooltipAdverts = searchContainer.querySelectorAll('.tooltip.advert:not([data-tracked])');
75        console.log('[AdViewsTracker] Найдено .tooltip.advert элементов:', tooltipAdverts.length);
76        
77        if (adBlocks.length === 0 && tooltipAdverts.length === 0) {
78            console.log('[AdViewsTracker] Нет новых блоков для отслеживания');
79            return;
80        }
81
82        // Создаем Intersection Observer, если его еще нет
83        if (!adObserver) {
84            const observerOptions = {
85                root: null, // viewport
86                rootMargin: '0px',
87                threshold: 0.5 // блок считается просмотренным, если видно 50%
88            };
89
90            adObserver = new IntersectionObserver(function(entries) {
91                console.log('[AdViewsTracker] IntersectionObserver обработал', entries.length, 'элементов');
92                entries.forEach(function(entry) {
93                    console.log('[AdViewsTracker] Элемент пересек viewport:', entry.isIntersecting, entry.target);
94                    if (entry.isIntersecting) {
95                        let erid = '';
96                        
97                        // Проверяем блоки с классом .reklama-block (главная страница)
98                        const eridElement = entry.target.querySelector('.reklama-description_erid');
99                        console.log('[AdViewsTracker] Найден элемент erid:', eridElement);
100                        if (eridElement) {
101                            erid = eridElement.textContent.trim();
102                            console.log('[AdViewsTracker] ERID из .reklama-description_erid:', erid);
103                        } else if (entry.target.classList.contains('tooltip') && entry.target.classList.contains('advert')) {
104                            // Проверяем блоки с классом .tooltip.advert (интернет-магазин)
105                            const tooltipBody = entry.target.querySelector('.tooltip__body');
106                            if (tooltipBody) {
107                                const text = tooltipBody.textContent || tooltipBody.innerText;
108                                const eridMatch = text.match(/erid:\s*([^\s\n<]+)/i);
109                                if (eridMatch && eridMatch[1]) {
110                                    erid = eridMatch[1].trim();
111                                    console.log('[AdViewsTracker] ERID из .tooltip.advert:', erid);
112                                }
113                            }
114                        }
115                        
116                        if (erid) {
117                            trackAdView(erid);
118                        } else {
119                            console.warn('[AdViewsTracker] ERID не найден в элементе:', entry.target);
120                        }
121                        
122                        // Помечаем блок как отслеженный и прекращаем наблюдение
123                        entry.target.setAttribute('data-tracked', '1');
124                        adObserver.unobserve(entry.target);
125                    }
126                });
127            }, observerOptions);
128        }
129
130        // Начинаем наблюдение за новыми рекламными блоками
131        adBlocks.forEach(function(block, index) {
132            console.log('[AdViewsTracker] Начинаем наблюдение за блоком', index + 1, ':', block);
133            // Проверяем ERID перед началом наблюдения
134            const eridElement = block.querySelector('.reklama-description_erid');
135            if (eridElement) {
136                const erid = eridElement.textContent.trim();
137                console.log('[AdViewsTracker] ERID в блоке', index + 1, ':', erid || '(пустой)');
138            }
139            adObserver.observe(block);
140        });
141        
142        // Также отслеживаем блоки .tooltip.advert
143        tooltipAdverts.forEach(function(block, index) {
144            console.log('[AdViewsTracker] Начинаем наблюдение за .tooltip.advert блоком', index + 1, ':', block);
145            adObserver.observe(block);
146        });
147    }
148
149    // Функция для отслеживания видимых слайдов в Swiper
150    function trackVisibleSwiperSlides(container) {
151        const searchContainer = container || document;
152        
153        // Находим Swiper контейнеры
154        const swiperContainers = searchContainer.querySelectorAll('.preview-gallery');
155        
156        swiperContainers.forEach(function(swiperContainer) {
157            // Получаем экземпляр Swiper, если он существует
158            const swiperInstance = swiperContainer.swiper;
159            if (!swiperInstance) {
160                return;
161            }
162
163            // Получаем видимые слайды
164            const visibleSlides = swiperInstance.visibleSlides || 
165                Array.from(swiperInstance.slides).filter(function(slide) {
166                    return slide.classList.contains('swiper-slide-visible');
167                });
168
169            // Проверяем рекламные блоки в видимых слайдах
170            visibleSlides.forEach(function(slide) {
171                const adBlock = slide.querySelector('.reklama-block:not([data-tracked])');
172                if (adBlock) {
173                    const eridElement = adBlock.querySelector('.reklama-description_erid');
174                    if (eridElement) {
175                        const erid = eridElement.textContent.trim();
176                        if (erid) {
177                            trackAdView(erid);
178                            adBlock.setAttribute('data-tracked', '1');
179                        }
180                    }
181                }
182            });
183        });
184    }
185
186    // Функция для отслеживания видимых слайдов в Slick слайдере
187    function trackVisibleSlickSlides(container) {
188        const searchContainer = container || document;
189        
190        // Находим Slick слайдеры
191        const slickSliders = searchContainer.querySelectorAll('.slick-slider, .hero.js-hero-slider');
192        
193        slickSliders.forEach(function(slider) {
194            // Проверяем, инициализирован ли Slick
195            if (typeof jQuery !== 'undefined' && jQuery(slider).hasClass('slick-initialized')) {
196                // Получаем видимые слайды
197                const visibleSlides = jQuery(slider).find('.slick-slide.slick-active, .slick-slide.slick-current');
198                
199                visibleSlides.each(function() {
200                    const slide = this;
201                    
202                    // Проверяем блоки с классом .reklama-block (главная страница)
203                    const adBlock = slide.querySelector('.reklama-block:not([data-tracked])');
204                    if (adBlock) {
205                        const eridElement = adBlock.querySelector('.reklama-description_erid');
206                        if (eridElement) {
207                            const erid = eridElement.textContent.trim();
208                            if (erid) {
209                                trackAdView(erid);
210                                adBlock.setAttribute('data-tracked', '1');
211                            }
212                        }
213                    }
214                    
215                    // Проверяем блоки с классом .tooltip.advert (интернет-магазин)
216                    const tooltipAdvert = slide.querySelector('.tooltip.advert:not([data-tracked])');
217                    if (tooltipAdvert) {
218                        // Ищем erid в тексте tooltip__body
219                        const tooltipBody = tooltipAdvert.querySelector('.tooltip__body');
220                        if (tooltipBody) {
221                            const text = tooltipBody.textContent || tooltipBody.innerText;
222                            const eridMatch = text.match(/erid:\s*([^\s\n<]+)/i);
223                            if (eridMatch && eridMatch[1]) {
224                                const erid = eridMatch[1].trim();
225                                if (erid) {
226                                    trackAdView(erid);
227                                    tooltipAdvert.setAttribute('data-tracked', '1');
228                                }
229                            }
230                        }
231                    }
232                });
233            }
234        });
235    }
236
237    // Основная функция инициализации
238    function init() {
239        console.log('[AdViewsTracker] Инициализация системы отслеживания просмотров баннеров');
240        // Небольшая задержка, чтобы убедиться, что DOM и слайдеры полностью загружены
241        setTimeout(function() {
242            console.log('[AdViewsTracker] Начало отслеживания после задержки');
243            // Отслеживание статических блоков
244            initAdTracking();
245            
246            // Отслеживание Swiper слайдеров
247            trackVisibleSwiperSlides();
248            
249            // Отслеживание Slick слайдеров
250            trackVisibleSlickSlides();
251        }, 500);
252    }
253
254    // Инициализация при загрузке страницы
255    console.log('[AdViewsTracker] Скрипт загружен. Состояние док
255умента:', document.readyState);
256    if (document.readyState === 'loading') {
257        console.log('[AdViewsTracker] Ожидание DOMContentLoaded');
258        document.addEventListener('DOMContentLoaded', init);
259    } else {
260        console.log('[AdViewsTracker] DOM уже загружен, запускаем инициализацию');
261        init();
262    }
263
264    // Отслеживание переключения табов (для главной страницы)
265    if (typeof jQuery !== 'undefined') {
266        jQuery(document).on('click', '.tab-set__item > *', function() {
267            const $tabItem = jQuery(this).parent();
268            // Небольшая задержка для завершения анимации переключения таба
269            setTimeout(function() {
270                initAdTracking();
271                trackVisibleSwiperSlides();
272                trackVisibleSlickSlides();
273            }, 300);
274        });
275
276        // Отслеживание событий Slick слайдера
277        jQuery(document).on('afterChange', '.slick-slider, .hero.js-hero-slider', function() {
278            setTimeout(function() {
279                trackVisibleSlickSlides();
280            }, 100);
281        });
282
283        // Отслеживание AJAX подгрузки новостей
284        jQuery(document).on('click', '.show-more-news', function(e) {
285            e.preventDefault();
286            const $button = jQuery(this);
287            const offset = $button.attr('data-offset');
288            const id = $button.attr('data-id');
289            
290            jQuery.ajax({
291                url: '/forms/news-pager/?limit=9&offset=' + offset + '&id=' + id,
292                dataType: 'html',
293                success: function(data) {
294                    jQuery('.article-list').append(data);
295                    $button.attr('data-offset', parseInt(offset) + 9);
296                    
297                    if (jQuery('#news-all-count').length) {
298                        const newsAllCount = jQuery('#news-all-count').val();
299                        if (parseInt(offset) + 9 > newsAllCount) {
300                            $button.hide();
301                        }
302                    }
303                    
304                    // Инициализируем отслеживание для новых загруженных блоков
305                    setTimeout(function() {
306                        initAdTracking();
307                    }, 200);
308                },
309                complete: function() {
310                    // Инициализация LazyLoad для новых изображений
311                    if (typeof LazyLoad !== 'undefined') {
312                        const ll = new LazyLoad({
313                            elements_selector: '.lazy',
314                            callback_set: function(el) {
315                                jQuery(el).addClass('hidden');
316                                setTimeout(function() {
317                                    jQuery(el).removeClass('hidden');
318                                }, 400);
319                            }
320                        });
321                    }
322                }
323            });
324        });
325    }
326
327    // Отслеживание изменений в Swiper через MutationObserver
328    const swiperContainers = document.querySelectorAll('.preview-gallery');
329    swiperContainers.forEach(function(container) {
330        const observer = new MutationObserver(function(mutations) {
331            mutations.forEach(function(mutation) {
332                if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
333                    const target = mutation.target;
334                    if (target.classList.contains('preview-gallery__item') || 
335                        target.classList.contains('swiper-slide')) {
336                        setTimeout(function() {
337                            trackVisibleSwiperSlides();
338                        }, 100);
339                    }
340                }
341            });
342        });
343
344        observer.observe(container, {
345            attributes: true,
346            subtree: true,
347            attributeFilter: ['class']
348        });
349    });
350
351    // Отслеживание событий Swiper напрямую, если доступны
352    setTimeout(function() {
353        swiperContainers.forEach(function(container) {
354            if (container.swiper) {
355                container.swiper.on('slideChange', function() {
356                    setTimeout(function() {
357                        trackVisibleSwiperSlides();
358                    }, 100);
359                });
360            }
361        });
362    }, 1000);
363
364    // Экспорт функций для использования извне (если нужно)
365    window.AdViewsTracker = {
366        init: init,
367        initAdTracking: initAdTracking,
368        trackVisibleSwiperSlides: trackVisibleSwiperSlides,
369        trackVisibleSlickSlides: trackVisibleSlickSlides
370    };
371
372})();
373

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.