WikiPartner (talk | contribs) No edit summary |
WikiPartner (talk | contribs) No edit summary |
||
| Line 1: | Line 1: | ||
/* | /* ============================================================ | ||
WIKIPARTNER | |||
LIGHT / DARK THEME SYSTEM | |||
============================================================ */ | |||
(function () { | (function () { | ||
'use strict'; | 'use strict'; | ||
var | var STORAGE_KEY = 'wikipartner-theme'; | ||
var | var LIGHT = 'light'; | ||
var DARK = 'dark'; | |||
/* ======================================================== | |||
STORAGE | |||
======================================================== */ | |||
function getSavedTheme() { | |||
try { | |||
var saved = window.localStorage.getItem(STORAGE_KEY); | |||
if (saved === DARK || saved === LIGHT) { | |||
if ( | return saved; | ||
return | |||
} | } | ||
} catch (e) { | |||
/* localStorage unavailable */ | |||
} | } | ||
return | return LIGHT; | ||
} | } | ||
function applyTheme(theme) { | |||
function saveTheme(theme) { | |||
try { | |||
window.localStorage.setItem(STORAGE_KEY, theme); | |||
} catch (e) { | |||
/* localStorage unavailable */ | |||
} | |||
} | |||
/* ======================================================== | |||
APPLY THEME | |||
======================================================== */ | |||
function applyTheme(theme, shouldSave) { | |||
var html = document.documentElement; | var html = document.documentElement; | ||
var isDark = theme === DARK; | |||
html.classList.toggle('wp-theme-dark', isDark); | |||
html.classList.toggle('wp-theme-light', !isDark); | |||
/* | |||
* Also expose standard MediaWiki-style theme classes. | |||
* This allows extensions/components that understand | |||
* these classes to respond too. | |||
*/ | |||
html.classList.remove( | html.classList.remove( | ||
'skin-theme-clientpref-day', | 'skin-theme-clientpref-day', | ||
| Line 39: | Line 63: | ||
); | ); | ||
html.classList.add('skin-theme-clientpref-' | html.classList.add( | ||
isDark | |||
? 'skin-theme-clientpref-night' | |||
: 'skin-theme-clientpref-day' | |||
); | |||
html.setAttribute( | |||
'data-wikipartner-theme', | |||
isDark ? DARK : LIGHT | |||
); | |||
if (shouldSave !== false) { | |||
saveTheme(theme); | |||
} | |||
updateThemeButton(); | |||
} | } | ||
/* ======================================================== | |||
CURRENT THEME | |||
======================================================== */ | |||
function getCurrentTheme() { | |||
return document.documentElement.classList.contains( | |||
'wp-theme-dark' | |||
) ? DARK : LIGHT; | |||
} | |||
/* ======================================================== | |||
ICONS | |||
======================================================== */ | |||
function getMoonIcon() { | |||
return [ | |||
'<svg viewBox="0 0 24 24" aria-hidden="true">', | |||
'<path ', | |||
'd="M20.3 15.2A8.4 8.4 0 0 1 8.8 3.7 ', | |||
'8.5 8.5 0 1 0 11.5 11.5Z"', | |||
'></path>', | |||
'<path ', | |||
'class="wp-theme-star" ', | |||
'd="M17.5 4v3M16 5.5h3"', | |||
'></path>', | |||
'<path ', | |||
'class="wp-theme-star wp-theme-star-small" ', | |||
'd="M20.2 8.8v1.8M19.3 9.7h1.8"', | |||
/ | '></path>', | ||
'</svg>' | |||
].join(''); | |||
} | } | ||
function | |||
return | function getSunIcon() { | ||
'<svg viewBox="0 0 24 24" aria-hidden="true">' | return [ | ||
'<path d=" | '<svg viewBox="0 0 24 24" aria-hidden="true">', | ||
'<path | '<circle cx="12" cy="12" r="3.8"></circle>', | ||
'</svg>'; | |||
'<path d="M12 2.3v2.1"></path>', | |||
'<path d="M12 19.6v2.1"></path>', | |||
'<path d="M4.93 4.93l1.5 1.5"></path>', | |||
'<path d="M17.57 17.57l1.5 1.5"></path>', | |||
'<path d="M2.3 12h2.1"></path>', | |||
'<path d="M19.6 12h2.1"></path>', | |||
'<path d="M4.93 19.07l1.5-1.5"></path>', | |||
'<path d="M17.57 6.43l1.5-1.5"></path>', | |||
'</svg>' | |||
].join(''); | |||
} | } | ||
function | /* ======================================================== | ||
var theme | UPDATE BUTTON | ||
var isDark = | ======================================================== */ | ||
function updateThemeButton() { | |||
var button = document.getElementById( | |||
'wikipartner-theme-toggle' | |||
); | |||
if (!button) { | |||
return; | |||
} | |||
var isDark = getCurrentTheme() === DARK; | |||
button.innerHTML = | button.innerHTML = | ||
'<span class="wp-theme-icon">' + | '<span class="wp-theme-icon">' + | ||
(isDark ? | (isDark ? getSunIcon() : getMoonIcon()) + | ||
'</span>'; | '</span>'; | ||
button.setAttribute( | button.setAttribute( | ||
'aria-label', | 'aria-label', | ||
isDark ? 'Switch to light mode' : 'Switch to dark mode' | isDark | ||
? 'Switch to light mode' | |||
: 'Switch to dark mode' | |||
); | ); | ||
button.setAttribute( | button.setAttribute( | ||
'title', | 'title', | ||
isDark ? 'Light mode' : 'Dark mode' | isDark | ||
? 'Light mode' | |||
: 'Dark mode' | |||
); | ); | ||
} | } | ||
/* ======================================================== | |||
CREATE BUTTON | |||
======================================================== */ | |||
function createThemeToggle() { | function createThemeToggle() { | ||
if (document.getElementById('wikipartner-theme-toggle')) { | if ( | ||
document.getElementById( | |||
'wikipartner-theme-toggle' | |||
) | |||
) { | |||
return; | return; | ||
} | } | ||
/* | /* | ||
* | * WikiPartner's MediaWiki user menu. | ||
*/ | */ | ||
var list = document.querySelector( | |||
'.wp-user-menu-list' | |||
); | |||
); | |||
if (!list) { | if (!list) { | ||
| Line 133: | Line 201: | ||
var item = document.createElement('li'); | var item = document.createElement('li'); | ||
item.className = 'mw-list-item wikipartner-theme-item'; | |||
item.id = 'wikipartner-theme-item'; | |||
item.className = | |||
'mw-list-item wikipartner-theme-item'; | |||
var button = document.createElement('button'); | var button = document.createElement('button'); | ||
button.id = 'wikipartner-theme-toggle'; | button.id = 'wikipartner-theme-toggle'; | ||
button.className = 'wikipartner-theme-toggle'; | button.className = | ||
'wikipartner-theme-toggle'; | |||
button.type = 'button'; | button.type = 'button'; | ||
button.addEventListener('click', function () { | button.addEventListener( | ||
'click', | |||
function () { | |||
var nextTheme = | |||
getCurrentTheme() === DARK | |||
? LIGHT | |||
: DARK; | |||
applyTheme(nextTheme, true); | |||
} | |||
); | |||
item.appendChild(button); | item.appendChild(button); | ||
/* | /* | ||
* | * LOGGED OUT: | ||
* | * | ||
* Create account | Theme | Log in | |||
* | |||
* The requested position is immediately | |||
* to the LEFT of Log in. | |||
*/ | */ | ||
var loginItem = list.querySelector('#pt-login'); | |||
if (loginItem) { | if (loginItem) { | ||
list.insertBefore(item, loginItem); | list.insertBefore( | ||
item, | |||
loginItem | |||
); | |||
} else { | } else { | ||
list.insertBefore(item, list.firstChild); | |||
/* | |||
* LOGGED IN: | |||
* | |||
* Put theme button at beginning of | |||
* account controls. | |||
*/ | |||
list.insertBefore( | |||
item, | |||
list.firstChild | |||
); | |||
} | } | ||
updateThemeButton(); | |||
} | |||
/* ======================================================== | |||
INITIAL THEME | |||
======================================================== */ | |||
/* | |||
* Apply saved preference as soon as Common.js runs. | |||
*/ | |||
applyTheme( | |||
getSavedTheme(), | |||
false | |||
); | |||
/* ======================================================== | |||
INITIALIZE HEADER BUTTON | |||
======================================================== */ | |||
function initialize() { | |||
createThemeToggle(); | |||
updateThemeButton(); | |||
} | } | ||
if (document.readyState === 'loading') { | if (document.readyState === 'loading') { | ||
document.addEventListener('DOMContentLoaded', | |||
document.addEventListener( | |||
'DOMContentLoaded', | |||
initialize | |||
); | |||
} else { | } else { | ||
initialize(); | |||
} | } | ||
})(); | })(); | ||
Revision as of 22:46, 19 August 2026
/* ============================================================
WIKIPARTNER
LIGHT / DARK THEME SYSTEM
============================================================ */
(function () {
'use strict';
var STORAGE_KEY = 'wikipartner-theme';
var LIGHT = 'light';
var DARK = 'dark';
/* ========================================================
STORAGE
======================================================== */
function getSavedTheme() {
try {
var saved = window.localStorage.getItem(STORAGE_KEY);
if (saved === DARK || saved === LIGHT) {
return saved;
}
} catch (e) {
/* localStorage unavailable */
}
return LIGHT;
}
function saveTheme(theme) {
try {
window.localStorage.setItem(STORAGE_KEY, theme);
} catch (e) {
/* localStorage unavailable */
}
}
/* ========================================================
APPLY THEME
======================================================== */
function applyTheme(theme, shouldSave) {
var html = document.documentElement;
var isDark = theme === DARK;
html.classList.toggle('wp-theme-dark', isDark);
html.classList.toggle('wp-theme-light', !isDark);
/*
* Also expose standard MediaWiki-style theme classes.
* This allows extensions/components that understand
* these classes to respond too.
*/
html.classList.remove(
'skin-theme-clientpref-day',
'skin-theme-clientpref-night',
'skin-theme-clientpref-os'
);
html.classList.add(
isDark
? 'skin-theme-clientpref-night'
: 'skin-theme-clientpref-day'
);
html.setAttribute(
'data-wikipartner-theme',
isDark ? DARK : LIGHT
);
if (shouldSave !== false) {
saveTheme(theme);
}
updateThemeButton();
}
/* ========================================================
CURRENT THEME
======================================================== */
function getCurrentTheme() {
return document.documentElement.classList.contains(
'wp-theme-dark'
) ? DARK : LIGHT;
}
/* ========================================================
ICONS
======================================================== */
function getMoonIcon() {
return [
'<svg viewBox="0 0 24 24" aria-hidden="true">',
'<path ',
'd="M20.3 15.2A8.4 8.4 0 0 1 8.8 3.7 ',
'8.5 8.5 0 1 0 11.5 11.5Z"',
'></path>',
'<path ',
'class="wp-theme-star" ',
'd="M17.5 4v3M16 5.5h3"',
'></path>',
'<path ',
'class="wp-theme-star wp-theme-star-small" ',
'd="M20.2 8.8v1.8M19.3 9.7h1.8"',
'></path>',
'</svg>'
].join('');
}
function getSunIcon() {
return [
'<svg viewBox="0 0 24 24" aria-hidden="true">',
'<circle cx="12" cy="12" r="3.8"></circle>',
'<path d="M12 2.3v2.1"></path>',
'<path d="M12 19.6v2.1"></path>',
'<path d="M4.93 4.93l1.5 1.5"></path>',
'<path d="M17.57 17.57l1.5 1.5"></path>',
'<path d="M2.3 12h2.1"></path>',
'<path d="M19.6 12h2.1"></path>',
'<path d="M4.93 19.07l1.5-1.5"></path>',
'<path d="M17.57 6.43l1.5-1.5"></path>',
'</svg>'
].join('');
}
/* ========================================================
UPDATE BUTTON
======================================================== */
function updateThemeButton() {
var button = document.getElementById(
'wikipartner-theme-toggle'
);
if (!button) {
return;
}
var isDark = getCurrentTheme() === DARK;
button.innerHTML =
'<span class="wp-theme-icon">' +
(isDark ? getSunIcon() : getMoonIcon()) +
'</span>';
button.setAttribute(
'aria-label',
isDark
? 'Switch to light mode'
: 'Switch to dark mode'
);
button.setAttribute(
'title',
isDark
? 'Light mode'
: 'Dark mode'
);
}
/* ========================================================
CREATE BUTTON
======================================================== */
function createThemeToggle() {
if (
document.getElementById(
'wikipartner-theme-toggle'
)
) {
return;
}
/*
* WikiPartner's MediaWiki user menu.
*/
var list = document.querySelector(
'.wp-user-menu-list'
);
if (!list) {
return;
}
var item = document.createElement('li');
item.id = 'wikipartner-theme-item';
item.className =
'mw-list-item wikipartner-theme-item';
var button = document.createElement('button');
button.id = 'wikipartner-theme-toggle';
button.className =
'wikipartner-theme-toggle';
button.type = 'button';
button.addEventListener(
'click',
function () {
var nextTheme =
getCurrentTheme() === DARK
? LIGHT
: DARK;
applyTheme(nextTheme, true);
}
);
item.appendChild(button);
/*
* LOGGED OUT:
*
* Create account | Theme | Log in
*
* The requested position is immediately
* to the LEFT of Log in.
*/
var loginItem = list.querySelector('#pt-login');
if (loginItem) {
list.insertBefore(
item,
loginItem
);
} else {
/*
* LOGGED IN:
*
* Put theme button at beginning of
* account controls.
*/
list.insertBefore(
item,
list.firstChild
);
}
updateThemeButton();
}
/* ========================================================
INITIAL THEME
======================================================== */
/*
* Apply saved preference as soon as Common.js runs.
*/
applyTheme(
getSavedTheme(),
false
);
/* ========================================================
INITIALIZE HEADER BUTTON
======================================================== */
function initialize() {
createThemeToggle();
updateThemeButton();
}
if (document.readyState === 'loading') {
document.addEventListener(
'DOMContentLoaded',
initialize
);
} else {
initialize();
}
})();