PageSourceSearch

https://bitontolive.it/wp-content/themes/livenetwork-theme/assets/js/lnv-sticky.js?ver=1790418968

js bitontolive.it collected 2026-10-02 18:49:46 UTC 12,563 bytes, 386 lines download raw bytes

1(function () {
2  "use strict";
3
4// Breakpoint LiveNetwork (4 step)
5var MQ = {
6  mobile: window.matchMedia("(max-width: 767px)"),
7  tablet: window.matchMedia("(min-width: 768px) and (max-width: 1023px)"),
8  laptop: window.matchMedia("(min-width: 1024px) and (max-width: 1199px)"),
9  desktop: window.matchMedia("(min-width: 1200px)")
10};
11
12// più stabile di matchMedia sul bordo (no scrollbar jitter)
13function currentBp() {
14  var w = document.documentElement.clientWidth;
15  if (w >= 1200) return "desktop";
16  if (w >= 1024) return "laptop";
17  if (w >= 768)  return "tablet";
18  return "mobile";
19}
20
21function parseOn(value) {
22  if (!value) return new Set(["desktop", "laptop", "tablet", "mobile"]);
23  var v = String(value).trim().toLowerCase();
24  if (v === "all") return new Set(["desktop", "laptop", "tablet", "mobile"]);
25  if (v === "none") return new Set([]);
26  return new Set(v.split(",").map(function (s) { return s.trim(); }).filter(Boolean));
27}
28
29
30  function readNumberLikePx(s) {
31    if (s == null) return null;
32    var n = parseFloat(String(s).replace(",", "."));
33    return isFinite(n) ? n : null;
34  }
35
36  function getCssVarPx(name) {
37    var raw = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
38    var n = readNumberLikePx(raw);
39    return n != null ? n : 0;
40  }
41
42  function getScrollY() {
43    return window.pageYOffset || document.documentElement.scrollTop || 0;
44  }
45
46  function clamp(n, min, max) {
47    return Math.max(min, Math.min(max, n));
48  }
49
50  function ensureGhost(host, beforeEl) {
51    var ghost = host.querySelector(":scope > .lnv-sticky-ghost");
52    if (ghost) return ghost;
53    ghost = document.createElement("div");
54    ghost.className = "lnv-sticky-ghost";
55    ghost.setAttribute("aria-hidden", "true");
56    host.insertBefore(ghost, beforeEl);
57    return ghost;
58  }
59
60  function setupOne(host) {
61    var onSet = parseOn(host.getAttribute("data-sticky-on"));
62    var targetSel = host.getAttribute("data-sticky-target");
63    var containerSel = host.getAttribute("data-sticky-container");
64
65    var bottomGapPx = readNumberLikePx(host.getAttribute("data-sticky-bottom-gap"));
66    bottomGapPx = (bottomGapPx != null) ? bottomGapPx : 16;
67
68    var topMode = "auto";
69    var topPx = 0;
70    var topAttr = host.getAttribute("data-sticky-top"); // "auto" | "80"
71    if (topAttr && String(topAttr).trim().toLowerCase() !== "auto") {
72      var n = readNumberLikePx(topAttr);
73      if (n != null) {
74        topMode = "number";
75        topPx = n;
76      }
77    }
78
79    var target = targetSel ? host.querySelector(targetSel) : host;
80    if (!target) return;
81
82    // container che definisce il limite inferiore
83    var container = null;
84    if (containerSel) container = document.querySelector(containerSel);
85    if (!container) container = host.closest(".lnv-necrologi-wrapper") || host.parentElement;
86
87    var ghost = ensureGhost(host, target);
88
89    var state = {
90      enabled: false,
91      needsMeasure: true,
92      raf: 0,
93      lastCss: "",
94
95      // runtime
96      prevScrollY: 0,
97      dy: 0,           // translateY corrente (smart)
98      inFixed: false,  // siamo in fixed?
99
100      // misure
101      scrollY: 0,
102      topOffset: 0,
103      hostLeft: 0,
104      hostWidth: 0,
105      targetH: 0,
106
107      // doc limits
108      minDocTop: 0,
109      maxDocTop: 0,
110
111      // viewport
112      viewportAvail: 0,
113      minDy: 0, // (viewportAvail - targetH) se target > viewportAvail, altrimenti 0
114
115      // start
116      startScroll: 0
117    };
118
119    function shouldEnable() {
120      return onSet.has(currentBp());
121    }
122
123    function getTopOffset() {
124      if (topMode === "number") return topPx;
125      return getCssVarPx("--lnv-sticky-top");
126    }
127
128    function measure() {
129      state.scrollY = getScrollY();
130      state.topOffset = getTopOffset();
131
132      // ====== CONTAINER (limite + padding) ======
133      var cr = container.getBoundingClientRect();
134      var ccs = getComputedStyle(container);
135      var cpl = parseFloat(ccs.paddingLeft) || 0;
136      var cpr = parseFloat(ccs.paddingRight) || 0;
137      var cpt = parseFloat(ccs.paddingTop) || 0;
138      var cpb = parseFloat(ccs.paddingBottom) || 0;
139
140      var containerTopDoc    = cr.top + state.scrollY + cpt;
141      var containerBottomDoc = cr.bottom + state.scrollY - cpb;
142
143      // ====== HOST (colonna destra) ======
144      var hostRect = host.getBoundingClientRect();
145      var hostCS = getComputedStyle(host);
146      var hpl = parseFloat(hostCS.paddingLeft) || 0;
147      var hpr = parseFloat(hostCS.paddingRight) || 0;
148
149      var hostInnerLeft  = hostRect.left + (window.scrollX || 0) + hpl;
150      var hostInnerRight = hostRect.right + (window.scrollX || 0) - hpr;
151
152      var containerInnerLeft  = cr.left + (window.scrollX || 0) + cpl;
153      var containerInnerRight = cr.right + (window.scrollX || 0) - cpr;
154
155      var left  = Math.max(hostInnerLeft,  containerInnerLeft);
156      var right = Math.min(hostInnerRight, containerInnerRight);
157
158      state.hostLeft  = left;
159      state.hostWidth = Math.max(0, right - left);
160
161      // altezza target
162      var tr = target.getBoundingClientRect();
163      state.targetH = Math.ceil(tr.height);
164
165      // viewport usable
166      state.viewportAvail = Math.max(0, window.innerHeight - state.topOffset - bottomGapPx);
167
168      // limiti doc (sidebar deve restare dentro container)
169      state.minDocTop = containerTopDoc;
170      state.maxDocTop = containerBottomDoc - bottomGapPx - state.targetH;
171
172      if (state.maxDocTop < state.minDocTop) {
173        state.maxDocTop = state.minDocTop;
174      }
175
176      // start: quando il ghost supera topOffset
177      var gr = ghost.getBoundingClientRect();
178      var ghostTopDoc = gr.top + state.scrollY;
179      state.startScroll = ghostTopDoc - state.topOffset;
180
181      // minDy per modalità “tall”
182      state.minDy = (state.targetH > state.viewportAvail)
183        ? (state.viewportAvail - state.targetH)  // negativo
184        : 0;
185
186      state.needsMeasure = false;
187    }
188
189    function reset() {
190      target.classList.remove("lnv-sticky-fixed");
191      target.style.left = "";
192      target.style.width = "";
193      target.style.transform = "";
194      target.style.top = "";
195      ghost.style.setProperty("--lnv-sticky-ghost-h", "0px");
196
197      state.lastCss = "";
198      state.dy = 0;
199      state.inFixed = false;
200    }
201
202    function applyConstraints(dy, scrollY) {
203  var EPS = 1; // px (tolleranza anti-oscillazione)
204  var docTop = scrollY + state.topOffset + dy;
205
206  // BORDO ALTO: trattalo come ">=/<= con tolleranza"
207  if (docTop <= state.minDocTop + EPS) {
208    return state.minDocTop - scrollY - state.topOffset;
209  }
210
211  // BORDO BASSO: idem (qui è dove ti lampeggia)
212  if (docTop >= state.maxDocTop - EPS) {
213    return state.maxDocTop - scrollY - state.topOffset;
214  }
215
216  // dentro al container: clamp viewport (smart)
217  return clamp(dy, state.minDy, 0);
218}
219
220
221
222    function apply() {
223      var enable = shouldEnable();
224      state.enabled = enable;
225
226      var scrollY = getScrollY();
227      state.scrollY = scrollY;
228
229      if (!enable) {
230        state.prevScrollY = scrollY;
231        reset();
232        return;
233      }
234
235      if (state.needsMeasure) measure();
236
237      if (!state.targetH || !state.hostWidth) {
238        state.prevScrollY = scrollY;
239        reset();
240        return;
241      }
242
243      // prima della start: tutto static
244      if (scrollY <= state.startScroll) {
245        state.prevScrollY = scrollY;
246        reset();
247        return;
248      }
249
250      // attivo fixed
251      target.classList.add("lnv-sticky-fixed");
252      target.style.left = Math.round(state.hostLeft) + "px";
253      target.style.width = Math.round(state.hostWidth) + "px";
254      target.style.top = state.topOffset + "px";
255      ghost.style.setProperty("--lnv-sticky-ghost-h", state.targetH + "px");
256
257      var delta = scrollY - (state.prevScrollY || scrollY);
258      state.prevScrollY = scrollY;
259
260      // ====== MODE A: sidebar “corta” (entra in viewport) ======
261      if (state.targetH <= state.viewportAvail) {
262        // docTop ideale = scrollY + topOffset (dy=0), ma non oltre maxDocTop
263        var dyShort = 0;
264        var docTopNoDy = scrollY + state.topOffset;
265        if (docTopNoDy > state.maxDocTop) {
266          dyShort = state.maxDocTop - docTopNoDy; // negativo
267        }
268        dyShort = clamp(dyShort, -999999, 0);
269        // quantizzo
270        dyShort = Math.round(dyShort * 2) / 2;
271
272        var cssA = "A|l:" + Math.round(state.hostLeft) +
273                   "|w:" + Math.round(state.hostWidth) +
274                   "|t:" + state.topOffset +
275                   "|y:" + dyShort;
276
277        if (cssA !== state.lastCss) {
278          target.style.transform = "translate3d(0," + dyShort + "px,0)";
279          state.lastCss = cssA;
280        }
281        state.dy = dyShort;
282        state.inFixed = true;
283        return;
284      }
285
286      // ====== MODE B: sidebar “tall” (più alta della viewport) ======
287      // init deterministico (utile se ricarichi a metà pagina)
288      if (!state.inFixed) {
289        // progressione naturale dal punto di start: “scroll interno” 1:1
290        var dyInit = clamp(-(scrollY - state.startScroll), state.minDy, 0);
291        state.dy = applyConstraints(dyInit, scrollY);
292        state.inFixed = true;
293      }
294
295      // comportamento “smart” (direction-aware, no jitter)
296      if (delta > 0) {
297        // scroll down: spingo su finché il bottom non entra in viewport
298        var bottom = state.topOffset + state.dy + state.targetH;
299        var vBottom = window.innerHeight - bottomGapPx;
300        if (bottom > vBottom) {
301          state.dy -= delta;
302        }
303      } else if (delta < 0) {
304        // scroll up: tiro giù finché il top torna visibile
305        var top = state.topOffset + state.dy;
306        if (top < state.topOffset) {
307          state.dy -= delta; // delta negativo => dy aumenta
308        }
309      }
310
311      // clamp viewport + clamp container
312      state.dy = applyConstraints(state.dy, scrollY);
313
314      // quantizzo
315      var dy = Math.round(state.dy * 2) / 2;
316state.dy = dy;
317
318      var cssB = "B|l:" + Math.round(state.hostLeft) +
319                 "|w:" + Math.round(state.hostWidth) +
320                 "|t:" + state.topOffset +
321                 "|y:" + dy;
322
323      if (cssB !== state.lastCss) {
324        target.style.transform = "translate3d(0," + dy + "px,0)";
325        state.lastCss = cssB;
326      }
327    }
328
329    function requestApply(measureFirst) {
330      if (measureFirst) state.needsMeasure = true;
331      if (state.raf) return;
332      state.raf = requestAnimationFrame(function () {
333        state.raf = 0;
334        apply();
335      });
336    }
337
338    // listeners
339    window.addEventListener("scroll", function () { requestApply(false); }, { passive: true });
340    window.addEventListener("resize", function () { requestApply(true); });
341
342    // breakpoint change
343    Object.keys(MQ).forEach(function (k) {
344      var m = MQ[k];
345      if (typeof m.addEventListener === "function") {
346        m.addEventListener("change", function () { requestApply(true); });
347      } else {
348        m.addListener(function () { requestApply(true); });
349      }
350    });
351
352    // ResizeObserver: ads che cambiano altezza/width
353    if (window.ResizeObserver) {
354      var ro = new ResizeObserver(function () { requestApply(true); });
355      ro.observe(host);
356      ro.observe(target);
357      ro.observe(container);
358    }
359
360    // MutationObserver: skin che aggiunge/toglie classi
361    if (window.MutationObserver) {
362      var mo = new MutationObserver(function () { requestApply(true); });
363      mo.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
364      mo.observe(document.body, { attributes: true, attributeFilter: ["class"] });
365    }
366
367    // init + stabilizzazione post-load
368    state.prevScrollY = getScrollY();
369    requestApply(true);
370    window.addEventListener("load", function () { requestApply(true); });
371    setTimeout(function () { requestApply(true); }, 250);
372    setTimeout(function () { requestApply(true); }, 1000);
373  }
374
375  function init() {
376    var nodes = document.querySelectorAll("[data-lnv-sticky]");
377    if (!nodes.length) return;
378    nodes.forEach(setupOne);
379  }
380
381  if (document.readyState === "loading") {
382    document.addEventListener("DOMContentLoaded", init);
383  } else {
384    init();
385  }
386})();

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.