PageSourceSearch

https://www.montetamaro.ch/site/js/cms-analytics-sections.js?v=21.501

js montetamaro.ch collected 2026-10-02 08:01:44 UTC 5,744 bytes, 186 lines download raw bytes

1/* cms-analytics-sections.js
2   Track "section views" (per-page sections) using IntersectionObserver.
3   Expects config either in window.cmsAnalytics OR in <body data-*>.
4
5   Required:
6   - pageId (number > 0)
7   - baseUrl (string) OR app_site_start global (string) OR "/"
8
9   Optional:
10   - device (0..3)
11   - source (0..4)
12   - endpoint (string) default: "ajaxrequest/cms-track-sections"
13
14   Section selection:
15   - elements matching ".section[id]"
16   Section key:
17   - uses element.id normalized to [a-z0-9_-], max 64 chars
18
19   Behavior:
20   - counts a section once per page load
21   - considers "viewed" when 50% visible
22   - batches events and sends via sendBeacon/fetch
23*/
24
25(function () {
26  'use strict';
27
28  // -----------------------------
29  // Config (window.cmsAnalytics OR body dataset)
30  // -----------------------------
31  var cfg = (window.cmsAnalytics && typeof window.cmsAnalytics === 'object') ? window.cmsAnalytics : null;
32
33  var ds = (document.body && document.body.dataset) ? document.body.dataset : {};
34  function toInt(v, fallback) {
35    var n = parseInt(v, 10);
36    return Number.isFinite(n) ? n : fallback;
37  }
38
39  var PAGE_ID = toInt(cfg?.pageId ?? ds.pageId ?? window.CMS_PAGE_ID, 0);
40  if (!PAGE_ID) return; // Nothing to do
41
42  var DEVICE_ID = toInt(cfg?.device ?? ds.deviceId ?? window.CMS_DEVICE_ID, 0);
43  var SOURCE_ID = toInt(cfg?.source ?? ds.sourceId ?? window.CMS_SOURCE_ID, 0);
44
45  // Whitelists (keeps DB clean)
46  if (![0, 1, 2, 3].includes(DEVICE_ID)) DEVICE_ID = 0;
47  if (![0, 1, 2, 3, 4].includes(SOURCE_ID)) SOURCE_ID = 0;
48
49  var baseUrl =
50    (cfg?.baseUrl ?? ds.baseUrl ?? window.app_site_start ?? '').toString();
51
52  // Normalize baseUrl to end with "/"
53  baseUrl = baseUrl ? baseUrl.replace(/\/?$/, '/') : '/';
54
55  var endpoint = (cfg?.endpoint ?? ds.endpoint ?? 'ajaxrequest/cms-track-sections').toString();
56  // If someone passes "/ajaxrequest/..." keep it; if they pass "ajaxrequest/..." keep it.
57  // We join safely:
58  function joinUrl(base, path) {
59    if (!path) return base;
60    // absolute URL
61    if (/^https?:\/\//i.test(path)) return path;
62    // absolute path
63    if (path[0] === '/') return base.replace(/\/+$/, '') + path;
64    return base + path;
65  }
66
67  var TRACK_URL = joinUrl(baseUrl, endpoint);
68
69  // -----------------------------
70  // Section selection + key normalization
71  // -----------------------------
72  function normalizeKey(id) {
73    if (!id) return null;
74    var key = String(id).toLowerCase().replace(/[^a-z0-9\-_]/g, '');
75    if (!key.length) return null;
76    return key.substring(0, 64);
77  }
78
79  var sections = document.querySelectorAll('.section[data-codice-sezione]');
80  if (!sections || !sections.length) return;
81
82  // -----------------------------
83  // Queue + sending
84  // -----------------------------
85  var seen = new Set();
86  var queue = [];
87  var flushTimer = null;
88
89  function enqueue(sectionKey) {
90    if (!sectionKey || seen.has(sectionKey)) return;
91    seen.add(sectionKey);
92    queue.push(sectionKey);
93    if (!flushTimer) flushTimer = setTimeout(flush, 800);
94  }
95
96  function flush() {
97    flushTimer = null;
98    if (!queue.length) return;
99
100    var payload = {
101      page_id: PAGE_ID,
102      device: DEVICE_ID,
103      source: SOURCE_ID,
104      sections: queue.splice(0, queue.length)
105    };
106
107    var ok = false;
108
109    // Prefer sendBeacon for reliability during navigation/unload
110    try {
111      if (navigator.sendBeacon) {
112        ok = navigator.sendBeacon(
113          TRACK_URL,
114          new Blob([JSON.stringify(payload)], { type: 'application/json' })
115        );
116      }
117    } catch (e) {
118      ok = false;
119    }
120
121    // Fallback fetch
122    if (!ok && window.fetch) {
123      fetch(TRACK_URL, {
124        method: 'POST',
125        headers: { 'Content-Type': 'application/json' },
126        body: JSON.stringify(payload),
127        keepalive: true,
128        credentials: 'same-origin'
129      }).catch(function () { /* silent */ });
130      return;
131    }
132
133    // Final fallback (very old browsers): image beacon (no JSON)
134    // We skip it here to keep server simple; enable only if needed.
135  }
136
137  // Flush on pagehide (better than beforeunload)
138  window.addEventListener('pagehide', flush);
139
140  // -----------------------------
141  // IntersectionObserver (50% threshold)
142  // -----------------------------
143  var hasIO = typeof IntersectionObserver !== 'undefined';
144
145  // Pre-enqueue section that's already "center-visible" at load (helps in some edge cases)
146  (function preEnqueueVisible() {
147    try {
148      var vh = window.innerHeight || document.documentElement.clientHeight || 0;
149      if (!vh) return;
150
151      sections.forEach(function (el) {
152        var r = el.getBoundingClientRect();
153        // center line of viewport at 50%
154        var center = vh * 0.5;
155        if (r.top < center && r.bottom > center) {
156          var key = el.dataset.codiceSezione;
157          if (key) enqueue(key);
158        }
159      });
160    } catch (e) {
161      // ignore
162    }
163  })();
164
165  if (!hasIO) {
166    // No IntersectionObserver: degrade gracefully – count only the first visible section
167    // (Better than nothing, avoids heavy scroll listeners)
168    return;
169  }
170
171  var observer = new IntersectionObserver(function (entries) {
172    entries.forEach(function (entry) {
173      if (!entry.isIntersecting) return;
174      if (entry.intersectionRatio < 0.5) return;
175
176      var key = entry.target.dataset.codiceSezione;
177      if (key) enqueue(key);
178
179      observer.unobserve(entry.target); // only once per section
180    });
181  }, { threshold: [0.5] });
182
183  sections.forEach(function (el) {
184    observer.observe(el);
185  });
186})();

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.