Benutzer:Nw520/SisterWeblinks.js
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Internet Explorer/Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
- Opera: Strg+F5
( () => {
const strings = {
'voy-sisterweblinks-sisterdescription-commons': {
de: 'Sammlung von Bildern, Videos und Audiodateien'
},
'voy-sisterweblinks-sisterdescription-wikinews': {
de: 'Nachrichten'
},
'voy-sisterweblinks-sisterdescription-wikipedia': {
de: 'Enzyklopädie'
}
};
function main() {
mw.hook( 'wikipage.content' ).add( ( $el ) => {
const parserOutput = $el[ 0 ].querySelector( '.mw-parser-output' );
if ( parserOutput === null ) {
return;
}
let insertPoint = findWeblinksInsertPoint( parserOutput );
if ( insertPoint.insertPoint === null ) {
mw.log.warn( '[SisterWeblinks] Failed to find insert point.' );
return;
}
// Create list after insert point, if needed
if ( !insertPoint.isList ) {
const list = document.createElement( 'ul' );
insertPoint.insertPoint.insertAdjacentElement( 'afterend', list );
insertPoint.insertPoint = list;
insertPoint.location = 'beforeend';
}
const sitelinks = getSitelinks();
if ( sitelinks === null ) {
throw new Error( '[SisterWeblinks] Failed to get insert point.' );
}
setupStrings();
insertSitelinks( sitelinks, insertPoint.insertPoint, insertPoint.location );
} );
}
/**
* Creates a list item for a sitelink.
*
* @param {Object} sitelink
* @return {HTMLElement}
*/
function createItem( sitelink ) {
let projectMeta = getProjectMeta( sitelink.project );
if ( projectMeta === null ) {
return null;
}
const li = document.createElement( 'li' );
const a = document.createElement( 'a' );
a.href = sitelink.url;
if ( ( projectMeta.logo ?? null ) !== null ) {
const logo = document.createElement( 'img' );
logo.height = 16;
logo.width = 16;
logo.src = projectMeta.logo;
a.appendChild( logo );
a.appendChild( document.createTextNode( ' ' ) );
}
a.appendChild( document.createTextNode( projectMeta.name ) );
li.appendChild( a );
if ( ( projectMeta.description ?? null ) !== null ) {
li.appendChild( document.createTextNode( ` – ${projectMeta.description}` ) );
}
return li;
}
/**
* Scans an article for the #Weblinks-section.
*
* @param {HTMLElement} parserOutput
* @return {Object}
*/
function findWeblinksInsertPoint( parserOutput ) {
let weblinksSectionElement = null;
let weblinksLastListElement = null;
let weblinksLastElement = null;
for ( const el of [ ...parserOutput.querySelectorAll( '.mw-parser-output > *' ) ] ) {
if ( el.matches( 'h2' ) ) {
if ( weblinksSectionElement !== null ) {
// We already found #Weblinks.
// Since this section is on the same hierarchical level, #Weblinks has just concluded.
// Therefore, we can terminate.
break;
}
if ( el.querySelector( '.mw-headline' ) !== null && el.querySelector( '.mw-headline' ).textContent === 'Weblinks' ) {
// We found #Weblinks.
weblinksSectionElement = el;
} else {
// This section is not #Weblinks.
continue;
}
}
if ( weblinksSectionElement === null ) {
// #Weblinks was not found yet
continue;
}
if ( el.matches( 'ul' ) ) {
weblinksLastListElement = el;
}
if ( el.matches( 'div' ) || el.matches( 'table' ) ) {
// We don't want to insert below divs or tables, therefore terminate
break;
}
weblinksLastElement = el;
}
return {
insertPoint: weblinksLastListElement?.lastChild ?? weblinksLastElement,
isList: weblinksLastListElement !== null,
location: 'afterend',
sectionElement: weblinksSectionElement,
lastListElement: weblinksLastListElement,
lastElement: weblinksLastElement
};
}
/**
* Returns meta data for sister projects.
*
* @param {string} project
* @return {Object}
*/
function getProjectMeta( project ) {
switch ( project ) {
case 'commons':
return {
description: mw.msg( 'voy-sisterweblinks-sisterdescription-commons' ),
logo: 'https://commons.wikimedia.org/wiki/Special:FilePath/File:Commons-logo.svg',
name: 'Commons'
};
case 'wikinews':
return {
description: mw.msg( 'voy-sisterweblinks-sisterdescription-wikinews' ),
logo: 'https://commons.wikimedia.org/wiki/Special:FilePath/File:Wikinews-logo.svg',
name: 'Wikinews'
};
case 'wikipedia':
return {
description: mw.msg( 'voy-sisterweblinks-sisterdescription-wikipedia' ),
logo: 'https://commons.wikimedia.org/wiki/Special:FilePath/File:Wikipedia-logo-v2.svg',
name: 'Wikipedia'
};
default:
mw.log.error( `[SisterWeblinks] Unknown sister project ${sitelink.project}` );
return null;
}
}
/**
* Extracts the sister project's name from a portlet.
* @param {HTMLElement} el
* @return {string}
*/
function getProjectName( el ) {
if ( el.matches( '.wb-otherproject-commons' ) ) {
return 'commons';
} else if ( el.matches( '.wb-otherproject-wikinews' ) ) {
return 'wikinews';
} else if ( el.matches( '.wb-otherproject-wikipedia' ) ) {
return 'wikipedia';
} else if ( el.matches( '.wb-otherproject-wikiquote' ) ) {
return 'wikiquote';
} else if ( el.matches( '.wb-otherproject-wikisource' ) ) {
return 'wikisource';
} else {
return null;
}
}
/**
* Returns a list of sitelinks.
*
* Tested for monobook, timeless, vector-{2020,legacy}.
* Does not work for Minerva.
*
* @return {Object}
*/
function getSitelinks() {
const links = document.querySelectorAll( '.wb-otherproject-link' );
return [ ...links ].map( ( el ) => {
return {
project: getProjectName( el ),
url: el.querySelector( 'a' ).href
};
} );
}
/**
* Generates and inserts a sister project's sitelink.
*
* @param {Object[]} sitelinks
* @param {HTMLElement} insertPoint
* @param {string} location
*/
function insertSitelinks( sitelinks, insertPoint, location ) {
let insertAfter = insertPoint;
let insertLocation = location;
const sitelinksOrder = {
commons: 1,
wikinews: 2,
wikipedia: 0
};
// Filter and sort
const orderedSitelinks = sitelinks.filter( ( sitelink ) => {
return sitelinksOrder[ sitelink.project ] !== undefined;
} ).sort( ( left, right ) => {
const l = sitelinksOrder[ left.project ];
const r = sitelinksOrder[ right.project ];
if ( l < r ) {
return -1;
} else if ( l > r ) {
return 1;
} else {
return 0;
}
} );
for ( const sitelink of orderedSitelinks ) {
const li = createItem( sitelink );
if ( li === null ) {
continue;
}
insertAfter.insertAdjacentElement( location, li );
insertAfter = li;
location = 'afterend';
}
}
function setupStrings() {
const lang = mw.config.get( 'wgUserLanguage' );
mw.messages.set( Object.fromEntries( Object.keys( strings ).map( ( stringKey ) => {
return [ stringKey, strings[ stringKey ][ lang ] ?? strings[ stringKey ].en ];
} ) ) );
}
main();
} )();