Candidate:Common.js: Difference between revisions
From PickiPedia: A knowledge base of bluegrass, old time psychedelic jams, and other public domain music
Jump to navigationJump to search
Staging full proposed Common.js for sysop review (new Show: detector IIFE at bottom) |
Re-stage clean (drop a stray MCP footer line that rode in with the live fetch); night toggle + zine sync unchanged (via update-page on MediaWiki MCP Server) |
||
| (One intermediate revision by the same user not shown) | |||
| Line 1: | Line 1: | ||
// <proposed by="Magent"> | |||
// |status=proposed -- bot-staged; remove this line when promoting. | // |status=proposed -- bot-staged; remove this line when promoting. | ||
/** | /** | ||
* | * What used to be here now ships with the PickiPediaReleases extension. | ||
* | * | ||
* | * the HLS video player -> ext.pickipediaReleases.videoPlayer.js (#124) | ||
* Blue Railroad date picker -> ext.pickipediaReleases.blueRailroadDate.js (#131) | |||
* "X blocks ago" -> ext.pickipediaReleases.blocksAgo.js (#131) | |||
* Instrument form checks -> ext.pickipediaReleases.instrumentForm.js (#131) | |||
* | * | ||
* | * All four load on every page from the extension's BeforePageDisplay hook, | ||
* | * exactly as they did from here, and each does nothing unless the form or | ||
* element it cares about is on the page being shown. Changing any of them is | |||
* a pull request now: reviewed, syntax-checked by CI, deployed by Jenkins — | |||
* rather than several hundred lines pasted into this box, which is how the | |||
* whole of this file went down for 23 minutes on 15 September 2026. | |||
* | * | ||
* | * What remains below is the user-subpage zine styling. It stays because it is | ||
* with the | * bound to classes defined in MediaWiki:Common.css, and moving the script | ||
* without its stylesheet would split one feature across two homes. It goes | |||
* next, with the CSS, after which this page can be empty. | |||
* | * | ||
* | * See cryptograss/pickipedia#124 and #131. | ||
*/ | */ | ||
/* ============================================================ | /* ============================================================ | ||
| Line 393: | Line 77: | ||
var saved = null; | var saved = null; | ||
try { saved = localStorage.getItem(STORAGE_KEY); } catch (e) {} | try { saved = localStorage.getItem(STORAGE_KEY); } catch (e) {} | ||
if (saved === 'dark') body.classList.add('pickipedia-dark'); | // Follow the site's night preference (set by the toggle below, on any | ||
// page): a saved dark/light choice, else the OS's prefers-color-scheme. | |||
var systemDark = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches); | |||
if (saved === 'dark' || (saved === null && systemDark)) body.classList.add('pickipedia-dark'); | |||
var LOGO_URL = '/images/thumb/8/80/Pickipedia-quarter-transparent.png/400px-Pickipedia-quarter-transparent.png'; | var LOGO_URL = '/images/thumb/8/80/Pickipedia-quarter-transparent.png/400px-Pickipedia-quarter-transparent.png'; | ||
| Line 470: | Line 157: | ||
var dark = body.classList.toggle('pickipedia-dark'); | var dark = body.classList.toggle('pickipedia-dark'); | ||
toggle.textContent = dark ? '☼ Light' : '☾ Dark'; | toggle.textContent = dark ? '☼ Light' : '☾ Dark'; | ||
// Tell the rest of the site (html.pp-night / html.pp-day) too, so a | |||
// choice on a zine page holds everywhere it goes next. | |||
var htmlEl = document.documentElement; | |||
htmlEl.classList.remove('pp-night'); | |||
htmlEl.classList.remove('pp-day'); | |||
htmlEl.classList.add(dark ? 'pp-night' : 'pp-day'); | |||
try { localStorage.setItem(STORAGE_KEY, dark ? 'dark' : 'light'); } catch (e) {} | try { localStorage.setItem(STORAGE_KEY, dark ? 'dark' : 'light'); } catch (e) {} | ||
}); | }); | ||
| Line 482: | Line 175: | ||
} | } | ||
}()); | }()); | ||
/* ============================================================ | |||
/* | * Pickipedia — Site-wide night toggle | ||
* Pickipedia — | |||
* | * | ||
* | * Turns the stored preference into html.pp-night / html.pp-day, | ||
* | * the switches the night theme in MediaWiki:Common.css keys on: | ||
* | * pp-night — explicit dark; pp-day — explicit light, opting | ||
* | * out of the prefers-color-scheme media query. The zine pages | ||
* | * render their own toggle (see above) and share the same | ||
*/ | * localStorage key, so one choice holds across the whole site. | ||
* ============================================================ */ | |||
(function () { | (function () { | ||
'use strict'; | 'use strict'; | ||
var KEY = 'pickipedia-mode'; | |||
var saved = null; | |||
var | try { saved = localStorage.getItem(KEY); } catch (e) {} | ||
var htmlEl = document.documentElement; | |||
if ( | if (saved === 'dark') htmlEl.classList.add('pp-night'); | ||
if ( | if (saved === 'light') htmlEl.classList.add('pp-day'); | ||
// Zine pages carry their own control; don't add a second one. | |||
if (document.body && document.body.classList.contains('pickipedia-userpost')) return; | |||
var | var systemDark = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches); | ||
var darkNow = htmlEl.classList.contains('pp-night') || (saved === null && systemDark); | |||
var | var toggle = document.createElement('button'); | ||
toggle.className = 'pickipedia-mode-site'; | |||
toggle.type = 'button'; | |||
toggle.textContent = darkNow ? '☼ Light' : '☾ Dark'; | |||
toggle.title = darkNow ? 'Switch to light mode' : 'Switch to dark mode'; | |||
toggle.addEventListener('click', function () { | |||
darkNow = !darkNow; | |||
htmlEl.classList.remove('pp-night'); | |||
htmlEl.classList.remove('pp-day'); | |||
htmlEl.classList.add(darkNow ? 'pp-night' : 'pp-day'); | |||
try { localStorage.setItem(KEY, darkNow ? 'dark' : 'light'); } catch (e) {} | |||
toggle.textContent = darkNow ? '☼ Light' : '☾ Dark'; | |||
toggle.title = darkNow ? 'Switch to light mode' : 'Switch to dark mode'; | |||
}); | |||
document.body.appendChild(toggle); | |||
}()); | }()); | ||
// </proposed> | |||
Latest revision as of 20:16, 29 October 2026
// <proposed by="Magent">
// |status=proposed -- bot-staged; remove this line when promoting.
/**
* What used to be here now ships with the PickiPediaReleases extension.
*
* the HLS video player -> ext.pickipediaReleases.videoPlayer.js (#124)
* Blue Railroad date picker -> ext.pickipediaReleases.blueRailroadDate.js (#131)
* "X blocks ago" -> ext.pickipediaReleases.blocksAgo.js (#131)
* Instrument form checks -> ext.pickipediaReleases.instrumentForm.js (#131)
*
* All four load on every page from the extension's BeforePageDisplay hook,
* exactly as they did from here, and each does nothing unless the form or
* element it cares about is on the page being shown. Changing any of them is
* a pull request now: reviewed, syntax-checked by CI, deployed by Jenkins —
* rather than several hundred lines pasted into this box, which is how the
* whole of this file went down for 23 minutes on 15 September 2026.
*
* What remains below is the user-subpage zine styling. It stays because it is
* bound to classes defined in MediaWiki:Common.css, and moving the script
* without its stylesheet would split one feature across two homes. It goes
* next, with the CSS, after which this page can be empty.
*
* See cryptograss/pickipedia#124 and #131.
*/
/* ============================================================
* Pickipedia — User-subpage zine/Substack styling
*
* Layout:
* - corner logo (absolute-positioned upper-left of viewport,
* OUTSIDE all wiki wrappers — does not push content down)
* - non-sticky topbar (just nav) at top of body
* - hero block with title (top), then USER:Username kicker below,
* optional dek + meta
* - light/dark mode toggle (persisted in localStorage)
*
* Variant from HTML comment marker `pickipedia:variant=letter|masthead`
* or category membership; default letter.
* ============================================================ */
(function () {
'use strict';
if (typeof mw === 'undefined' || !mw.config) return;
var nsNumber = mw.config.get('wgNamespaceNumber');
var pageName = mw.config.get('wgPageName') || '';
var title = mw.config.get('wgTitle') || '';
var action = mw.config.get('wgAction') || 'view';
if (nsNumber !== 2) return;
if (action !== 'view') return;
if (title.indexOf('/') === -1) return;
var slash = title.indexOf('/');
var username = title.slice(0, slash).replace(/_/g, ' ');
var subpage = title.slice(slash + 1).replace(/_/g, ' ');
var body = document.body;
body.classList.add('pickipedia-userpost');
var variant = 'letter';
var content = document.querySelector('.mw-parser-output');
if (content) {
var html = content.innerHTML || '';
var m = html.match(/pickipedia:variant\s*=\s*(letter|masthead)/i);
if (m) variant = m[1].toLowerCase();
}
var cats = mw.config.get('wgCategories') || [];
if (cats.indexOf('Masthead style') !== -1) variant = 'masthead';
if (cats.indexOf('Letter style') !== -1) variant = 'letter';
body.classList.add('pickipedia-variant-' + variant);
var STORAGE_KEY = 'pickipedia-mode';
var saved = null;
try { saved = localStorage.getItem(STORAGE_KEY); } catch (e) {}
// Follow the site's night preference (set by the toggle below, on any
// page): a saved dark/light choice, else the OS's prefers-color-scheme.
var systemDark = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
if (saved === 'dark' || (saved === null && systemDark)) body.classList.add('pickipedia-dark');
var LOGO_URL = '/images/thumb/8/80/Pickipedia-quarter-transparent.png/400px-Pickipedia-quarter-transparent.png';
// ---------- Corner logo: stand-alone body child, absolute-positioned. ----------
var cornerLogo = document.createElement('a');
cornerLogo.className = 'pp-corner-logo';
cornerLogo.href = '/wiki/Main_Page';
cornerLogo.title = 'PickiPedia home';
cornerLogo.innerHTML = '<img src="' + LOGO_URL + '" alt="PickiPedia">';
document.body.insertBefore(cornerLogo, document.body.firstChild);
// ---------- Topbar (nav only) ----------
var topbar = document.createElement('header');
topbar.className = 'pickipedia-topbar';
topbar.innerHTML =
'<nav class="pickipedia-topbar-nav">' +
'<a href="' + mw.util.getUrl(pageName, { action: 'edit' }) + '">edit</a>' +
'<a href="' + mw.util.getUrl(pageName, { action: 'history' }) + '">history</a>' +
'<a href="' + mw.util.getUrl('Talk:' + pageName) + '">talk</a>' +
'</nav>';
document.body.insertBefore(topbar, cornerLogo.nextSibling);
// ---------- Hero ----------
if (content) {
var dek = '';
var dekEl = content.querySelector('.pp-dek-source');
if (dekEl) {
dek = dekEl.innerHTML;
dekEl.remove();
}
var kickerHtml = '<a href="' + mw.util.getUrl('User:' + username.replace(/ /g, '_')) +
'">User:' + escapeHtml(username) + '</a>';
var kickerEl = content.querySelector('.pp-kicker-source');
if (kickerEl) {
kickerHtml = escapeHtml(kickerEl.textContent.trim());
kickerEl.remove();
}
var lastMod = '';
var lastModEl = document.getElementById('footer-info-lastmod');
if (lastModEl) lastMod = lastModEl.textContent.replace(/^\s*This page was last (edited|modified) on\s*/i, '').trim();
var hero = document.createElement('header');
hero.className = 'pickipedia-hero';
if (variant === 'masthead') {
hero.innerHTML =
'<div class="pp-topbar">' +
'<div class="pp-kicker">' + kickerHtml + '</div>' +
'<div class="pp-topbar-rule"></div>' +
(lastMod ? '<div class="pp-topbar-vol">' + escapeHtml(lastMod) + '</div>' : '') +
'</div>' +
'<div class="pp-titlewrap">' +
'<h1 class="pp-title">' + escapeHtml(subpage) + '</h1>' +
(dek ? '<p class="pp-dek">' + dek + '</p>' : '') +
'</div>';
} else {
// Letter variant: title first, then kicker, then optional dek/meta.
hero.innerHTML =
'<h1 class="pp-title">' + escapeHtml(subpage) + '</h1>' +
'<div class="pp-kicker">' + kickerHtml + '</div>' +
(dek ? '<p class="pp-dek">' + dek + '</p>' : '') +
(lastMod ? '<div class="pp-meta">Last edited ' + escapeHtml(lastMod) + '</div>' : '');
}
content.parentNode.insertBefore(hero, content);
}
// ---------- Light/dark toggle ----------
var toggle = document.createElement('button');
toggle.className = 'pickipedia-mode';
toggle.type = 'button';
toggle.textContent = body.classList.contains('pickipedia-dark') ? '☼ Light' : '☾ Dark';
toggle.addEventListener('click', function () {
var dark = body.classList.toggle('pickipedia-dark');
toggle.textContent = dark ? '☼ Light' : '☾ Dark';
// Tell the rest of the site (html.pp-night / html.pp-day) too, so a
// choice on a zine page holds everywhere it goes next.
var htmlEl = document.documentElement;
htmlEl.classList.remove('pp-night');
htmlEl.classList.remove('pp-day');
htmlEl.classList.add(dark ? 'pp-night' : 'pp-day');
try { localStorage.setItem(STORAGE_KEY, dark ? 'dark' : 'light'); } catch (e) {}
});
document.body.appendChild(toggle);
function escapeHtml(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
}());
/* ============================================================
* Pickipedia — Site-wide night toggle
*
* Turns the stored preference into html.pp-night / html.pp-day,
* the switches the night theme in MediaWiki:Common.css keys on:
* pp-night — explicit dark; pp-day — explicit light, opting
* out of the prefers-color-scheme media query. The zine pages
* render their own toggle (see above) and share the same
* localStorage key, so one choice holds across the whole site.
* ============================================================ */
(function () {
'use strict';
var KEY = 'pickipedia-mode';
var saved = null;
try { saved = localStorage.getItem(KEY); } catch (e) {}
var htmlEl = document.documentElement;
if (saved === 'dark') htmlEl.classList.add('pp-night');
if (saved === 'light') htmlEl.classList.add('pp-day');
// Zine pages carry their own control; don't add a second one.
if (document.body && document.body.classList.contains('pickipedia-userpost')) return;
var systemDark = !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
var darkNow = htmlEl.classList.contains('pp-night') || (saved === null && systemDark);
var toggle = document.createElement('button');
toggle.className = 'pickipedia-mode-site';
toggle.type = 'button';
toggle.textContent = darkNow ? '☼ Light' : '☾ Dark';
toggle.title = darkNow ? 'Switch to light mode' : 'Switch to dark mode';
toggle.addEventListener('click', function () {
darkNow = !darkNow;
htmlEl.classList.remove('pp-night');
htmlEl.classList.remove('pp-day');
htmlEl.classList.add(darkNow ? 'pp-night' : 'pp-day');
try { localStorage.setItem(KEY, darkNow ? 'dark' : 'light'); } catch (e) {}
toggle.textContent = darkNow ? '☼ Light' : '☾ Dark';
toggle.title = darkNow ? 'Switch to light mode' : 'Switch to dark mode';
});
document.body.appendChild(toggle);
}());
// </proposed>