PageSourceSearch

https://bitontolive.it/wp-content/themes/livenetwork-theme/inc/necrologi/frontend.js?ver=1790418968

js bitontolive.it collected 2026-10-02 18:49:52 UTC 11,094 bytes, 388 lines download raw bytes

1(function () {
2  const MAX_DOTS = 5;
3
4  const reduceMotion =
5    window.matchMedia &&
6    window.matchMedia("(prefers-reduced-motion: reduce)").matches;
7
8  const BEHAVIOR = reduceMotion ? "auto" : "smooth";
9
10  function clamp(n, min, max) {
11    return Math.max(min, Math.min(max, n));
12  }
13
14  function isDesktopMode() {
15    return (
16      window.matchMedia &&
17      window.matchMedia("(min-width: 992px)").matches &&
18      window.matchMedia("(pointer: fine)").matches &&
19      window.matchMedia("(hover: hover)").matches
20    );
21  }
22
23  // =========================
24  // PhotoSwipe SOLO manifesto
25  // =========================
26function initManifestoPhotoSwipe() {
27  if (!window.PhotoSwipeLightbox || !window.PhotoSwipe) return;
28
29  const sizeCache = new Map();
30
31  function getImageSize(url) {
32    if (sizeCache.has(url)) return Promise.resolve(sizeCache.get(url));
33
34    return new Promise((resolve, reject) => {
35      const img = new Image();
36      img.onload = () => {
37        const w = img.naturalWidth || 0;
38        const h = img.naturalHeight || 0;
39        if (w && h) {
40          const v = { w, h };
41          sizeCache.set(url, v);
42          resolve(v);
43        } else {
44          reject(new Error("No natural size"));
45        }
46      };
47      img.onerror = () => reject(new Error("Image load error"));
48      img.src = url;
49    });
50  }
51
52  // Lightbox “manuale”: la apriamo noi con loadAndOpen()
53  const lb = new PhotoSwipeLightbox({
54    pswpModule: PhotoSwipe,
55  });
56  lb.init();
57
58  // Intercetta click prima che altri handler possano intervenire
59  document.addEventListener(
60    "click",
61    async function (e) {
62      const a = e.target.closest("a.lnv-pswp-manifesto");
63      if (!a) return;
64
65      const href = a.getAttribute("href") || "";
66      if (!href) return;
67
68      const clean = href.split("?")[0].toLowerCase();
69
70      // PDF: apri in nuova scheda
71      if (clean.endsWith(".pdf")) {
72        e.preventDefault();
73        window.open(a.href, "_blank", "noopener");
74        return;
75      }
76
77      // Immagine: apri in PhotoSwipe con dimensioni REALI
78      e.preventDefault();
79      e.stopPropagation();
80
81      try {
82        const { w, h } = await getImageSize(href);
83
84        lb.loadAndOpen(0, [
85          {
86            src: href,
87            width: w,
88            height: h,
89            // compat extra
90            w: w,
91            h: h,
92          },
93        ]);
94      } catch (err) {
95        // fallback sicuro: apri l’immagine in nuova scheda
96        window.open(a.href, "_blank", "noopener");
97      }
98    },
99    true
100  );
101}
102
103
104  // =========================
105  // Carousel helpers
106  // =========================
107  function findClosestIndexByRects(track, slides) {
108    const trackRect = track.getBoundingClientRect();
109    let bestIdx = 0;
110    let bestDist = Infinity;
111
112    for (let i = 0; i < slides.length; i++) {
113      const r = slides[i].getBoundingClientRect();
114      const dist = Math.abs(r.left - trackRect.left);
115      if (dist < bestDist) {
116        bestDist = dist;
117        bestIdx = i;
118      }
119    }
120    return bestIdx;
121  }
122
123  function getTargetLeftByRects(track, slide) {
124    const trackRect = track.getBoundingClientRect();
125    const slideRect = slide.getBoundingClientRect();
126
127    const delta = slideRect.left - trackRect.left;
128    const targetLeft = track.scrollLeft + delta;
129    const maxLeft = track.scrollWidth - track.clientWidth;
130
131    return clamp(targetLeft, 0, maxLeft);
132  }
133
134  // =========================
135  // Dots UI
136  // =========================
137  function ensureDotsUI(root, label) {
138    let dots = root.querySelector(".lnv-carousel-dots");
139    if (!dots) {
140      dots = document.createElement("div");
141      dots.className = "lnv-carousel-dots";
142      root.appendChild(dots);
143    }
144
145    if (reduceMotion) dots.classList.add("is-reduced-motion");
146
147    dots.setAttribute("role", "tablist");
148    dots.setAttribute("aria-label", label || "Paginazione");
149    dots.innerHTML = "";
150
151    const dotsTrack = document.createElement("div");
152    dotsTrack.className = "lnv-carousel-dots-track";
153    dots.appendChild(dotsTrack);
154
155    let counter = root.querySelector(".lnv-carousel-counter");
156    if (!counter) {
157      counter = document.createElement("div");
158      counter.className = "lnv-carousel-counter";
159      counter.setAttribute("aria-hidden", "true");
160      root.appendChild(counter);
161    }
162
163    return { dots, dotsTrack, counter, buttons: [], dotStep: 0 };
164  }
165
166  function computeDotStep(ui) {
167    const csDots = getComputedStyle(ui.dots);
168    const dot = parseFloat(csDots.getPropertyValue("--dot")) || 0;
169    const gap = parseFloat(csDots.getPropertyValue("--gap")) || 0;
170    if (dot > 0) return dot + gap;
171
172    const first = ui.buttons[0];
173    if (!first) return 0;
174
175    const w = first.getBoundingClientRect().width;
176    const csTrack = getComputedStyle(ui.dotsTrack);
177    const g = parseFloat(csTrack.columnGap || csTrack.gap || "0") || 0;
178
179    return w + g;
180  }
181
182  function buildAllDots(ui, total, onGo) {
183    ui.dotsTrack.innerHTML = "";
184    ui.buttons = [];
185
186    for (let i = 0; i < total; i++) {
187      const btn = document.createElement("button");
188      btn.type = "button";
189      btn.className = "lnv-carousel-dot";
190      btn.setAttribute("aria-label", `Vai all'elemento ${i + 1} di ${total}`);
191      btn.setAttribute("aria-current", "false");
192      btn.addEventListener("click", () => onGo(i));
193      ui.dotsTrack.appendChild(btn);
194      ui.buttons.push(btn);
195    }
196
197    ui.dots.style.setProperty("--max-dots", String(Math.min(MAX_DOTS, total)));
198
199    requestAnimationFrame(() => {
200      ui.dotStep = computeDotStep(ui);
201    });
202  }
203
204  function updateDots(ui, active, total) {
205    for (let i = 0; i < ui.buttons.length; i++) {
206      const isActive = i === active;
207      ui.buttons[i].classList.toggle("is-active", isActive);
208      ui.buttons[i].setAttribute("aria-current", isActive ? "true" : "false");
209    }
210
211    ui.counter.textContent = `${active + 1} / ${total}`;
212
213    if (total <= MAX_DOTS) {
214      ui.dotsTrack.style.transform = "translateX(0px)";
215      return;
216    }
217
218    if (!ui.dotStep) return;
219
220    const start = clamp(active - 2, 0, total - MAX_DOTS);
221    ui.dotsTrack.style.transform = `translateX(${-(start * ui.dotStep)}px)`;
222  }
223
224  // =========================
225  // Init carousel (per root)
226  // =========================
227  function initCarousel(root) {
228    if (root.dataset.lnvInit === "1") return;
229    root.dataset.lnvInit = "1";
230
231    const track = root.querySelector(".lnv-carousel-track");
232    const slides = Array.from(root.querySelectorAll(".lnv-carousel-slide"));
233    if (!track || slides.length < 2) return;
234
235    const label =
236      track.getAttribute("aria-label") ||
237      root.getAttribute("data-carousel") ||
238      "Paginazione";
239
240    const ui = ensureDotsUI(root, "Paginazione " + label);
241
242    let currentIndex = 0;
243    let lockTargetLeft = null;
244    let isLocked = false;
245
246    function setActive(i) {
247      const next = clamp(i, 0, slides.length - 1);
248      if (next !== currentIndex) currentIndex = next;
249      updateDots(ui, currentIndex, slides.length);
250    }
251
252    function goTo(idx) {
253      const next = clamp(idx, 0, slides.length - 1);
254      if (next === currentIndex) return;
255
256      const targetLeft = getTargetLeftByRects(track, slides[next]);
257      lockTargetLeft = targetLeft;
258      isLocked = true;
259
260      setActive(next);
261      track.scrollTo({ left: targetLeft, behavior: BEHAVIOR });
262    }
263
264    buildAllDots(ui, slides.length, (goIdx) => goTo(goIdx));
265
266    // Desktop drag prev/next
267    if (isDesktopMode()) {
268      (function enableDragPrevNext() {
269        const THRESHOLD = 45;
270        let pointerId = null;
271        let startX = 0;
272        let fired = false;
273        let suppressClickUntil = 0;
274
275        function isInteractiveTarget(el) {
276          return !!el.closest("a, button, input, textarea, select, label");
277        }
278
279        function onPointerDown(e) {
280          if (e.pointerType === "mouse" && e.button !== 0) return;
281          if (isInteractiveTarget(e.target)) return;
282
283          pointerId = e.pointerId;
284          startX = e.clientX;
285          fired = false;
286
287          isLocked = false;
288          lockTargetLeft = null;
289
290          try {
291            track.setPointerCapture(pointerId);
292          } catch (err) {}
293        }
294
295        function onPointerMove(e) {
296          if (pointerId === null || e.pointerId !== pointerId) return;
297          if (fired) return;
298
299          const dx = e.clientX - startX;
300
301          if (Math.abs(dx) >= THRESHOLD) {
302            fired = true;
303            if (dx < 0) goTo(currentIndex + 1);
304            else goTo(currentIndex - 1);
305
306            e.preventDefault();
307            suppressClickUntil = Date.now() + 350;
308          }
309        }
310
311        function onPointerUp(e) {
312          if (pointerId === null || e.pointerId !== pointerId) return;
313          pointerId = null;
314        }
315
316        track.addEventListener(
317          "click",
318          (e) => {
319            if (Date.now() < suppressClickUntil) {
320              e.preventDefault();
321              e.stopPropagation();
322            }
323          },
324          true
325        );
326
327        track.addEventListener("pointerdown", onPointerDown, { passive: true });
328        track.addEventListener("pointermove", onPointerMove, { passive: false });
329        track.addEventListener("pointerup", onPointerUp, { passive: true });
330        track.addEventListener("pointercancel", onPointerUp, { passive: true });
331      })();
332    }
333
334    // scroll/swipe -> update index
335    let raf = 0;
336    let settleT = 0;
337
338    track.addEventListener(
339      "scroll",
340      () => {
341        if (raf) return;
342
343        raf = requestAnimationFrame(() => {
344          raf = 0;
345
346          if (isLocked && lockTargetLeft !== null) {
347            if (Math.abs(track.scrollLeft - lockTargetLeft) <= 2) {
348              isLocked = false;
349              lockTargetLeft = null;
350            } else {
351              return;
352            }
353          }
354
355          const idx = findClosestIndexByRects(track, slides);
356          setActive(idx);
357
358          clearTimeout(settleT);
359          settleT = setTimeout(() => {
360            setActive(findClosestIndexByRects(track, slides));
361          }, 80);
362        });
363      },
364      { passive: true }
365    );
366
367    // resize
368    let resizeT = 0;
369    window.addEventListener(
370      "resize",
371      () => {
372        clearTimeout(resizeT);
373        resizeT = setTimeout(() => {
374          ui.dotStep = computeDotStep(ui);
375          setActive(findClosestIndexByRects(track, slides));
376        }, 120);
377      },
378      { passive: true }
379    );
380
381    setActive(0);
382  }
383
384  document.addEventListener("DOMContentLoaded", function () {
385    initManifestoPhotoSwipe();
386    document.querySelectorAll(".lnv-carousel").forEach(initCarousel);
387  });
388})();

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.