MediaWiki:Common.js : Différence entre versions
Sauter à la navigation
Sauter à la recherche
(Page créée avec « →Tout JavaScript ici sera chargé avec chaque page accédée par n’importe quel utilisateur. : /** * Scripts placed here are loaded on both desktop and mobile views.... ») |
(Aucune différence)
|
Version du 17 octobre 2019 à 21:50
/* Tout JavaScript ici sera chargé avec chaque page accédée par n’importe quel utilisateur. */ /** * Scripts placed here are loaded on both desktop and mobile views. * * Desktop-only scripts should go in [[MediaWiki:Common.js]] * Mobile-only scripts should go in [[MediaWiki:Mobile.js]]. */ ( function() { 'use strict'; /* Variables for interface text used throughout the script, for ease of translating */ var i18n = { // Collapsible elements and page loader hideText: 'hide', showText: 'show', loadErrorTitle: 'An error occurred loading the content' }; /* Fired whenever wiki content is added. (#mw-content-text, live preview, load page, etc.) */ mw.hook( 'wikipage.content' ).add( function( $wikipageContent ) { /** * Collapsible elements * * Add the "collapsible" class to an element and the child element with class "collapsible-content" * (or everything but the header row if a table) will be hidden when the element is collapsed. * * * Add the class "collapsed" to the element to make it start out collapsed. * * Add either "collapsetoggle-left" or "collapsetoggle-inline" to the element to choose the collapse * toggle alignment (defaults to right). * * Add an ID in the format of "collapsible-<x>" to the element to make any element with the class * "collapsetoggle-custom" and a matching class in the format of "collapsible-<x>-toggle" control * the collapsing instead of the standard button. * If the custom toggle contains an element with the "jslink" class, only that will be clickable. */ ( function() { var $collapsibles = $wikipageContent.find( '.collapsible' ); if ( !$collapsibles.length ) { return; } var $toggleTemplate = $( '<span>' ).addClass( 'collapsetoggle' ).append( '[', $( '<span>' ).addClass( 'jslink' ), ']' ); $collapsibles.each( function() { var $collapsible = $( this ); if ( $collapsible.data( 'made-collapsible' ) ) { return true; } var $children = $collapsible.children(); var showText = $collapsible.data( 'expandtext' ) || i18n.showText; var hideText = $collapsible.data( 'collapsetext' ) || i18n.hideText; // If there is no content area, add it if ( !$collapsible.is( 'table' ) && !$children.filter( '.collapsible-content' ).length ) { if ( $collapsible.is( 'tr' ) ) { $children.addClass( 'collapsible-content' ); } else { $collapsible.wrapInner( '<div class="collapsible-content">' ); $children = $collapsible.children(); } } var $toggle; var id = $collapsible.attr( 'id' ); if ( id && id.match( /^collapsible-./ ) ) { $toggle = $( $wikipageContent[0].getElementsByClassName( id + '-toggle' ) ) .filter( '.collapsetoggle-custom' ).css( 'visibility', 'visible' ); } // Create and insert the toggle button if there is no custom one if ( !$toggle || !$toggle.length ) { var $toggleContainer; if ( $collapsible.is( 'table' ) ) { var $rows = $children.filter( 'thead' ).children(); if ( !$rows.length ) { $rows = $children.filter( 'tbody' ).first().children(); if ( !$rows.length ) { $rows = $children.filter( 'tr' ); } } $toggleContainer = $rows.first().children().last(); } else { $toggleContainer = $children.first(); if ( $toggleContainer.hasClass( 'collapsible-content' ) ) { $toggleContainer = $collapsible; } } $toggle = $toggleTemplate.clone(); if ( $toggleContainer !== $collapsible && ( $collapsible.hasClass( 'collapsetoggle-inline' ) || $collapsible.hasClass( 'collapse-button-none' ) ) ) { $toggleContainer.append( $toggle ); } else { $toggleContainer.prepend( $toggle ); } } var $toggleLink = $toggle.find( '.jslink' ); if ( !$toggleLink.length ) { $toggleLink = $toggle; } $toggleLink.attr( 'tabindex', 0 ).text( hideText ); // Find max toggle size, and set its min-width to it var hideWidth = $toggle.width(); $toggleLink.text( showText ); var showWidth = $toggle.width(); if ( hideWidth !== showWidth ) { $toggle.css( 'min-width', hideWidth > showWidth ? hideWidth : showWidth ); } // Set the text back to hide if it's not collapsed to begin with if ( !$collapsible.hasClass( 'collapsed' ) ) { $toggleLink.text( hideText ); } $toggleLink.on( 'click keydown', function( e ) { // Only trigger on enter press if ( e.keyCode && e.keyCode !== 13 ) { return; } // Don't toggle when clicking buttons or links inside the toggle var $target = $( e.target ); if ( $target.is( 'button' ) || $target.is( 'a' ) ) { return; } $collapsible.toggleClass( 'collapsed' ); if ( $collapsible.hasClass( 'collapsed' ) ) { $toggleLink.text( showText ); } else { $toggleLink.text( hideText ); } // Stop table sorting activating when clicking the link e.stopPropagation(); } ); $collapsible.data( 'made-collapsible', true ); } ); }() ); /** * Page loader * * Allows a page to be downloaded and displayed on demand. * Use with [[Template:LoadPage]] and [[Template:LoadBox]] */ ( function() { var $loadPage = $wikipageContent.find( '.load-page' ); if ( !$loadPage.length ) { return; } // We need the spinner to show loading is happening, but we don't want // to have a delay while the module downloads, so we'll load this now, // regardless of if something is clicked mw.loader.load( 'jquery.spinner' ); // Create button starting with hide text // Will be changed to the show text while calculating the maximum button size var $buttonTemplate = $( '<span>' ).addClass( 'mw-editsection-like load-page-button' ) .append( '[', $( '<span>' ).addClass( 'jslink' ).text( i18n.hideText ), ']' ); var extractList = function( $contentContainer, listClass ) { var $content = $contentContainer.find( '.mw-parser-output > ul > li > ul' ).children( ':not(.nbttree-inherited)' ); if ( listClass ) { $content.addClass( listClass ); } return $content; }; $loadPage.each( function() { var $body = $( this ); var page = $body.data( 'page' ); if ( !page ) { return; } var template = $body.data( 'template' ); var treeview = $body.data( 'treeview' ); var treeviewClass = $body.data( 'treeviewclass' ); var $heading; var $contentContainer; var $content; var $button = $buttonTemplate.clone(); var $buttonLink = $button.find( '.jslink' ); if ( treeview ) { $heading = $body; $contentContainer = $( '<div>' ); } else { $heading = $body.children().first(); $contentContainer = $body.find( '.load-page-content' ); } // Add the button $heading.append( $button ); // Move the edit button to the right spot $contentContainer.find( '.mw-editsection, .mw-editsection-like' ).insertAfter( $button ); // Find max button width, and set its min-width to it var hideWidth = $button.width(); $buttonLink.text( i18n.showText ); var showWidth = $button.width(); if ( hideWidth !== showWidth ) { $button.css( 'min-width', hideWidth > showWidth ? hideWidth : showWidth ); } $buttonLink.click( function() { if ( $body.hasClass( 'pageloader-contentloaded' ) ) { if ( $buttonLink.text() === i18n.showText ) { if ( treeview ) { $content.insertAfter( $body ); } else { $contentContainer.show(); } $buttonLink.text( i18n.hideText ); } else { if ( treeview ) { $content.detach(); } else { $contentContainer.hide(); } $buttonLink.text( i18n.showText ); } return; } // See if this was loaded elsewhere before making a request var gotContent; $( '.pageloader-contentloaded' ).each( function() { var $fLoader = $( this ); if ( $fLoader.data( 'page' ) === page && $fLoader.data( 'pageloader-content' ) ) { $contentContainer.html( $fLoader.data( 'pageloader-content' ) ).removeClass( 'noscript' ); mw.hook( 'wikipage.content' ).fire( $contentContainer ); if ( treeview ) { $body.find( '.noscript' ).remove(); $content = extractList( $contentContainer, treeviewClass ); $content.insertAfter( $body ); } $buttonLink.text( i18n.hideText ); $body.addClass( 'pageloader-contentloaded' ); gotContent = true; return false; } } ); if ( gotContent ) { return; } // Just in-case the spinner module is still not ready yet var $spinner = $(); mw.loader.using( 'jquery.spinner', function() { // $spinner will be false if the content somehow loaded before the module did if ( $spinner ) { $spinner = $.createSpinner().addClass( 'mw-editsection-like' ) .css( 'min-width', $button.css( 'min-width' ) ); $button.hide().after( $spinner ); } } ); var requestData = { action: 'parse', prop: 'text|modules|jsconfigvars' }; if ( template ) { requestData.page = page; } else { requestData.title = mw.config.get( 'wgPageName' ); requestData.text = '{' + '{:' + page + '}}'; } new mw.Api().get( requestData ).done( function( data ) { // Add config and modules if ( data.parse.jsconfigvars ) { mw.config.set( data.parse.jsconfigvars ); } if ( data.parse.modules ) { mw.loader.load( data.parse.modules.concat( data.parse.modulescripts, data.parse.modulestyles ) ); } var html = data.parse.text['*']; $contentContainer.html( html ).removeClass( 'noscript' ); // Resolve self-links if ( template ) { var curPage = '/' + mw.config.get( 'wgPageName' ); $contentContainer.find( 'a' ).each( function() { var $link = $( this ); if ( $link.attr( 'href' ) === curPage ) { $link.replaceWith( $( '<strong>' ).addClass( 'selflink' ).append( $link.contents() ) ); } } ); html = $contentContainer.html(); } $body.data( 'pageloader-content', html ); // Fire content hook on the new content, running all this stuff again and more :) mw.hook( 'wikipage.content' ).fire( $contentContainer ); if ( treeview ) { $body.find( '.noscript' ).remove(); $content = extractList( $contentContainer, treeviewClass ); $content.insertAfter( $body ); } $spinner.remove(); $spinner = false; $buttonLink.text( i18n.hideText ); $button.show(); $body.addClass( 'pageloader-contentloaded' ); } ).fail( function( _, error ) { $spinner.remove(); $spinner = false; $button.show(); var errorText = ''; if ( error.textStatus ) { errorText = error.textStatus; } else if ( error.error ) { errorText = error.error.info; } mw.notify( errorText, { title: i18n.loadErrorTitle, autoHide: false } ); } ); } ); } ); }() ); /** * Set minimum height for animations to prevent moving the page if the frames * differ in height */ ( function() { // Set frames to be visible for measuring height var $animated = $wikipageContent.find( '.animated' ).addClass( 'animated-visible' ); // Group frames per animation var animateds = []; $animated.each( function() { animateds.push( { $: $( this ).find( '> .animated-subframe' ).addBack() .find( '> *:not(.animated-subframe)' ), } ); } ); // Get highest frame for each animation (if heights differ) $.each( animateds, function() { var minHeight = 0, differentHeights; this.$.each( function() { var height = this.offsetHeight; differentHeights = differentHeights || minHeight && height !== minHeight; minHeight = Math.max( height, minHeight ); } ); if ( differentHeights ) { this.height = minHeight; } } ); // Set animation to be at least as tall as the tallest frame, // and set the non-active frames to be hidden again $animated.each( function( i ) { $( this ).css( 'min-height', animateds[i].height ); } ).removeClass( 'animated-visible' ); }() ); } ); /* End wiki content hook */ /* Fires when DOM is ready */ $( function() { /** * Element animator * * Cycles through a set of elements (or "frames") on a 2 second timer per frame * Add the "animated" class to the frame containing the elements to animate. * Optionally, add the "animated-active" class to the frame to display first. * Optionally, add the "animated-subframe" class to a frame, and the * "animated-active" class to a subframe within, in order to designate a set of * subframes which will only be cycled every time the parent frame is displayed. * Animations with the "animated-paused" class will be skipped each interval. * * Requires some styling from [[MediaWiki:Gadget-site-styles.css]]. */ ( function() { var $content = $( '#mw-content-text' ); var advanceFrame = function( parentElem, parentSelector ) { var curFrame = parentElem.querySelector( parentSelector + ' > .animated-active' ); $( curFrame ).removeClass( 'animated-active' ); var $nextFrame = $( curFrame && curFrame.nextElementSibling || parentElem.firstElementChild ); return $nextFrame.addClass( 'animated-active' ); }; // Set the name of the hidden property var hidden; if ( typeof document.hidden !== 'undefined' ) { hidden = 'hidden'; } else if ( typeof document.msHidden !== 'undefined' ) { hidden = 'msHidden'; } else if ( typeof document.webkitHidden !== 'undefined' ) { hidden = 'webkitHidden'; } setInterval( function() { if ( hidden && document[hidden] ) { return; } $content.find( '.animated' ).each( function() { if ( $( this ).hasClass( 'animated-paused' ) ) { return; } var $nextFrame = advanceFrame( this, '.animated' ); if ( $nextFrame.hasClass( 'animated-subframe' ) ) { advanceFrame( $nextFrame[0], '.animated-subframe' ); } } ); }, 2000 ); }() ); } ); /* End DOM ready */ }() );