PageSourceSearch

https://nadpo.ru/local/templates/nadpo_visually/js/analytics.js?v=1790916878

js nadpo.ru collected 2026-10-02 04:56:11 UTC 14,111 bytes, 359 lines download raw bytes

1window.addEventListener("DOMContentLoaded", startup, false);
2
3let visibleAds = new Set();
4let previouslyVisibleAds = null;
5
6let adObserver;
7let refreshIntervalID = 0;
8
9var rounded = function(number){
10    return +number.toFixed(2);
11}
12
13function startup() {
14  document.addEventListener("visibilitychange", handleVisibilityChange, false);
15
16  let observerOptions = {
17    root: null,
18    rootMargin: "0px",
19    threshold: buildThresholdList(), // было [0.0, 0.75]
20  };
21
22  adObserver = new IntersectionObserver(intersectionCallback, observerOptions);
23
24  const analyticsBlocks = document.querySelectorAll("[data-analytics-block]");
25
26  var params = {};
27  params["structure"] = [];
28  var index = 0;
29  analyticsBlocks.forEach((block) => {
30    adObserver.observe(block);
31    index++;
32    params["structure"].push(block.dataset.analyticsBlock + " " + index);
33  });
34  trackEvent('page_structure', params);
35
36  refreshIntervalID = window.setInterval(handleRefreshInterval, 300);
37}
38
39// чтобы ловить изменение видимости и доли экрана
40function buildThresholdList() {
41  const thresholds = [];
42  for (let i = 0; i <= 100; i++) thresholds.push(i / 100);
43  return thresholds;
44}
45
46// какую долю экрана занимает видимая часть блока
47function getViewportOccupancy(entry) {
48  const iRect = entry.intersectionRect;
49  if (!iRect) return 0;
50  const intersectArea = Math.max(0, iRect.width) * Math.max(0, iRect.height);
51  const rootRect = entry.rootBounds;
52  const viewportArea = rootRect ? rootRect.width * rootRect.height : window.innerWidth * window.innerHeight;
53  return viewportArea > 0 ? intersectArea / viewportArea : 0;
54}
55
56
57function handleVisibilityChange() {
58  if (document.hidden) {
59    if (!previouslyVisibleAds) {
60      previouslyVisibleAds = new Set(visibleAds);
61      visibleAds = new Set();
62      previouslyVisibleAds.forEach(function (adBox) {
63        updateAdTimer(adBox);
64        adBox.dataset.lastViewStarted = 0;
65      });
66    }
67  } else if (previouslyVisibleAds) {
68    previouslyVisibleAds.forEach(function (adBox) {
69      adBox.dataset.lastViewStarted = performance.now();
70    });
71    visibleAds = previouslyVisibleAds;
72    previouslyVisibleAds = null;
73  }
74}
75
76
77/*function intersectionCallback(entries) {
78  entries.forEach(function (entry) {
79    let adBox = entry.target;
80
81    if (entry.isIntersecting) {
82      if (entry.intersectionRatio >= 0.75) {
83        adBox.dataset.lastViewStarted = entry.time;
84        visibleAds.add(adBox);
85        adBox.dataset.analyticsBlocksend = "N";
86      }
87    } else {
88      visibleAds.delete(adBox);
89      if(adBox.dataset.formStart == 'Y'){
90        adBox.dataset.formStart = 'N';
91        const elementWithAnalytics = adBox.querySelector('[data-analytics-element]');
92        const params = {
93          block_id: adBox.dataset.analyticsBlock,
94          element_id: elementWithAnalytics.dataset.analyticsElement,
95          last_view_started: rounded(adBox.dataset.lastViewStarted/1000),
96          request_from: adBox.dataset.requestFrom,
97          value: rounded(adBox.dataset.totalViewTime/1000),
98        };
99        if (adBox.dataset.analyticsElement) {
100          params.element_id = adBox.dataset.analyticsElement;
101        }
102        trackEvent('form_end_view', params);
103      }
104      if (adBox.dataset.totalViewTime >= 1000) {
105        adBox.dataset.analyticsBlocksend = "Y";
106      }
107    }
108  });
109}*/
110
111function intersectionCallback(entries) {
112  entries.forEach(function (entry) {
113    const adBox = entry.target;
114
115    const occupiesViewport = getViewportOccupancy(entry) >= 0.70;
116    const enoughVisible = entry.intersectionRatio >= 0.75 || occupiesViewport;
117
118    if (entry.isIntersecting && enoughVisible) {
119      if (!visibleAds.has(adBox)) { // важно: не переустанавливать старт, если уже считаем
120        adBox.dataset.lastViewStarted = entry.time;
121        visibleAds.add(adBox);
122        adBox.dataset.analyticsBlocksend = "N";
123      }
124    } else if (!entry.isIntersecting) {
125      // как было
126      visibleAds.delete(adBox);
127      if (adBox.dataset.formStart == 'Y') {
128        adBox.dataset.formStart = 'N';
129        const elementWithAnalytics = adBox.querySelector('[data-analytics-element]');
130        const params = {
131          block_id: adBox.dataset.analyticsBlock,
132          element_id: elementWithAnalytics?.dataset.analyticsElement,
133          last_view_started: rounded(adBox.dataset.lastViewStarted/1000),
134          request_from: adBox.dataset.requestFrom,
135          value: rounded(adBox.dataset.totalViewTime/1000),
136        };
137        if (adBox.dataset.analyticsElement) params.element_id = adBox.dataset.analyticsElement;
138        trackEvent('form_end_view', params);
139      }
140      if (adBox.dataset.totalViewTime >= 1000) {
141        adBox.dataset.analyticsBlocksend = "Y";
142      }
143    }
144  });
145}
146
147
148function handleRefreshInterval() {
149  let redrawList = [];
150
151  visibleAds.forEach(function (adBox) {
152    let previousTime = adBox.dataset.totalViewTime;
153    updateAdTimer(adBox);
154
155    if (previousTime != adBox.dataset.totalViewTime) {
156      redrawList.push(adBox);
157    }
158  });
159
160  if (redrawList.length) {
161    window.requestAnimationFrame(function (time) {
162      redrawList.forEach(function (adBox) {
163        if (adBox.dataset.totalViewTime >= 1000 && adBox.dataset.analyticsBlocksend != "Y") {
164          const params = {
165            block_id: adBox.dataset.analyticsBlock,
166            last_view_started: rounded(adBox.dataset.lastViewStarted/1000),
167            request_from: adBox.dataset.requestFrom,
168            value: rounded(adBox.dataset.totalViewTime/1000),
169          };
170          if (adBox.dataset.analyticsElement) {
171            params.element_id = adBox.dataset.analyticsElement;
172          }
173          trackEvent('block_view', params);
174          adBox.dataset.analyticsBlocksend = "Y";
175        }
176      });
177    });
178  }
179}
180
181function updateAdTimer(adBox) {
182  let lastStarted = adBox.dataset.lastViewStarted;
183  let currentTime = performance.now();
184
185  if (lastStarted) {
186    let diff = currentTime - lastStarted;
187
188    adBox.dataset.totalViewTime = (adBox.dataset.totalViewTime ? parseFloat(adBox.dataset.totalViewTime) : 0) + diff;
189  }
190
191  adBox.dataset.lastViewStarted = currentTime;
192}
193
194function trackEvent(eventName, params) {
195
196  // Просто выводим в консоль для демонстрации
197  console.log(`Tracking event: ${eventName}`, params);
198  ym(49308325, 'reachGoal', eventName, params);
199}
200
201
202function trackVisibleAnalyticsElements() {
203  const observer = new IntersectionObserver(
204    (entries) => {
205      entries.forEach((entry) => {
206        if (entry.isIntersecting) {
207          // Элемент видим
208          const element = entry.target;
209          if (!element.dataset.analyticsFired) { // Проверяем, не сработало ли событие уже
210            let startTime = null;
211
212            // Функция для отслеживания времени видимости
213            function trackVisibilityTime(timestamp) {
214              if (!startTime) startTime = timestamp;
215              const progress = timestamp - startTime;
216
217              if (entry.isIntersecting) {
218                // Элемент все еще видим, продолжаем отслеживать
219                element.dataset.visibleTimer = progress;
220                element.dataset.visibleTimerNew = entry.time;
221                requestAnimationFrame(trackVisibilityTime);
222              } else {
223                // Элемент больше не видим, сбрасываем таймер
224                startTime = null;
225              }
226            }
227
228            // Начинаем отслеживание времени видимости
229            requestAnimationFrame(trackVisibilityTime);
230          }
231        }
232      });
233    },
234    {
235      threshold: 0.5 // Элемент должен быть видим хотя бы на 50%
236    }
237  );
238
239  const analyticsElements = document.querySelectorAll("[data-analytics-element]");
240
241  analyticsElements.forEach((block) => {
242    observer.observe(block);
243  });
244}
245
246// Запускаем отслеживание после загрузки страницы
247document.addEventListener("DOMContentLoaded", trackVisibleAnalyticsElements);
248
249
250document.addEventListener("DOMContentLoaded", function(){
251  document.addEventListener('click', (event) => {
252    // Находим ближайший родительский элемент с атрибутом data-analytics-element
253    const targetElement = event.target.closest('[data-analytics-element]');
254
255    // Если элемент с data-analytics-element не найден, выходим из функции
256    if (!targetElement) {
257      return;
258    }
259    const blockElement = targetElement.closest('[data-analytics-block]');
260
261    if(targetElement.dataset.analyticsType == "form") {
262      const isInput = event.target.closest('input');
263      if (!isInput) {
264        return;
265      }
266      if (isInput.type == 'submit') {
267        const block_id = blockElement ? blockElement.dataset.analyticsBlock || '' : ''; // Название блока в котором лежит элемент
268        const element_id = targetElement.dataset.analyticsElement || ''; // Название элемента
269        const element_type = targetElement.dataset.analyticsType || ''; // Тип элемента
270        const css_selector = getCssSelector(isInput); // Функция для получения CSS-селектора
271        const visibilityTime = rounded(targetElement.dataset.visibleTimer/1000); // сколько пользователь смотрел на элемент прежде чем кликнуть
272        const visibilityTimeNew = rounded(targetElement.dataset.visibleTimerNew/1000); // сколько прошло времени от загрузки страницы до Ð
272ºÐ»Ð¸ÐºÐ°
273
274        const eventParams = {
275          block_id: block_id,
276          element_id: element_id,
277          element_type: element_type,
278          css_selector: css_selector,
279          last_view_started: visibilityTimeNew,
280          value: visibilityTime,
281        };
282        // Вызываем функцию для отправки данных аналитики
283        trackEvent('form_try_submit', eventParams);
284      }
285      else {
286        const block_id = blockElement ? blockElement.dataset.analyticsBlock || '' : ''; // Название блока в котором лежит элемент
287        const element_id = targetElement.dataset.analyticsElement || ''; // Название элемента
288        const element_type = targetElement.dataset.analyticsType || ''; // Тип элемента
289        const input_name = isInput.name || ''; // название инпута
290        const css_selector = getCssSelector(isInput); // Функция для получения CSS-селектора
291        const visibilityTime = rounded(targetElement.dataset.visibleTimer/1000); // сколько пользователь смотрел на элемент прежде чем кликнуть
292        const visibilityTimeNew = rounded(targetElement.dataset.visibleTimerNew/1000); // сколько прошло времени от загрузки страницы до клика
293
294        const eventParams = {
295          block_id: block_id,
296          element_id: element_id,
297          element_type: element_type,
298          element_text: input_name,
299          css_selector: css_selector,
300          last_view_started: visibilityTimeNew,
301          value: visibilityTime,
302        };
303
304        if(blockElement.dataset.formStart =='Y'){
305          trackEvent('input_click', eventParams);
306        }
307        else {
308          blockElement.dataset.formStart = 'Y';
309          trackEvent('form_start', eventParams);
310        }
311      }
312    }
313    else {
314
315      // Получаем значения атрибутов data-analytics-*
316      const block_id = blockElement ? blockElement.dataset.analyticsBlock || '' : ''; // Название блока в котором лежит элемент
317      const element_id = targetElement.dataset.analyticsElement || ''; // Название элемента
318      const element_type = targetElement.dataset.analyticsType || ''; // Тип элемента
319      const element_text = targetElement.textContent.trim() || ''; // Текст элемента
320      const css_selector = getCssSelector(targetElement); // Функция для получения CSS-селектора
321      const visibilityTime = rounded(targetElement.dataset.visibleTimer/1000); // сколько пользователь смотрел на элемент прежде чем кликнуть
322      const visibilityTimeNew = rounded(targetElement.dataset.visibleTimerNew/1000); // сколько прошло времени от загрузки страницы до клика
323
324      if (targetElement.dataset.target) {
325        document.querySelector(targetElement.dataset.target).dataset.requestFrom = block_id;
326      }
327
328      // Формируем объект с параметрами
329      const eventParams = {
330        block_id: block_id,
331        element_id: element_id,
332        element_type: element_type,
333        element_text: element_text,
334        css_selector: css_selector,
335        last_view_started: visibilityTimeNew,
336        value: visibilityTime,
337      };
338
339      // Вызываем функцию для отправки данных аналитики
340      trackEvent('element_click', eventParams);
341
342      // Отладочный вывод в консоль
343    }
344
345  });
346
347  // Функция для получения CSS-селектора элемента (пример реализации)
348  function getCssSelector(element) {
349    let selector = element.tagName.toLowerCase();
350
351    if (element.id) {
352      selector += `#${element.id}`;
353    } else if (element.className) {
354      selector += `.${element.className.split(' ').join('.')}`;
355    }
356
357    return selector; // Простой селектор, можно усовершенствовать
358  }
359});

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.