1/** 2 * Counter Widget JavaScript 3 * 4 * Handles the counter animation functionality 5 */ 6 7(function($) { 8 'use strict'; 9 10 var HfeCounter = { 11 12 /** 13 * Initialize the counter 14 */ 15 init: function() { 16 elementorFrontend.hooks.addAction('frontend/element_ready/hfe-counter.default', this.initCounter); 17 }, 18 19 /** 20 * Initialize counter for specific element 21 */ 22 initCounter: function($scope) { 23 var $counter = $scope.find('.hfe-counter-number'); 24 25 if ($counter.length) { 26 HfeCounter.setupCounterAnimation($counter); 27 } 28 }, 29 30 /** 31 * Setup counter animation 32 */ 33 setupCounterAnimation: function($counter) { 34 var startNumber = parseInt($counter.data('start')) || 0; 35 var endNumber = parseInt($counter.data('end')); 36 endNumber = isNaN(endNumber) ? 100 : endNumber; 37 var speed = parseInt($counter.data('speed')) || 3000; 38 var separator = $counter.data('separator') || ''; 39 40 // Use Intersection Observer for better performance 41 if ('IntersectionObserver' in window) { 42 var observer = new IntersectionObserver(function(entries) { 43 entries.forEach(function(entry) { 44 if (entry.isIntersecting && !$counter.hasClass('hfe-counter-animated')) { 45 $counter.addClass('hfe-counter-animated'); 46 HfeCounter.animateCounter($counter[0], startNumber, endNumber, speed, separator); 47 observer.unobserve(entry.target); 48 } 49 }); 50 }, { 51 threshold: 0.5 52 }); 53 54 observer.observe($counter[0]); 55 } else { 56 // Fallback for older browsers 57 $(window).on('scroll', function() { 58 if (HfeCounter.isElementInViewport($counter[0]) && !$counter.hasClass('hfe-counter-animated')) { 59 $counter.addClass('hfe-counter-animated'); 60 HfeCounter.animateCounter($counter[0], startNumber, endNumber, speed, separator); 61 } 62 }); 63 } 64 }, 65 66 /** 67 * Animate the counter 68 */ 69 animateCounter: function(element, start, end, duration, separator) { 70 var startTime = null; 71 var $element = $(element); 72 73 function animate(currentTime) { 74 if (startTime === null) startTime = currentTime; 75 var progress = Math.min((currentTime - startTime) / duration, 1); 76 77 // Use easing function for smooth animation 78 var easedProgress = HfeCounter.easeOutQuart(progress); 79 var currentNumber = Math.floor(start + (end - start) * easedProgress); 80 81 $element.text(HfeCounter.formatNumber(currentNumber, separator)); 82 83 if (progress < 1) { 84 requestAnimationFrame(animate); 85 } else { 86 $element.text(HfeCounter.formatNumber(end, separator)); 87 } 88 } 89 90 requestAnimationFrame(animate); 91 }, 92 93 /** 94 * Format number with separator 95 */ 96 formatNumber: function(number, separator) { 97 if (!separator) { 98 return number.toString(); 99 } 100 101 return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, separator); 102 }, 103 104 /** 105 * Easing function for smooth animation 106 */ 107 easeOutQuart: function(t) { 108 return 1 - (--t) * t * t * t; 109 }, 110 111 /** 112 * Check if element is in viewport (fallback) 113 */ 114 isElementInViewport: function(element) { 115 var rect = element.getBoundingClientRect(); 116 return ( 117 rect.top >= 0 && 118 rect.left >= 0 && 119 rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && 120 rect.right <= (window.innerWidth || document.documentElement.clientWidth) 121 ); 122 } 123 }; 124 125 // Initialize when Elementor frontend is ready 126 $(window).on('elementor/frontend/init', function() { 127 HfeCounter.init(); 128 }); 129 130 // Fallback for when Elementor frontend is not available 131 $(document).ready(function() { 132 if (typeof elementorFrontend === 'undefined') { 133 // Create a mock elementorFrontend for standalone use 134 window.elementorFrontend = { 135 hooks: { 136 addAction: function(event, callback) { 137 $(document).ready(callback); 138 } 139 } 140 }; 141 } 142 HfeCounter.init(); 143 }); 144 145})(jQuery);
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.