1/** 2 * Elementor counter widget fallback. 3 * 4 * Elementor ships the counter handler in a lazily imported webpack chunk 5 * (l.<hash>.bundle.min.js) that is not part of the mirrored plugin assets, so 6 * the import 404s and `.elementor-counter-number` spans stay empty. This fills 7 * them from data-to-value and counts up the first time they scroll into view. 8 */ 9(function () { 10 var SELECTOR = "span.elementor-counter-number[data-to-value]"; 11 var DONE_ATTR = "data-pc-counter"; 12 13 var prefersReducedMotion = 14 window.matchMedia && 15 window.matchMedia("(prefers-reduced-motion: reduce)").matches; 16 17 function decimalsOf(value) { 18 var dot = value.indexOf("."); 19 return dot === -1 ? 0 : value.length - dot - 1; 20 } 21 22 function format(value, decimals, delimiter) { 23 var text = value.toFixed(decimals); 24 if (!delimiter) return text; 25 var parts = text.split("."); 26 parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, delimiter); 27 return parts.join("."); 28 } 29 30 function animate(el, from, to, decimals, delimiter, duration) { 31 var start = 0; 32 33 function frame(now) { 34 if (!start) start = now; 35 var progress = Math.min((now - start) / duration, 1); 36 // jQuery "swing" easing, matching Elementor's numerator default. 37 var eased = 0.5 - Math.cos(progress * Math.PI) / 2; 38 el.textContent = format(from + (to - from) * eased, decimals, delimiter); 39 if (progress < 1) requestAnimationFrame(frame); 40 } 41 42 requestAnimationFrame(frame); 43 } 44 45 function prepare(el) { 46 if (el.hasAttribute(DONE_ATTR)) return null; 47 var raw = (el.getAttribute("data-to-value") || "").trim(); 48 var to = parseFloat(raw); 49 if (!raw || isNaN(to)) return null; 50 51 el.setAttribute(DONE_ATTR, "1"); 52 53 var from = parseFloat(el.getAttribute("data-from-value")); 54 var duration = parseInt(el.getAttribute("data-duration"), 10); 55 var config = { 56 el: el, 57 from: isNaN(from) ? 0 : from, 58 to: to, 59 decimals: decimalsOf(raw), 60 delimiter: el.getAttribute("data-delimiter") || "", 61 duration: isNaN(duration) || duration <= 0 ? 2000 : duration, 62 }; 63 64 // Show the final value straight away; the count-up (below) only replaces it 65 // once we know the element is on screen, so a stalled observer or a hidden 66 // ancestor can never leave the number blank. 67 el.textContent = format(to, config.decimals, config.delimiter); 68 return config; 69 } 70 71 function init() { 72 var counters = []; 73 var nodes = document.querySelectorAll(SELECTOR); 74 for (var i = 0; i < nodes.length; i++) { 75 var config = prepare(nodes[i]); 76 if (config) counters.push(config); 77 } 78 79 if (!counters.length || prefersReducedMotion) return; 80 if (!("IntersectionObserver" in window) || !window.requestAnimationFrame) { 81 return; 82 } 83 84 var observer = new IntersectionObserver( 85 function (entries) { 86 entries.forEach(function (entry) { 87 if (!entry.isIntersecting) return; 88 observer.unobserve(entry.target); 89 var config = entry.target.pcCounterConfig; 90 animate( 91 config.el, 92 config.from, 93 config.to, 94 config.decimals, 95 config.delimiter, 96 config.duration, 97 ); 98 }); 99 }, 100 { threshold: 0.25 }, 101 ); 102 103 counters.forEach(function (config) { 104 config.el.pcCounterConfig = config; 105 observer.observe(config.el); 106 }); 107 } 108 109 if (document.readyState === "loading") { 110 document.addEventListener("DOMContentLoaded", init); 111 } else { 112 init(); 113 } 114})();
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.