MediaWiki:Common.js: Difference between revisions
MediaWiki interface page
More actions
Git release 98258910502c |
Git release b32d298a7432 |
||
| Line 14: | Line 14: | ||
' ' | ' ' | ||
]; | ]; | ||
function setupHomeCarousel() { | |||
var carousel = document.querySelector( '.lcd-home-carousel' ); | |||
if ( !carousel || carousel.dataset.techdocsCarouselReady === '1' ) { | |||
return; | |||
} | |||
var slides = Array.prototype.slice.call( | |||
carousel.querySelectorAll( '.lcd-home-carousel__slide' ) | |||
); | |||
if ( slides.length === 0 ) { | |||
return; | |||
} | |||
carousel.dataset.techdocsCarouselReady = '1'; | |||
var interval = parseInt( carousel.dataset.interval, 10 ); | |||
if ( !Number.isFinite( interval ) || interval < 1000 ) { | |||
interval = 2000; | |||
} | |||
var currentIndex = Math.max( 0, slides.findIndex( function ( slide ) { | |||
return slide.classList.contains( 'lcd-home-carousel__slide--active' ); | |||
} ) ); | |||
var timerId = 0; | |||
var dots = []; | |||
var dotsContainer = document.createElement( 'div' ); | |||
dotsContainer.className = 'lcd-home-carousel__dots'; | |||
dotsContainer.setAttribute( 'role', 'group' ); | |||
dotsContainer.setAttribute( 'aria-label', '轮播进度' ); | |||
function showSlide( index ) { | |||
currentIndex = ( index + slides.length ) % slides.length; | |||
slides.forEach( function ( slide, slideIndex ) { | |||
var isActive = slideIndex === currentIndex; | |||
slide.classList.toggle( | |||
'lcd-home-carousel__slide--active', | |||
isActive | |||
); | |||
slide.setAttribute( 'aria-hidden', isActive ? 'false' : 'true' ); | |||
} ); | |||
dots.forEach( function ( dot, dotIndex ) { | |||
if ( dotIndex === currentIndex ) { | |||
dot.setAttribute( 'aria-current', 'true' ); | |||
} else { | |||
dot.removeAttribute( 'aria-current' ); | |||
} | |||
} ); | |||
} | |||
function stopTimer() { | |||
if ( timerId ) { | |||
window.clearInterval( timerId ); | |||
timerId = 0; | |||
} | |||
} | |||
function startTimer() { | |||
stopTimer(); | |||
if ( slides.length > 1 && !document.hidden ) { | |||
timerId = window.setInterval( function () { | |||
showSlide( currentIndex + 1 ); | |||
}, interval ); | |||
} | |||
} | |||
slides.forEach( function ( slide, slideIndex ) { | |||
var dot = document.createElement( 'button' ); | |||
dot.type = 'button'; | |||
dot.className = 'lcd-home-carousel__dot'; | |||
dot.setAttribute( | |||
'aria-label', | |||
'显示第 ' + ( slideIndex + 1 ) + ' 张轮播图片' | |||
); | |||
dot.addEventListener( 'click', function () { | |||
showSlide( slideIndex ); | |||
startTimer(); | |||
} ); | |||
dots.push( dot ); | |||
dotsContainer.appendChild( dot ); | |||
} ); | |||
carousel.appendChild( dotsContainer ); | |||
showSlide( currentIndex ); | |||
startTimer(); | |||
document.addEventListener( 'visibilitychange', function () { | |||
if ( document.hidden ) { | |||
stopTimer(); | |||
} else { | |||
startTimer(); | |||
} | |||
} ); | |||
window.addEventListener( 'pagehide', stopTimer ); | |||
} | |||
function decodeFragment( hash ) { | function decodeFragment( hash ) { | ||
| Line 224: | Line 323: | ||
mw.loader.using( 'skins.citizen.scripts' ).then( function () { | mw.loader.using( 'skins.citizen.scripts' ).then( function () { | ||
window.requestAnimationFrame( setupSingleActiveToc ); | window.requestAnimationFrame( function () { | ||
setupHomeCarousel(); | |||
setupSingleActiveToc(); | |||
} ); | |||
} ); | } ); | ||
}() ); | }() ); | ||
Revision as of 18:33, 26 July 2026
( function () {
'use strict';
var ACTIVE_CLASS = 'citizen-toc-list-item--active';
var ACTIVE_TOP_CLASS = 'citizen-toc-level-1--active';
var LINK_SELECTOR = '.citizen-toc-link:not(.citizen-toc-top)';
var NAVIGATION_KEYS = [
'ArrowDown',
'ArrowUp',
'End',
'Home',
'PageDown',
'PageUp',
' '
];
function setupHomeCarousel() {
var carousel = document.querySelector( '.lcd-home-carousel' );
if ( !carousel || carousel.dataset.techdocsCarouselReady === '1' ) {
return;
}
var slides = Array.prototype.slice.call(
carousel.querySelectorAll( '.lcd-home-carousel__slide' )
);
if ( slides.length === 0 ) {
return;
}
carousel.dataset.techdocsCarouselReady = '1';
var interval = parseInt( carousel.dataset.interval, 10 );
if ( !Number.isFinite( interval ) || interval < 1000 ) {
interval = 2000;
}
var currentIndex = Math.max( 0, slides.findIndex( function ( slide ) {
return slide.classList.contains( 'lcd-home-carousel__slide--active' );
} ) );
var timerId = 0;
var dots = [];
var dotsContainer = document.createElement( 'div' );
dotsContainer.className = 'lcd-home-carousel__dots';
dotsContainer.setAttribute( 'role', 'group' );
dotsContainer.setAttribute( 'aria-label', '轮播进度' );
function showSlide( index ) {
currentIndex = ( index + slides.length ) % slides.length;
slides.forEach( function ( slide, slideIndex ) {
var isActive = slideIndex === currentIndex;
slide.classList.toggle(
'lcd-home-carousel__slide--active',
isActive
);
slide.setAttribute( 'aria-hidden', isActive ? 'false' : 'true' );
} );
dots.forEach( function ( dot, dotIndex ) {
if ( dotIndex === currentIndex ) {
dot.setAttribute( 'aria-current', 'true' );
} else {
dot.removeAttribute( 'aria-current' );
}
} );
}
function stopTimer() {
if ( timerId ) {
window.clearInterval( timerId );
timerId = 0;
}
}
function startTimer() {
stopTimer();
if ( slides.length > 1 && !document.hidden ) {
timerId = window.setInterval( function () {
showSlide( currentIndex + 1 );
}, interval );
}
}
slides.forEach( function ( slide, slideIndex ) {
var dot = document.createElement( 'button' );
dot.type = 'button';
dot.className = 'lcd-home-carousel__dot';
dot.setAttribute(
'aria-label',
'显示第 ' + ( slideIndex + 1 ) + ' 张轮播图片'
);
dot.addEventListener( 'click', function () {
showSlide( slideIndex );
startTimer();
} );
dots.push( dot );
dotsContainer.appendChild( dot );
} );
carousel.appendChild( dotsContainer );
showSlide( currentIndex );
startTimer();
document.addEventListener( 'visibilitychange', function () {
if ( document.hidden ) {
stopTimer();
} else {
startTimer();
}
} );
window.addEventListener( 'pagehide', stopTimer );
}
function decodeFragment( hash ) {
if ( !hash || hash.charAt( 0 ) !== '#' ) {
return '';
}
try {
return decodeURIComponent( hash.slice( 1 ) );
} catch ( error ) {
return hash.slice( 1 );
}
}
function setupSingleActiveToc() {
var toc = document.querySelector( '.citizen-toc' );
var indicator = toc && toc.querySelector( '.citizen-toc-indicator' );
if ( !toc || !indicator || toc.dataset.techdocsSingleActiveToc === '1' ) {
return;
}
var entries = Array.prototype.map.call(
toc.querySelectorAll( LINK_SELECTOR ),
function ( link ) {
var target = document.getElementById( decodeFragment( link.hash ) );
var item = link.closest( '.citizen-toc-list-item' );
return target && item ? {
item: item,
link: link,
target: target
} : null;
}
).filter( Boolean );
if ( entries.length === 0 ) {
return;
}
toc.dataset.techdocsSingleActiveToc = '1';
var applying = false;
var frameId = 0;
var manualEntry = null;
function getActivationTop() {
var value = parseFloat(
window.getComputedStyle( document.documentElement ).scrollPaddingTop
);
return Number.isFinite( value ) ? value : 0;
}
function findEntryForHash() {
var id = decodeFragment( window.location.hash );
return entries.find( function ( entry ) {
return entry.target.id === id;
} ) || null;
}
function findEntryForScroll() {
var activationTop = getActivationTop();
var selected = entries[ 0 ];
entries.some( function ( entry ) {
if ( entry.target.getBoundingClientRect().top <= activationTop + 1 ) {
selected = entry;
return false;
}
return true;
} );
return selected;
}
function updateIndicator( entry ) {
var positioningParent = indicator.offsetParent || toc;
var parentRect = positioningParent.getBoundingClientRect();
var linkRect = entry.link.getBoundingClientRect();
var unitHeight = linkRect.height || entry.link.offsetHeight || 32;
var top = linkRect.top - parentRect.top + positioningParent.scrollTop;
indicator.style.setProperty( '--indicator-unit-height', unitHeight + 'px' );
indicator.style.setProperty( '--indicator-top', top + 'px' );
indicator.style.setProperty( '--indicator-scale', '1' );
}
function applyEntry( entry ) {
if ( !entry ) {
return;
}
applying = true;
var topItem = entry.item.closest( '.citizen-toc-level-1' );
entries.forEach( function ( candidate ) {
var isActive = candidate.item === entry.item;
candidate.item.classList.toggle( ACTIVE_CLASS, isActive );
candidate.item.classList.toggle(
ACTIVE_TOP_CLASS,
candidate.item === topItem
);
if ( isActive ) {
candidate.link.setAttribute( 'aria-current', 'location' );
} else {
candidate.link.removeAttribute( 'aria-current' );
}
} );
if ( topItem && !entries.some( function ( candidate ) {
return candidate.item === topItem;
} ) ) {
topItem.classList.add( ACTIVE_TOP_CLASS );
}
updateIndicator( entry );
applying = false;
}
function update() {
frameId = 0;
applyEntry( manualEntry || findEntryForScroll() );
}
function scheduleUpdate() {
if ( frameId === 0 ) {
frameId = window.requestAnimationFrame( update );
}
}
function releaseManualEntry() {
if ( manualEntry ) {
manualEntry = null;
scheduleUpdate();
}
}
toc.addEventListener( 'click', function ( event ) {
if (
event.defaultPrevented ||
event.button !== 0 ||
event.altKey ||
event.ctrlKey ||
event.metaKey ||
event.shiftKey
) {
return;
}
var link = event.target.closest( LINK_SELECTOR );
if ( !link || !toc.contains( link ) ) {
return;
}
var entry = entries.find( function ( candidate ) {
return candidate.link === link;
} );
if ( !entry ) {
return;
}
event.preventDefault();
event.stopImmediatePropagation();
manualEntry = entry;
applyEntry( entry );
var hash = link.getAttribute( 'href' );
if ( hash ) {
window.history.pushState( null, '', hash );
}
window.requestAnimationFrame( function () {
var targetTop = window.scrollY +
entry.target.getBoundingClientRect().top -
getActivationTop();
window.scrollTo( 0, Math.max( 0, targetTop ) );
} );
}, true );
window.addEventListener( 'scroll', scheduleUpdate, { passive: true } );
window.addEventListener( 'resize', scheduleUpdate );
window.addEventListener( 'wheel', releaseManualEntry, { passive: true } );
window.addEventListener( 'touchstart', releaseManualEntry, { passive: true } );
window.addEventListener( 'pointerdown', function ( event ) {
if ( !toc.contains( event.target ) ) {
releaseManualEntry();
}
}, true );
window.addEventListener( 'keydown', function ( event ) {
if ( NAVIGATION_KEYS.indexOf( event.key ) !== -1 ) {
releaseManualEntry();
}
} );
window.addEventListener( 'hashchange', function () {
manualEntry = findEntryForHash();
scheduleUpdate();
} );
var classObserver = new MutationObserver( function () {
if ( !applying ) {
scheduleUpdate();
}
} );
classObserver.observe( toc, {
attributes: true,
attributeFilter: [ 'class' ],
subtree: true
} );
scheduleUpdate();
}
mw.loader.using( 'skins.citizen.scripts' ).then( function () {
window.requestAnimationFrame( function () {
setupHomeCarousel();
setupSingleActiveToc();
} );
} );
}() );