PageSourceSearch

https://geaugalibrary.net/wp-content/plugins/divi-background-plus/slideshow.js?ver=1779200772

js geaugalibrary.net collected 2026-10-03 21:37:43 UTC 13,396 bytes, 322 lines download raw bytes

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.