PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.11
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.11
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / team-members / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.11, at blocks/team-members/frontend.js

330 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // Team Members carousel navigation (scroll-snap based).
2 (function () {
3 function initCarousel(root) {
4 var track = root.querySelector('.bkbg-tm-track');
5 if (!track) return;
6
7 var settingsRoot = root.closest('.bkbg-tm-wrap') || root;
8 var infiniteEnabled = settingsRoot.getAttribute('data-bkbg-infinite') !== '0';
9 var autoScrollEnabled = settingsRoot.getAttribute('data-bkbg-autoscroll') === '1';
10 var autoScrollSpeed = parseFloat(settingsRoot.getAttribute('data-bkbg-autoscroll-speed') || '40');
11 if (!isFinite(autoScrollSpeed) || autoScrollSpeed < 0) autoScrollSpeed = 40;
12 var autoScrollPauseOnHover = settingsRoot.getAttribute('data-bkbg-autoscroll-hover') !== '0';
13
14 var prev = root.querySelector('[data-bkbg-nav="prev"], .bkbg-tm-prev');
15 var next = root.querySelector('[data-bkbg-nav="next"], .bkbg-tm-next');
16 var dots = root.querySelectorAll('[data-bkbg-dot]');
17 var progressBar = root.querySelector('.bkbg-tm-progress-bar');
18 var fractionCurrent = root.querySelector('[data-bkbg-fraction="current"]');
19 var fractionTotal = root.querySelector('[data-bkbg-fraction="total"]');
20
21 var baseCount = 0;
22 var didClone = false;
23 var isNormalizing = false;
24 var segmentStart = 0;
25 var segmentWidth = 0;
26
27 function disableTabbing(el) {
28 try {
29 var focusables = el.querySelectorAll('a, button, input, textarea, select');
30 for (var i = 0; i < focusables.length; i++) {
31 focusables[i].setAttribute('tabindex', '-1');
32 }
33 } catch (e) { }
34 }
35
36 function setupInfinite() {
37 if (!infiniteEnabled) return;
38 var slides = getSlides();
39 baseCount = slides.length;
40 if (baseCount < 2) return;
41 if (didClone) return;
42
43 var originals = Array.prototype.slice.call(slides);
44 var before = [];
45 var after = [];
46 for (var i = 0; i < originals.length; i++) {
47 var c1 = originals[i].cloneNode(true);
48 c1.classList.add('is-clone');
49 c1.setAttribute('aria-hidden', 'true');
50 disableTabbing(c1);
51 after.push(c1);
52
53 var c2 = originals[i].cloneNode(true);
54 c2.classList.add('is-clone');
55 c2.setAttribute('aria-hidden', 'true');
56 disableTabbing(c2);
57 before.push(c2);
58 }
59
60 for (var j = before.length - 1; j >= 0; j--) {
61 track.insertBefore(before[j], track.firstChild);
62 }
63 for (var k = 0; k < after.length; k++) {
64 track.appendChild(after[k]);
65 }
66
67 didClone = true;
68 window.requestAnimationFrame(function () {
69 measureInfinite(true);
70 syncIndicators();
71 });
72 }
73
74 function measureInfinite(recenter) {
75 if (!infiniteEnabled || !didClone || !baseCount) return;
76 var slides = getSlides();
77 if (!slides || slides.length < baseCount * 3) return;
78 var middleFirst = slides[baseCount];
79 var afterFirst = slides[baseCount * 2];
80 if (!middleFirst || !afterFirst) return;
81
82 var nextStart = middleFirst.offsetLeft;
83 var nextWidth = afterFirst.offsetLeft - middleFirst.offsetLeft;
84 if (!isFinite(nextStart) || !isFinite(nextWidth) || nextWidth <= 0) return;
85
86 if (recenter) {
87 var local = track.scrollLeft - segmentStart;
88 if (segmentWidth > 0) {
89 local = local % segmentWidth;
90 if (local < 0) local += segmentWidth;
91 } else {
92 local = 0;
93 }
94 segmentStart = nextStart;
95 segmentWidth = nextWidth;
96 track.scrollLeft = segmentStart + local;
97 } else {
98 segmentStart = nextStart;
99 segmentWidth = nextWidth;
100 }
101 }
102
103 function normalizeInfiniteScroll() {
104 if (!infiniteEnabled || !didClone) return;
105 if (!segmentWidth || !isFinite(segmentWidth)) {
106 measureInfinite(false);
107 }
108 if (!segmentWidth) return;
109
110 if (track.scrollLeft < (segmentStart - 1) || track.scrollLeft >= (segmentStart + segmentWidth + 1)) {
111 var local = track.scrollLeft - segmentStart;
112 local = local % segmentWidth;
113 if (local < 0) local += segmentWidth;
114 isNormalizing = true;
115 track.scrollLeft = segmentStart + local;
116 isNormalizing = false;
117 }
118 }
119
120 function getSlides() {
121 return track.querySelectorAll('.bkbg-tm-slide');
122 }
123
124 function getCurrentIndex() {
125 var slides = getSlides();
126 if (!slides.length) return 0;
127 var trackRect = track.getBoundingClientRect();
128 var currentIndex = 0;
129 for (var i = 0; i < slides.length; i++) {
130 var r = slides[i].getBoundingClientRect();
131 if (r.left >= trackRect.left - 10) {
132 currentIndex = i;
133 break;
134 }
135 }
136 return currentIndex;
137 }
138
139 function scrollToIndex(index) {
140 var slides = getSlides();
141 if (!slides.length) return;
142 var i = Math.max(0, Math.min(slides.length - 1, index));
143 slides[i].scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });
144 }
145
146 function scrollToBaseIndex(index) {
147 if (!infiniteEnabled || !didClone || !baseCount) {
148 scrollToIndex(index);
149 return;
150 }
151 var slides = getSlides();
152 if (!slides.length) return;
153 var idx = index % baseCount;
154 if (idx < 0) idx += baseCount;
155 var target = baseCount + idx;
156 if (target < 0) target = 0;
157 if (target > slides.length - 1) target = slides.length - 1;
158 slides[target].scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });
159 }
160
161 function scrollByDir(dir) {
162 var slides = getSlides();
163 if (!slides.length) return;
164 var currentIndex = getCurrentIndex();
165 scrollToIndex(currentIndex + dir);
166 }
167
168 function syncIndicators() {
169 var slides = getSlides();
170 if (!slides.length) return;
171
172 var idx = getCurrentIndex();
173 var total = slides.length;
174 if (infiniteEnabled && didClone && baseCount) {
175 total = baseCount;
176 idx = idx % baseCount;
177 if (idx < 0) idx += baseCount;
178 }
179
180 if (dots && dots.length) {
181 for (var i = 0; i < dots.length; i++) {
182 if (i === idx) dots[i].classList.add('is-active');
183 else dots[i].classList.remove('is-active');
184 }
185 }
186
187
188 if (progressBar) {
189 var pct = 0;
190 if (infiniteEnabled && didClone) {
191 if (!segmentWidth) measureInfinite(false);
192 var local = track.scrollLeft - segmentStart;
193 if (segmentWidth > 0) {
194 local = local % segmentWidth;
195 if (local < 0) local += segmentWidth;
196 }
197 var maxLocal = segmentWidth - track.clientWidth;
198 pct = maxLocal > 0 ? (local / maxLocal) * 100 : 0;
199 } else {
200 var max = track.scrollWidth - track.clientWidth;
201 pct = max > 0 ? (track.scrollLeft / max) * 100 : 0;
202 }
203 if (pct < 0) pct = 0;
204 if (pct > 100) pct = 100;
205 progressBar.style.width = pct + '%';
206 }
207
208 if (fractionCurrent) fractionCurrent.textContent = String(idx + 1);
209 if (fractionTotal) fractionTotal.textContent = String(total);
210 }
211
212 if (prev) {
213 prev.addEventListener('click', function (e) {
214 e.preventDefault();
215 scrollByDir(-1);
216 });
217 }
218
219 if (next) {
220 next.addEventListener('click', function (e) {
221 e.preventDefault();
222 scrollByDir(1);
223 });
224 }
225
226 if (dots && dots.length) {
227 for (var i = 0; i < dots.length; i++) {
228 (function (btn) {
229 btn.addEventListener('click', function (e) {
230 e.preventDefault();
231 var idx = parseInt(btn.getAttribute('data-bkbg-dot') || '0', 10);
232 scrollToBaseIndex(idx);
233 });
234 })(dots[i]);
235 }
236 }
237
238 // Auto-scroll (continuous)
239 var rafId = null;
240 var lastTs = 0;
241 var autoPaused = false;
242 var originalSnap = track.style.scrollSnapType;
243
244 function tick(ts) {
245 if (!autoScrollEnabled) return;
246 if (!lastTs) lastTs = ts;
247 var dt = ts - lastTs;
248 lastTs = ts;
249 if (!document.hidden && !autoPaused) {
250 track.scrollLeft += (autoScrollSpeed * dt) / 1000;
251 normalizeInfiniteScroll();
252 syncIndicators();
253 }
254 rafId = window.requestAnimationFrame(tick);
255 }
256
257 function startAutoScroll() {
258 if (!autoScrollEnabled) return;
259 if (rafId) return;
260 track.style.scrollSnapType = 'none';
261 lastTs = 0;
262 rafId = window.requestAnimationFrame(tick);
263 }
264
265 function stopAutoScroll() {
266 if (rafId) {
267 window.cancelAnimationFrame(rafId);
268 rafId = null;
269 }
270 track.style.scrollSnapType = originalSnap;
271 lastTs = 0;
272 }
273
274 if (autoScrollEnabled) {
275 startAutoScroll();
276 if (autoScrollPauseOnHover) {
277 root.addEventListener('mouseenter', function () { autoPaused = true; });
278 root.addEventListener('mouseleave', function () { autoPaused = false; });
279 }
280 document.addEventListener('visibilitychange', function () {
281 if (document.hidden) stopAutoScroll();
282 else startAutoScroll();
283 });
284 }
285
286 setupInfinite();
287
288 try {
289 var imgs = track.querySelectorAll('img');
290 for (var ii = 0; ii < imgs.length; ii++) {
291 (function (img) {
292 if (!img) return;
293 if (img.complete) return;
294 img.addEventListener('load', function () {
295 window.requestAnimationFrame(function () {
296 measureInfinite(true);
297 syncIndicators();
298 });
299 }, { passive: true });
300 })(imgs[ii]);
301 }
302 } catch (e) { }
303
304 window.addEventListener('resize', function () {
305 window.requestAnimationFrame(function () {
306 measureInfinite(true);
307 syncIndicators();
308 });
309 }, { passive: true });
310
311 syncIndicators();
312 track.addEventListener('scroll', function () {
313 normalizeInfiniteScroll();
314 if (isNormalizing) return;
315 syncIndicators();
316 }, { passive: true });
317 }
318
319 function initAll() {
320 var blocks = document.querySelectorAll('.wp-block-blockenberg-team-members .bkbg-tm-carousel');
321 for (var i = 0; i < blocks.length; i++) initCarousel(blocks[i]);
322 }
323
324 if (document.readyState === 'loading') {
325 document.addEventListener('DOMContentLoaded', initAll);
326 } else {
327 initAll();
328 }
329 })();
330