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
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
// <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>