MediaWiki:Common.js

De Hispanopedia

Nota: Después de publicar, quizás necesite actualizar la caché de su navegador para ver los cambios.

  • Firefox/Safari: Mantenga presionada la tecla Shift mientras pulsa el botón Actualizar, o presiona Ctrl+F5 o Ctrl+R (⌘+R en Mac)
  • Google Chrome: presione Ctrl+Shift+R (⌘+Shift+R en Mac)
  • Edge: mantenga presionada Ctrl mientras pulsa Actualizar, o presione Ctrl+F5
// MediaWiki:Common.js
// Optimized for MediaWiki 1.43.3+ with Timeless skin exclusively.
// Features: skin redirects, URL parameter loading, collapsible templates, progressive image loading,
// interwiki highlighting, table sorting, map integration.
// Dependencies: mediawiki.util (always), jquery.tablesorter (conditional).
//
// Setup:
// 1. Host these pages in MediaWiki namespace:
//    - MediaWiki:Wdsearch.js, MediaWiki:Wikiminiatlas.js, MediaWiki:OSM.js
//    - MediaWiki:Edittools.js, MediaWiki:Common.js/seguimiento.js
// 2. Upload Erioll_world.svg to /images/Erioll_world.svg
// 3. Define message pages: MediaWiki:Commonjs-collapse, MediaWiki:Commonjs-expand,
//    MediaWiki:Commonjs-invalid-withcss, MediaWiki:Commonjs-invalid-withcss-title,
//    MediaWiki:Commonjs-invalid-withjs, MediaWiki:Commonjs-invalid-withjs-title,
//    MediaWiki:Commonjs-featured-article, MediaWiki:Commonjs-good-article,
//    MediaWiki:Commonjs-wikipedia-list, MediaWiki:Commonjs-wikipedia-list-tooltip
// 4. Add to MediaWiki:Common.css:
//    .overflow-x-auto { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 1em 0; }
//    #p-lang li.destacado::before { content: "★ "; color: #ffd700; }
//    #p-lang li.bueno::before { content: "◆ "; color: #a3d977; }

( function ( $, mw ) {
	'use strict';

	// ===== IMMEDIATE EXECUTION =====
	$( function () {
		// Closeable messages
		var $cierraPadre = $( '#cierraPadre' );
		if ( $cierraPadre.length ) {
			$cierraPadre.css( 'cursor', 'pointer' ).on( 'click', function ( e ) {
				e.preventDefault();
				$( this ).parent().hide();
			} );
		}
	} );

	// ===== DEFERRED INITIALIZATION =====
	var deferredInit = window.requestIdleCallback || function ( cb ) {
		setTimeout( cb, 1 );
	};

	deferredInit( function () {
		mw.loader.using( 'mediawiki.util' ).then( function () {
			// Cache frequently accessed config
			var config = mw.config.get( [
				'wgArticleId',
				'wgNamespaceNumber',
				'wgPageName',
				'wgAction',
				'wgCanonicalSpecialPageName',
				'wgScriptPath'
			] );

			// ===== USER SKIN REDIRECT =====
			// Redirect User:Name/skin.js → User:Name/timeless.js
			if ( config.wgArticleId === 0 && config.wgNamespaceNumber === 2 ) {
				var titleParts = config.wgPageName.split( '/' );
				if ( titleParts.length === 2 ) {
					var targetPage = titleParts[0] + '/timeless';
					if ( titleParts[1] === 'skin.js' ) {
						window.location.replace( mw.util.getUrl( targetPage + '.js' ) );
						return;
					} else if ( titleParts[1] === 'skin.css' ) {
						window.location.replace( mw.util.getUrl( targetPage + '.css' ) );
						return;
					}
				}
			}

			// ===== URL PARAMETER LOADING =====
			var extraCSS = mw.util.getParamValue( 'withCSS' ),
				extraJS = mw.util.getParamValue( 'withJS' );

			if ( extraCSS ) {
				if ( /^MediaWiki:[a-zA-Z0-9_-]+\.css$/.test( extraCSS ) ) {
					mw.loader.load( mw.util.getUrl( extraCSS, { action: 'raw', ctype: 'text/css' } ), 'text/css' );
				} else {
					mw.notify(
						mw.msg( 'commonjs-invalid-withcss' ) || 'Solo se pueden cargar páginas del espacio de nombres MediaWiki.',
						{ title: mw.msg( 'commonjs-invalid-withcss-title' ) || 'Valor withCSS no permitido' }
					);
				}
			}

			if ( extraJS ) {
				if ( /^MediaWiki:[a-zA-Z0-9_-]+\.js$/.test( extraJS ) ) {
					mw.loader.load( mw.util.getUrl( extraJS, { action: 'raw', ctype: 'text/javascript' } ) );
				} else {
					mw.notify(
						mw.msg( 'commonjs-invalid-withjs' ) || 'Solo se pueden cargar páginas del espacio de nombres MediaWiki.',
						{ title: mw.msg( 'commonjs-invalid-withjs-title' ) || 'Valor withJS no permitido' }
					);
				}
			}

			// ===== CONDITIONAL PAGE-SPECIFIC SCRIPTS =====
			
			// Wikidata search - Search pages or 404s
			if ( config.wgCanonicalSpecialPageName === 'Search' ||
				 ( config.wgArticleId === 0 && !config.wgCanonicalSpecialPageName ) ) {
				mw.loader.load( mw.util.getUrl( 'MediaWiki:Wdsearch.js', { action: 'raw', ctype: 'text/javascript' } ) );
			}

			// Edit functionality
			if ( config.wgAction === 'edit' || config.wgAction === 'submit' ) {
				// Fix undo edit summary
				if ( window.location.search.indexOf( 'undo=' ) !== -1 ) {
					$( 'input[name="wpAutoSummary"]' ).val( '1' );
				}
				// Load edit tools
				mw.loader.load( mw.util.getUrl( 'MediaWiki:Edittools.js', { action: 'raw', ctype: 'text/javascript' } ) );
			}

			// Seguimiento page
			if ( config.wgPageName === 'Especial:Seguimiento' ) {
				mw.loader.load( mw.util.getUrl( 'MediaWiki:Common.js/seguimiento.js', { action: 'raw', ctype: 'text/javascript' } ) );
			}

			// Main page - Add interwiki completelist link
			if ( config.wgPageName === 'Hispanopedia:Portada' ) {
				mw.util.addPortletLink(
					'p-lang',
					'//es.wikipedia.org/wiki/Anexo:Wikipedias',
					mw.msg( 'commonjs-wikipedia-list' ) || 'Lista completa',
					'interwiki-completelist',
					mw.msg( 'commonjs-wikipedia-list-tooltip' ) || 'Lista completa de Wikipedias'
				);
			}

			// Hide edit summary for specific preload pages
			var preloadValue = mw.util.getParamValue( 'preload' );
			if ( preloadValue && [
				'Hispanopedia:Tablón_de_anuncios_de_los_bibliotecarios/Portal/Plantillas/Fusión_de_historiales/precarga',
				'Hispanopedia:Tablón_de_anuncios_de_los_bibliotecarios/Portal/Plantillas/Permisos/precarga',
				'Hispanopedia:Bot/Solicitudes/Precarga'
			].indexOf( preloadValue ) !== -1 ) {
				$( '#wpSummary, #wpSummaryLabel' ).hide();
			}

			// ===== LAZY MAP SCRIPT LOADING =====
			deferredInit( function () {
				if ( $( '.coordinates, [class*="coord"], #coordinates' ).length ) {
					window.wma_settings = {
						buttonImage: config.wgScriptPath + '/images/Erioll_world.svg'
					};
					mw.loader.load( mw.util.getUrl( 'MediaWiki:Wikiminiatlas.js', { action: 'raw', ctype: 'text/javascript' } ) );
					
					mw.config.set( { osm_proj_map: 'mapa', osm_proj_lang: 'es' } );
					mw.loader.load( mw.util.getUrl( 'MediaWiki:OSM.js', { action: 'raw', ctype: 'text/javascript' } ) );
				}
			} );

			// ===== CONTENT ENHANCEMENT FUNCTIONS =====

			// Collapsible templates
			function initCollapsibleTemplates( $content ) {
				var collapseText = mw.msg( 'commonjs-collapse' ) || 'ocultar',
					expandText = mw.msg( 'commonjs-expand' ) || 'mostrar',
					hideLabel = '[' + collapseText + ']',
					showLabel = '[' + expandText + ']';

				var indexNavigationBar = 0;
				$content.find( 'div.NavFrame' ).each( function () {
					indexNavigationBar++;
					var $frame = $( this ),
						isCollapsed = $frame.hasClass( 'collapsed' ) || $frame.find( '.NavPic, .NavContent' ).is( ':hidden' ),
						frameId = 'NavFrame' + indexNavigationBar,
						toggleId = 'NavToggle' + indexNavigationBar;

					var $toggle = $( '<a>' )
						.addClass( 'NavToggle' )
						.attr( { id: toggleId, href: '#' } )
						.text( isCollapsed ? showLabel : hideLabel )
						.on( 'click', function ( e ) {
							e.preventDefault();
							var $tgl = $( '#' + toggleId ),
								$frm = $( '#' + frameId );
							if ( $tgl.text() === hideLabel ) {
								$frm.find( '.NavContent, .NavPic' ).hide();
								$tgl.text( showLabel );
							} else {
								$frm.find( '.NavContent, .NavPic' ).show();
								$tgl.text( hideLabel );
							}
						} );

					$frame.attr( 'id', frameId )
						.find( '.NavHead' ).first().append( $toggle );

					if ( isCollapsed ) {
						$frame.find( '.NavPic, .NavContent' ).hide();
					}
				} );
			}

			// Progressive image loading
			function initProgressiveImageLoading( $content ) {
				var $images = $content.find( 'img[data-src]' );
				if ( !$images.length ) return;

				if ( !( 'IntersectionObserver' in window ) ) {
					// Fallback: load immediately
					$images.each( function () {
						var $img = $( this );
						$img.attr( 'src', $img.data( 'src' ) )
							.removeAttr( 'data-src' )
							.addClass( 'loaded' );
					} );
					return;
				}

				// Use IntersectionObserver for lazy loading
				var imageObserver = new IntersectionObserver( function ( entries ) {
					entries.forEach( function ( entry ) {
						if ( entry.isIntersecting ) {
							var $img = $( entry.target ),
								dataSrc = $img.data( 'src' );
							if ( dataSrc ) {
								$img.attr( 'src', dataSrc )
									.removeAttr( 'data-src' )
									.addClass( 'loaded' );
							}
							imageObserver.unobserve( entry.target );
						}
					} );
				}, {
					rootMargin: '100px 0px',
					threshold: 0.01
				} );

				$images.each( function () {
					imageObserver.observe( this );
				} );
			}

			// Wrap wide tables for horizontal scrolling
			function wrapWideTables( $content ) {
				var windowWidth = $( window ).width(),
					maxWidth = windowWidth * 0.95;

				$content.find( 'table.wikitable' ).each( function () {
					var $table = $( this );
					// Skip if already wrapped or marked as ok
					if ( $table.hasClass( 'overflow-ok' ) || $table.parent().hasClass( 'overflow-x-auto' ) ) {
						return;
					}
					if ( this.offsetWidth > maxWidth ) {
						$table.wrap( '<div class="overflow-x-auto"></div>' );
					}
				} );
			}

			// Highlight featured/good articles in Timeless sidebar
			function highlightInterwikiLinks() {
				var $langLinks = $( '#p-lang li' );
				if ( !$langLinks.length ) return;

				$langLinks.each( function () {
					var $li = $( this ),
						className = $li.attr( 'class' );
					if ( !className ) return;

					var match = className.match( /interwiki-([\w-]+)/ );
					if ( !match ) return;

					var baseClass = match[0];
					// Check for featured article marker
					if ( $( '#' + baseClass + '-fa' ).length && !$li.hasClass( 'badge-featuredarticle' ) ) {
						$li.addClass( 'destacado' )
							.attr( 'title', mw.msg( 'commonjs-featured-article' ) || 
								'Éste es un artículo destacado de Hispanopedia.' );
					}
					// Check for good article marker
					else if ( $( '#' + baseClass + '-ga' ).length && !$li.hasClass( 'badge-goodarticle' ) ) {
						$li.addClass( 'bueno' )
							.attr( 'title', mw.msg( 'commonjs-good-article' ) || 
								'Éste es un artículo bueno de Hispanopedia.' );
					}
				} );
			}

			// Add new section link to discussion pages
			function addNewSectionLink() {
				var $newSectionLink = $( '#ca-addsection a' );
				if ( !$newSectionLink.length ) return;

				var $link = $newSectionLink.clone()
					.removeAttr( 'accesskey id' )
					.removeClass()
					.attr( 'title', function ( i, oldTitle ) {
						return oldTitle ? oldTitle.replace( /\s*\[.*?\]\s*$/g, '' ) : '';
					} )
					.css( 'text-transform', 'lowercase' );

				// Remove empty spans
				$link.find( 'span' ).filter( function () {
					return !$.trim( $( this ).text() );
				} ).remove();

				// Add to last edit section
				var $lastEditSection = $( 'span.mw-editsection:last a:last' );
				if ( $lastEditSection.length ) {
					$lastEditSection.after( ' · ', $link );
				}
			}

			// Main content enhancement function
			function initContentEnhancements( $content ) {
				$content = $content && $content.length ? $content : $( '#mw-content-text' );
				var ns = config.wgNamespaceNumber;

				// Collapsible templates - all namespaces
				initCollapsibleTemplates( $content );

				// Image loading and table wrapping - main/project namespaces only
				if ( ns === 0 || ns === 104 ) {
					initProgressiveImageLoading( $content );
					wrapWideTables( $content );
				}
			}

			// Global enhancements (run once per page load)
			function initGlobalEnhancements() {
				highlightInterwikiLinks();
				addNewSectionLink();
			}

			// ===== TABLE SORTING =====
			// Load jquery.tablesorter only if sortable tables exist
			var $sortableTables = $( 'table.sortable' );
			if ( $sortableTables.length ) {
				mw.loader.using( 'jquery.tablesorter' ).then( function () {
					var ts = $.tablesorter;

					// Spanish month names
					var monthNames = {
						enero: 1, febrero: 2, marzo: 3, abril: 4, mayo: 5, junio: 6,
						julio: 7, agosto: 8, septiembre: 9, octubre: 10, noviembre: 11, diciembre: 12,
						ene: 1, feb: 2, mar: 3, abr: 4, may: 5, jun: 6,
						jul: 7, ago: 8, sep: 9, oct: 10, nov: 11, dic: 12
					};

					ts.monthNames = monthNames;

					// Spanish date pattern: "1 de enero de 2024"
					ts.dateRegexCustom = /^(\d{1,2})\s+de\s+(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene|feb|mar|abr|may|jun|jul|ago|sep|oct|nov|dic)\s+de\s+(\d{2,4})$/i;

					// Add custom date parser
					ts.addParser( {
						id: 'dateCustom',
						is: function ( s ) {
							return ts.dateRegexCustom.test( $.trim( s ) );
						},
						format: function ( s ) {
							var cleaned = $.trim( s.toLowerCase() ),
								match = cleaned.match( ts.dateRegexCustom );

							if ( !match ) return 99999999;

							var day = match[1],
								monthName = match[2],
								year = parseInt( match[3], 10 ),
								month = monthNames[monthName];

							if ( !month ) return 99999999;

							// Pad day and month
							day = day.length === 1 ? '0' + day : day;
							month = month < 10 ? '0' + month : '' + month;

							// Handle 2-digit years
							if ( year < 100 ) {
								year = year < 30 ? 2000 + year : 1900 + year;
							}

							// Ensure 4-digit year
							var yearStr = String( year );
							while ( yearStr.length < 4 ) {
								yearStr = '0' + yearStr;
							}

							return parseInt( yearStr + month + day, 10 );
						},
						type: 'numeric'
					} );

					// Custom collation for Spanish characters
					var accentMap = {
						á: 'a', à: 'a', â: 'a', ä: 'a', ã: 'a', ā: 'a', ă: 'a', å: 'a', ą: 'a',
						æ: 'ae',
						ć: 'c', ĉ: 'c', č: 'c', ç: 'c',
						ď: 'd', : 'd', đ: 'd', ð: 'd',
						é: 'e', è: 'e', ê: 'e', ë: 'e', : 'e', ě: 'e', ē: 'e', ĕ: 'e', ę: 'e',
						ĝ: 'g', : 'g', ğ: 'g', ģ: 'g', ǥ: 'g',
						ĥ: 'h', : 'h', : 'h', ħ: 'h',
						í: 'i', ì: 'i', î: 'i', ï: 'i', ĩ: 'i', ī: 'i', ĭ: 'i', į: 'i', ı: 'i',
						ĵ: 'j',
						ķ: 'k',
						ĺ: 'l', ľ: 'l', ļ: 'l', ł: 'l',
						ń: 'n', ň: 'n', ņ: 'n',
						ñ: 'n~',
						ó: 'o', ò: 'o', ô: 'o', ö: 'o', õ: 'o', ō: 'o', ŏ: 'o', ǫ: 'o', ő: 'o', ø: 'o',
						œ: 'oe',
						ŕ: 'r', ř: 'r', ŗ: 'r',
						ś: 's', ŝ: 's', š: 's', ş: 's',
						ß: 'ss',
						ť: 't', ţ: 't', ŧ: 't',
						ú: 'u', ù: 'u', û: 'u', ü: 'u', ũ: 'u', ū: 'u', ŭ: 'u', ů: 'u', ų: 'u', ű: 'u',
						: 'v',
						ŵ: 'w', : 'w',
						: 'x',
						ý: 'y', ŷ: 'y', ÿ: 'y', : 'y',
						ź: 'z', : 'z', ž: 'z', ƶ: 'z'
					};

					mw.config.set( 'tableSorterCollation', accentMap );
				} );
			}

			// ===== INITIALIZATION =====
			// Register hook for dynamic content changes (AJAX loads, VisualEditor, etc.)
			mw.hook( 'wikipage.content' ).add( initContentEnhancements );

			// Run on initial page load
			initContentEnhancements();
			initGlobalEnhancements();
		} );
	} );

}( jQuery, mediaWiki ) );