PageSourceSearch

https://kanalsportowy.pl/wp-content/plugins/doit-analytics/assets/frontend.js?ver=1790926492

js kanalsportowy.pl collected 2026-10-02 20:36:21 UTC 14,444 bytes, 539 lines download raw bytes

1/**
2 * DOIT Analytics — theme-agnostic GA4 tracker (uses existing window.gtag).
3 *
4 * Integration contract:
5 * - Themes mark DOM with data-analytics-* (see Config / INTEGRATION.md)
6 * - Optional: dispatch CustomEvent('doit-analytics:share', { detail: { method } })
7 * - Optional plugins: doit-quizzes:started|completed, doit-polls:voted
8 */
9(function () {
10  'use strict';
11
12  var DEFAULT_EVENTS = {
13    comment_submit: true,
14    select_content: true,
15    article_read_progress: true
16  };
17
18  var BLOCKED_PARAM_KEYS = {
19    email: true,
20    user_email: true,
21    login: true,
22    user_login: true,
23    username: true,
24    user_name: true,
25    display_name: true,
26    first_name: true,
27    last_name: true,
28    name: true,
29    ip: true,
30    ip_address: true,
31    comment: true,
32    comment_content: true,
33    comment_author: true,
34    comment_author_email: true,
35    password: true,
36    phone: true
37  };
38
39  var sentKeys = Object.create(null);
40  var runtime = window.doitAnalyticsRuntime || {};
41  var page = runtime.page || {};
42  var cfg = runtime.config || {};
43  var attr = cfg.attr || {};
44  var debug = !!runtime.debug;
45  var dryRun = !!cfg.dryRun;
46  var gaDebugMode = !!cfg.gaDebugMode;
47  var trackingEnabled = cfg.trackingEnabled !== false;
48
49  var ATTR_MODULE = attr.module || 'data-analytics-module';
50  var ATTR_POSITION = attr.position || 'data-analytics-position';
51  var ATTR_CONTENT_ID = attr.contentId || 'data-analytics-content-id';
52  var ATTR_CONTENT_TYPE = attr.contentType || 'data-analytics-content-type';
53  var ATTR_CONTENT_GROUP = attr.contentGroup || 'data-analytics-content-group';
54  var ATTR_SURFACE = attr.surface || 'data-analytics-surface';
55  var ATTR_SHARE = attr.share || 'data-analytics-share';
56  var ATTR_SELECT = attr.select || 'data-analytics-select';
57  var ATTR_ARTICLE = attr.articleContent || 'data-analytics-article-content';
58
59  function allowedEventMap() {
60    var map = {};
61    var list = Array.isArray(cfg.allowedEvents) ? cfg.allowedEvents : null;
62    var i;
63    if (list && list.length) {
64      for (i = 0; i < list.length; i++) {
65        map[String(list[i])] = true;
66      }
67      return map;
68    }
69    return DEFAULT_EVENTS;
70  }
71
72  var ALLOWED_EVENTS = allowedEventMap();
73
74  function isDebug() {
75    return debug || !!(window.doitAnalytics && window.doitAnalytics.debug);
76  }
77
78  function cleanParams(params) {
79    var out = {};
80    var key;
81    if (!params || typeof params !== 'object') {
82      return out;
83    }
84    for (key in params) {
85      if (!Object.prototype.hasOwnProperty.call(params, key)) {
86        continue;
87      }
88      if (BLOCKED_PARAM_KEYS[key]) {
89        continue;
90      }
91      if (params[key] === undefined || params[key] === null || params[key] === '') {
92        continue;
93      }
94      out[key] = params[key];
95    }
96    return out;
97  }
98
99  function track(eventName, params, options) {
100    var name = String(eventName || '');
101    var opts = options || {};
102    var payload;
103
104    if (!trackingEnabled) {
105      return false;
106    }
107
108    if (!name || !ALLOWED_EVENTS[name]) {
109      if (isDebug()) {
110        console.warn('[doit-analytics] blocked event', name, params);
111      }
112      return false;
113    }
114
115    payload = cleanParams(params);
116
117    if (gaDebugMode) {
118      payload.debug_mode = true;
119    }
120
121    if (opts.dedupeKey) {
122      if (sentKeys[opts.dedupeKey]) {
123        return false;
124      }
125      sentKeys[opts.dedupeKey] = true;
126    }
127
128    if (isDebug() || dryRun) {
129      console.info('[doit-analytics]' + (dryRun ? ' [dry-run]' : ''), name, payload);
130    }
131
132    if (dryRun) {
133      return true;
134    }
135
136    if (typeof window.gtag !== 'function') {
137      return false;
138    }
139
140    try {
141      window.gtag('event', name, payload);
142    } catch (err) {
143      if (isDebug()) {
144        console.warn('[doit-analytics] gtag error', err);
145      }
146      return false;
147    }
148
149    return true;
150  }
151
152  function flushPending() {
153    var list = Array.isArray(runtime.pendingEvents) ? runtime.pendingEvents : [];
154    var i;
155    for (i = 0; i < list.length; i++) {
156      var item = list[i];
157      if (!item || !item.name) {
158        continue;
159      }
160      track(item.name, item.params || {}, {
161        dedupeKey: 'pending:' + item.name + ':' + JSON.stringify(item.params || {})
162      });
163    }
164  }
165
166  function pageDefaults() {
167    return {
168      content_id: page.content_id || '',
169      content_type: page.content_type || '',
170      content_group: page.content_group || '',
171      surface: page.surface || ''
172    };
173  }
174
175  function trackShare(method, extra) {
176    var defaults = pageDefaults();
177    var params = {
178      method: method,
179      content_type: defaults.content_type || 'article',
180      content_id: defaults.content_id,
181      item_id: defaults.content_id,
182      content_group: defaults.content_group,
183      surface: defaults.surface || 'article'
184    };
185    var key;
186    if (extra && typeof extra === 'object') {
187      for (key in extra) {
188        if (Object.prototype.hasOwnProperty.call(extra, key) && extra[key] != null && extra[key] !== '') {
189          params[key] = extra[key];
190        }
191      }
192    }
193    track('share', params);
194  }
195
196  function bindShare() {
197    document.addEventListener(
198      'click',
199      function (event) {
200        var target = event.target;
201        if (!target || !target.closest) {
202          return;
203        }
204        var link = target.closest('[' + ATTR_SHARE + ']');
205        if (!link) {
206          return;
207        }
208        var method = link.getAttribute(ATTR_SHARE) || '';
209        if (!method || method === 'copy_link') {
210          // copy_link is tracked after confirmed copy via CustomEvent
211          return;
212        }
213        trackShare(method);
214      },
215      true
216    );
217
218    var copyEvents = Array.isArray(cfg.shareCopyEvents) && cfg.shareCopyEvents.length
219      ? cfg.shareCopyEvents
220      : ['doit-analytics:share'];
221
222    copyEvents.forEach(function (eventName) {
223      document.addEventListener(String(eventName), function (event) {
224        var detail = (event && event.detail) || {};
225        var method = detail.method || 'copy_link';
226        trackShare(method, detail);
227      });
228    });
229  }
230
231  function selectLinkSelector() {
232    return cfg.selectLinkSelector || 'a[' + ATTR_SELECT + ']';
233  }
234
235  function selectIgnoreSelector() {
236    return cfg.selectIgnoreSelector || '';
237  }
238
239  function isIgnoredSelectTarget(el) {
240    var ignore = selectIgnoreSelector();
241    if (!ignore || !el || !el.closest) {
242      return false;
243    }
244    try {
245      return !!el.closest(ignore);
246    } catch (err) {
247      return false;
248    }
249  }
250
251  function bindSelectContent() {
252    document.addEventListener(
253      'click',
254      function (event) {
255        var target = event.target;
256        if (!target || !target.closest || isIgnoredSelectTarget(target)) {
257          return;
258        }
259
260        var link;
261        try {
262          link = target.closest(selectLinkSelector());
263        } catch (err) {
264          link = null;
265        }
266        if (!link) {
267          return;
268        }
269
270        var host = link.closest('[' + ATTR_MODULE + ']') || link;
271        var moduleName = host.getAttribute(ATTR_MODULE) || '';
272        if (!moduleName) {
273          return;
274        }
275
276        var position = parseInt(host.getAttribute(ATTR_POSITION) || '0', 10);
277        var contentId = host.getAttribute(ATTR_CONTENT_ID) || '';
278        var contentType = host.getAttribute(ATTR_CONTENT_TYPE) || 'article';
279        var contentGroup = host.getAttribute(ATTR_CONTENT_GROUP) || '';
280        var surface =
281          host.getAttribute(ATTR_SURFACE) ||
282          page.surface ||
283          '';
284
285        track('select_content', {
286          content_type: contentType,
287          content_id: contentId,
288          content_group: contentGroup,
289          surface: surface,
290          module_name: moduleName,
291          module_position: position > 0 ? position : 1
292        });
293      },
294      true
295    );
296  }
297
298  function getReadDepth(el) {
299    var rect = el.getBoundingClientRect();
300    var viewH = window.innerHeight || document.documentElement.clientHeight || 0;
301    var top = rect.top;
302    var height = rect.height;
303
304    if (height <= 0) {
305      return 100;
306    }
307
308    var scrolled = viewH - top;
309    var pct = (scrolled / height) * 100;
310    if (pct < 0) {
311      return 0;
312    }
313    if (pct > 100) {
314      return 100;
315    }
316    return pct;
317  }
318
319  function bindReadProgress() {
320    if ((page.content_type || '') !== 'article' || !page.article) {
321      return;
322    }
323
324    var thresholds = Array.isArray(runtime.readThresholds)
325      ? runtime.readThresholds.slice()
326      : [25, 50, 75, 90];
327    var fired = Object.create(null);
328    var selector =
329      (page.article && page.article.content_selector) ||
330      '[' + ATTR_ARTICLE + '], .entry-content';
331    var ticking = false;
332
333    function contentEl() {
334      try {
335        return document.querySelector(selector);
336      } catch (err) {
337        return null;
338      }
339    }
340
341    function emitForDepth(depth) {
342      var i;
343      var t;
344      for (i = 0; i < thresholds.length; i++) {
345        t = thresholds[i];
346        if (depth >= t && !fired[t]) {
347          fired[t] = true;
348          track(
349            'article_read_progress',
350            {
351              content_id: page.content_id,
352              content_type: 'article',
353              content_group: page.content_group || '',
354              read_depth: t,
355              article_length_chars: page.article.article_length_chars || 0,
356              article_length_bucket: page.article.article_length_bucket || ''
357            },
358            { dedupeKey: 'read:' + page.content_id + ':' + t }
359          );
360        }
361      }
362    }
363
364    function onScroll() {
365      if (ticking) {
366        return;
367      }
368      ticking = true;
369      window.requestAnimationFrame(function () {
370        ticking = false;
371        var el = contentEl();
372        if (!el) {
373          return;
374        }
375        emitForDepth(getReadDepth(el));
376      });
377    }
378
379    window.addEventListener('scroll', onScroll, { passive: true });
380    window.addEventListener('resize', onScroll, { passive: true });
381    onScroll();
382
383    var observeSelector = cfg.mutationObserveSelector || '';
384    var container = null;
385    if (observeSelector) {
386      try {
387        container = document.querySelector(observeSelector);
388      } catch (err) {
389        container = null;
390      }
391    }
392    if (!container) {
393      container = contentEl() || document.body;
394    }
395
396    if (container && typeof MutationObserver !== 'undefined') {
397      var observer = new MutationObserver(function () {
398        onScroll();
399      });
400      observer.observe(container, { childList: true, subtree: true });
401    }
402  }
403
404  function bindQuizEvents() {
405    document.addEventListener('doit-quizzes:started', function (event) {
406      var detail = (event && event.detail) || {};
407      var quizId = parseInt(detail.quizId || 0, 10);
408      if (!quizId) {
409        return;
410      }
411      track(
412        'quiz_start',
413        {
414          content_id: 'quiz_' + quizId,
415          content_type: 'quiz',
416          content_group: detail.content_group || page.content_group || '',
417          surface: 'quiz',
418          question_count: parseInt(detail.question_count || 0, 10) || undefined
419        },
420        { dedupeKey: 'quiz_start:' + quizId + ':' + (detail.attemptKey || '') }
421      );
422    });
423
424    document.addEventListener('doit-quizzes:completed', function (event) {
425      var detail = (event && event.detail) || {};
426      var quizId = parseInt(detail.quizId || 0, 10);
427      if (!quizId) {
428        return;
429      }
430      track(
431        'quiz_complete',
432        {
433          content_id: 'quiz_' + quizId,
434          content_type: 'quiz',
435          content_group: detail.content_group || page.content_group || '',
436          surface: 'quiz',
437          question_count: parseInt(detail.question_count || detail.total || 0, 10) || undefined,
438          quiz_score: detail.score,
439          quiz_max_score: detail.total,
440          quiz_score_percent: detail.percent,
441          quiz_duration_seconds: detail.durationSeconds
442        },
443        { dedupeKey: 'quiz_complete:' + quizId + ':' + (detail.attemptKey || '') }
444      );
445    });
446  }
447
448  function resolvePollSurface(instanceId) {
449    if (page.surface === 'homepage' || page.surface === 'article') {
450      return page.surface;
451    }
452
453    var prefixes = cfg.pollSurfacePrefixes || {};
454    var raw = String(instanceId || '');
455    var key;
456    for (key in prefixes) {
457      if (Object.prototype.hasOwnProperty.call(prefixes, key) && raw.indexOf(key) === 0) {
458        return prefixes[key];
459      }
460    }
461
462    return page.surface || 'other';
463  }
464
465  function resolvePollModule(instanceId) {
466    if (!instanceId) {
467      return '';
468    }
469    var raw = String(instanceId);
470    if (raw.indexOf('slot-') !== 0) {
471      return 'poll_embed';
472    }
473    var without = raw.slice(5);
474    var lastDash = without.lastIndexOf('-');
475    if (lastDash <= 0) {
476      return without || 'poll_slot';
477    }
478    return without.slice(0, lastDash) || 'poll_slot';
479  }
480
481  function bindPollEvents() {
482    document.addEventListener('doit-polls:voted', function (event) {
483      var detail = (event && event.detail) || {};
484      var pollId = parseInt(detail.pollId || 0, 10);
485      var optionId = detail.voted_option_id || '';
486      if (!pollId || !optionId) {
487        return;
488      }
489
490      var instanceId = detail.instanceId || '';
491      if (!instanceId) {
492        var root = document.querySelector(
493          '[data-doit-polls-root][data-poll-id="' + String(pollId) + '"]'
494        );
495        instanceId = root ? root.getAttribute('data-instance-id') || '' : '';
496      }
497
498      var params = {
499        poll_id: 'poll_' + pollId,
500        poll_option_id:
501          String(optionId).indexOf('option_') === 0
502            ? String(optionId)
503            : 'option_' + String(optionId),
504        surface: resolvePollSurface(instanceId),
505        module_name: resolvePollModule(instanceId)
506      };
507
508      if (page.content_id) {
509        params.content_id = page.content_id;
510      }
511
512      track('poll_vote', params, {
513        dedupeKey: 'poll_vote:' + pollId + ':' + optionId
514      });
515    });
516  }
517
518  function init() {
519    flushPending();
520    bindShare();
521    bindSelectContent();
522    bindReadProgress();
523    bindQuizEvents();
524    bindPollEvents();
525  }
526
527  window.doitAnalytics = {
528    track: track,
529    debug: debug,
530    version: '1.2.0',
531    runtime: runtime
532  };
533
534  if (document.readyState === 'loading') {
535    document.addEventListener('DOMContentLoaded', init);
536  } else {
537    init();
538  }
539})();

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.