MediaWiki:Gadget-problemy-w-odn.js

Uwaga: aby zobaczyć zmiany po opublikowaniu, może zajść potrzeba wyczyszczenia pamięci podręcznej przeglądarki.

  • Firefox / Safari: Przytrzymaj Shift podczas klikania Odśwież bieżącą stronę, lub naciśnij klawisze Ctrl+F5, lub Ctrl+R (⌘-R na komputerze Mac)
  • Google Chrome: Naciśnij Ctrl-Shift-R (⌘-Shift-R na komputerze Mac)
  • Edge: Przytrzymaj Ctrl, jednocześnie klikając Odśwież, lub naciśnij klawisze Ctrl+F5.
  • Opera: Naciśnij klawisze Ctrl+F5.
/**
 * <nowiki>
 * Skrypt zaznacza linki do kotwic, które nie istnieją na aktualnie odwiedzanej stronie.
 * Zaznacza również elementy bibliografii, które nie zostały podlinkowane z użyciem {{odn}}.
 * Nie sprawdza linków do innych stron.
 * 
 * @author [[w:pl:User:Msz2001]]
 */
$(function(){

    /**
     * Returns an URL without the fragment part.
     * @param {string} url The URL
     * @returns {string}
     */
    function stripFragmentFromUrl(url){
        if(url.indexOf('#') > -1){
            url = url.substring(0, url.indexOf('#'));
        }
        return url;
    }

    /**
     * Normalizes an element id to be safe for comparison with fragment taken from URL.
     * @param {string} id The element's id
     * @returns {string}
     */
    function normalizeId(id){
        return '#' + id.replace(/%25/g, '%')
    }

    /**
     * Adds a passed string to the element's title
     * @param {HTMLElement} element An element to add to the title
     * @param {string} newTitle A new portion of the title
     */
    function addToTitle(element, newTitle){
        if(element.title){
            element.title += ' – ' + newTitle;
        } else {
            element.title = newTitle;
        }
    }

    /**
     * Returns a list of anchor ids that are present on the page.
     * @returns {string[]}
     */
    function listValidAnchorTargets(){
        var elementsWithId = Array.from(document.querySelectorAll('.mw-parser-output [id]'));
        var ids = elementsWithId.map(function(element){
            // Prepended with hash to match HTMLAnchorElement.hash property
            // Sometimes MediaWiki escapes % to %25 in id, so we need to replace it back
            return normalizeId(element.id);
        });

        return ids;
    }

    /**
     * Returns a list of links that point to the current page.
     * @returns {HTMLAnchorElement[]}
     */
    function listLinksToSelf(){
        var currentUrl = stripFragmentFromUrl(window.location.href);
    
        var links = Array.from(document.querySelectorAll('.mw-parser-output a[href^="#"]'));
        links = links.filter(function(link){
            return stripFragmentFromUrl(link.href) === currentUrl;
        });

        return links;
    }

    /**
     * Iterates over the links and checks if the target anchor exists on the page.
     * @param {HTMLAnchorElement[]} links The links to check
     * @param {string[]} validAnchors The valid target anchor ids
     */
    function markInvalidLinks(links, validAnchors){
        links.forEach(function(link){
            if(link.hash === '') return;

            var target = decodeURIComponent(link.hash);
            if(validAnchors.indexOf(target) === -1){
                link.classList.add('missing-anchor');
                addToTitle(link, 'Link do nieistniejącej kotwicy');
            }
        });
    }

    /**
     * Returns a list of citations on the page
     * @returns {HTMLElement[]}
     */
    function listCitationsThatShouldBeLinked(){
        // Include all the citations with an id
        var include = Array.from(document.querySelectorAll('.mw-parser-output cite.citation[id]'));
        // But exclude those in the reference list as they are already linked from the content
        var exclude = Array.from(document.querySelectorAll('.mw-parser-output .references cite.citation[id]'));
        
        return include.filter(function(citation){
            return !exclude.includes(citation);
        });
    }

    /**
     * Marks elements that are not linked to (using #anchor links)
     * @param {HTMLElement[]} elements The elements to check
     * @param {HTMLAnchorElement[]} links The links to check
     */
    function markElementsWithNoLinkTo(elements, links){
        elements.forEach(function(element){
            var id = normalizeId(element.id);

            var linkExists = links.some(function(link){
                var target = decodeURIComponent(link.hash);
                return target === id;
            });

            if(!linkExists){
                element.classList.add('missing-anchor');
                addToTitle(element, 'Brak linku do tej pozycji w tekście');
            }
        });
    }

    /**
     * Marks all the elements that have duplicate ids.
     */
    function markElementsWithDuplicateIds(){
        var elements = Array.from(document.querySelectorAll('.mw-parser-output [id]'));
        var ids = elements.map(function(element){
            return element.id;
        });

        var idCount = {};
        ids.forEach(function(id){
            idCount[id] = (idCount[id] || 0) + 1;
        });

        elements.forEach(function(element){
            if (idCount[element.id] <= 1) return;
            element.classList.add('duplicate-id');
            addToTitle(element, 'Identyfikator występuje więcej niż raz na stronie (#' + element.id + ')');
        });
    }

    var validAnchors = listValidAnchorTargets();
    var linksToSelf = listLinksToSelf();
    markInvalidLinks(linksToSelf, validAnchors);

    var citations = listCitationsThatShouldBeLinked();
    markElementsWithNoLinkTo(citations, linksToSelf);

    // Flow produces a ton of duplicate ids, so we skip checking them if Flow is enabled
    // Hopefully, in early 2025, Flow will be disabled and this check can be dropped
    if (mw.config.get('wgFlowData') === null)
        markElementsWithDuplicateIds();

    mw.util.addCSS('@media screen {' +
        '.missing-anchor { background-color: #fcc; }' +
        '.duplicate-id { background-color: #fdeec1; outline: 1px solid #c80; }' +
        'html.enable-dark-skin .missing-anchor, html.skin-theme-clientpref-night .missing-anchor { background-color: #514a16; }' +
        'html.enable-dark-skin .duplicate-id, html.skin-theme-clientpref-night .duplicate-id { background-color: #4f3f0e; }' +
        '}\n@media screen and (prefers-color-scheme: dark) {' +
        'html.skin-theme-clientpref-os .missing-anchor { background-color: #514a16; }' +
        'html.skin-theme-clientpref-os .duplicate-id { background-color: #4f3f0e; }' +
        '}'
    );
});
// </nowiki>