Jump to content

MediaWiki:Common.js: Difference between revisions

From Erindal Wiki
Content deleted Content added
Created page with "Erindal Wiki global navigation and live recent changes - v4.2: (function () { 'use strict'; function wikiUrl(title) { return mw.util.getUrl(title); } function buildTopbar() { if (document.getElementById('ew-global-topbar')) return; var bar = document.createElement('div'); bar.id = 'ew-global-topbar'; var links = [ ['Explore', 'Special:AllPages'], ['World', 'Category:Nations and Realms'], ['People', 'Category:Characters'],..."
 
Show five unique recent article pages
 
(13 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* ========================================================================
/* Erindal Wiki global navigation and live recent changes - v4.2 */
ERINDAL WIKI - VECTOR 2022.JS v1.0
(function () {
Skin-specific enhancements. Common.js continues to own article behaviour.
'use strict';
======================================================================== */


function wikiUrl(title) { return mw.util.getUrl(title); }
( function () {
'use strict';


var themeKey = 'erindal-theme';
function buildTopbar() {
var sidebarKey = 'erindal-sidebar';
if (document.getElementById('ew-global-topbar')) return;
var bar = document.createElement('div');
var themes = [ 'system', 'light', 'dark' ];
bar.id = 'ew-global-topbar';


var links = [
function onReady( callback ) {
if ( document.readyState === 'loading' ) {
['Explore', 'Special:AllPages'],
document.addEventListener( 'DOMContentLoaded', callback, { once: true } );
['World', 'Category:Nations and Realms'],
} else {
['People', 'Category:Characters'],
callback();
['Organizations', 'Category:Organizations'],
}
['Magic & Lore', 'Category:Magic and Cosmology'],
}
['History', 'Category:History and Timeline']
];


function readTheme() {
var nav = document.createElement('nav');
nav.className = 'ew-global-menu';
var value = 'system';
links.forEach(function (entry) {
var a = document.createElement('a');
a.href = wikiUrl(entry[1]);
a.appendChild(document.createTextNode(entry[0]));
var arrow = document.createElement('span');
arrow.className = 'ew-menu-arrow';
arrow.textContent = '⌄';
a.appendChild(arrow);
nav.appendChild(a);
});


try {
var search = document.createElement('form');
value = window.localStorage.getItem( themeKey ) || 'system';
search.className = 'ew-global-search';
} catch ( error ) {
search.method = 'get';
value = 'system';
search.action = mw.config.get('wgScriptPath') + '/index.php';
}
var titleField = document.createElement('input');
titleField.type = 'hidden';
titleField.name = 'title';
titleField.value = 'Special:Search';
var input = document.createElement('input');
input.type = 'search';
input.name = 'search';
input.placeholder = 'Search the Erindal wiki...';
input.setAttribute('aria-label', 'Search the Erindal wiki');
var button = document.createElement('button');
button.type = 'submit';
button.textContent = '⌕';
button.setAttribute('aria-label', 'Search');
search.appendChild(titleField);
search.appendChild(input);
search.appendChild(button);


return themes.indexOf( value ) === -1 ? 'system' : value;
var tools = document.createElement('div');
}
tools.className = 'ew-global-tools';
var prefs = document.createElement('a');
prefs.className = 'ew-tool-link';
prefs.href = wikiUrl('Special:Preferences');
prefs.textContent = '⚙';
prefs.title = 'Preferences';
var user = document.createElement('a');
user.className = 'ew-tool-link';
var username = mw.config.get('wgUserName');
user.href = username ? wikiUrl('User:' + username) : wikiUrl('Special:UserLogin');
user.textContent = '♙';
user.title = username || 'Log in';
tools.appendChild(prefs);
tools.appendChild(user);


function applyTheme( value ) {
bar.appendChild(nav);
document.documentElement.setAttribute( 'data-ew-theme', value );
bar.appendChild(search);
bar.appendChild(tools);
document.body.insertBefore(bar, document.body.firstChild);
document.documentElement.classList.add('ew-js-ready');
}


try {
function loadRecentChanges() {
window.localStorage.setItem( themeKey, value );
var target = document.getElementById('ew-recent-list');
if (!target) return;
} catch ( error ) {
/* The selected mode still applies to the current page. */
var api = new mw.Api();
api.get({
}
}
action: 'query',
list: 'recentchanges',
rcprop: 'title|timestamp|user',
rclimit: 5,
rcnamespace: 0,
formatversion: 2
}).done(function (data) {
var rows = data && data.query && data.query.recentchanges ? data.query.recentchanges : [];
target.innerHTML = '';
if (!rows.length) {
target.innerHTML = '<div class="ew-recent-loading">No article edits yet.</div>';
return;
}
rows.forEach(function (item) {
var row = document.createElement('div');
row.className = 'ew-recent-row';
var icon = document.createElement('span');
icon.className = 'ew-recent-icon';
icon.textContent = '◆';
var copy = document.createElement('div');
var link = document.createElement('a');
link.href = wikiUrl(item.title);
link.textContent = item.title;
var small = document.createElement('small');
var date = new Date(item.timestamp);
small.textContent = 'Edited ' + date.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
copy.appendChild(link);
copy.appendChild(small);
row.appendChild(icon);
row.appendChild(copy);
target.appendChild(row);
});
}).fail(function () {
target.innerHTML = '<div class="ew-recent-loading">Recent changes unavailable.</div>';
});
}


function installThemeToggle() {
/* Callback form is compatible with the ResourceLoader API used by MW 1.43. */
var topbar = document.getElementById( 'ew-server-topbar' );
mw.loader.using(['mediawiki.util', 'mediawiki.api'], function () {
if (document.readyState === 'loading') {
if ( !topbar || document.getElementById( 'ew-theme-toggle' ) ) {
return;
document.addEventListener('DOMContentLoaded', function () {
buildTopbar();
}

loadRecentChanges();
var tools = topbar.querySelector( '.ew-topbar-tools' );
}, { once: true });
} else {
if ( !tools ) {
tools = document.createElement( 'div' );
buildTopbar();
tools.className = 'ew-topbar-tools';
loadRecentChanges();
topbar.appendChild( tools );
}

var button = document.createElement( 'button' );
button.id = 'ew-theme-toggle';
button.type = 'button';

function updateButton() {
var value = readTheme();
var labels = {
system: '\u25d0 Auto',
light: '\u2600 Light',
dark: '\u263e Dark'
};

button.textContent = labels[ value ];
button.dataset.ewTheme = value;
button.setAttribute( 'aria-label', labels[ value ] + ' colour mode. Activate to change mode.' );
button.title = labels[ value ] + ' colour mode';
}

button.addEventListener( 'click', function () {
var current = readTheme();
var next = themes[ ( themes.indexOf( current ) + 1 ) % themes.length ];
applyTheme( next );
updateButton();
} );

tools.appendChild( button );
applyTheme( readTheme() );
updateButton();
}
}

});
function installSidebarToggleFallback() {
}());
var button = document.getElementById( 'ew-sidebar-toggle' );
if ( !button || button.dataset.ewVectorBound === '1' || button.getAttribute( 'data-ew-bound' ) === '1' ) {
return;
}

button.dataset.ewVectorBound = '1';

function updateButton() {
var collapsed = document.documentElement.classList.contains( 'ew-sidebar-collapsed' );
button.textContent = collapsed ? '\u203a' : '\u2039';
button.setAttribute( 'aria-expanded', collapsed ? 'false' : 'true' );
button.setAttribute( 'aria-label', collapsed ? 'Open sidebar' : 'Close sidebar' );
button.title = collapsed ? 'Open sidebar' : 'Close sidebar';
}

button.addEventListener( 'click', function () {
var collapsed = document.documentElement.classList.toggle( 'ew-sidebar-collapsed' );

try {
window.localStorage.setItem( sidebarKey, collapsed ? 'collapsed' : 'open' );
} catch ( error ) {
/* The button still works for the current page. */
}

updateButton();
} );

updateButton();
}

function installTopbarDetails() {
var topbar = document.getElementById( 'ew-server-topbar' );
if ( !topbar || topbar.dataset.ewDetailsBound === '1' ) {
return;
}

topbar.dataset.ewDetailsBound = '1';

Array.prototype.forEach.call( topbar.querySelectorAll( 'details.ew-global-section' ), function ( section ) {
section.addEventListener( 'toggle', function () {
if ( !section.open ) {
return;
}

Array.prototype.forEach.call( topbar.querySelectorAll( 'details.ew-global-section[open]' ), function ( other ) {
if ( other !== section ) {
other.open = false;
}
} );
} );
} );

document.addEventListener( 'click', function ( event ) {
if ( topbar.contains( event.target ) ) {
return;
}

Array.prototype.forEach.call( topbar.querySelectorAll( 'details.ew-global-section[open]' ), function ( section ) {
section.open = false;
} );
} );

document.addEventListener( 'keydown', function ( event ) {
if ( event.key !== 'Escape' ) {
return;
}

Array.prototype.forEach.call( topbar.querySelectorAll( 'details.ew-global-section[open]' ), function ( section ) {
section.open = false;
} );
} );
}

function installRecentChanges() {
var container = document.querySelector( '.ew-recent .ew-recent-list' );
if ( !container ) {
return;
}

function showUnavailable() {
container.textContent = 'Recent changes are temporarily unavailable.';
}

mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () {
var api = new mw.Api();
return api.get( {
action: 'query',
list: 'recentchanges',
rcnamespace: 0,
rcprop: 'title|timestamp',
rclimit: 50,
rcshow: '!bot'
} );
} ).then( function ( data ) {
var changes = data && data.query ? data.query.recentchanges : [];
var seenTitles = Object.create( null );

changes = ( changes || [] ).filter( function ( change ) {
if ( /\.(?:css|js)$/i.test( change.title ) || seenTitles[ change.title ] ) {
return false;
}

seenTitles[ change.title ] = true;
return true;
} ).slice( 0, 5 );

if ( !changes.length ) {
container.textContent = 'No recent changes yet.';
return;
}

container.textContent = '';
changes.forEach( function ( change ) {
var row = document.createElement( 'div' );
var icon = document.createElement( 'span' );
var details = document.createElement( 'div' );
var link = document.createElement( 'a' );
var meta = document.createElement( 'small' );
var date = new Date( change.timestamp );

row.className = 'ew-recent-row';
icon.className = 'ew-recent-icon';
icon.setAttribute( 'aria-hidden', 'true' );
icon.textContent = '\u25c6';
link.href = mw.util.getUrl( change.title );
link.textContent = change.title;
meta.textContent = 'Updated ' + date.toLocaleDateString( undefined, {
year: 'numeric',
month: 'short',
day: 'numeric'
} );

details.appendChild( link );
details.appendChild( meta );
row.appendChild( icon );
row.appendChild( details );
container.appendChild( row );
} );
}, showUnavailable );
}

onReady( function () {
installThemeToggle();
installSidebarToggleFallback();
installTopbarDetails();
installRecentChanges();
} );
}() );

Latest revision as of 09:59, 14 September 2026

/* ========================================================================
   ERINDAL WIKI - VECTOR 2022.JS v1.0
   Skin-specific enhancements. Common.js continues to own article behaviour.
   ======================================================================== */

( function () {
    'use strict';

    var themeKey = 'erindal-theme';
    var sidebarKey = 'erindal-sidebar';
    var themes = [ 'system', 'light', 'dark' ];

    function onReady( callback ) {
        if ( document.readyState === 'loading' ) {
            document.addEventListener( 'DOMContentLoaded', callback, { once: true } );
        } else {
            callback();
        }
    }

    function readTheme() {
        var value = 'system';

        try {
            value = window.localStorage.getItem( themeKey ) || 'system';
        } catch ( error ) {
            value = 'system';
        }

        return themes.indexOf( value ) === -1 ? 'system' : value;
    }

    function applyTheme( value ) {
        document.documentElement.setAttribute( 'data-ew-theme', value );

        try {
            window.localStorage.setItem( themeKey, value );
        } catch ( error ) {
            /* The selected mode still applies to the current page. */
        }
    }

    function installThemeToggle() {
        var topbar = document.getElementById( 'ew-server-topbar' );
        if ( !topbar || document.getElementById( 'ew-theme-toggle' ) ) {
            return;
        }

        var tools = topbar.querySelector( '.ew-topbar-tools' );
        if ( !tools ) {
            tools = document.createElement( 'div' );
            tools.className = 'ew-topbar-tools';
            topbar.appendChild( tools );
        }

        var button = document.createElement( 'button' );
        button.id = 'ew-theme-toggle';
        button.type = 'button';

        function updateButton() {
            var value = readTheme();
            var labels = {
                system: '\u25d0 Auto',
                light: '\u2600 Light',
                dark: '\u263e Dark'
            };

            button.textContent = labels[ value ];
            button.dataset.ewTheme = value;
            button.setAttribute( 'aria-label', labels[ value ] + ' colour mode. Activate to change mode.' );
            button.title = labels[ value ] + ' colour mode';
        }

        button.addEventListener( 'click', function () {
            var current = readTheme();
            var next = themes[ ( themes.indexOf( current ) + 1 ) % themes.length ];
            applyTheme( next );
            updateButton();
        } );

        tools.appendChild( button );
        applyTheme( readTheme() );
        updateButton();
    }

    function installSidebarToggleFallback() {
        var button = document.getElementById( 'ew-sidebar-toggle' );
        if ( !button || button.dataset.ewVectorBound === '1' || button.getAttribute( 'data-ew-bound' ) === '1' ) {
            return;
        }

        button.dataset.ewVectorBound = '1';

        function updateButton() {
            var collapsed = document.documentElement.classList.contains( 'ew-sidebar-collapsed' );
            button.textContent = collapsed ? '\u203a' : '\u2039';
            button.setAttribute( 'aria-expanded', collapsed ? 'false' : 'true' );
            button.setAttribute( 'aria-label', collapsed ? 'Open sidebar' : 'Close sidebar' );
            button.title = collapsed ? 'Open sidebar' : 'Close sidebar';
        }

        button.addEventListener( 'click', function () {
            var collapsed = document.documentElement.classList.toggle( 'ew-sidebar-collapsed' );

            try {
                window.localStorage.setItem( sidebarKey, collapsed ? 'collapsed' : 'open' );
            } catch ( error ) {
                /* The button still works for the current page. */
            }

            updateButton();
        } );

        updateButton();
    }

    function installTopbarDetails() {
        var topbar = document.getElementById( 'ew-server-topbar' );
        if ( !topbar || topbar.dataset.ewDetailsBound === '1' ) {
            return;
        }

        topbar.dataset.ewDetailsBound = '1';

        Array.prototype.forEach.call( topbar.querySelectorAll( 'details.ew-global-section' ), function ( section ) {
            section.addEventListener( 'toggle', function () {
                if ( !section.open ) {
                    return;
                }

                Array.prototype.forEach.call( topbar.querySelectorAll( 'details.ew-global-section[open]' ), function ( other ) {
                    if ( other !== section ) {
                        other.open = false;
                    }
                } );
            } );
        } );

        document.addEventListener( 'click', function ( event ) {
            if ( topbar.contains( event.target ) ) {
                return;
            }

            Array.prototype.forEach.call( topbar.querySelectorAll( 'details.ew-global-section[open]' ), function ( section ) {
                section.open = false;
            } );
        } );

        document.addEventListener( 'keydown', function ( event ) {
            if ( event.key !== 'Escape' ) {
                return;
            }

            Array.prototype.forEach.call( topbar.querySelectorAll( 'details.ew-global-section[open]' ), function ( section ) {
                section.open = false;
            } );
        } );
    }

    function installRecentChanges() {
        var container = document.querySelector( '.ew-recent .ew-recent-list' );
        if ( !container ) {
            return;
        }

        function showUnavailable() {
            container.textContent = 'Recent changes are temporarily unavailable.';
        }

        mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () {
            var api = new mw.Api();
            return api.get( {
                action: 'query',
                list: 'recentchanges',
                rcnamespace: 0,
                rcprop: 'title|timestamp',
                rclimit: 50,
                rcshow: '!bot'
            } );
        } ).then( function ( data ) {
            var changes = data && data.query ? data.query.recentchanges : [];
            var seenTitles = Object.create( null );

            changes = ( changes || [] ).filter( function ( change ) {
                if ( /\.(?:css|js)$/i.test( change.title ) || seenTitles[ change.title ] ) {
                    return false;
                }

                seenTitles[ change.title ] = true;
                return true;
            } ).slice( 0, 5 );

            if ( !changes.length ) {
                container.textContent = 'No recent changes yet.';
                return;
            }

            container.textContent = '';
            changes.forEach( function ( change ) {
                var row = document.createElement( 'div' );
                var icon = document.createElement( 'span' );
                var details = document.createElement( 'div' );
                var link = document.createElement( 'a' );
                var meta = document.createElement( 'small' );
                var date = new Date( change.timestamp );

                row.className = 'ew-recent-row';
                icon.className = 'ew-recent-icon';
                icon.setAttribute( 'aria-hidden', 'true' );
                icon.textContent = '\u25c6';
                link.href = mw.util.getUrl( change.title );
                link.textContent = change.title;
                meta.textContent = 'Updated ' + date.toLocaleDateString( undefined, {
                    year: 'numeric',
                    month: 'short',
                    day: 'numeric'
                } );

                details.appendChild( link );
                details.appendChild( meta );
                row.appendChild( icon );
                row.appendChild( details );
                container.appendChild( row );
            } );
        }, showUnavailable );
    }

    onReady( function () {
        installThemeToggle();
        installSidebarToggleFallback();
        installTopbarDetails();
        installRecentChanges();
    } );
}() );