PageSourceSearch

https://bitontolive.it/wp-content/themes/livenetwork-theme/inc/blo…/view.js?ver=1790418968

js bitontolive.it collected 2026-10-02 18:49:44 UTC 8,647 bytes, 293 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 (più affidabile su mobile)
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    function getActivePageFromScroll() {
138      if (!pageStarts.length) return 0;
139
140      const left = track.scrollLeft;
141      let best = 0;
142      let bestDist = Infinity;
143
144      for (let i = 0; i < pageStarts.length; i++) {
145        const dist = Math.abs(pageStarts[i] - left);
146        if (dist < bestDist) {
147          bestDist = dist;
148          best = i;
149        }
150      }
151      return best;
152    }
153
154    // ✅ IntersectionObserver setup: osserva la prima card di ogni "pagina"
155    function setupObserver() {
156      if (!("IntersectionObserver" in window)) return null;
157      if (count <= currentSlides) return null;
158
159      ioPageCards = [];
160      const pages = Math.ceil(count / currentSlides);
161      for (let p = 0; p < pages; p++) {
162        ioPageCards.push(cards[Math.min(p * currentSlides, count - 1)]);
163      }
164
165      const observer = new IntersectionObserver(
166        (entries) => {
167          if (isProgrammatic) return;
168
169          let bestIdx = activePage;
170          let bestRatio = 0;
171
172          for (const e of entries) {
173            if (!e.isIntersecting) continue;
174            const idx = ioPageCards.indexOf(e.target);
175            if (idx >= 0 && e.intersectionRatio > bestRatio) {
176              bestRatio = e.intersectionRatio;
177              bestIdx = idx;
178            }
179          }
180
181          setActive(bestIdx);
182        },
183        {
184          root: track,
185          threshold: [0.5, 0.6, 0.7],
186        }
187      );
188
189      ioPageCards.forEach((el) => el && observer.observe(el));
190      return observer;
191    }
192
193    const onScroll = rafThrottle(() => {
194      if (!pageStarts.length) return;
195
196      if (isProgrammatic) {
197        const targetLeft = pageStarts[programTarget];
198        const delta = Math.abs(track.scrollLeft - targetLeft);
199
200        if (delta <= 3) {
201          isProgrammatic = false;
202          setActive(programTarget);
203        } else {
204          setActive(programTarget);
205        }
206        return;
207      }
208
209      // fallback se IO non è disponibile
210      if (!io) {
211        setActive(getActivePageFromScroll());
212      }
213    });
214
215    if (!root.__lnvPubblInited) {
216      root.__lnvPubblInited = true;
217
218      track.addEventListener("scroll", onScroll, { passive: true });
219
220      // ✅ importantissimo su mobile: se l’utente tocca/trascina, sblocca il lock
221      track.addEventListener("touchstart", () => { isProgrammatic = false; }, { passive: true });
222      track.addEventListener("pointerdown", () => { isProgrammatic = false; }, { passive: true });
223
224      track.addEventListener("keydown", (e) => {
225        if (!pageStarts.length) return;
226        if (e.key === "ArrowRight") {
227          e.preventDefault();
228          goToPage(activePage + 1, "smooth");
229        } else if (e.key === "ArrowLeft") {
230          e.preventDefault();
231          goToPage(activePage - 1, "smooth");
232        }
233      });
234    }
235
236    function rebuild(keepPage) {
237      const prev = activePage;
238
239      applyLayout();
240      computePages();
241      buildDots();
242
243      // ✅ ricrea observer (layout/breakpoint possono cambiare)
244      if (io) {
245        io.disconnect();
246        io = null;
247      }
248      io = setupObserver();
249
250      const target = keepPage
251        ? clamp(prev, 0, pageStarts.length ? pageStarts.length - 1 : 0)
252        : 0;
253
254      isProgrammatic = false;
255      programTarget = target;
256
257      setActive(target);
258      if (pageStarts.length) {
259        track.scrollTo({ left: pageStarts[target], behavior: "auto" });
260      }
261
262      // ✅ micro-rebuild post layout (font/immagini)
263      clearTimeout(root.__lnvPostLayoutT);
264      root.__lnvPostLayoutT = setTimeout(() => {
265        computePages();
266        if (!io) setActive(getActivePageFromScroll());
267      }, 180);
268    }
269
270    rebuild(false);
271    window.setTimeout(() => rebuild(true), 150);
272
273    let rT;
274    window.addEventListener(
275      "resize",
276      () => {
277        clearTimeout(rT);
278        rT = setTimeout(() => rebuild(true), 120);
279      },
280      { passive: true }
281    );
282  }
283
284  function boot() {
285    document.querySelectorAll(".lnv-pubbl-slider[data-lnv-pubbl-slider]").forEach(initSlider);
286  }
287
288  if (document.readyState === "loading") {
289    document.addEventListener("DOMContentLoaded", boot);
290  } else {
291    boot();
292  }
293})();

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.