1/** 2 * Divi Essential â Preloader extension (front-end runtime). 3 * 4 * Hides the overlay once BOTH the content-ready gate has passed AND the minimum 5 * display time has elapsed (whichever is later), then fades out. The gate is 6 * `viewport` (above-the-fold media only â the default) or `full_load` 7 * (window.load); see `hideOn` below. 8 * Loaded in <head>, so the overlay element may not exist yet at parse time â 9 * DOM access is deferred to DOMContentLoaded. 10 */ 11(function () { 12 "use strict"; 13 14 // Config ships as an inert <script type="application/json"> tag printed 15 // before this script, because JS optimizers (NitroPack, WP Rocket delay-JS, 16 // LiteSpeedâ¦) rewrite executable inline scripts into their delayed loaders 17 // but leave non-executable types alone. The global is the legacy fallback 18 // for cached pages generated by older plugin versions. 19 var cfg = (function () { 20 var node = document.getElementById("dnxte-preloader-config"); 21 if (node) { 22 try { 23 var parsed = JSON.parse(node.textContent); 24 if (parsed && typeof parsed === "object") { 25 return parsed; 26 } 27 } catch (e) {} 28 } 29 return window.dnxtePreloaderData || {}; 30 })(); 31 var minTime = parseInt(cfg.minDisplayTime, 10) || 0; 32 var fadeDur = parseInt(cfg.fadeOutDuration, 10) || 0; 33 var onceSession = cfg.showFrequency === "once_session"; 34 var SESSION_KEY = "dnxtePreloaderShown"; 35 var SAFETY_MS = 10000; // Force-hide if `load` never fires. 36 var ASSET_MS = 3000; // Per-asset ceiling for the viewport gate (see whenViewportReady). 37 38 var root = document.documentElement; 39 40 // "Once per session": if already shown, suppress before paint and bail. 41 if (onceSession && sessionStorage.getItem(SESSION_KEY)) { 42 var skip = document.createElement("style"); 43 skip.textContent = "#dnxte-preloader{display:none!important}"; 44 (document.head || root).appendChild(skip); 45 return; 46 } 47 48 // Lock scroll while the overlay is visible. 49 root.classList.add("dnxte-preloader-active"); 50 51 var loaded = false; 52 var minElapsed = false; 53 var done = false; 54 55 // Set by initStepText so the content-ready gate can advance any step bound to 56 // the `load` milestone. In viewport mode we hide before window.load fires, so 57 // without this the final step would never be shown. 58 var onLoadedForSteps = null; 59 60 var start = Date.now(); 61 62 function getEl() { 63 return document.getElementById("dnxte-preloader"); 64 } 65 66 function unlock() { 67 root.classList.remove("dnxte-preloader-active"); 68 } 69 70 function hide() { 71 if (done) { 72 return; 73 } 74 done = true; 75 76 var el = getEl(); 77 if (!el) { 78 unlock(); 79 return; 80 } 81 82 el.classList.add("dnxte-preloader--hide"); 83 84 var remove = function () { 85 if (el && el.parentNode) { 86 el.parentNode.removeChild(el); 87 } 88 unlock(); 89 if (onceSession) { 90 try { 91 sessionStorage.setItem(SESSION_KEY, "1"); 92 } catch (e) {} 93 } 94 }; 95 96 // Remove after the fade transition (with a small buffer). 97 window.setTimeout(remove, fadeDur + 50); 98 } 99 100 function maybeHide() { 101 if (loaded && minElapsed) { 102 hide(); 103 } 104 } 105 106 // Gate 1: minimum display time. 107 window.setTimeout(function () { 108 minElapsed = true; 109 maybeHide(); 110 }, minTime); 111 112 // Gate 2: content ready. 113 // 114 // `full_load` waits for window.load â i.e. every image and iframe on the page, 115 // including everything below the fold. `viewport` waits only for the media that 116 // is actually visible in the first screenful (plus fonts), which is all the user 117 // can see behind the overlay. window.load is still wired up in both modes as a 118 // fallback, so viewport mode can only ever hide EARLIER, never later. 119 function markLoaded() { 120 loaded = true; 121 if (typeof onLoadedForSteps === "function") { 122 onLoadedForSteps(); 123 } 124 maybeHide(); 125 } 126 127 if (document.readyState === "complete") { 128 markLoaded(); 129 } else { 130 window.addEventListener("load", markLoaded); 131 132 if (cfg.hideOn === "viewport") { 133 whenViewportReady(markLoaded); 134 } 135 } 136 137 /** 138 * Resolve once the above-the-fold media has settled. 139 * 140 * "Above the fold" is measured at DOMContentLoaded against the initial 141 * viewport height. Each wait resolves on error too, and every wait is capped at 142 * ASSET_MS, so a broken or never-settling asset cannot stall the overlay â and 143 * the window.load + SAFETY_MS fallbacks still apply. 144 */ 145 function whenViewportReady(cb) { 146 function run() { 147 var fold = window.innerHeight || 0; 148 var waits = []; 149 150 function inFold(node) { 151 var r = node.getBoundingClientRect();
152 // Skip zero-size (display:none / not yet laid out) nodes. 153 if (!r.width && !r.height) { 154 return false; 155 } 156 return r.top < fold && r.bottom > -1; 157 } 158 159 // Resolve once `node` has its content, per element type. Getting the 160 // "already done" test right matters more than the listener: if an asset 161 // finished before we got here and we only listen, the event has already 162 // been missed and the wait hangs forever. 163 function settle(node) { 164 return new Promise(function (res) { 165 var tag = node.tagName; 166 167 if ("IMG" === tag) { 168 // `complete` covers cached images. 169 if (node.complete) { 170 res(); 171 return; 172 } 173 } else if ("VIDEO" === tag) { 174 // <video> never fires `load` â only loadedmetadata/loadeddata. 175 // preload="none" fetches nothing until play(), so there is 176 // nothing to await. HAVE_METADATA (1) is our bar: preload 177 // defaults to "metadata" in Chrome, which stops at 1 and never 178 // fires `loadeddata`, so waiting for a decoded frame would hang 179 // on nearly every video. At metadata the box is laid out and the 180 // poster paints, which is all the gate needs. 181 if ("none" === node.preload || node.readyState >= 1) { 182 res(); 183 return; 184 } 185 node.addEventListener("loadedmetadata", res, { once: true }); 186 } else if ("IFRAME" === tag) { 187 // Same-origin (about:blank, srcdoc, src-less) is readable and 188 // is usually already complete by DOMContentLoaded. Cross-origin 189 // throws â fall through to the load listener + the ASSET_MS cap. 190 try { 191 var doc = node.contentDocument; 192 if (doc && "complete" === doc.readyState) { 193 res(); 194 return; 195 } 196 } catch (e) {} 197 } 198 199 // `error` resolves too, so a 404 asset can never hold the gate open. 200 node.addEventListener("load", res, { once: true }); 201 node.addEventListener("error", res, { once: true }); 202 }); 203 } 204 205 // Nothing may hold the gate open indefinitely. Racing every wait against 206 // a fixed ceiling makes that true for asset types we haven't special-cased 207 // yet, instead of silently falling back to window.load. 208 function capped(p) { 209 return Promise.race([ 210 p, 211 new Promise(function (res) { 212 window.setTimeout(res, ASSET_MS); 213 }), 214 ]); 215 } 216 217 var nodes = document.querySelectorAll("img, video, iframe"); 218 for (var i = 0; i < nodes.length; i++) { 219 if (inFold(nodes[i])) { 220 waits.push(settle(nodes[i])); 221 } 222 } 223 224 // CSS background-images are the common case for Divi heroes and are NOT 225 // covered by the tag query above; without this the overlay would lift 226 // before the hero paints, which looks worse than waiting. 227 var all = document.querySelectorAll("*"); 228 var seen = {}; 229 for (var j = 0; j < all.length; j++) { 230 var node = all[j]; 231 if (!inFold(node)) { 232 continue; 233 } 234 var bg = getComputedStyle(node).backgroundImage; 235 if (!bg || bg === "none") { 236 continue; 237 } 238 var m = bg.match(/url\((['"]?)(.*?)\1\)/); 239 if (!m || !m[2] || seen[m[2]]) { 240 continue; 241 } 242 seen[m[2]] = true; 243 waits.push(settle(Object.assign(new Image(), { src: m[2] }))); 244 } 245 246 if (document.fonts && document.fonts.ready && typeof document.fonts.ready.then === "function") { 247 waits.push(document.fonts.ready); 248 } 249 250 Promise.all(waits.map(capped)).then(cb, cb); 251 } 252 253 if (document.readyState === "loading") { 254 document.addEventListener("DOMContentLoaded", run, { once: true }); 255 } else { 256 run(); 257 } 258 } 259 260 // Safety net: never block the page indefinitely. 261 window.setTimeout(function () { 262 loaded = true; 263 minElapsed = true; 264 maybeHide(); 265 }, Math.max(SAFETY_MS, minTime + 1000)); 266 267 // --- Step-by-step loading text (milestone-based) --- 268 // Advances the text node through enabled steps as real load milestones fire, 269 // holding each step for at least `dwell` ms so it stays readable. The first 270 // step is already rendered server-side; this only handles steps 2..N. 271 var stepCfg = cfg.stepText || {}; 272 if (stepCfg.enabled && Array.isArray(stepCfg.steps) && stepCfg.steps.length > 1) { 273 initStepText(stepCfg); 274 } 275 276 function initStepText(sc) { 277 var steps = sc.steps; 278 var dwell = parseInt(sc.dwell, 10) || 0; 279 var SWAP_MS = 150; // must match the CSS fade duration. 280 281 // Which milestones have fired. `init` is true from the start. 282 var fired = { init: true, dom: false, fonts: false, load: false }; 283 284 var current = 0; // index of the currently shown step (0 = server-seeded). 285 var lastShownAt = Date.now(); 286 var stepEl = null; 287 288 function el() { 289 if (!stepEl) { 290 stepEl = document.querySelector(".dnxte-preloader__steps"); 291 } 292 return stepEl; 293 } 294 295 function show(idx) { 296 current = idx; 297 lastShownAt = Date.now(); 298 var node = el(); 299 if (!node) { 300 return; 301 } 302 node.classList.add("is-swapping"); 303 window.setTimeout(function () { 304 node.textContent = steps[idx].text; 305 node.classList.remove("is-swapping"); 306 }, SWAP_MS); 307 } 308 309 // Advance as far as the fired milestones and the dwell gate allow. 310 function tick() { 311 if (done) { 312 return; // overlay already hidden. 313 } 314 var next = current + 1; 315 if (next >= steps.length) { 316 return; 317 } 318 if (!fired[steps[next].milestone]) { 319 return; // its milestone hasn't fired yet. 320 } 321 var waited = Date.now() - lastShownAt; 322 if (waited < dwell) { 323 window.setTimeout(tick, dwell - waited); 324 return; 325 } 326 show(next); 327 window.setTimeout(tick, dwell); // the following milestone may already be ready. 328 } 329 330 function markFired(name) { 331 fired[name] = true; 332 tick(); 333 } 334 335 // DOMContentLoaded milestone. 336 if (document.readyState === "interactive" || document.readyState === "complete") { 337 fired.dom = true; 338 } else { 339 document.addEventListener("DOMContentLoaded", function () { 340 markFired("dom"); 341 }); 342 } 343 344 // Fonts milestone â guard for browsers without the Font Loading API. 345 if (document.fonts && document.fonts.ready && typeof document.fonts.ready.then === "function") { 346 document.fonts.ready.then(function () { 347 markFired("fonts"); 348 }); 349 } else { 350 fired.fonts = true; // treat as reached so it never stalls. 351 } 352 353 // window.load milestone. 354 if (document.readyState === "complete") { 355 fired.load = true; 356 } else { 357 window.addEventListener("load", function () { 358 markFired("load"); 359 }); 360 } 361 362 // In viewport mode the overlay hides before window.load, so let the 363 // content-ready gate satisfy this milestone too (whichever comes first). 364 onLoadedForSteps = function () { 365 markFired("load"); 366 }; 367 368 // Kick off (covers milestones that were already satisfied synchronously). 369 tick(); 370 } 371})();
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.