MediaWiki:Gadget-Zukunft.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
// <nowiki>
/* eslint-disable max-len */
/******************************************************************************
* Zukunft.js
* Gebündelte Anzeige aller mit {{Zukunft}} als veraltet markierten Informationen.
*
* Depends on: .voy-zukunft[data-note], .voy-zukunft[data-timestamp]
* License: CC0
* Maintainer: nw520
******************************************************************************/
mw.loader.using( 'jquery', 'mw.html', 'mw.util' ).then( function () {
mw.hook( 'wikipage.content' ).add( function () {
/**
* @constant
* @type {string}
*/
var NOTE_ATTR = 'data-note';
/**
* @constant
* @type {string}
*/
var TIMESTAMP_ATTR = 'data-timestamp';
/**
* @constant
* @type {string}
*/
var OUTDATED_CLASS = 'voy-zukunft';
/**
* Dauer in Millisekunden, für welche ein Element hervorgehoben wird, wenn ein Benutzer auf die Warnung geklickt hat.
* 0 um zu deaktivieren.
*
* @constant
* @type {number}
*/
var HIGHLIGHT_DURATION = 5000;
/**
* @class
* @param {HTMLElement} element
* @param {string} h2
* @param {string} h3
* @param {string} h4
* @param {string} h5
* @param {string} h6
*/
var SectionedMatch = function ( element, h2, h3, h4, h5, h6 ) {
this.element = element;
this.h2 = h2;
this.h3 = h3;
this.h4 = h4;
this.h5 = h5;
this.h6 = h6;
return this;
};
/**
* Gibt einen Brotkrümelpfad für die Abschnitte aus.
* @return {string}
*/
SectionedMatch.prototype.getSection = function () {
if ( this.h2 === null && this.h3 === null && this.h4 === null && this.h5 === null && this.h6 === null ) {
return null;
} else {
var out = [ this.h2, this.h3, this.h4, this.h5, this.h6 ];
out = out.filter( function ( item ) {
return item !== null;
} );
return out.join( ' / ' );
}
};
/**
* @class
* @param {SectionedMatch} match Zugehöriges Element, für das die Warnung gilt.
* @param {number} reason Begründung der Fehlermeldung, siehe {@link WarningItem.reasons}.
* @param {string} note Vom Nutzer hinterlegter Hinweis.
*/
var WarningItem = function ( match, reason, note ) {
this.match = match;
this.reason = reason;
this.note = note || null;
return this;
};
/**
* @enum {number}
*/
WarningItem.reasons = {
UNSPECIFIED: 0,
OUTDATED: 1,
ERRORNEOUS_TIMESTAMP: 2
};
function main() {
mw.util.addCSS( '.voy-zukunft { background-color: transparent; transition: background .4s ease-in-out; } .voy-zukunft .mw-redirect { background: none; } .voy-zukunft-highlight { animation: voy-zukunft-pulse .7s infinite alternate; } .voy-zukunft-warnbox { background: #f9f9f9; border: 1px solid #f66; border-left: 10px solid #f66; box-sizing: border-box; margin: .5em 0; overflow: hidden; padding: .5em; text-align: left; width: auto; } @keyframes voy-zukunft-pulse { 0% { background: transparent; } 100% { background: lightsalmon; } }' );
var warnings = filter( findAndDetermineSection( '.' + OUTDATED_CLASS + '[' + TIMESTAMP_ATTR + ']', document.getElementById( 'bodyContent' ) ) );
if ( warnings.length === 0 ) {
return;
}
var container = document.createElement( 'div' );
container.classList.add( 'voy-zukunft-warnbox' );
container.innerHTML = '<p>In diesem Reiseführer finden sich einige veraltete Angaben. Hilf mit, indem du sie aktualisierst:</p>';
var matchesUl = document.createElement( 'ul' );
container.appendChild( matchesUl );
generateWarnings( warnings ).forEach( function ( match ) {
matchesUl.appendChild( match );
} );
document.getElementById( 'bodyContent' ).insertAdjacentElement( 'afterbegin', container );
}
/**
* Überprüft, ob die Elemente tatsächlich veraltet sind anhand des in {@link TIMESTAMP_ATTR} definierten Attributs und gibt eine Liste an veralteten Elementen aus. Es wird das Format YYYY, YYYY-MM, oder YYYY-MM-DD erwartet.
*
* @param {Array<SectionedMatch>} matches
* @return {Array<WarningItem>} Liste an {@link WarningItem} mit veralteten Werten.
*/
function filter( matches ) {
var out = [];
matches.forEach( function ( match ) {
try {
var splitTimestamp = match.element.getAttribute( TIMESTAMP_ATTR ).split( '-' );
var date = new Date();
if ( splitTimestamp.length >= 1 && parseInt( splitTimestamp[ 0 ] ) < date.getFullYear() ) {
out.push( new WarningItem( match, WarningItem.reasons.OUTDATED, match.element.getAttribute( NOTE_ATTR ) ) );
} else if ( parseInt( splitTimestamp[ 0 ] ) === date.getFullYear() ) {
if ( splitTimestamp.length >= 2 && parseInt( splitTimestamp[ 1 ] ) < date.getMonth() + 1 ) {
out.push( new WarningItem( match, WarningItem.reasons.OUTDATED, match.element.getAttribute( NOTE_ATTR ) ) );
} else if ( parseInt( splitTimestamp[ 1 ] ) === date.getMonth() + 1 ) {
if ( splitTimestamp.length >= 3 && parseInt( splitTimestamp[ 2 ] ) < date.getDate() ) {
out.push( new WarningItem( match, WarningItem.reasons.OUTDATED, match.element.getAttribute( NOTE_ATTR ) ) );
}
}
}
} catch ( e ) {
mw.log.warn( e );
out.push( new WarningItem( match, WarningItem.reasons.ERRORNEOUS_TIMESTAMP, $( match.element ).attr( NOTE_ATTR ) ) );
}
} );
return out;
}
/**
* @param {string|Array<string>} selectors Selektoren, mit den Elemente gefunden werden sollen.
* @param {HTMLElement} parent Elternelement, in dem Selektoren gefunden werden sollen.
* @return {Array<SectionedMatch>}
*/
function findAndDetermineSection( selectors, parent ) {
var castedSelectors = typeof selectors === 'string' ? selectors : selectors.join( ',' );
var itemsAndSections = parent.querySelectorAll( 'h2,h3,h4,h5,h6,' + selectors );
var sections = {
H6: null,
H5: null,
H4: null,
H3: null,
H2: null
};
var sectionOrder = [ 'H6', 'H5', 'H4', 'H3', 'H2' ];
var items = [];
itemsAndSections.forEach( function ( itemOrSection ) {
if ( itemOrSection.matches( castedSelectors ) ) { // Item
items.push( new SectionedMatch( itemOrSection, sections.H2, sections.H3, sections.H4, sections.H5, sections.H6 ) );
} else { // Section
if ( itemOrSection.closest( '#toc' ) === null ) {
for ( var i = 0; i < sectionOrder.length; i++ ) {
var sectionTag = sectionOrder[ i ];
if ( itemOrSection.tagName === sectionTag && itemOrSection.querySelectorAll( '.mw-headline' ).length > 0 ) { // Section
// Set this section
sections[ sectionTag ] = itemOrSection.querySelector( '.mw-headline' ).textContent;
break;
} else {
sections[ sectionTag ] = null;
}
}
}
}
} );
return items;
}
/**
* Erzeugt aus einer Liste an {@link WarningItem} als Liste von li-Elementen (HTMLElement).
*
* @param {Array<WarningItem>} warnings Liste an veralteten {@link WarningItem}.
* @return {Array<HTMLElement>} Text in Wikimedia-Markup für Ausgabe der Warnungen.
*/
function generateWarnings( warnings ) {
return warnings.map( function ( warning ) {
var section = warning.match.getSection();
var location = '<a href="#zukunft">' + ( section === null ? 'In der Einleitung' : 'Im Abschnitt "' + mw.html.escape( section ) + '"' ) + '</a>';
var note = warning.note !== null ? ': ' + mw.html.escape( warning.note ) : '';
var matchLi = document.createElement( 'li' );
if ( warning.reason === WarningItem.reasons.OUTDATED ) {
matchLi.innerHTML = '<li>' + location + ' ist eine potentiell veraltete Angabe' + note + '</li>';
} else {
matchLi.innerHTML = '<li>' + location + ' ist eine fehlerhafte Angabe' + note + '</li>';
}
matchLi.querySelector( 'a' ).addEventListener( 'click', function ( e ) {
e.preventDefault();
// eslint-disable-next-line no-jquery/no-global-selector
$( 'html, body' ).animate( {
scrollTop: warning.match.element.offsetTop
}, 300 );
highlightElement( warning.match.element );
} );
return matchLi;
} );
}
/**
* Hebt das gegebene Element {@link HIGHLIGHT_DURATION} lang hervor.
*
* @param {HTMLElement} element Element, welches hervorgehoben werden soll.
*/
function highlightElement( element ) {
if ( HIGHLIGHT_DURATION > 0 ) {
element.classList.add( 'voy-zukunft-highlight' );
setTimeout( function () {
element.classList.remove( 'voy-zukunft-highlight' );
}, HIGHLIGHT_DURATION );
}
}
main();
} );
} );
// </nowiki>