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');
});