MediaWiki:Gadget-ElementPortraitCarouselUI.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// ElementPortraitCarousel dynamics
$(function() {
    // Only execute if there are any ".person-portrait-carousel" on the page
    if ($('.person-portrait-carousel').length === 0) return;
    
    console.log('Initializing person template carousel');
    
    // Initialize all carousels on the page
    $('.carousel-container').each(function() {
        var $container = $(this);
        var $items = $container.find('.carousel-item');
        
        // Skip if no items
        if ($items.length === 0) return;
        
        // Wait for images to load
        $items.find('img').on('load', function() {
            $(this).data('loaded', true);
        }).each(function() {
            // If already complete, trigger load event
            if (this.complete) {
                $(this).trigger('load');
            }
        });
        
        // Initial positioning of carousel items
        function initializeCarousel() {
            // Show first image, position others
            $items.eq(0).addClass('carousel-visible').removeClass('carousel-hidden');
            
            // Logic for exactly 2 images (orbital layout)
            if ($items.length === 2) {
                $items.eq(0).addClass('carousel-visible carousel-orbital-1').removeClass('carousel-hidden carousel-orbital-2 carousel-left carousel-right');
                $items.eq(1).addClass('carousel-hidden carousel-orbital-2').removeClass('carousel-visible carousel-orbital-1 carousel-left carousel-right');
            } 
            // Logic for 3+ images (card shuffle)
            else if ($items.length > 2) {
                $items.eq(1).addClass('carousel-right').removeClass('carousel-left carousel-visible');
                $items.eq($items.length - 1).addClass('carousel-left').removeClass('carousel-right carousel-visible');
            }
            
            // Let images dimension within the fixed container
            $container.find('.carousel-item img').css({
                'max-height': '100%',
                'max-width': '100%',
                'object-fit': 'contain'
            });
        }
        
        // Initialize carousel after a delay to ensure proper rendering
        setTimeout(initializeCarousel, 100);
    });
    
// Click handler for buttons
$('.carousel-next').on('click', function() {
    var $container = $(this).closest('.carousel-container');
    var $items = $container.find('.carousel-item');
    var $current = $container.find('.carousel-visible');
    var currentIndex = parseInt($current.data('index'));
    var nextIndex = (currentIndex % $items.length) + 1;
    var $next = $container.find('.carousel-item[data-index="' + nextIndex + '"]');
    
    // Check if there are exactly 2 images
    if ($items.length === 2) {
        // Clear any existing classes and animations
        $items.removeClass('carousel-left carousel-right orbital-animating-forward orbital-animating-backward');
        
        // Apply the animation class to create orbital motion
        $current.addClass('orbital-animating-forward');
        $next.addClass('orbital-animating-backward');
        
        // After animation completes, update the final state classes
        setTimeout(function() {
            $current.removeClass('carousel-visible carousel-orbital-1 orbital-animating-forward')
                   .addClass('carousel-hidden carousel-orbital-2');
            $next.removeClass('carousel-hidden carousel-orbital-2 orbital-animating-backward')
                 .addClass('carousel-visible carousel-orbital-1');
        }, 600); // Match CSS animation duration (0.6s)
    } else {
        // Logic for 3+ images
        // Remove position classes
        $items.removeClass('carousel-left carousel-right');
        
        // Removals and additions
        $current.removeClass('carousel-visible').addClass('carousel-hidden carousel-left');
        $next.removeClass('carousel-hidden carousel-left carousel-right').addClass('carousel-visible');
        
        // Set the item after next to right
        var afterNextIndex = (nextIndex % $items.length) + 1;
        if (afterNextIndex > $items.length) afterNextIndex = 1;
        var $afterNext = $container.find('.carousel-item[data-index="' + afterNextIndex + '"]');
        if ($afterNext.length && !$afterNext.is($next)) {
            $afterNext.removeClass('carousel-visible carousel-left').addClass('carousel-hidden carousel-right');
        }
    }
});

// Click handler for the previous button for 2-image case
    $('.carousel-prev').on('click', function() {
        // Get container and items
        var $container = $(this).closest('.carousel-container');
        var $items = $container.find('.carousel-item');
        var $current = $container.find('.carousel-visible');
        
        // Calculate previous index
        var currentIndex = parseInt($current.data('index'));
        var prevIndex = currentIndex - 1;
        if (prevIndex < 1) prevIndex = $items.length;
        var $prev = $container.find('.carousel-item[data-index="' + prevIndex + '"]');
        
        // Check if we have exactly 2 images: use reversed logic compared to next button
        if ($items.length === 2) {
            // Clear any existing classes and animations
            $items.removeClass('carousel-left carousel-right orbital-animating-forward orbital-animating-backward');
            
            // Apply correct animation classes for leftward movement
            $current.addClass('orbital-animating-forward'); // Front item moves back
            $prev.addClass('orbital-animating-backward');  // Back item moves forward
            
            // Update final state and remove correct animation classes
            var updateFinalState = function() {
                $current.removeClass('carousel-visible carousel-orbital-1 orbital-animating-forward') // Remove forward animation class
                       .addClass('carousel-hidden carousel-orbital-2');
                $prev.removeClass('carousel-hidden carousel-orbital-2 orbital-animating-backward') // Remove backward animation class
                     .addClass('carousel-visible carousel-orbital-1');
            };
            
            // Match CSS animation duration (0.6s)
            setTimeout(updateFinalState, 600);
        } else {
            // Logic for 3+ images (card shuffle)
            // Remove position classes
            $items.removeClass('carousel-left carousel-right');
            
            // Removals and additions
            $current.removeClass('carousel-visible').addClass('carousel-hidden carousel-right');
            $prev.removeClass('carousel-hidden carousel-left carousel-right').addClass('carousel-visible');
            
            // Set the item before prev to left
            var beforePrevIndex = prevIndex - 1;
            if (beforePrevIndex < 1) beforePrevIndex = $items.length;
            var $beforePrev = $container.find('.carousel-item[data-index="' + beforePrevIndex + '"]');
            if ($beforePrev.length && !$beforePrev.is($prev)) {
                $beforePrev.removeClass('carousel-visible carousel-right').addClass('carousel-hidden carousel-left');
            }
        }
    });
    
    // Touch swipe support for mobile users
    $('.carousel-container').each(function() {
        var $container = $(this);
        var startX, startY, endX, endY;
        var MIN_SWIPE_DISTANCE = 50; // Minimum distance for a swipe to be registered
        var MAX_VERTICAL_DISTANCE = 50; // Maximum vertical movement allowed for horizontal swipe

        // Touch start: record initial position
        $container.on('touchstart', function(e) {
            // Store initial touch coordinates
            startX = e.originalEvent.touches[0].clientX;
            startY = e.originalEvent.touches[0].clientY;
        });

        // Touch end: determine swipe direction and trigger appropriate action
        $container.on('touchend', function(e) {
            // Get final touch coordinates
            endX = e.originalEvent.changedTouches[0].clientX;
            endY = e.originalEvent.changedTouches[0].clientY;
            
            // Calculate horizontal and vertical distance
            var horizontalDistance = endX - startX;
            var verticalDistance = Math.abs(endY - startY);
            
            // Only register as swipe if horizontal movement is significant and vertical movement is limited
            if (Math.abs(horizontalDistance) >= MIN_SWIPE_DISTANCE && verticalDistance <= MAX_VERTICAL_DISTANCE) {
                // Prevent default behavior if it's a horizontal swipe
                e.preventDefault();
                
                if (horizontalDistance > 0) {
                    // Swipe right: go to previous slide
                    $container.find('.carousel-prev').trigger('click');
                } else {
                    // Swipe left: go to next slide
                    $container.find('.carousel-next').trigger('click');
                }
            }
        });
    });
    
    console.log('Template:Person carousel initialization successful');
});