PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
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 / post-carousel / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/post-carousel/frontend.js

329 lines 14.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 var _typoKeys = {
5 family:'font-family', weight:'font-weight', style:'font-style',
6 decoration:'text-decoration', transform:'text-transform',
7 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
8 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
9 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
10 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
11 };
12 function typoCssVarsForEl(el, obj, prefix) {
13 if (!obj || typeof obj !== 'object') return;
14 Object.keys(_typoKeys).forEach(function (k) {
15 var v = obj[k];
16 if (v === undefined || v === '' || v === null) return;
17 if (k === 'sizeDesktop' || k === 'sizeTablet' || k === 'sizeMobile') v = v + (obj.sizeUnit || 'px');
18 else if (k === 'lineHeightDesktop' || k === 'lineHeightTablet' || k === 'lineHeightMobile') v = v + (obj.lineHeightUnit || '');
19 else if (k === 'letterSpacingDesktop' || k === 'letterSpacingTablet' || k === 'letterSpacingMobile') v = v + (obj.letterSpacingUnit || 'px');
20 else if (k === 'wordSpacingDesktop' || k === 'wordSpacingTablet' || k === 'wordSpacingMobile') v = v + (obj.wordSpacingUnit || 'px');
21 el.style.setProperty(prefix + _typoKeys[k], String(v));
22 });
23 }
24
25 function escHtml(str) {
26 if (!str) return '';
27 return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
28 }
29
30 function trimWords(html, count) {
31 var text = html.replace(/<[^>]+>/g, '');
32 var words = text.split(/\s+/).filter(Boolean);
33 return words.slice(0, count).join(' ') + (words.length > count ? '' : '');
34 }
35
36 function initCarousel(wrap) {
37 var d = wrap.dataset;
38
39 var postType = d.postType || 'post';
40 var orderby = d.orderby || 'date';
41 var order = d.order || 'desc';
42 var perPage = parseInt(d.perPage, 10) || 6;
43 var excerptLen = parseInt(d.excerptLen, 10) || 20;
44 var gap = parseInt(d.gap, 10) || 24;
45 var spvDesk = parseInt(d.spv, 10) || 3;
46 var spvTablet = parseInt(d.spvTablet, 10) || 2;
47 var spvMobile = parseInt(d.spvMobile, 10) || 1;
48 var autoplay = d.autoplay !== '0';
49 var apSpeed = parseInt(d.apSpeed, 10) || 4000;
50 var pauseHover = d.pauseHover !== '0';
51 var loop = d.loop !== '0';
52 var showArrows = d.arrows !== '0';
53 var arrowStyle = d.arrowStyle || 'circle';
54 var showDots = d.dots !== '0';
55 var showImage = d.showImage !== '0';
56 var showMeta = d.showMeta !== '0';
57 var showExcerpt = d.showExcerpt !== '0';
58 var showRm = d.showRm !== '0';
59 var rmLabel = d.rmLabel || 'Read More';
60 var ratio = d.ratio || '56.25';
61 var cardStyle = d.cardStyle || 'shadow';
62 var cardBg = d.cardBg || '#ffffff';
63 var cardRadius = d.cardRadius || '10';
64 var titleC = d.titleC || '#111827';
65 var excerptC = d.excerptC || '#6b7280';
66 var metaC = d.metaC || '#9ca3af';
67 var linkC = d.linkC || '#2563eb';
68 var arrowBg = d.arrowBg || '#ffffff';
69 var arrowC = d.arrowC || '#111827';
70 var dotC = d.dotC || '#d1d5db';
71 var dotActiveC = d.dotActive || '#111827';
72
73 var titleTypo = JSON.parse(d.titleTypo || '{}');
74 var excerptTypo = JSON.parse(d.excerptTypo || '{}');
75 var metaTypo = JSON.parse(d.metaTypo || '{}');
76
77 /* Loading placeholder */
78 wrap.innerHTML = '<div class="bkbg-poc-loading">Loading posts…</div>';
79
80 /* Determine REST base */
81 var restBase = (window.blockenbergData && window.blockenbergData.restUrl)
82 ? window.blockenbergData.restUrl
83 : window.location.origin + '/wp-json/';
84
85 var endpoint = restBase + 'wp/v2/' + postType +
86 '?per_page=' + perPage +
87 '&orderby=' + orderby +
88 '&order=' + order +
89 '&_embed=1';
90
91 fetch(endpoint)
92 .then(function (r) { return r.ok ? r.json() : Promise.reject(r.status); })
93 .then(function (posts) { buildCarousel(posts); })
94 .catch(function () {
95 wrap.innerHTML = '<div class="bkbg-poc-loading">Could not load posts.</div>';
96 });
97
98 function getSpv() {
99 var w = window.innerWidth;
100 if (w < 600) return spvMobile;
101 if (w < 960) return spvTablet;
102 return spvDesk;
103 }
104
105 function buildCarousel(posts) {
106 if (!posts || !posts.length) {
107 wrap.innerHTML = '<div class="bkbg-poc-loading">No posts found.</div>';
108 return;
109 }
110 wrap.innerHTML = '';
111
112 typoCssVarsForEl(wrap, titleTypo, '--bkbg-poc-tt-');
113 typoCssVarsForEl(wrap, excerptTypo, '--bkbg-poc-ex-');
114 typoCssVarsForEl(wrap, metaTypo, '--bkbg-poc-mt-');
115
116 /* Track outer */
117 var trackOuter = document.createElement('div');
118 trackOuter.className = 'bkbg-poc-track-outer';
119 var track = document.createElement('div');
120 track.className = 'bkbg-poc-track';
121
122 posts.forEach(function (post) {
123 var card = document.createElement('div');
124 card.className = 'bkbg-poc-card bkbg-poc-card--' + cardStyle;
125 card.style.cssText = 'background:' + cardBg + ';border-radius:' + cardRadius + 'px;';
126
127 /* Image */
128 if (showImage) {
129 var thumb = post._embedded && post._embedded['wp:featuredmedia'] && post._embedded['wp:featuredmedia'][0];
130 var imgUrl= thumb ? (thumb.media_details && thumb.media_details.sizes && thumb.media_details.sizes.medium_large
131 ? thumb.media_details.sizes.medium_large.source_url
132 : thumb.source_url) : '';
133 var imgWrap = document.createElement('div');
134 imgWrap.className = 'bkbg-poc-img-wrap';
135 imgWrap.style.paddingBottom = ratio + '%';
136 if (imgUrl) {
137 var img = document.createElement('img');
138 img.src = imgUrl;
139 img.alt = post.title && post.title.rendered ? post.title.rendered : '';
140 img.loading = 'lazy';
141 imgWrap.appendChild(img);
142 }
143 card.appendChild(imgWrap);
144 }
145
146 /* Body */
147 var body = document.createElement('div');
148 body.className = 'bkbg-poc-body';
149
150 if (showMeta) {
151 var meta = document.createElement('p');
152 meta.className = 'bkbg-poc-meta';
153 meta.style.color = metaC;
154 meta.textContent = new Date(post.date).toLocaleDateString(undefined, { year:'numeric', month:'short', day:'numeric' });
155 body.appendChild(meta);
156 }
157
158 var titleLink = document.createElement('a');
159 titleLink.className = 'bkbg-poc-title';
160 titleLink.href = post.link;
161 titleLink.style.color = titleC;
162 titleLink.innerHTML = post.title ? post.title.rendered : '';
163 body.appendChild(titleLink);
164
165 if (showExcerpt) {
166 var ex = document.createElement('p');
167 ex.className = 'bkbg-poc-excerpt';
168 ex.style.color = excerptC;
169 var rawExcerpt = post.excerpt ? post.excerpt.rendered : '';
170 ex.textContent = trimWords(rawExcerpt, excerptLen);
171 body.appendChild(ex);
172 }
173
174 if (showRm) {
175 var rm = document.createElement('a');
176 rm.className = 'bkbg-poc-rm';
177 rm.href = post.link;
178 rm.style.color = linkC;
179 rm.textContent = rmLabel + '';
180 body.appendChild(rm);
181 }
182
183 card.appendChild(body);
184 track.appendChild(card);
185 });
186
187 trackOuter.appendChild(track);
188 wrap.appendChild(trackOuter);
189
190 /* Navigation */
191 var navDiv = document.createElement('div');
192 navDiv.className = 'bkbg-poc-nav';
193
194 var prevBtn, nextBtn, dotsContainer;
195 if (showArrows) {
196 var arrowsDiv = document.createElement('div');
197 arrowsDiv.className = 'bkbg-poc-arrows';
198
199 prevBtn = document.createElement('button');
200 prevBtn.className = 'bkbg-poc-arrow bkbg-poc-arrow--' + arrowStyle;
201 prevBtn.innerHTML = '';
202 prevBtn.style.background = arrowBg;
203 prevBtn.style.color = arrowC;
204 prevBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.12)';
205
206 nextBtn = document.createElement('button');
207 nextBtn.className = 'bkbg-poc-arrow bkbg-poc-arrow--' + arrowStyle;
208 nextBtn.innerHTML = '';
209 nextBtn.style.background = arrowBg;
210 nextBtn.style.color = arrowC;
211 nextBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.12)';
212
213 arrowsDiv.appendChild(prevBtn);
214 arrowsDiv.appendChild(nextBtn);
215 navDiv.appendChild(arrowsDiv);
216 }
217
218 if (showDots) {
219 dotsContainer = document.createElement('div');
220 dotsContainer.className = 'bkbg-poc-dots';
221 var numDots = Math.ceil(posts.length / getSpv());
222 for (var di = 0; di < numDots; di++) {
223 (function (i) {
224 var dot = document.createElement('button');
225 dot.className = 'bkbg-poc-dot' + (i === 0 ? ' is-active' : '');
226 dot.style.background = i === 0 ? dotActiveC : dotC;
227 dot.addEventListener('click', function () { goTo(i); });
228 dotsContainer.appendChild(dot);
229 })(di);
230 }
231 navDiv.appendChild(dotsContainer);
232 }
233
234 wrap.appendChild(navDiv);
235
236 /* State */
237 var currentIndex = 0;
238
239 function updateCardWidths() {
240 var spv = getSpv();
241 var totalGap = gap * (spv - 1);
242 var cardW = 'calc((' + (100 / spv) + '%) - ' + (totalGap / spv) + 'px)';
243 var cards = track.querySelectorAll('.bkbg-poc-card');
244 cards.forEach(function (c) {
245 c.style.width = cardW;
246 c.style.marginRight = gap + 'px';
247 });
248 if (cards.length) {
249 cards[cards.length - 1].style.marginRight = '0';
250 }
251 }
252
253 function goTo(idx) {
254 var spv = getSpv();
255 var total = posts.length;
256 var max = loop ? total - spv : Math.max(0, total - spv);
257
258 if (loop) {
259 if (idx >= total) idx = 0;
260 if (idx < 0) idx = total - 1;
261 } else {
262 if (idx > max) idx = max;
263 if (idx < 0) idx = 0;
264 }
265 currentIndex = idx;
266
267 var cards = track.querySelectorAll('.bkbg-poc-card');
268 if (!cards.length) return;
269 var cardW = cards[0].offsetWidth + gap;
270 track.style.transform = 'translateX(-' + (cardW * currentIndex) + 'px)';
271
272 /* Update dots */
273 if (dotsContainer) {
274 var dots = dotsContainer.querySelectorAll('.bkbg-poc-dot');
275 var activeGroup = Math.floor(currentIndex / spv);
276 dots.forEach(function (dot, i) {
277 var isActive = i === activeGroup;
278 dot.classList.toggle('is-active', isActive);
279 dot.style.background = isActive ? dotActiveC : dotC;
280 });
281 }
282
283 /* Disable arrows at ends */
284 if (prevBtn && !loop) { prevBtn.disabled = currentIndex === 0; prevBtn.style.opacity = currentIndex === 0 ? '0.4' : '1'; }
285 if (nextBtn && !loop) { nextBtn.disabled = currentIndex >= max; nextBtn.style.opacity = currentIndex >= max ? '0.4' : '1'; }
286 }
287
288 /* Autoplay */
289 var apTimer;
290 function startAutoplay() {
291 if (!autoplay) return;
292 apTimer = setInterval(function () { goTo(currentIndex + 1); }, apSpeed);
293 }
294 function stopAutoplay() { clearInterval(apTimer); }
295
296 /* Events */
297 if (prevBtn) prevBtn.addEventListener('click', function () { stopAutoplay(); goTo(currentIndex - 1); startAutoplay(); });
298 if (nextBtn) nextBtn.addEventListener('click', function () { stopAutoplay(); goTo(currentIndex + 1); startAutoplay(); });
299
300 if (pauseHover) {
301 wrap.addEventListener('mouseenter', stopAutoplay);
302 wrap.addEventListener('mouseleave', startAutoplay);
303 }
304
305 /* Touch swipe */
306 var touchStartX = 0;
307 trackOuter.addEventListener('touchstart', function (e) { touchStartX = e.touches[0].clientX; }, { passive: true });
308 trackOuter.addEventListener('touchend', function (e) {
309 var diff = touchStartX - e.changedTouches[0].clientX;
310 if (Math.abs(diff) > 50) { goTo(currentIndex + (diff > 0 ? 1 : -1)); }
311 });
312
313 window.addEventListener('resize', function () {
314 updateCardWidths();
315 goTo(currentIndex);
316 });
317
318 /* Init */
319 updateCardWidths();
320 goTo(0);
321 startAutoplay();
322 }
323 }
324
325 document.addEventListener('DOMContentLoaded', function () {
326 document.querySelectorAll('.bkbg-poc-wrap[data-post-type]').forEach(initCarousel);
327 });
328 })();
329