PageSourceSearch

https://bitontolive.it/wp-content/themes/livenetwork-theme/inc/blocks/live-speciali/view.js?ver=1787240211

js bitontolive.it collected 2026-09-25 05:50:52 UTC 9,139 bytes, 310 lines download raw bytes

1(function () {
2  function rafThrottle(fn) {
3    let ticking = false;
4    return function () {
5      if (ticking) return;
6      ticking = true;
7      requestAnimationFrame(() => {
8        ticking = false;
9        fn();
10      });
11    };
12  }
13
14  function clamp(n, min, max) {
15    return Math.max(min, Math.min(max, n));
16  }
17
18  function initSlider(root) {
19    const track = root.querySelector(".lnv-pubbl-track");
20    const dotsWrap = root.querySelector(".lnv-pubbl-dots");
21    if (!track || !dotsWrap) return;
22
23    const cards = Array.from(root.querySelectorAll(".lnv-pubbl-card"));
24    const count = cards.length;
25    if (!count) return;
26
27    // ✅ 4 breakpoint config (da dataset)
28    const slidesL = parseInt(root.dataset.slidesL || "3", 10); // >=1200
29    const slidesD = parseInt(root.dataset.slidesD || "2", 10); // 1024-1199
30    const slidesT = parseInt(root.dataset.slidesT || "1", 10); // 768-1023
31    const slidesM = parseInt(root.dataset.slidesM || "1", 10); // <768
32
33    const peekL = parseFloat(root.dataset.peekL || root.dataset.peekD || "0.25");
34    const peekD = parseFloat(root.dataset.peekD || "0.20");
35    const peekT = parseFloat(root.dataset.peekT || "0.15");
36    const peekM = parseFloat(root.dataset.peekM || "0.15");
37
38    root.style.setProperty("--count", String(count));
39
40    let currentSlides = 2;
41    let pageStarts = [];
42    let dots = [];
43    let activePage = 0;
44
45    // Lock per evitare rimbalzi durante smooth scroll
46    let isProgrammatic = false;
47    let programTarget = 0;
48
49    // IntersectionObserver per mobile swipe (più affidabile)
50    let io = null;
51    let ioPageCards = [];
52
53    function getDeviceConfig() {
54      // ✅ breakpoints:
55      // large >=1200
56      // desktop 1024-1199
57      // tablet 768-1023
58      // mobile <768
59      if (window.matchMedia("(min-width: 1200px)").matches) return { slides: slidesL, peek: peekL };
60      if (window.matchMedia("(min-width: 1024px)").matches) return { slides: slidesD, peek: peekD };
61      if (window.matchMedia("(min-width: 768px)").matches) return { slides: slidesT, peek: peekT };
62      return { slides: slidesM, peek: peekM };
63    }
64
65    function setActive(pageIndex) {
66      if (!dots.length) return;
67
68      const idx = clamp(pageIndex, 0, dots.length - 1);
69      activePage = idx;
70
71      dots.forEach((d, i) => {
72        const on = i === idx;
73        d.setAttribute("aria-current", on ? "true" : "false");
74        d.classList.toggle("is-active", on);
75      });
76    }
77
78    function applyLayout() {
79      const { slides, peek } = getDeviceConfig();
80      currentSlides = Math.max(1, slides);
81
82      if (count <= currentSlides) {
83        root.classList.add("is-fit");
84        root.style.setProperty("--perview", String(count));
85        dotsWrap.style.display = "none";
86      } else {
87        root.classList.remove("is-fit");
88        root.style.setProperty("--perview", String(currentSlides + Math.max(0, peek)));
89        dotsWrap.style.display = "";
90      }
91    }
92
93    function computePages() {
94      pageStarts = [];
95      if (count <= currentSlides) return;
96
97      const pages = Math.ceil(count / currentSlides);
98      for (let p = 0; p < pages; p++) {
99        const idx = p * currentSlides;
100        const card = cards[Math.min(idx, count - 1)];
101        pageStarts.push(card.offsetLeft); // relativo al track
102      }
103    }
104
105    function buildDots() {
106      dotsWrap.innerHTML = "";
107      dots = [];
108
109      if (count <= currentSlides) return;
110
111      for (let p = 0; p < pageStarts.length; p++) {
112        const b = document.createElement("button");
113        b.type = "button";
114        b.className = "lnv-pubbl-dot";
115        b.setAttribute("aria-label", `Vai alla pagina ${p + 1}`);
116        b.addEventListener("click", () => goToPage(p, "smooth"));
117        dotsWrap.appendChild(b);
118        dots.push(b);
119      }
120    }
121
122    function goToPage(p, behavior) {
123      if (!pageStarts.length) return;
124
125      const idx = clamp(p, 0, pageStarts.length - 1);
126
127      isProgrammatic = true;
128      programTarget = idx;
129      setActive(idx); // attivo subito quello cliccato
130
131      track.scrollTo({
132        left: pageStarts[idx],
133        behavior: behavior || "smooth",
134      });
135    }
136
137    // fallback: calcola pagina attiva dal scrollLeft
138    function getActivePageFromScroll() {
139      if (!pageStarts.length) return 0;
140
141      const left = track.scrollLeft;
142      let best = 0;
143      let bestDist = Infinity;
144
145      for (let i = 0; i < pageStarts.length; i++) {
146        const dist = Math.abs(pageStarts[i] - left);
147        if (dist < bestDist) {
148          bestDist = dist;
149          best = i;
150        }
151      }
152      return best;
153    }
154
155    // ✅ IntersectionObserver (soluzione robusta su mobile)
156    function setupObserver() {
157      if (!("IntersectionObserver" in window)) return null;
158
159      // osserva la prima card di ogni pagina: 0, currentSlides, 2*currentSlides...
160      ioPageCards = [];
161      const pages = Math.ceil(count / currentSlides);
162      for (let p = 0; p < pages; p++) {
163        ioPageCards.push(cards[Math.min(p * currentSlides, count - 1)]);
164      }
165
166      const observer = new IntersectionObserver(
167        (entries) => {
168          // se sto scrollando programmaticamente, non farti “rubare” l’attivo
169          if (isProgrammatic) return;
170
171          let bestIdx = activePage;
172          let bestRatio = 0;
173
174          for (const e of entries) {
175            if (!e.isIntersecting) continue;
176            const idx = ioPageCards.indexOf(e.target);
177            if (idx >= 0 && e.intersectionRatio > bestRatio) {
178              bestRatio = e.intersectionRatio;
179              bestIdx = idx;
180            }
181          }
182
183          setActive(bestIdx);
184        },
185        {
186          root: track,
187          // quando circa metà della pagina è visibile, aggiorna il dot
188          threshold: [0.5, 0.6, 0.7],
189        }
190      );
191
192      ioPageCards.forEach((el) => el && observer.observe(el));
193      return observer;
194    }
195
196    const onScroll = rafThrottle(() => {
197      if (!pageStarts.length) return;
198
199      if (isProgrammatic) {
200        const targetLeft = pageStarts[programTarget];
201        const delta = Math.abs(track.scrollLeft - targetLeft);
202
203        // tolleranza piccola (alcuni browser non arrivano mai a 0 perfetto)
204        if (delta <= 3) {
205          isProgrammatic = false;
206          setActive(programTarget);
207        } else {
208          setActive(programTarget);
209        }
210        return;
211      }
212
213      // se IO non c'è, aggiorna da scrollLeft
214      if (!io) {
215        setActive(getActivePageFromScroll());
216      }
217    });
218
219    if (!root.__lnvPubblInited) {
220      root.__lnvPubblInited = true;
221
222      track.addEventListener("scroll", onScroll, { passive: true });
223
224      // ✅ su mobile: se l’utente tocca/trascina, sblocca il lock programmatico
225      track.addEventListener(
226        "touchstart",
227        () => {
228          isProgrammatic = false;
229        },
230        { passive: true }
231      );
232      track.addEventListener(
233        "pointerdown",
234        () => {
235          isProgrammatic = false;
236        },
237        { passive: true }
238      );
239
240      track.addEventListener("keydown", (e) => {
241        if (!pageStarts.length) return;
242        if (e.key === "ArrowRight") {
243          e.preventDefault();
244          goToPage(activePage + 1, "smooth");
245        } else if (e.key === "ArrowLeft") {
246          e.preventDefault();
247          goToPage(activePage - 1, "smooth");
248        }
249      });
250    }
251
252    function rebuild(keepPage) {
253      const prev = activePage;
254
255      applyLayout();
256      computePages();
257      buildDots();
258
259      // ricrea observer dopo ogni cambio layout
260      if (io) {
261        io.disconnect();
262        io = null;
263      }
264      io = setupObserver();
265
266      const target = keepPage
267        ? clamp(prev, 0, pageStarts.length ? pageStarts.length - 1 : 0)
268        : 0;
269
270      isProgrammatic = false;
271      programTarget = target;
272
273      setActive(target);
274      if (pageStarts.length) {
275        track.scrollTo({ left: pageStarts[target], behavior: "auto" });
276      }
277
278      // per sicurezza: dopo un attimo ricalcola ancora (font/immagini)
279      window.clearTimeout(root.__lnvRebuildT);
280      root.__lnvRebuildT = window.setTimeout(() => {
281        computePages();
282        // se IO non esiste, riallinea il pallino
283        if (!io) setActive(getActivePageFromScroll());
284      }, 180);
285    }
286
287    rebuild(false);
288    window.setTimeout(() => rebuild(true), 150);
289
290    let rT;
291    window.addEventListener(
292      "resize",
293      () => {
294        clearTimeout(rT);
295        rT = setTimeout(() => rebuild(true), 120);
296      },
297      { passive: true }
298    );
299  }
300
301  function boot() {
302    document.querySelectorAll(".lnv-pubbl-slider[data-lnv-pubbl-slider]").forEach(initSlider);
303  }
304
305  if (document.readyState === "loading") {
306    document.addEventListener("DOMContentLoaded", boot);
307  } else {
308    boot();
309  }
310})();

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.