1// Clean testimonial (vanilla port) - click to cycle, monogram avatars, no photos. 2(function () { 3 "use strict"; 4 document.addEventListener("DOMContentLoaded", function () { 5 const root = document.getElementById("testi"); 6 const dataEl = document.getElementById("testiData"); 7 if (!root || !dataEl) return; 8 9 const items = Array.from(dataEl.querySelectorAll("li")).map(function (li) { 10 return { 11 quote: li.textContent.trim(), 12 name: li.getAttribute("data-name"), 13 role: li.getAttribute("data-role"), 14 mono: li.getAttribute("data-mono"), 15 }; 16 }); 17 if (!items.length) return; 18 19 const elQuote = document.getElementById("testiQuote"); 20 const elName = document.getElementById("testiName"); 21 const elRole = document.getElementById("testiRole"); 22 const elMono = document.getElementById("testiMono"); 23 const elCur = document.getElementById("testiCur"); 24 const elTotal = document.getElementById("testiTotal"); 25 const elBar = document.getElementById("testiBar"); 26 const elAvatars = document.getElementById("testiAvatars"); 27 28 let index = 0; 29 let timer = null; 30 31 // build avatar preview chips 32 items.forEach(function (it, i) { 33 const chip = document.createElement("span"); 34 chip.className = "testi-chip"; 35 chip.textContent = it.mono; 36 chip.addEventListener("click", function (e) { e.stopPropagation(); go(i); }); 37 elAvatars.appendChild(chip); 38 }); 39 const chips = Array.from(elAvatars.children); 40 41 elTotal.textContent = String(items.length).padStart(2, "0"); 42 43 function pad(n) { return String(n + 1).padStart(2, "0"); } 44 45 function render() { 46 const it = items[index]; 47 elQuote.textContent = it.quote; 48 elName.textContent = it.name; 49 elRole.textContent = it.role; 50 elMono.textContent = it.mono; 51 elCur.textContent = pad(index); 52 elBar.style.width = ((index + 1) / items.length) * 100 + "%"; 53 chips.forEach(function (c, i) { c.classList.toggle("is-active", i === index); }); 54 } 55 56 function go(i) { 57 index = (i + items.length) % items.length; 58 // swap animation 59 elQuote.classList.add("is-swapping"); 60 elMono.parentElement.classList.add("is-swapping"); 61 setTimeout(function () { 62 render(); 63 elQuote.classList.remove("is-swapping"); 64 elMono.parentElement.classList.remove("is-swapping"); 65 }, 220); 66 restartAuto(); 67 } 68 69 function next() { go(index + 1); } 70 71 function restartAuto() { 72 if (timer) clearInterval(timer); 73 timer = setInterval(next, 7000); 74 } 75 76 root.addEventListener("click", next); 77 root.addEventListener("keydown", function (e) { 78 if (e.key === "Enter" || e.key === " ") { e.preventDefault(); next(); } 79 }); 80 81 render(); 82 restartAuto(); 83 }); 84})();
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.