1/** 2 * Horizontal Scroll â Frontend Runtime 3 * GSAP ScrollTrigger-powered horizontal scroll for Divi Sections. 4 * Modes: pinned, snap, parallax, gallery 5 * 6 * @package DNXTE 7 */ 8(function () { 9 'use strict'; 10 11 function DnxteHScroll(el) { 12 this.el = el; 13 this._tweens = []; 14 this._sts = []; 15 this._injected = []; 16 this._destroyed = false; 17 18 var gsap = window.gsap; 19 var ST = window.ScrollTrigger; 20 if (!gsap || !ST) return; 21 22 this.gsap = gsap; 23 this.ST = ST; 24 25 // Read config from data attributes 26 this.type = el.getAttribute('data-hscroll-type') || 'pinned'; 27 this.speed = parseFloat(el.getAttribute('data-hscroll-speed')) || 1; 28 this.scrubRaw = el.getAttribute('data-hscroll-scrub') || '1'; 29 this.itemGap = Math.max(0, parseFloat(el.getAttribute('data-hscroll-item-gap')) || 0); 30 this.anticipate = el.getAttribute('data-hscroll-anticipate') === 'true'; 31 this.contentPx = el.getAttribute('data-hscroll-content-parallax') === 'true'; 32 this.contentSpd = parseFloat(el.getAttribute('data-hscroll-content-speed')) || 0.5; 33 this.enterFx = el.getAttribute('data-hscroll-enter') || 'none'; 34 this.stagger = el.getAttribute('data-hscroll-stagger') !== 'false'; 35 this.staggerDel = parseFloat(el.getAttribute('data-hscroll-stagger-delay')) || 0.15; 36 37 this._init(); 38 } 39 40 var proto = DnxteHScroll.prototype; 41 42 proto._init = function () { 43 var self = this; 44 var gsap = this.gsap; 45 var ST = this.ST; 46 var section = this.el; 47 48 // Find child "slides" â try rows first, then columns inside a single row 49 var slides = Array.prototype.slice.call(section.querySelectorAll(':scope > .et_pb_row, :scope > .et_pb_row_inner')); 50 51 // If only 1 row, use its columns as slides (most common DE use case) 52 if (slides.length === 1) { 53 var cols = slides[0].querySelectorAll(':scope > .et_pb_column, :scope > .et_pb_column_inner'); 54 if (cols.length >= 2) { 55 slides = Array.prototype.slice.call(cols); 56 } 57 } 58 59 // Fallback: direct children that aren't decorations 60 if (!slides.length) { 61 slides = Array.prototype.slice.call(section.children).filter(function (c) { 62 return !c.classList.contains('et_pb_section_video_bg') && 63 !c.classList.contains('et_pb_background_overlay') && 64 c.tagName !== 'STYLE' && c.tagName !== 'SCRIPT'; 65 }); 66 } 67 68 if (!slides || slides.length < 2) return; 69 70 // Section: only clip horizontal overflow so the off-screen slides don't 71 // leak. Leaves Divi's own min-height / height / padding / background alone. 72 section.style.setProperty('overflow', 'hidden', 'important'); 73 section.style.position = 'relative'; 74 75 // Track parent row for potential cleanup, but don't override its styles â 76 // whatever Divi row/column settings the user configured stay intact. 77 var parentRow = slides[0].parentElement; 78 if (parentRow && parentRow !== section && 79 (parentRow.classList.contains('et_pb_row') || parentRow.classList.contains('et_pb_row_inner'))) { 80 this._parentRow = parentRow; 81 } 82 83 // Inject horizontal flex wrapper â IN FLOW (not absolute). Section picks 84 // up its natural height from the slides' content. No height/width/padding 85 // forcing â Divi's column/row widths and paddings drive everything. 86 var wrapper = document.createElement('div'); 87 wrapper.className = 'dnxte-hscroll-wrapper'; 88 wrapper.style.cssText = [ 89 'display:flex', 'flex-wrap:nowrap', 90 'gap:' + this.itemGap + 'px', 91 'will-change:transform' 92 ].join(';') + ';'; 93 94 // Move slides into wrapper. Only prevent flex shrink/grow so the slides' 95 // Divi-configured widths survive when their total exceeds the viewport. 96 var items = []; 97 slides.forEach(function (slide) { 98 slide.style.setProperty('flex-shrink', '0', 'important'); 99 slide.style.setProperty('flex-grow', '0', 'important'); 100 wrapper.appendChild(slide); 101 items.push(slide); 102 }); 103 104 section.appendChild(wrapper); 105 this._injected.push(wrapper); 106 107 // wrapper is block-level inside the section's content-box, so its 108 // clientWidth already equals (section.offsetWidth - paddingLeft - paddingRight). 109 // scrollWidth - clientWidth = how much the wrapper must translate left so 110 // the last item's right edge lands at the content-area's right edge â 111 // section padding (left and right) is naturally respected. 112 var scrollDist = wrapper.scrollWidth - wrapper.clientWidth; 113 if (scrollDist <= 0) return; 114 115 // Parse scrub value 116 var scrubValue = this.scrubRaw === 'true' ? true : parseFloat(this.scrubRaw); 117 if (isNaN(scrubValue)) scrubValue = 1; 118 119 // ââ Main horizontal scroll timeline ââ 120 var stConfig = { 121 trigger: section, 122 start: 'top top', 123 end: function () { return '+=' + (scrollDist / self.speed); }, 124 scrub: scrubValue, 125 pin: true, 126 anticipatePin: this.anticipate ? 1 : 0, 127 }; 128 129 // Snap mode
130 if (this.type === 'snap' && items.length > 1) { 131 stConfig.snap = { 132 snapTo: 1 / (items.length - 1), 133 duration: { min: 0.2, max: 0.5 }, 134 ease: 'power1.inOut' 135 }; 136 } 137 138 var mainTL = gsap.to(wrapper, { 139 x: -scrollDist, 140 ease: 'none', 141 scrollTrigger: stConfig 142 }); 143 this._tweens.push(mainTL); 144 if (mainTL.scrollTrigger) this._sts.push(mainTL.scrollTrigger); 145 this._mainTL = mainTL; 146 147 // ââ Content Parallax ââ 148 if (this.contentPx || this.type === 'parallax') { 149 items.forEach(function (item) { 150 var content = item.querySelector('.et_pb_column, .et_pb_text, .et_pb_blurb, .et_pb_heading') || item.firstElementChild; 151 if (!content) return; 152 var tween = gsap.to(content, { 153 x: function () { return -window.innerWidth * self.contentSpd * 0.5; }, 154 scrollTrigger: { 155 trigger: item, 156 containerAnimation: mainTL, 157 start: 'left right', 158 end: 'right left', 159 scrub: true 160 } 161 }); 162 self._tweens.push(tween); 163 }); 164 } 165 166 // ââ Enter Effects ââ 167 if (this.enterFx !== 'none') { 168 items.forEach(function (item) { 169 var els = item.querySelectorAll('.et_pb_text, .et_pb_blurb, .et_pb_heading, .et_pb_button, img, h1, h2, h3, p'); 170 if (!els.length) els = [item]; 171 172 Array.prototype.forEach.call(els, function (target, j) { 173 var fromVars = { opacity: 0 }; 174 var toVars = { opacity: 1, duration: 0.6, ease: 'power2.out' }; 175 176 switch (self.enterFx) { 177 case 'slideUp': fromVars.y = 50; toVars.y = 0; break; 178 case 'slideDown': fromVars.y = -50; toVars.y = 0; break; 179 case 'scale': fromVars.scale = 0.85; toVars.scale = 1; break; 180 case 'blur': fromVars.filter = 'blur(10px)'; toVars.filter = 'blur(0px)'; break; 181 } 182 183 gsap.set(target, fromVars); 184 var tween = gsap.to(target, Object.assign({}, toVars, { 185 delay: self.stagger ? j * self.staggerDel : 0, 186 scrollTrigger: { 187 trigger: item, 188 containerAnimation: mainTL, 189 start: 'left 80%', 190 toggleActions: 'play none none reverse' 191 } 192 })); 193 self._tweens.push(tween); 194 }); 195 }); 196 } 197 198 ST.refresh(); 199 }; 200 201 proto.destroy = function () { 202 this._destroyed = true; 203 this._tweens.forEach(function (t) { try { t.kill(); } catch (e) {} }); 204 this._sts.forEach(function (st) { try { st.kill(); } catch (e) {} }); 205 this._injected.forEach(function (el) { 206 if (el && el.parentNode) el.parentNode.removeChild(el); 207 }); 208 if (this.el) this.el.style.overflow = ''; 209 this._tweens = []; this._sts = []; this._injected = []; 210 }; 211 212 // ââ Auto-init ââ 213 function initAll() { 214 var els = document.querySelectorAll('[data-hscroll-init="true"]'); 215 els.forEach(function (el) { 216 if (el._dnxteHScroll) return; 217 el._dnxteHScroll = new DnxteHScroll(el); 218 }); 219 } 220 221 function boot() { 222 initAll(); 223 if (document.body) { 224 var observer = new MutationObserver(function (mutations) { 225 var shouldInit = false; 226 mutations.forEach(function (m) { if (m.addedNodes && m.addedNodes.length) shouldInit = true; }); 227 if (shouldInit) initAll(); 228 }); 229 observer.observe(document.body, { childList: true, subtree: true }); 230 } 231 } 232 233 if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { 234 window.DnxteHScroll = DnxteHScroll; 235 return; 236 } 237 238 if (document.readyState === 'loading') { 239 document.addEventListener('DOMContentLoaded', boot); 240 } else { 241 boot(); 242 } 243 244 window.DnxteHScroll = DnxteHScroll; 245})();
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.