(function () { 'use strict'; var _typoKeys = { family:'font-family', weight:'font-weight', style:'font-style', decoration:'text-decoration', transform:'text-transform', sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m', lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m', letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m', wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m' }; function typoCssVarsForEl(el, obj, prefix) { if (!obj || typeof obj !== 'object') return; Object.keys(_typoKeys).forEach(function (k) { var v = obj[k]; if (v === undefined || v === '' || v === null) return; if (k === 'sizeDesktop' || k === 'sizeTablet' || k === 'sizeMobile') v = v + (obj.sizeUnit || 'px'); else if (k === 'lineHeightDesktop' || k === 'lineHeightTablet' || k === 'lineHeightMobile') v = v + (obj.lineHeightUnit || ''); else if (k === 'letterSpacingDesktop' || k === 'letterSpacingTablet' || k === 'letterSpacingMobile') v = v + (obj.letterSpacingUnit || 'px'); else if (k === 'wordSpacingDesktop' || k === 'wordSpacingTablet' || k === 'wordSpacingMobile') v = v + (obj.wordSpacingUnit || 'px'); el.style.setProperty(prefix + _typoKeys[k], String(v)); }); } function escHtml(str) { if (!str) return ''; return String(str).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } function trimWords(html, count) { var text = html.replace(/<[^>]+>/g, ''); var words = text.split(/\s+/).filter(Boolean); return words.slice(0, count).join(' ') + (words.length > count ? '…' : ''); } function initCarousel(wrap) { var d = wrap.dataset; var postType = d.postType || 'post'; var orderby = d.orderby || 'date'; var order = d.order || 'desc'; var perPage = parseInt(d.perPage, 10) || 6; var excerptLen = parseInt(d.excerptLen, 10) || 20; var gap = parseInt(d.gap, 10) || 24; var spvDesk = parseInt(d.spv, 10) || 3; var spvTablet = parseInt(d.spvTablet, 10) || 2; var spvMobile = parseInt(d.spvMobile, 10) || 1; var autoplay = d.autoplay !== '0'; var apSpeed = parseInt(d.apSpeed, 10) || 4000; var pauseHover = d.pauseHover !== '0'; var loop = d.loop !== '0'; var showArrows = d.arrows !== '0'; var arrowStyle = d.arrowStyle || 'circle'; var showDots = d.dots !== '0'; var showImage = d.showImage !== '0'; var showMeta = d.showMeta !== '0'; var showExcerpt = d.showExcerpt !== '0'; var showRm = d.showRm !== '0'; var rmLabel = d.rmLabel || 'Read More'; var ratio = d.ratio || '56.25'; var cardStyle = d.cardStyle || 'shadow'; var cardBg = d.cardBg || '#ffffff'; var cardRadius = d.cardRadius || '10'; var titleC = d.titleC || '#111827'; var excerptC = d.excerptC || '#6b7280'; var metaC = d.metaC || '#9ca3af'; var linkC = d.linkC || '#2563eb'; var arrowBg = d.arrowBg || '#ffffff'; var arrowC = d.arrowC || '#111827'; var dotC = d.dotC || '#d1d5db'; var dotActiveC = d.dotActive || '#111827'; var titleTypo = JSON.parse(d.titleTypo || '{}'); var excerptTypo = JSON.parse(d.excerptTypo || '{}'); var metaTypo = JSON.parse(d.metaTypo || '{}'); /* Loading placeholder */ wrap.innerHTML = '
Loading posts…
'; /* Determine REST base */ var restBase = (window.blockenbergData && window.blockenbergData.restUrl) ? window.blockenbergData.restUrl : window.location.origin + '/wp-json/'; var endpoint = restBase + 'wp/v2/' + postType + '?per_page=' + perPage + '&orderby=' + orderby + '&order=' + order + '&_embed=1'; fetch(endpoint) .then(function (r) { return r.ok ? r.json() : Promise.reject(r.status); }) .then(function (posts) { buildCarousel(posts); }) .catch(function () { wrap.innerHTML = '
Could not load posts.
'; }); function getSpv() { var w = window.innerWidth; if (w < 600) return spvMobile; if (w < 960) return spvTablet; return spvDesk; } function buildCarousel(posts) { if (!posts || !posts.length) { wrap.innerHTML = '
No posts found.
'; return; } wrap.innerHTML = ''; typoCssVarsForEl(wrap, titleTypo, '--bkbg-poc-tt-'); typoCssVarsForEl(wrap, excerptTypo, '--bkbg-poc-ex-'); typoCssVarsForEl(wrap, metaTypo, '--bkbg-poc-mt-'); /* Track outer */ var trackOuter = document.createElement('div'); trackOuter.className = 'bkbg-poc-track-outer'; var track = document.createElement('div'); track.className = 'bkbg-poc-track'; posts.forEach(function (post) { var card = document.createElement('div'); card.className = 'bkbg-poc-card bkbg-poc-card--' + cardStyle; card.style.cssText = 'background:' + cardBg + ';border-radius:' + cardRadius + 'px;'; /* Image */ if (showImage) { var thumb = post._embedded && post._embedded['wp:featuredmedia'] && post._embedded['wp:featuredmedia'][0]; var imgUrl= thumb ? (thumb.media_details && thumb.media_details.sizes && thumb.media_details.sizes.medium_large ? thumb.media_details.sizes.medium_large.source_url : thumb.source_url) : ''; var imgWrap = document.createElement('div'); imgWrap.className = 'bkbg-poc-img-wrap'; imgWrap.style.paddingBottom = ratio + '%'; if (imgUrl) { var img = document.createElement('img'); img.src = imgUrl; img.alt = post.title && post.title.rendered ? post.title.rendered : ''; img.loading = 'lazy'; imgWrap.appendChild(img); } card.appendChild(imgWrap); } /* Body */ var body = document.createElement('div'); body.className = 'bkbg-poc-body'; if (showMeta) { var meta = document.createElement('p'); meta.className = 'bkbg-poc-meta'; meta.style.color = metaC; meta.textContent = new Date(post.date).toLocaleDateString(undefined, { year:'numeric', month:'short', day:'numeric' }); body.appendChild(meta); } var titleLink = document.createElement('a'); titleLink.className = 'bkbg-poc-title'; titleLink.href = post.link; titleLink.style.color = titleC; titleLink.innerHTML = post.title ? post.title.rendered : ''; body.appendChild(titleLink); if (showExcerpt) { var ex = document.createElement('p'); ex.className = 'bkbg-poc-excerpt'; ex.style.color = excerptC; var rawExcerpt = post.excerpt ? post.excerpt.rendered : ''; ex.textContent = trimWords(rawExcerpt, excerptLen); body.appendChild(ex); } if (showRm) { var rm = document.createElement('a'); rm.className = 'bkbg-poc-rm'; rm.href = post.link; rm.style.color = linkC; rm.textContent = rmLabel + ' →'; body.appendChild(rm); } card.appendChild(body); track.appendChild(card); }); trackOuter.appendChild(track); wrap.appendChild(trackOuter); /* Navigation */ var navDiv = document.createElement('div'); navDiv.className = 'bkbg-poc-nav'; var prevBtn, nextBtn, dotsContainer; if (showArrows) { var arrowsDiv = document.createElement('div'); arrowsDiv.className = 'bkbg-poc-arrows'; prevBtn = document.createElement('button'); prevBtn.className = 'bkbg-poc-arrow bkbg-poc-arrow--' + arrowStyle; prevBtn.innerHTML = '‹'; prevBtn.style.background = arrowBg; prevBtn.style.color = arrowC; prevBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.12)'; nextBtn = document.createElement('button'); nextBtn.className = 'bkbg-poc-arrow bkbg-poc-arrow--' + arrowStyle; nextBtn.innerHTML = '›'; nextBtn.style.background = arrowBg; nextBtn.style.color = arrowC; nextBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.12)'; arrowsDiv.appendChild(prevBtn); arrowsDiv.appendChild(nextBtn); navDiv.appendChild(arrowsDiv); } if (showDots) { dotsContainer = document.createElement('div'); dotsContainer.className = 'bkbg-poc-dots'; var numDots = Math.ceil(posts.length / getSpv()); for (var di = 0; di < numDots; di++) { (function (i) { var dot = document.createElement('button'); dot.className = 'bkbg-poc-dot' + (i === 0 ? ' is-active' : ''); dot.style.background = i === 0 ? dotActiveC : dotC; dot.addEventListener('click', function () { goTo(i); }); dotsContainer.appendChild(dot); })(di); } navDiv.appendChild(dotsContainer); } wrap.appendChild(navDiv); /* State */ var currentIndex = 0; function updateCardWidths() { var spv = getSpv(); var totalGap = gap * (spv - 1); var cardW = 'calc((' + (100 / spv) + '%) - ' + (totalGap / spv) + 'px)'; var cards = track.querySelectorAll('.bkbg-poc-card'); cards.forEach(function (c) { c.style.width = cardW; c.style.marginRight = gap + 'px'; }); if (cards.length) { cards[cards.length - 1].style.marginRight = '0'; } } function goTo(idx) { var spv = getSpv(); var total = posts.length; var max = loop ? total - spv : Math.max(0, total - spv); if (loop) { if (idx >= total) idx = 0; if (idx < 0) idx = total - 1; } else { if (idx > max) idx = max; if (idx < 0) idx = 0; } currentIndex = idx; var cards = track.querySelectorAll('.bkbg-poc-card'); if (!cards.length) return; var cardW = cards[0].offsetWidth + gap; track.style.transform = 'translateX(-' + (cardW * currentIndex) + 'px)'; /* Update dots */ if (dotsContainer) { var dots = dotsContainer.querySelectorAll('.bkbg-poc-dot'); var activeGroup = Math.floor(currentIndex / spv); dots.forEach(function (dot, i) { var isActive = i === activeGroup; dot.classList.toggle('is-active', isActive); dot.style.background = isActive ? dotActiveC : dotC; }); } /* Disable arrows at ends */ if (prevBtn && !loop) { prevBtn.disabled = currentIndex === 0; prevBtn.style.opacity = currentIndex === 0 ? '0.4' : '1'; } if (nextBtn && !loop) { nextBtn.disabled = currentIndex >= max; nextBtn.style.opacity = currentIndex >= max ? '0.4' : '1'; } } /* Autoplay */ var apTimer; function startAutoplay() { if (!autoplay) return; apTimer = setInterval(function () { goTo(currentIndex + 1); }, apSpeed); } function stopAutoplay() { clearInterval(apTimer); } /* Events */ if (prevBtn) prevBtn.addEventListener('click', function () { stopAutoplay(); goTo(currentIndex - 1); startAutoplay(); }); if (nextBtn) nextBtn.addEventListener('click', function () { stopAutoplay(); goTo(currentIndex + 1); startAutoplay(); }); if (pauseHover) { wrap.addEventListener('mouseenter', stopAutoplay); wrap.addEventListener('mouseleave', startAutoplay); } /* Touch swipe */ var touchStartX = 0; trackOuter.addEventListener('touchstart', function (e) { touchStartX = e.touches[0].clientX; }, { passive: true }); trackOuter.addEventListener('touchend', function (e) { var diff = touchStartX - e.changedTouches[0].clientX; if (Math.abs(diff) > 50) { goTo(currentIndex + (diff > 0 ? 1 : -1)); } }); window.addEventListener('resize', function () { updateCardWidths(); goTo(currentIndex); }); /* Init */ updateCardWidths(); goTo(0); startAutoplay(); } } document.addEventListener('DOMContentLoaded', function () { document.querySelectorAll('.bkbg-poc-wrap[data-post-type]').forEach(initCarousel); }); })();