1/** 2 * d5bgo slideshow.js 3 * Handles ALL frontend overlay rendering â tint and image slideshow. 4 * Both are appended directly to the target ancestor (section/row/column) 5 * so they never conflict with sibling modules in the same column as the module. 6 */ 7(function () { 8 'use strict'; 9 10 // ââ Fisher-Yates shuffle ââââââââââââââââââââââââââââââââââââââââââââââââââââ 11 function shuffle(arr) { 12 var a = arr.slice(); 13 for (var i = a.length - 1; i > 0; i--) { 14 var j = Math.floor(Math.random() * (i + 1)); 15 var tmp = a[i]; a[i] = a[j]; a[j] = tmp; 16 } 17 return a; 18 } 19 20 // ââ Ancestor class map ââââââââââââââââââââââââââââââââââââââââââââââââââââââ 21 var ANCESTOR_CLASS = { 22 section: 'et_pb_section', 23 row: 'et_pb_row', 24 column: 'et_pb_column', 25 'fullwidth-section': 'et_pb_fullwidth_section', // D4 fullwidth-module target 26 }; 27
28 // ââ Find the target ancestor from the anchor span ââââââââââââââââââââââââââ 29 function findAncestor(moduleId, target) { 30 var anchor = document.querySelector('.d5bgo-anchor[data-module-id="' + moduleId + '"]'); 31 if (!anchor) return null; 32 var cls = ANCESTOR_CLASS[target] || 'et_pb_section'; 33 return anchor.closest('.' + cls) || null; 34 } 35 36 // ââ Ensure ancestor is a positioning context âââââââââââââââââââââââââââââââ 37 function ensurePositioned(ancestor) { 38 var pos = window.getComputedStyle(ancestor).position; 39 if (pos === 'static') ancestor.style.position = 'relative'; 40 } 41 42 // ââ Create a portal div appended to ancestor âââââââââââââââââââââââââââââââ 43 // All portals use positive z-index and are appended to the ANCESTOR, not the column. 44 // This means they never conflict with sibling modules in the same column. 45 function createPortal(ancestor, zIndex, extraCss) { 46 var portal = document.createElement('div'); 47 portal.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:' + zIndex + ';' + (extraCss || ''); 48 ancestor.appendChild(portal); 49 return portal; 50 } 51 52 // ââ Create an <img> layer ââââââââââââââââââââââââââââââââââââââââââââââââââ 53 function createImgLayer(portal, transitionMs) { 54 var img = document.createElement('img'); 55 img.alt = ''; // decorative â no alt text needed 56 img.style.cssText = [ 57 'position:absolute', 'inset:0', 'width:100%', 'height:100%', 58 'object-fit:cover', 'object-position:center', 59 'opacity:0', 60 'transition:opacity ' + transitionMs + 'ms ease-in-out', 61 'pointer-events:none', 62 ].join(';'); 63 portal.appendChild(img); 64 return img; 65 } 66 67 // ââ Bump content above overlays ââââââââââââââââââââââââââââââââââââââââââââ 68 // Injects a <style> tag that gives direct children of the ancestor z-index:2 69 // so they sit above the tint (z-index:1) and slideshow (z-index:0) portals. 70 function bumpContentZIndex(ancestor, moduleId) { 71 // Use a data attribute on the ancestor to track which modules have bumped it.
72 // Supports multiple Background Plus modules targeting the same ancestor. 73 var existing = ancestor.getAttribute('data-d5bgo-bumped') || ''; 74 if (existing.indexOf(moduleId) === -1) { 75 ancestor.setAttribute('data-d5bgo-bumped', existing ? existing + ' ' + moduleId : moduleId); 76 } 77 78 // Use a unique style ID per module so multiple instances don't conflict 79 var styleId = 'd5bgo-bump-' + moduleId; 80 if (document.getElementById(styleId)) return; // already injected 81 82 var attrSel = '[data-d5bgo-bumped~="' + moduleId + '"]'; // ~= matches space-separated list 83 var style = document.createElement('style'); 84 style.id = styleId; 85 style.textContent = [ 86 attrSel + ' > *:not([data-d5bgo-tint]):not([data-d5bgo-ss]):not(.et-pb-background-video):not(.et_pb_section_video_bg):not(.et-pb-parallax-wrapper):not(.et_parallax_bg_wrap) {', 87 ' position:relative;', 88 ' z-index:2;', 89 '}', 90 ].join('\n'); 91 document.head.appendChild(style); 92 } 93 94 // ââ TINT PORTAL âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 95 function initTint(cfg, ancestor) { 96 if (!cfg.tintBg) return; 97 var portal = createPortal(ancestor, 1); // z-index:1 â above section bg, below content (z-index:2+) 98 portal.setAttribute('data-d5bgo-tint', cfg.moduleId); 99 portal.style.background = cfg.tintBg; 100 } 101 102 // ââ SLIDESHOW MODE ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 103 function initSlideshow(cfg, ancestor) { 104 var images = cfg.images; 105 var duration = cfg.duration; 106 var transition = cfg.transition; 107 var isRandom = cfg.order === 'random'; 108 109 var portal = createPortal(ancestor, 0, 'overflow:hidden;'); // z-index:0 â behind tint (1) 110 portal.setAttribute('data-d5bgo-ss', cfg.moduleId); 111 112 if (images.length === 1) { 113 var imgEl = createImgLayer(portal, 0); 114 imgEl.src = images[0]; 115 imgEl.style.opacity = '1'; 116 return; 117 } 118 119 var layerA = createImgLayer(portal, transition); 120 var layerB = createImgLayer(portal, transition); 121 122 var queue = isRandom ? shuffle(images) : images.slice(); 123 var qIndex = 0; 124 var lastUsed = ''; 125 126 function nextImage() { 127 if (isRandom) { 128 if (!queue.length) queue = shuffle(images); 129 var pick = queue.pop(); 130 // Prevent back-to-back repeat at shuffle boundary 131 if (pick === lastUsed && queue.length > 0) { 132 queue.unshift(pick); 133 pick = queue.pop(); 134 } 135 lastUsed = pick; 136 return pick; 137 } 138 var img = images[qIndex % images.length]; 139 qIndex++; 140 return img; 141 } 142 143 var active = layerA; 144 var inactive = layerB; 145 146 // Show first image immediately 147 active.src = nextImage(); 148 active.onload = function () { 149 active.style.transition = 'none'; 150 active.style.opacity = '1'; 151 requestAnimationFrame(function () { 152 requestAnimationFrame(function () { 153 active.style.transition = 'opacity ' + transition + 'ms ease-in-out'; 154 }); 155 }); 156 }; 157 158 function preloadNext() { 159 inactive.src = nextImage(); 160 inactive.style.opacity = '0'; 161 } 162 preloadNext(); 163 164 function doSwap() { 165 inactive.style.opacity = '1'; 166 active.style.opacity = '0'; 167 setTimeout(function () { 168 var tmp = active; 169 active = inactive; 170 inactive = tmp; 171 preloadNext(); 172 }, transition); 173 } 174 175 var timer = setInterval(doSwap, duration + transition); 176 177 // Stop the timer and self-clean if the ancestor is ever removed from the DOM. 178 // Scoped to the ancestor's parent (childList only) so unrelated page mutations 179 // â typical on heavy SPAs and the Divi VB canvas â don't trigger a callback. 180 var observerHost = ancestor.parentNode || document.body; 181 var observer = new MutationObserver(function () { 182 if (!document.contains(ancestor)) { 183 clearInterval(timer); 184 observer.disconnect(); 185 } 186 }); 187 observer.observe(observerHost, { childList: true }); 188 } 189 190 // ââ PAGE RELOAD MODE ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 191 function initPageReload(cfg, ancestor) { 192 var images = cfg.images; 193 var moduleId = cfg.moduleId; 194 var isRandom = cfg.order === 'random'; 195 var lsKey = 'd5bgo_reload_' + moduleId; 196 197 if (!images.length) return; 198 199 var state = null; 200 try { 201 var raw = localStorage.getItem(lsKey); 202 if (raw) { 203 var parsed = JSON.parse(raw); 204 // Validate shape: must be a plain object with expected keys 205 if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { 206 state = parsed; 207 } 208 } 209 } catch (e) { /* corrupt or unavailable â start fresh */ } 210 211 var chosenUrl; 212 if (images.length === 1) { 213 chosenUrl = images[0]; 214 } else if (isRandom) { 215 // Restore queue from localStorage, or start a fresh shuffle.
216 // The queue must: exist, be a non-empty array of strings, AND 217 // every entry must be a URL that's still in the current image set. 218 // If images were added/removed in the module, the stale queue is 219 // discarded and a fresh full-set shuffle starts. 220 var imageSet = {}; 221 images.forEach(function(u) { imageSet[u] = true; }); 222 223 var qValid = state && Array.isArray(state.queue) && state.queue.length 224 && state.queue.every(function(u) { return typeof u === 'string' && imageSet[u]; }); 225 var q = qValid ? state.queue : shuffle(images); 226 var lastShown = (state && typeof state.last === 'string') ? state.last : ''; 227 228 chosenUrl = q.pop(); 229 230 // Prevent back-to-back repeat when a fresh shuffle starts. 231 // If the picked image matches the last one shown, swap it with 232 // another item still in the queue (if any remain). 233 if (chosenUrl === lastShown && q.length > 0) { 234 q.unshift(chosenUrl); // put it back at the front 235 chosenUrl = q.pop(); // take the next one instead 236 } 237 238 try { localStorage.setItem(lsKey, JSON.stringify({ queue: q, last: chosenUrl })); } catch (e) {} 239 } else { 240 var idx = (state && typeof state.index === 'number' && state.index >= 0) ? state.index : 0; 241 chosenUrl = images[idx % images.length]; 242 try { localStorage.setItem(lsKey, JSON.stringify({ index: (idx + 1) % images.length })); } catch (e) {} 243 } 244 245 var portal = createPortal(ancestor, 0, 'overflow:hidden;'); 246 portal.setAttribute('data-d5bgo-ss', moduleId); 247 var imgEl = createImgLayer(portal, 0); 248 imgEl.src = chosenUrl; 249 imgEl.style.opacity = '1'; 250 imgEl.style.transition = 'none'; 251 } 252 253 // ââ Visibility: inject media queries to hide portals on disabled devices ââ 254 function injectVisibilityStyles(cfg) { 255 var d = cfg.disabledOn; 256 if (!d || (!d.phone && !d.tablet && !d.desktop)) return; 257 258 var id = cfg.moduleId; 259 var styleId = 'd5bgo-visibility-' + id; 260 if (document.getElementById(styleId)) return; 261 262 var tintSel = '[data-d5bgo-tint="' + id + '"]'; 263 var ssSel = '[data-d5bgo-ss="' + id + '"]'; 264 var hideCss = tintSel + ',' + ssSel + '{display:none!important}'; 265 var rules = []; 266 267 if (d.phone) rules.push('@media(max-width:767px){' + hideCss + '}'); 268 if (d.tablet) rules.push('@media(min-width:768px) and (max-width:980px){' + hideCss + '}'); 269 if (d.desktop) rules.push('@media(min-width:981px){' + hideCss + '}'); 270 271 var style = document.createElement('style'); 272 style.id = styleId; 273 style.textContent = rules.join('\n'); 274 document.head.appendChild(style); 275 } 276 277 // ââ Bootstrap âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 278 function init() { 279 var configs = window.d5bgoSlideshow || []; 280 configs.forEach(function (cfg) { 281 // Decide up-front whether this module will actually inject a portal. 282 // D4's common case (gradient overlay only, no slideshow) has tintBg='' 283 // because the tint is painted via Divi's native background group on the 284 // ancestor â so there's nothing for slideshow.js to do. Skipping all 285 // DOM mutations in that case avoids stomping on Divi features like 286 // parallax backgrounds, which rely on absolutely-positioned wrapper 287 // elements the z-index bump would otherwise collapse. 288 var willRenderTint = !!cfg.tintBg; 289 var willRenderSlideshow = cfg.ssEnabled && cfg.images && cfg.images.length > 0; 290 if (!willRenderTint && !willRenderSlideshow) return; 291 292 var ancestor = findAncestor(cfg.moduleId, cfg.target); 293 if (!ancestor) return; 294 295 ensurePositioned(ancestor); 296 297 // Inject visibility media queries (hide portals on disabled devices) 298 injectVisibilityStyles(cfg); 299 300 // Bump content children above our portals 301 bumpContentZIndex(ancestor, cfg.moduleId); 302 303 // Tint portal (only when a tint colour/gradient is configured) 304 if (willRenderTint) initTint(cfg, ancestor); 305 306 // Slideshow or page reload 307 if (willRenderSlideshow) { 308 if (cfg.mode === 'page_reload') { 309 initPageReload(cfg, ancestor); 310 } else { 311 initSlideshow(cfg, ancestor); 312 } 313 } 314 }); 315 } 316 317 if (document.readyState === 'loading') { 318 document.addEventListener('DOMContentLoaded', init); 319 } else { 320 init(); 321 } 322}());
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.