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.