PageSourceSearch

https://arbousiers.fr/wp-content/plugins/frontblocks/assets/headline/frontblocks-headline-marquee.js?ver=1.3.3

js arbousiers.fr collected 2026-09-25 07:35:29 UTC 21,844 bytes, 451 lines download raw bytes

1/**
2 * FrontBlocks Headline Marquee Effect
3 * Duplicates content for infinite scrolling marquee effect
4 */
5
6(function() {
7    'use strict';
8
9    /**
10     * Initialize marquee effect for headlines
11     */
12    function initMarquee() {
13        const marqueeElements = document.querySelectorAll('.gb-marquee-infinite-scroll:not([data-marquee-initialized="true"])');
14        
15        marqueeElements.forEach(function(element) {
16            // Skip if already initialized or has wrapper
17            if (element.dataset.marqueeInitialized === 'true' || element.querySelector('.gb-marquee-wrapper')) {
18                return;
19            }
20
21            // Find the text content - prioritize .gb-headline-text
22            let textElement = element.querySelector('.gb-headline-text');
23            
24            // If no .gb-headline-text, look for direct text content or first text node
25            if (!textElement) {
26                // Check if element has direct text content
27                const hasDirectText = element.childNodes.length > 0 && 
28                    Array.from(element.childNodes).some(node => 
29                        node.nodeType === 3 && node.textContent.trim() !== ''
30                    );
31                
32                if (hasDirectText) {
33                    textElement = element;
34                } else {
35                    // Try to find a span or other inline element
36                    textElement = element.querySelector('span, a, strong, em, b, i') || element;
37                }
38            }
39
40            if (!textElement) {
41                return;
42            }
43
44            // Get the HTML content (preserves formatting)
45            const textContent = textElement.innerHTML || textElement.textContent;
46            
47            if (!textContent || textContent.trim() === '') {
48                return;
49            }
50
51            // Marquee speed presets mapping
52            const MARQUEE_SPEEDS = {
53                'fast': 10,    // 10 seconds - fast
54                'medium': 20,  // 20 seconds - medium
55                'slow': 40     // 40 seconds - slow
56            };
57            
58            // Function to get marquee speed from element or parent - VERY AGGRESSIVE SEARCH
59            const getMarqueeSpeed = function(el) {
60                let speed = null;
61                
62                // Method 1: Check dataset
63                if (el.dataset && el.dataset.marqueeSpeed) {
64                    speed = el.dataset.marqueeSpeed;
65                }
66                
67                // Method 2: Check getAttribute
68                if (!speed) {
69                    speed = el.getAttribute('data-marquee-speed');
70                }
71                
72                // Method 3: Check all attributes manually
73                if (!speed && el.attributes) {
74                    try {
75                        Array.from(el.attributes).forEach(function(attr) {
76                            if (attr.name === 'data-marquee-speed' && attr.value) {
77                                speed = attr.value;
78                            }
79                        });
80                    } catch(e) {}
81                }
82                
83                // Method 4: Check parent element
84                if (!speed && el.parentElement) {
85                    if (el.parentElement.dataset && el.parentElement.dataset.marqueeSpeed) {
86                        speed = el.parentElement.dataset.marqueeSpeed;
87                    }
88                    if (!speed) {
89                        speed = el.parentElement.getAttribute('data-marquee-speed');
90                    }
91                }
92                
93                // Method 5: Search in all ancestors
94                if (!speed) {
95                    let parent = el.parentElement;
96                    while (parent && parent !== document.body) {
97                        if (parent.dataset && parent.dataset.marqueeSpeed) {
98                            speed = parent.dataset.marqueeSpeed;
99                            break;
100                        }
101                        if (parent.getAttribute && parent.getAttribute('data-marquee-speed')) {
102                            speed = parent.getAttribute('data-marquee-speed');
103                            break;
104                        }
105                        parent = parent.parentElement;
106                    }
107                }
108                
109                // Method 6: Search in children
110                if (!speed) {
111                    const speedElement = el.querySelector('[data-marquee-speed]');
112                    if (speedElement) {
113                        speed = speedElement.dataset.marqueeSpeed || speedElement.getAttribute('data-marquee-speed');
114                    }
115                }
116                
117                // Parse and validate - handle both numeric and preset values
118                let speedValue = 20; // default
119                
120                if (speed) {
121                    // Check if it's a preset string (fast, medium, slow)
122                    if (MARQUEE_SPEEDS.hasOwnProperty(speed)) {
123                        speedValue = MARQUEE_SPEEDS[speed];
124                    } else {
125                        // Try to parse as number
126                        const parsed = parseFloat(speed);
127                        if (!isNaN(parsed) && parsed > 0) {
128                            speedValue = parsed;
129                        }
130                    }
131                }
132                
133                return speedValue;
134            };
135            
136            // Get initial speed value
137            const speedValue = getMarqueeSpeed(element);
138
139            // Function to calculate and setup marquee
140            const setupMarquee = function() {
141                // Create a temporary element to measure text width with same font styles
142                const tempElement = document.createElement('span');
143                tempElement.style.cssText = 'position: absolute; visibility: hidden; white-space: nowrap; padding-right: 2em;';
144                tempElement.innerHTML = textContent;
145                
146                // Copy computed styles from original element if possible
147                if (textElement !== element) {
148                    const computedStyle = window.getComputedStyle(textElement);
149                    tempElement.style.fontSize = computedStyle.fontSize;
150                    tempElement.style.fontFamily = computedStyle.fontFamily;
151                    tempElement.style.fontWeight = computedStyle.fontWeight;
152                    tempElement.style.letterSpacing = computedStyle.letterSpacing;
153                }
154                
155                document.body.appendChild(tempElement);
156                
157                // Get container width - ensure we have the actual width
158                let containerWidth = element.offsetWidth || element.clientWidth;
159                if (!containerWidth || containerWidth === 0) {
160                    // Try to get from computed style
161                    const computedWidth = window.getComputedStyle(element).width;
162                    containerWidth = parseFloat(computedWidth) || 0;
163                }
164                
165                // Fallback to parent width if still 0
166                if (!containerWidth || containerWidth === 0) {
167                    containerWidth = element.parentElement ? (element.parentElement.offsetWidth || 0) : 0;
168                }
169                
170                // Get single text copy width
171                const singleTextWidth = tempElement.offsetWidth;
172                
173                // Remove temporary element
174                document.body.removeChild(tempElement);
175                
176                // Calculate how many copies we need to fill the container width
177                // We need at least 2 copies for seamless loop, but more if text is short
178                let copiesNeeded = 2;
179                if (containerWidth > 0 && singleTextWidth > 0) {
180                    // Calculate copies needed to fill container width
181                    // Add 2 extra copies to ensure smooth continuous scrolling
182                    copiesNeeded = Math.ceil((containerWidth / singleTextWidth) + 2);
183                    // Ensure minimum of 2 copies for seamless loop
184                    copiesNeeded = Math.max(copiesNeeded, 2);
185                }
186
187                // Create a wrapper for the marquee content
188                const wrapper = document.createElement('div');
189                wrapper.className = 'gb-marquee-wrapper';
190                // Store speed directly in wrapper as data attribute AND CSS variable
191                wrapper.setAttribute('data-marquee-speed', speedValue);
192                wrapper.style.setProperty('--marquee-speed', speedValue + 's');
193                wrapper.style.cssText += 'display: flex; white-space: nowrap; will-change: transform; backface-visibility: hidden; -webkit-backface-visibility: hidden; transform: translateZ(0); -webkit-transform: translateZ(0); width: auto; min-width: 100%;';
194
195                // Create copies based on calculated need
196                for (let i = 0; i < copiesNeeded; i++) {
197                    const copy = document.createElement('span');
198                    copy.className = 'gb-marquee-copy';
199                    copy.innerHTML = textContent;
200                    copy.style.cssText = 'display: inline-block; padding-right: 2em; flex-shrink: 0; backface-visibility: hidden; -webkit-backface-visibility: hidden;';
201                    wrapper.appendChild(copy);
202                }
203                
204                return wrapper;
205            };
206            
207            // Setup marquee - use requestAnimationFrame to ensure layout is ready
208            requestAnimationFrame(function() {
209                const wrapper = setupMarquee();
210
211                // Replace content with wrapper
212                if (textElement === element) {
213                    // Save any attributes that might be on the element
214                    const savedAttributes = {};
215                    Array.from(element.attributes).forEach(function(attr) {
216                        if (attr.name !== 'class' && attr.name !== 'data-marquee-initialized') {
217                            savedAttributes[attr.name] = attr.value;
218                        }
219                    });
220                    
221                    element.innerHTML = '';
222                    element.appendChild(wrapper);
223                    
224                    // Restore attributes
225                    Object.keys(savedAttributes).forEach(function(key) {
226                        element.setAttribute(key, savedAttributes[key]);
227                    });
228                } else {
229                    textElement.innerHTML = '';
230                    textElement.appendChild(wrapper);
231                }
232
233                // Function to update speed - can be called from outside
234                const updateSpeed = function(newSpeed) {
235                    if (newSpeed && !isNaN(newSpeed) && newSpeed > 0) {
236                        wrapper.setAttribute('data-marquee-speed', newSpeed);
237                        wrapper.style.setProperty('--marquee-speed', newSpeed + 's');
238                        applyAnimation();
239                    }
240                };
241                
242                // Function to apply animation - reads speed from wrapper
243                const applyAnimation = function() {
244                    // Get speed from wrapper (most reliable source)
245                    let currentSpeed = wrapper.getAttribute('data-marquee-speed');
246                    
247                    // If not in wrapper, try to get from element and save to wrapper
248                    if (!currentSpeed) {
249                        currentSpeed = getMarqueeSpeed(element);
250                        if (currentSpeed) {
251                            wrapper.setAttribute('data-marquee-speed', currentSpeed);
252                            wrapper.style.setProperty('--marquee-speed', currentSpeed + 's');
253                        }
254                    } else {
255                        currentSpeed = parseFloat(currentSpeed);
256                    }
257                    
258                    // Validate speed
259                    if (!currentSpeed || isNaN(currentSpeed) || currentSpeed <= 0) {
260                        currentSpeed = 20;
261                        wrapper.setAttribute('data-marquee-speed', currentSpeed);
262                        wrapper.style.setProperty('--marquee-speed', currentSpeed + 's');
263                    }
264                    
265                    // Get the actual width of one copy after it's rendered
266                    const firstCopy = wrapper.querySelector('.gb-marquee-copy');
267                    if (!firstCopy) return;
268                    
269                    const copyWidth = firstCopy.offsetWidth;
270                    
271                    if (copyWidth > 0) {
272                        // Calculate animation distance in pixels (exactly one copy width)
273                        // This ensures perfect seamless loop - no jumps or resets
274                        const animationDistancePx = copyWidth;
275                        
276                        // Get or create style ID
277                        let styleId = wrapper.getAttribute('data-marquee-style-id');
278                        if (!styleId) {
279                            styleId = 'marquee-style-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
280                            wrapper.setAttribute('data-marquee-style-id', styleId);
281                        }
282                        
283                        // Check if style already exists, if not create it
284                        let style = document.getElementById(styleId);
285                        if (!style) {
286                            style = document.createElement('style');
287                            style.id = styleId;
288                            document.head.appendChild(style);
289                        }
290                        
291                        // Update keyframes
292                        style.textContent = '@keyframes marquee-scroll-' + styleId + ' { 0% { transform: translateX(0) translateZ(0); } 100% { transform: translateX(-' + animationDistancePx + 'px) translateZ(0); } }';
293                        
294                        // FORCE UPDATE - Remove animation completely
295                        wrapper.style.animation = 'none';
296                        wrapper.style.animationName = 'none';
297                        wrapper.style.animationDuration = 'none';
298                        
299                        // Force reflow
300                        void wrapper.offsetWidth;
301                        
302                        // Re-apply animation with new speed
303                        requestAnimationFrame(function() {
304                            // Use CSS variable for speed
305                            wrapper.style.setProperty('--marquee-speed', currentSpeed + 's');
306                            wrapper.style.animation = 'marquee-scroll-' + styleId + ' var(--marquee-speed, ' + currentSpeed + 's) linear infinite';
307                            wrapper.style.animationName = 'marquee-scroll-' + styleId;
308                            wrapper.style.animationDuration = 'var(--marquee-speed, ' + currentSpeed + 's)';
309                            wrapper.style.animationTimingFunction = 'linear';
310                            wrapper.style.animationIterationCount = 'infinite';
311                            wrapper.style.animationFillMode = 'none';
312                            wrapper.style.animationPlayState = 'running';
313                            
314                            // Force another reflow to ensure it applies
315                            void wrapper.offsetWidth;
316                        });
317                    }
318                };
319
320                // Wait for layout to calculate actual copy width after wrapper is in DOM
321                requestAnimationFrame(function() {
322                    applyAnimation();
323                });
324
325                // Watch for changes in data-marquee-speed attribute - VERY AGGRESSIVE
326                if (typeof MutationObserver !== 'undefined') {
327                    const speedObserver = new MutationObserver(function(mutations) {
328                        let shouldUpdate = false;
329                        mutations.forEach(function(mutation) {
330                            // Check for attribute changes on element or wrapper
331                            if (mutation.type === 'attributes') {
332                                if (mutation.attributeName === 'data-marquee-speed') {
333                                    // Update wrapper if element changed
334                                    if (mutation.target === element || mutation.target === element.parentElement) {
335                                        const newSpeed = getMarqueeSpeed(element);
336                                        wrapper.setAttribute('data-marquee-speed', newSpeed);
337                                        shouldUpdate = true;
338                                    }
339                                    // Update if wrapper changed
340                                    if (mutation.target === wrapper) {
341                                        shouldUpdate = true;
342                                    }
343                                }
344                            }
345                            // Also check for child changes (in case element is re-rendered)
346                            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
347                                shouldUpdate = true;
348                            }
349                        });
350                        
351                        if (shouldUpdate) {
352                            // Get new speed and update
353                            const newSpeed = getMarqueeSpeed(element);
354                            updateSpeed(newSpeed);
355                        }
356                    });
357
358                    // Observe wrapper for speed changes
359                    speedObserver.observe(wrapper, {
360                        attributes: true,
361                        attributeFilter: ['data-marquee-speed']
362                    });
363
364                    // Observe element for speed changes
365                    speedObserver.observe(element, {
366                        attributes: true,
367                        attributeFilter: ['data-marquee-speed', 'class'],
368                        childList: true,
369                        subtree: false
370                    });
371                    
372                    // Also observe parent element
373                    if (element.parentElement) {
374                        speedObserver.observe(element.parentElement, {
375                            attributes: true,
376                            attributeFilter: ['data-marquee-speed']
377                        });
378                    }
379                    
380                    // Store observer reference for cleanup if needed
381                    element.setAttribute('data-marquee-observer', 'active');
382                }
383                
384                // Expose updateSpeed function globally on wrapper for external access
385                wrapper.updateMarqueeSpeed = updateSpeed;
386                
387                // AGGRESSIVE periodic check - check very frequently
388                // Read speed from element and update wrapper if changed
389                let lastKnownSpeed = speedValue;
390                const speedCheckInterval = setInterval(function() {
391                    // Only check if element is still in DOM
392                    if (!document.body.contains(element)) {
393                        clearInterval(speedCheckInterval);
394                        return;
395                    }
396                    
397                    // Always get fresh speed value from element
398                    const currentSpeed = getMarqueeSpeed(element);
399                    const wrapperSpeed = parseFloat(wrapper.getAttribute('data-marquee-speed')) || 0;
400                    
401                    // If speed from element is different from wrapper, update
402                    if (Math.abs(currentSpeed - wrapperSpeed) > 0.01) {
403                        updateSpeed(currentSpeed);
404                    }
405                }, 100); // Check every 100ms - EXTREMELY AGGRESSIVE
406                
407                // Store interval ID for potential cleanup
408                wrapper.setAttribute('data-speed-check-interval', 'active');
409
410                // Mark as initialized
411                element.dataset.marqueeInitialized = 'true';
412            });
413        });
414    }
415
416    // Initialize on DOM ready
417    if (document.readyState === 'loading') {
418        document.addEventListener('DOMContentLoaded', initMarquee);
419    } else {
420        initMarquee();
421    }
422
423    // Re-initialize for dynamically loaded content (e.g., AJAX)
424    if (typeof MutationObserver !== 'undefined') {
425        const observer = new MutationObserver(function(mutations) {
426            let shouldReinit = false;
427            mutations.forEach(function(mutation) {
428                if (mutation.addedNodes.length > 0) {
429                    mutation.addedNodes.forEach(function(node) {
430                        if (node.nodeType === 1) { // Element node
431                            if (node.classList && node.classList.contains('gb-marquee-infinite-scroll')) {
432                                shouldReinit = true;
433                            } else if (node.querySelector && node.querySelector('.gb-marquee-infinite-scroll')) {
434                                shouldReinit = true;
435                            }
436                        }
437                    });
438                }
439            });
440            if (shouldReinit) {
441                initMarquee();
442            }
443        });
444
445        observer.observe(document.body, {
446            childList: true,
447            subtree: true
448        });
449    }
450})();
451

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.