PageSourceSearch

https://artbrussels.com/wp-content/themes/aaab/assets/js/main.js?ver=1790356368

js artbrussels.com collected 2026-09-25 18:42:17 UTC 29,536 bytes, 628 lines download raw bytes

1(function($){
2    if ('scrollRestoration' in history) {
3        history.scrollRestoration = 'manual';
4    }
5
6    function updateVHVariable() {
7        let vh = window.innerHeight * 0.01;
8        document.documentElement.style.setProperty('--vh', `${vh}px`);
9    }
10    var timeout = null;
11        $(document).ready(function() {
12            $('aside').trigger('scroll');
13            $('.related.video, .related.off-programme-detail').mouseenter(function() {
14                $('.hover').removeClass('hover');
15                $(this).addClass('hover');
16                clearTimeout(timeout);
17            })
18            $('.related.video, .related.off-programme-detail').mouseleave(function() {
19                const $this = $(this);
20                timeout = setTimeout(function() {
21                    $this.removeClass('hover');
22                }, 500);
23                
24            })
25
26            var homeHoverMq = window.matchMedia('(min-width: 769px)');
27            $(document).on('mouseenter mousemove', '.home-item.has-hover-image', function(e) {
28                if (!homeHoverMq.matches) {
29                    return;
30                }
31                $(this).find('.home-item-hover').css({
32                    top: e.clientY + 'px',
33                    left: e.clientX + 'px'
34                });
35            });
36            
37           /* $('aside').on('scroll', function(e) {
38                $('.related .video-container, .related .preview-off-programme').each(function() {
39                    var overlayHeight = $(this).outerHeight();
40                    var windowHeight = $(window).height();
41                    var asideHeight = $('aside').height();
42                    
43                    // Calculate position, ensuring overlay doesn't go below viewport
44                    let topPosition = $(this).parent().offset().top;
45                    let scrollPosition = $('aside').scrollTop();
46                    
47                    let asideDiff = asideHeight - windowHeight;
48                    topPosition = topPosition + scrollPosition + asideDiff;
49                    
50                    console.log('topPosition'+topPosition+'asideDiff'+asideDiff);
51                    console.log('scrollPos ' + scrollPosition);
52                    if (topPosition + overlayHeight + 10 > windowHeight) {
53                        topPosition = windowHeight - overlayHeight - 10;
54                    }
55                    if(topPosition < 10) {
56                        topPosition = 10;
57                    }
58                    
59                    // Position the overlay
60                    $(this).css({
61                        'top': topPosition + 'px'
62                    });
63                })
64            })*/
65            
66            $('aside').on('scroll', function(e) {
67                $('.related .video-container, .related .preview-off-programme').each(function() {
68                    var $overlay = $(this);
69                    var $parent = $overlay.parent();
70                    var overlayHeight = $overlay.outerHeight();
71                    var windowHeight = $(window).height();
72                    
73                    // Get the parent's position relative to the aside
74                    var parentOffset = $parent.offset();
75                    var asideOffset = $('aside').offset();
76                    var asideScrollTop = $('aside').scrollTop();
77                    
78                    // Calculate the parent's position relative to the aside
79                    var parentRelativeTop = parentOffset.top - asideOffset.top + asideScrollTop;
80                    
81                    // Position the overlay at the same height as its parent
82                    var topPosition = parentRelativeTop;
83                    
84                    // Ensure overlay doesn't go below viewport
85                    if (topPosition + overlayHeight > windowHeight) {
86                        topPosition = windowHeight - overlayHeight - 10;
87                    }
88                    
89                    // Ensure overlay doesn't go above viewport
90                    if (topPosition < 10) {
91                        topPosition = 10;
92                    }
93                    
94                    // Position the overlay
95                    $overlay.css({
96                        'top': topPosition + 'px'
97                    });
98                });
99            });
100            
101            
102            
103            $('.artists-overlay').hide();
104            $('[data-artists]').mouseenter(function(e) {
105                $('.artists-overlay').removeClass('solo');
106                if($(this).attr('data-solo')) {
107                    $('.artists-overlay').addClass('solo');
108                    $('.artists-overlay h2').html($(this).data('solo'));
109                    $('.artists-overlay h3').text('Solo artist shown at the fair');
110                }
111                else {
112                    $('.artists-overlay h2').html($(this).data('artists'));
113                    $('.artists-overlay h3').text('Artists shown at the fair');
114                }
115
116                // Get overlay height and window height
117                const overlayHeight = $('.artists-overlay').outerHeight();
118                const windowHeight = $(window).height();
119                
120                // Calculate position, ensuring overlay doesn't go below viewport
121                let topPosition = e.clientY;
122                if (topPosition + overlayHeight > windowHeight) {
123                    topPosition = windowHeight - overlayHeight;
124                }
125
126                // Position the overlay
127                $('.artists-overlay').css({
128                    'top': topPosition + 'px'
129                }).show();
130            });
131            $('[data-artists]').mouseleave(function() {
132                $('.artists-overlay').hide();
133            });
134            $('.slider').flickity({
135                cellAlign: 'center',
136                arrowShape: 'M43.45,93.45l7.47-7.37-30.75-30.75H99.8v-10.67H20.17L50.92,14.02l-7.47-7.47L0,50l43.45,43.45Z',
137                wrapAround: true,
138                imagesLoaded: true,
139                pageDots: false,
140            });
141                if ($('body').hasClass('home')) {
142                    window.scrollTo(0, 0);
143                    setTimeout(() => {
144                        window.scrollTo(0, 0);
145                    }, 50);
146                }
147                $('.aside-toggle').click(function() {
148                        $('body').toggleClass('show-aside');
149                })
150                const titleElement = document.querySelector('header div.title h2');
151                
152                if (titleElement && titleElement.scrollWidth > titleElement.clientWidth) {
153                    // Wait a bit after page load before starting animation
154                    setTimeout(() => {
155                        // Custom scroll animation function with adjustable speed
156                        const scrollToElement = (element, to, duration) => {
157                            const start = element.scrollLeft;
158                            const change = to - start;
159                            const increment = 20; // Lower increment = smoother animation
160                            let currentTime = 0;
161                            
162                            const animateScroll = () => {
163                                currentTime += increment;
164                                const val = easeInOutQuad(currentTime, start, change, duration);
165                                element.scrollLeft = val;
166                                if (currentTime < duration) {
167                                    requestAnimationFrame(animateScroll);
168                                } else {
169                                    // After reaching the end, scroll back to start
170                                    setTimeout(() => {
171                                        scrollToElement(element, 0, 1200); // Scroll back with 3 second duration
172                                    }, 2000); // Pause at the end for 2 seconds
173                                }
174                            };
175                            
176                            // Easing function for smoother start/end
177                            const easeInOutQuad = (t, b, c, d) => {
178                                t /= d/2;
179                                if (t < 1) return c/2*t*t + b;
180                                t--;
181                                return -c/2 * (t*(t-2) - 1) + b;
182                            };
183                            
184                            requestAnimationFrame(animateScroll);
185                        };
186                        
187                        // Start scrolling to the end with 4 second duration (slower scroll)
188                        scrollToElement(titleElement, titleElement.scrollWidth, 2000);
189                    }, 1500);
190                }
191        updateVHVariable();
192
193        // Update on resize and orientation change
194        window.addEventListener('resize', updateVHVariable);
195        window.addEventListener('orientationchange', updateVHVariable);
196        $('section.foldouttxt h2').click(function() {
197            $(this).parent().toggleClass('open');
198            $(this).parent().find('.foldouttxt-content').slideToggle();
199        });
200        $('.fold-out-item h3').click(function() {
201            $(this).parent().toggleClass('open');
202            $(this).parent().find('.fold-out-content').slideToggle();
203        });
204        $('.back-to-top').click(function() {   
205            $('html, body').animate({
206                scrollTop: 0
207            }, 1000);
208            return false;
209        });
210        $('.menu-toggle').click(function() {
211            $('body').toggleClass('show-menu');
212            return false;
213        });
214        $('.filter-group h3').click(function() {
215            if($(this).parent().hasClass('display-filter')) {
216                $(this).parent().removeClass('display-filter');
217            }
218            else {
219                $('.filter-group').removeClass('display-filter');
220                $(this).parent().addClass('display-filter');
221            }
222        });
223        $('.section-preview .section-toggle').click(function() {
224            $(this).parent().toggleClass('show-content');
225        });
226        $('nav>div>p>span:first-child').click(function() {
227            $('.menu-open').removeClass('menu-open');
228            $(this).parent().addClass('menu-open');
229        });
230        
231        // Function to create infinite scrolling carousel
232        function setupTicker(sectionSelector, tickerSpeed = 0.8) {
233            if ($(sectionSelector).length) {
234                let $section = $(sectionSelector);
235                let $ticker = $section.find('.ticker');
236                
237                // For partners section, make sure all images have loaded before setting up the carousel
238                if (sectionSelector === '.partners') {
239                    let $images = $ticker.find('img');
240                    let imagesLoaded = 0;
241                    let totalImages = $images.length;
242                    
243                    // Only set up the ticker after all images have loaded
244                    if (totalImages > 0) {
245                        $images.each(function() {
246                            if (this.complete) {
247                                imagesLoaded++;
248                                if (imagesLoaded === totalImages) {
249                                    initializeTicker();
250                                }
251                            } else {
252                                $(this).on('load', function() {
253                                    imagesLoaded++;
254                                    if (imagesLoaded === totalImages) {
255                                        initializeTicker();
256                                    }
257                                });
258                                
259                                // Handle image loading errors
260                                $(this).on('error', function() {
261                                    imagesLoaded++;
262                                    if (imagesLoaded === totalImages) {
263                                        initializeTicker();
264                                    }
265                                });
266                            }
267                        });
268                    } else {
269                        initializeTicker();
270                    }
271                } else {
272                    // For non-image based tickers, initialize immediately
273                    initializeTicker();
274                }
275                
276                function initializeTicker() {
277                    // First, make sure each item has a consistent width
278                    // This is especially important for partners with images
279                    let initialItems = $ticker.children().toArray();
280                    let viewportWidth = $section.width();
281                    let totalWidth = 0;
282                    
283                    // For partners, ensure consistent figure widths
284                    if (sectionSelector === '.partners') {
285                        // Set a fixed width for each partner figure based on its content
286                        $(initialItems).each(function() {
287                            // If we want each figure to have the same width
288                            // $(this).css('width', '150px'); // Adjust this value as needed
289                            
290                            // Or if we want to preserve proportions but ensure consistency
291                            let imgWidth = $(this).find('img').width();
292                            let imgHeight = $(this).find('img').height();
293                            $(this).css({
294                                'width': imgWidth + 20 + 'px', // Width plus padding
295                                'height': '85px', // Fixed height for consistency
296                                'flex-shrink': 0 // Prevent shrinking
297                            });
298                        });
299                    }
300                    
301                    // Calculate initial width
302                    initialItems.forEach(item => {
303                        totalWidth += $(item).outerWidth(true);
304                    });
305                    
306                    // Clone items until we have enough content
307                    let iterations = Math.max(3, Math.ceil((viewportWidth * 3) / totalWidth));
308                    $ticker.empty(); // Clear ticker before adding cloned items
309                    
310                    // First add the original items
311                    for (let item of initialItems) {
312                        $ticker.append(item);
313                    }
314                    
315                    // Then add the clones
316                    for (let i = 0; i < iterations; i++) {
317                        let clonedItems = $(initialItems).clone(true);
318                        $ticker.append(clonedItems);
319                    }
320                    
321                    // Recalculate total width with all cloned items
322                    let itemWidth = 0;
323                    let clonePoint = 0;
324                    $ticker.children().each(function(index) {
325                        let width = $(this).outerWidth(true);
326                        itemWidth += width;
327                        
328                        // Mark the point where we should reset (end of first set)
329                        if (index === initialItems.length - 1) {
330                            clonePoint = itemWidth;
331                        }
332                    });
333                    
334                    // Setup animation
335                    let animationFrame;
336                    let scrollPosition = 0;
337                    let scrollSpeed = tickerSpeed; // Can be customized per ticker
338                    let pixelAccumulator = 0;
339                    
340                    // Create the scrolling animation
341                    function scrollTicker() {
342                        pixelAccumulator += scrollSpeed;
343                        
344                        // Only move when we've accumulated at least 1 pixel
345                        if (pixelAccumulator >= 1) {
346                            // Calculate how many pixels to move this frame
347                            let pixelsToMove = Math.floor(pixelAccumulator);
348                            scrollPosition += pixelsToMove;
349                            pixelAccumulator -= pixelsToMove;
350                            
351                            // When we reach the clone point, reset to beginning
352                            if (scrollPosition >= clonePoint) {
353                                // Jump back to start immediately without visual disruption
354                                scrollPosition = 0;
355                                $ticker.css({
356                                    'transform': 'translateX(0)',
357                                    'transition': 'none' // Ensure no transition for reset
358                                });
359                            } else {
360                                // Smooth transition during normal scrolling
361                                $ticker.css({
362                                    'transform': 'translateX(-' + scrollPosition + 'px)',
363                                    'transition': 'transform 0.1s linear' // Short transition for smoothness
364                                });
365                            }
366                        }
367                        
368                        animationFrame = requestAnimationFrame(scrollTicker);
369                    }
370                    
371                    // Force a reflow before starting animation to ensure transitions work properly
372                    $ticker[0].offsetHeight;
373                    
374                    // Start the animation
375                    animationFrame = requestAnimationFrame(scrollTicker);
376                    
377                    // Pause animation when ticker isn't visible
378                    $(window).on('scroll', function() {
379                        let sectionTop = $section.offset().top;
380                        let windowBottom = $(window).scrollTop() + $(window).height();
381                        let windowTop = $(window).scrollTop();
382                        
383                        if (sectionTop >
383 windowBottom || (sectionTop + $section.height()) < windowTop) {
384                            // Section not visible, cancel animation
385                            if (animationFrame) {
386                                cancelAnimationFrame(animationFrame);
387                                animationFrame = null;
388                            }
389                        } else if (!animationFrame) {
390                            // Section visible again, restart animation
391                            animationFrame = requestAnimationFrame(scrollTicker);
392                        }
393                    });
394                }
395            }
396        }
397        
398        // Setup both tickers with different speeds
399        setupTicker('.home-menu', 1.2);  // Home menu ticker with slower speed
400        setupTicker('.partners', 0.8);   // Partners ticker with faster speed
401        
402        // Gallery AJAX filtering
403        if ($('#gallery-filter-form').length) {
404            // Track if AJAX request is in progress
405            let ajaxRunning = false;
406            
407            // Handle filter form submission
408            $('#gallery-filter-form').on('submit', function(e) {
409                e.preventDefault();
410                
411                if (ajaxRunning) return false;
412                
413                ajaxRunning = true;
414                $('.gallery-links').addClass('loading');
415                
416                // Get all checked values
417                const sections = [];
418                $('input[name="sections[]"]:checked').each(function() {
419                    sections.push($(this).val());
420                });
421                
422                const countries = [];
423                $('input[name="countries[]"]:checked').each(function() {
424                    countries.push($(this).val());
425                });
426                
427                // Update URL with current filters (for bookmarking/sharing)
428                let newUrl = window.location.pathname;
429                let params = [];
430                
431                if (sections.length) {
432                    sections.forEach(section => {
433                        params.push(`sections[]=${encodeURIComponent(section)}`);
434                    });
435                }
436                
437                if (countries.length) {
438                    countries.forEach(country => {
439                        params.push(`countries[]=${encodeURIComponent(country)}`);
440                    });
441                }
442                
443                if (params.length) {
444                    newUrl += '?' + params.join('&');
445                }
446                
447                window.history.pushState({}, '', newUrl);
448                
449                // Make AJAX request
450                $.ajax({
451                    url: ajax_object.ajax_url,
452                    type: 'POST',
453                    data: {
454                        action: 'gallery_filter',
455                        sections: sections,
456                        countries: countries,
457                        nonce: ajax_object.nonce
458                    },
459                    success: function(response) {
460                        if (response.success) {
461                            $('.gallery-links').html(response.data.html);
462                        } else {
463                            console.error('AJAX error:', response);
464                            $('.gallery-links').html('<p class="error">Error loading galleries. Please try again.</p>');
465                        }
466                    },
467                    error: function(xhr, status, error) {
468                        console.error('AJAX error:', error);
469                        $('.gallery-links').html('<p class="error">Error loading galleries. Please try again.</p>');
470                    },
471                    complete: function() {
472                        ajaxRunning = false;
473                        $('.gallery-links').removeClass('loading');
474                    }
475                });
476            });
477            
478            // Auto-submit when checkboxes are clicked
479            $('.filter-checkbox').on('change', function() {
480                updateFilterActiveStatus(); // Make sure status is updated before form submission
481                $('#gallery-filter-form').submit();
482            });
483        }
484        
485        // Setup Vimeo background videos
486        function setupVimeoBackgroundVideos() {
487            // Check if we have video preview sections with data-video-url
488            if ($('.video-preview[data-video-url]').length) {
489                // Load Vimeo Player API if not already loaded
490                if (!window.Vimeo) {
491                    let tag = document.createElement('script');
492                    tag.src = "https://player.vimeo.com/api/player.js";
493                    let firstScriptTag = document.getElementsByTagName('script')[0];
494                    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
495                    
496                    // Wait for Vimeo API to load
497                    let checkVimeoAPI = setInterval(function() {
498                        if (window.Vimeo) {
499                            clearInterval(checkVimeoAPI);
500                            initVimeoPlayers();
501                        }
502                    }, 100);
503                } else {
504                    // Vimeo API already loaded
505                    initVimeoPlayers();
506                }
507            }
508        }
509        
510        function initVimeoPlayers() {
511            $('.video-preview[data-video-url]').each(function(index) {
512                const $container = $(this);
513                const videoUrl = $container.data('video-url');
514                const $playerContainer = $container.find('.video-player');
515                
516                if (videoUrl && $playerContainer.length) {
517                    // Initialize Vimeo player with specific options for background video
518                    const player = new Vimeo.Player($playerContainer[0], {
519                        url: videoUrl,
520                        background: true, // This creates a background video with no controls
521                        muted: true,
522                        loop: false,
523                        autoplay: false, // Make sure autoplay is explicitly set to false
524                        title: false,
525                        byline: false,
526                        portrait: false,
527                        dnt: true, // Do not track to avoid analytics cookies
528                        controls: false // Explicitly disable controls
529                    });
530                    
531                    // Make sure the player is paused initially
532                    player.ready().then(function() {
533                        player.pause();
534                        $container.removeClass('loading');
535                    }).catch(function(error) {
536                        console.error('Player ready error:', error);
537                    });
538                    
539                    // Store player instance in container for reference
540                    $container.data('player', player);
541                    
542                    // Set up hover events on parent link
543                    const $videoLink = $container.closest('.video-item-link');
544                    
545                    $videoLink.on('mouseenter', function() {
546                        const player = $container.data('player');
547                        $container.data('hovering', true);
548                        if (player) {
549                            player.getPaused().then(isPaused => {
550                                player.getCurrentTime().then(currentTime => {
551                                    if (Math.abs(currentTime - 20) > 0.1) {
552                                        return player.setCurrentTime(20);
553                                    }
554                                    return Promise.resolve();
555                                }).then(() => {
556                                    return player.play();
557                                }).then(() => {
558                                    if ($container.data('hovering')) {
559                                        $container.find('.video-thumbnail').addClass('hidden');
560                                    }
561
562                                    const loopInterval = setInterval(() => {
563                                        player.getCurrentTime().then(time => {
564                                            if (time >= 50) {
565                                                player.setCurrentTime(20);
566                                            }
567                                        }).catch(error => {
568                                            clearInterval(loopInterval);
569                                            console.error('Error in loop interval:', error);
570                                        });
571                                    }, 250);
572                                    
573                                    $container.data('loopInterval', loopInterval);
574                                }).catch(error => {
575                                    console.error('Video play sequence error:', error);
576                                });
577                            }).catch(error => {
578                                console.error('Get paused state error:', error);
579                            });
580                        }
581                    });
582                    
583                    $videoLink.on('mouseleave', function() {
584                        const player = $container.data('player');
585                        const loopInterval = $container.data('loopInterval');
586                        $container.data('hovering', false);
587                        
588                        if (player) {
589                            player.pause().catch(error => console.error('Video pause error:', error));
590                            $container.find('.video-thumbnail').removeClass('hidden');
591                        }
592                        
593                        if (loopInterval) {
594                            clearInterval(loopInterval);
595                            $container.data('loopInterval', null);
596                        }
597                    });
598                }
599            });
600        }
601        
602        // Initialize the Vimeo background videos
603        setupVimeoBackgroundVideos();
604
605        // Function to update filter active status
606        function updateFilterActiveStatus() {
607            $('.filter-group').each(function() {
608                const $filterGroup = $(this);
609                const $header = $filterGroup.find('h3');
610                const hasActiveFilters = $filterGroup.find('input[type="checkbox"]:checked').length > 0;
611                
612                if (hasActiveFilters) {
613                    $header.addClass('filter-active');
614                } else {
615                    $header.removeClass('filter-active');
616                }
617            });
618        }
619        
620        // Run on page load to set initial state
621        updateFilterActiveStatus();
622        
623        // Update when checkboxes change
624        $('.filter-checkbox').on('change', function() {
625            updateFilterActiveStatus();
626        });
627        })
628})(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.