1/** 2 * Artburst Show Ticker â Frontend JS 3 * 4 * Continuous marquee scroll for all messages (single or multiple). 5 * Messages flow seamlessly one after another in an infinite loop. 6 * Hover pauses the scroll. 7 * Vanilla JS (no jQuery dependency), auto-initializes on DOMContentLoaded. 8 */ 9(function(window, document) { 10 'use strict'; 11 12 function ArtburstTicker(el) { 13 this.el = el; 14 this.track = el.querySelector('.artburst-ticker-track'); 15 this.messages = el.querySelectorAll('.artburst-ticker-message'); 16 this.totalMessages = this.messages.length; 17 this.speed = parseInt(el.dataset.speed, 10) || 40; // px/sec 18 this.isPaused = false; 19 this.animationId = null; 20 this.offset = 0; // current translateX offset (negative = scrolled left) 21 this.lastTimestamp = 0; 22 this.trackWidth = 0; // width of one full set of messages 23 24 if (this.totalMessages <= 0 || !this.track) return; 25 26 this.buildContinuousTrack(); 27 this.bindEvents(); 28 this.startAnimation(); 29 } 30 31 ArtburstTicker.prototype = { 32 33 /** 34 * Build the continuous scrolling track. 35 * Messages are already visible (no display:none gate). 36 * Add separator dots, measure, clone enough times for seamless loop. 37 */ 38 buildContinuousTrack: function() { 39 // Insert separator dots between existing messages 40 var msgs = []; 41 for (var i = 0; i < this.messages.length; i++) { 42 msgs.push(this.messages[i]); 43 } 44 for (var k = 1; k < msgs.length; k++) { 45 var dot = document.createElement('span'); 46 dot.className = 'artburst-ticker-separator'; 47 dot.textContent = '\u00A0\u00A0\u2022\u00A0\u00A0'; 48 this.track.insertBefore(dot, msgs[k]); 49 } 50 51 // Add trailing separator so the loop is seamless 52 var trailDot = document.createElement('span'); 53 trailDot.className = 'artburst-ticker-separator'; 54 trailDot.textContent = '\u00A0\u00A0\u2022\u00A0\u00A0'; 55 this.track.appendChild(trailDot); 56 57 // Measure the width of one complete set 58 var oneSetWidth = this.track.scrollWidth; 59 var containerWidth = this.el.offsetWidth; 60 61 // Snapshot original nodes before cloning 62 var originalNodes = []; 63 for (var c = 0; c < this.track.childNodes.length; c++) { 64 originalNodes.push(this.track.childNodes[c]); 65 } 66 67 // Clone enough copies so the total width is at least 3x the container. 68 // This ensures there's never a visible gap during the loop reset. 69 var copies = Math.max(1, Math.ceil((containerWidth * 3) / oneSetWidth)); 70 for (var r = 0; r < copies; r++) { 71 for (var d = 0; d < originalNodes.length; d++) { 72 this.track.appendChild(originalNodes[d].cloneNode(true)); 73 } 74 } 75 76 // Store one-set width for the loop math 77 this.trackWidth = oneSetWidth; 78 79 // Start immediately visible â offset 0 means first message is at the left edge 80 this.offset = 0; 81 }, 82 83 /** 84 * Animation loop â scroll the track leftward at constant speed 85 */ 86 startAnimation: function() { 87 var self = this; 88 this.lastTimestamp = performance.now(); 89 90 function tick(timestamp) { 91 if (!self.isPaused) { 92 var delta = (timestamp - self.lastTimestamp) / 1000; 93 self.offset -= self.speed * delta; 94 95 // When one full set has scrolled off-screen left, jump forward 96 // This creates the seamless infinite loop 97 if (self.offset <= -self.trackWidth) { 98 self.offset += self.trackWidth; 99 } 100 101 self.track.style.transform = 'translateX(' + self.offset + 'px)'; 102 } 103 self.lastTimestamp = timestamp; 104 self.animationId = requestAnimationFrame(tick); 105 } 106 107 this.animationId = requestAnimationFrame(tick); 108 }, 109 110 /** 111 * Bind hover events for pause/resume 112 */ 113 bindEvents: function() { 114 var self = this; 115 116 // Pause on hover 117 this.el.addEventListener('mouseenter', function() { 118 self.isPaused = true; 119 }); 120 this.el.addEventListener('mouseleave', function() { 121 self.isPaused = false;
122 self.lastTimestamp = performance.now(); 123 }); 124 125 // Pause on touch (mobile) â resume when touch ends 126 this.el.addEventListener('touchstart', function() { 127 self.isPaused = true; 128 }, { passive: true }); 129 this.el.addEventListener('touchend', function() { 130 self.isPaused = false; 131 self.lastTimestamp = performance.now(); 132 }, { passive: true }); 133 } 134 }; 135 136 // Auto-initialize on DOM ready 137 document.addEventListener('DOMContentLoaded', function() { 138 var tickers = document.querySelectorAll('.artburst-ticker-wrap'); 139 for (var i = 0; i < tickers.length; i++) { 140 new ArtburstTicker(tickers[i]); 141 } 142 }); 143 144})(window, document);
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.