Main Page: Difference between revisions

No edit summary
Fix remaining broken arrow characters on main page
 
(8 intermediate revisions by 2 users not shown)
Line 1: Line 1:
from pathlib import Path
__NOTOC__
 
content = r'''__NOTOC__


<html>
<html>
Line 561: Line 559:
.vu-people-controls {
.vu-people-controls {
display: grid;
display: grid;
grid-template-columns: auto auto minmax(0, 1fr) auto;
grid-template-columns: auto auto minmax(0, 1fr) auto auto;
align-items: center;
align-items: center;
gap: 8px;
gap: 8px;
Line 929: Line 927:


.vu-people-controls {
.vu-people-controls {
grid-template-columns: auto auto minmax(50px, 1fr) auto;
grid-template-columns: auto auto minmax(50px, 1fr) auto auto;
}
}


Line 1,014: Line 1,012:
<span class="vu-explore-description">Biographies recorded across the wiki.</span>
<span class="vu-explore-description">Biographies recorded across the wiki.</span>
</span>
</span>
<span class="vu-explore-arrow"></span>
<span class="vu-explore-arrow">&gt;</span>
</a>
</a>


Line 1,023: Line 1,021:
<span class="vu-explore-description">Places documented throughout the history.</span>
<span class="vu-explore-description">Places documented throughout the history.</span>
</span>
</span>
<span class="vu-explore-arrow"></span>
<span class="vu-explore-arrow">&gt;</span>
</a>
</a>


Line 1,032: Line 1,030:
<span class="vu-explore-description">Family lines and their members.</span>
<span class="vu-explore-description">Family lines and their members.</span>
</span>
</span>
<span class="vu-explore-arrow"></span>
<span class="vu-explore-arrow">&gt;</span>
</a>
</a>


Line 1,041: Line 1,039:
<span class="vu-explore-description">Institutions and organized groups.</span>
<span class="vu-explore-description">Institutions and organized groups.</span>
</span>
</span>
<span class="vu-explore-arrow"></span>
<span class="vu-explore-arrow">&gt;</span>
</a>
</a>


Line 1,050: Line 1,048:
<span class="vu-explore-description">Events recorded throughout the history.</span>
<span class="vu-explore-description">Events recorded throughout the history.</span>
</span>
</span>
<span class="vu-explore-arrow"></span>
<span class="vu-explore-arrow">&gt;</span>
</a>
</a>


Line 1,059: Line 1,057:
<span class="vu-explore-description">The historical periods used by the wiki.</span>
<span class="vu-explore-description">The historical periods used by the wiki.</span>
</span>
</span>
<span class="vu-explore-arrow"></span>
<span class="vu-explore-arrow">&gt;</span>
</a>
</a>
</nav>
</nav>
Line 1,076: Line 1,074:
<h2>
<h2>
<a data-wiki-title="Reichsministerium für Bergbau und Rohstoffe" href="/wiki/Reichsministerium_f%C3%BCr_Bergbau_und_Rohstoffe">
<a data-wiki-title="Reichsministerium für Bergbau und Rohstoffe" href="/wiki/Reichsministerium_f%C3%BCr_Bergbau_und_Rohstoffe">
Reichsministerium für Bergbau und Rohstoffe
Reichsministerium f&uuml;r Bergbau und Rohstoffe
</a>
</a>
</h2>
</h2>


<p>
<p>
The Reichsministerium für Bergbau und Rohstoffe was a central ministry of the
The Reichsministerium f&uuml;r Bergbau und Rohstoffe was a central ministry of the
<a data-wiki-title="Government of the Tanoa Einsatzgruppen" href="/wiki/Government_of_the_Tanoa_Einsatzgruppen">Government of the Tanoa Einsatzgruppen</a>.
<a data-wiki-title="Government of the Tanoa Einsatzgruppen" href="/wiki/Government_of_the_Tanoa_Einsatzgruppen">Government of the Tanoa Einsatzgruppen</a>.
It administered mining and the state supply of raw materials.
It administered mining and the state supply of raw materials.
Line 1,119: Line 1,117:


<section class="vu-people" id="vu-people-slider" aria-label="People slideshow">
<section class="vu-people" id="vu-people-slider" aria-label="People slideshow">
<div class="vu-people-status" id="vu-people-status">Loading random biographies…</div>
<div class="vu-people-status" id="vu-people-status" hidden></div>


<div class="vu-people-stage">
<div class="vu-people-stage">
Line 1,130: Line 1,128:


<div class="vu-person-copy">
<div class="vu-person-copy">
<div class="vu-person-kicker">From Category:People</div>


<h2 class="vu-person-title" id="vu-person-title">Toonio Noord</h2>
<h2 class="vu-person-title" id="vu-person-title">Toonio Noord</h2>
Line 1,142: Line 1,139:
id="vu-person-link"
id="vu-person-link"
href="/wiki/Toonio_Noord">
href="/wiki/Toonio_Noord">
Open biography
Open page
</a>
</a>
</div>
</div>
Line 1,148: Line 1,145:


<div class="vu-people-controls">
<div class="vu-people-controls">
<button class="vu-slide-button" id="vu-person-prev" type="button" aria-label="Previous person"></button>
<button class="vu-slide-button" id="vu-person-prev" type="button" aria-label="Previous person">&lt;</button>
<button class="vu-slide-button" id="vu-person-toggle" type="button" aria-label="Pause slideshow">Pause</button>
<button class="vu-slide-button" id="vu-person-toggle" type="button" aria-label="Pause slideshow">Pause</button>


Line 1,157: Line 1,154:
<div class="vu-slide-count" id="vu-person-count">1 / 1</div>
<div class="vu-slide-count" id="vu-person-count">1 / 1</div>


<button class="vu-slide-button" id="vu-person-next" type="button" aria-label="Next person"></button>
<button class="vu-slide-button" id="vu-person-next" type="button" aria-label="Next person">&gt;</button>
</div>
</div>
</section>
</section>
<div class="vu-heading">
<div class="vu-heading">
<div class="vu-heading-title">How to use this wiki</div>
<div class="vu-heading-title">How to use this wiki</div>
Line 1,236: Line 1,232:
"use strict";
"use strict";


function ready(callback) {
function onReady(callback) {
if (document.readyState === "loading") {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", callback);
document.addEventListener("DOMContentLoaded", callback, { once: true });
} else {
} else {
callback();
callback();
Line 1,244: Line 1,240:
}
}


ready(function () {
onReady(function () {
var root = document.querySelector(".vu-main");
var root = document.querySelector(".vu-main");
var slider = document.getElementById("vu-people-slider");
var status = document.getElementById("vu-people-status");
var image = document.getElementById("vu-person-image");
var title = document.getElementById("vu-person-title");
var extract = document.getElementById("vu-person-extract");
var pageLink = document.getElementById("vu-person-link");
var count = document.getElementById("vu-person-count");
var previous = document.getElementById("vu-person-prev");
var next = document.getElementById("vu-person-next");
var toggle = document.getElementById("vu-person-toggle");


if (!root || !window.fetch) {
if (
!root ||
!slider ||
!status ||
!image ||
!title ||
!extract ||
!pageLink ||
!count ||
!previous ||
!next ||
!toggle
) {
return;
return;
}
}


var scriptPath = window.mw && mw.config
var fallbackImage = "/wiki/Special:Redirect/file/Vu-logo.png";
? mw.config.get("wgScriptPath")
var cacheKey = "vu-main-people-slides-v9";
: "/wiki";
var cacheLifetime = 6 * 60 * 60 * 1000;
var slideInterval = 9000;
var apiPath = "/wiki/api.php";
var people = [];
var currentIndex = 0;
var timer = null;
var playing = false;


var articlePath = window.mw && mw.config
previous.disabled = true;
? mw.config.get("wgArticlePath")
next.disabled = true;
: "/wiki/$1";
toggle.disabled = true;
count.textContent = "1 / 1";
toggle.textContent = "Play";
previous.textContent = "<";
next.textContent = ">";


var apiUrl = scriptPath + "/api.php";
function showStatus(message) {
var fallbackImage = "/wiki/Special:Redirect/file/Vu-logo.png";
if (!message) {
status.hidden = true;
status.textContent = "";
return;
}


function wikiUrl(title) {
status.textContent = message;
if (window.mw && mw.util) {
status.hidden = false;
return mw.util.getUrl(title);
}
 
function shuffle(items) {
var result = items.slice();
 
for (var index = result.length - 1; index > 0; index--) {
var randomIndex = Math.floor(Math.random() * (index + 1));
var temporary = result[index];
result[index] = result[randomIndex];
result[randomIndex] = temporary;
}
}


return articlePath.replace(
return result;
"$1",
encodeURIComponent(title.replace(/ /g, "_"))
);
}
}


function api(parameters) {
function cleanText(value) {
var search = new URLSearchParams(parameters);
return String(value || "")
return fetch(apiUrl + "?" + search.toString(), {
.replace(/\[\s*\d+(?:\.\d+)?\s*\]/g, "")
credentials: "same-origin"
.replace(/\s+/g, " ")
}).then(function (response) {
.trim();
if (!response.ok) {
throw new Error("API request failed");
}
return response.json();
});
}
}


/* Preserve normal red-link behavior for HTML anchors. */
function shorten(value, limit) {
var cleaned = cleanText(value);


var staticLinks = Array.prototype.slice.call(
if (!cleaned || cleaned.length <= limit) {
root.querySelectorAll("a[data-wiki-title]")
return cleaned;
);
}


var staticTitles = staticLinks
var cut = cleaned.slice(0, limit);
.map(function (link) {
var sentenceEnd = Math.max(
return link.getAttribute("data-wiki-title");
cut.lastIndexOf(". "),
})
cut.lastIndexOf("! "),
.filter(function (title, index, allTitles) {
cut.lastIndexOf("? ")
return title && allTitles.indexOf(title) === index;
);
});


function markMissingLinks() {
if (sentenceEnd > 180) {
if (!staticTitles.length) {
return cut.slice(0, sentenceEnd + 1);
return;
}
}


api({
var finalSpace = cut.lastIndexOf(" ");
action: "query",
return cut.slice(0, finalSpace > 0 ? finalSpace : limit) + "...";
format: "json",
}
formatversion: "2",
titles: staticTitles.join("|")
}).then(function (data) {
var missing = {};


if (data.query && data.query.pages) {
function getImage(page) {
data.query.pages.forEach(function (page) {
if (page.thumbnail && page.thumbnail.source) {
if (page.missing) {
return page.thumbnail.source;
missing[page.title] = true;
}
}
});
}


staticLinks.forEach(function (link) {
if (page.original && page.original.source) {
var title = link.getAttribute("data-wiki-title");
return page.original.source;
}


if (!missing[title]) {
return "";
return;
}
}


link.classList.add("new");
function pageUrl(pageTitle) {
link.href =
if (window.mw && mw.util) {
scriptPath +
return mw.util.getUrl(pageTitle);
"/index.php?title=" +
}
encodeURIComponent(title.replace(/ /g, "_")) +
 
"&action=edit&redlink=1";
return "/wiki/" + encodeURIComponent(pageTitle.replace(/ /g, "_"));
link.title = title + " (page does not exist)";
});
}).catch(function () {
return;
});
}
}


markMissingLinks();
function normalizePages(pages) {
return shuffle(
pages
.filter(function (page) {
return page && !page.missing && page.title;
})
.map(function (page) {
var pageExtract = shorten(page.extract, 650);


/* Random people slideshow. */
if (!pageExtract) {
pageExtract =
"This page is listed in Category:People. Open the page for the full article.";
}


var slider = document.getElementById("vu-people-slider");
return {
var status = document.getElementById("vu-people-status");
title: page.title,
var image = document.getElementById("vu-person-image");
extract: pageExtract,
var title = document.getElementById("vu-person-title");
image: getImage(page) || fallbackImage,
var extract = document.getElementById("vu-person-extract");
url: page.fullurl || pageUrl(page.title)
var link = document.getElementById("vu-person-link");
};
var count = document.getElementById("vu-person-count");
})
var previous = document.getElementById("vu-person-prev");
);
var next = document.getElementById("vu-person-next");
}
var toggle = document.getElementById("vu-person-toggle");


if (
function readCache() {
!slider ||
try {
!status ||
var cached = JSON.parse(
!image ||
window.sessionStorage.getItem(cacheKey) || "null"
!title ||
);
!extract ||
 
!link ||
if (
!count ||
cached &&
!previous ||
cached.savedAt &&
!next ||
Date.now() - cached.savedAt < cacheLifetime &&
!toggle
Array.isArray(cached.people) &&
) {
cached.people.length > 1
return;
) {
return cached.people;
}
} catch (error) {
return null;
}
 
return null;
}
}


var people = [];
function saveCache(items) {
var current = 0;
try {
var timer = null;
window.sessionStorage.setItem(
var playing = true;
cacheKey,
var interval = 9000;
JSON.stringify({
 
savedAt: Date.now(),
function shuffle(items) {
people: items
for (var i = items.length - 1; i > 0; i--) {
})
var j = Math.floor(Math.random() * (i + 1));
);
var temporary = items[i];
} catch (error) {
items[i] = items[j];
return;
items[j] = temporary;
}
}
return items;
}
}


function shorten(text, limit) {
function preloadNext() {
if (!text) {
if (people.length < 2) {
return "Open the biography to read more.";
return;
}
}


var clean = text.replace(/\s+/g, " ").trim();
var nextIndex = (currentIndex + 1) % people.length;
 
var preload = new Image();
if (clean.length <= limit) {
preload.src = people[nextIndex].image;
return clean;
}
}
 
var shortened = clean.slice(0, limit);
var sentence = Math.max(
shortened.lastIndexOf(". "),
shortened.lastIndexOf("! "),
shortened.lastIndexOf("? ")
);
 
if (sentence > 180) {
return shortened.slice(0, sentence + 1);
}
 
var space = shortened.lastIndexOf(" ");
return shortened.slice(0, space > 0 ? space : limit) + "…";
}


function restartProgress() {
function restartProgress() {
Line 1,416: Line 1,434:
void slider.offsetWidth;
void slider.offsetWidth;


if (playing) {
if (playing && people.length > 1) {
slider.classList.add("is-playing");
slider.classList.add("is-playing");
}
}
}
}


function schedule() {
function scheduleNext() {
if (timer) {
if (timer) {
window.clearTimeout(timer);
window.clearTimeout(timer);
Line 1,433: Line 1,451:


timer = window.setTimeout(function () {
timer = window.setTimeout(function () {
show(current + 1);
showSlide(currentIndex + 1);
}, interval);
}, slideInterval);
}
}


function show(index) {
function showSlide(index) {
if (!people.length) {
if (!people.length) {
return;
return;
}
}


current = (index + people.length) % people.length;
currentIndex = (index + people.length) % people.length;
var person = people[current];
var person = people[currentIndex];


slider.classList.add("is-changing");
slider.classList.add("is-changing");
Line 1,451: Line 1,469:
image.alt = person.title;
image.alt = person.title;
title.textContent = person.title;
title.textContent = person.title;
extract.textContent = shorten(person.extract, 620);
extract.textContent = person.extract;
link.href = person.url || wikiUrl(person.title);
pageLink.href = person.url;
count.textContent = (current + 1) + " / " + people.length;
pageLink.textContent = "Open page";
count.textContent =
String(currentIndex + 1) + " / " + String(people.length);


window.setTimeout(function () {
window.setTimeout(function () {
slider.classList.remove("is-changing");
slider.classList.remove("is-changing");
}, 40);
}, 40);
}, 150);


schedule();
preloadNext();
}, 140);
 
scheduleNext();
}
}


function setPlaying(value) {
function setPlaying(value) {
playing = value;
playing = Boolean(value && people.length > 1);
toggle.textContent = playing ? "Pause" : "Play";
toggle.textContent = playing ? "Pause" : "Play";
toggle.setAttribute(
toggle.setAttribute(
Line 1,472: Line 1,494:


if (playing) {
if (playing) {
schedule();
scheduleNext();
} else {
} else {
if (timer) {
if (timer) {
window.clearTimeout(timer);
window.clearTimeout(timer);
}
}
slider.classList.remove("is-playing");
slider.classList.remove("is-playing");
}
}
}
function activateSlides(items) {
if (!Array.isArray(items) || !items.length) {
showStatus("No people pages were loaded.");
return;
}
people = shuffle(items);
currentIndex = 0;
previous.disabled = people.length < 2;
next.disabled = people.length < 2;
toggle.disabled = people.length < 2;
showStatus("");
showSlide(0);
setPlaying(people.length > 1);
}
}


previous.addEventListener("click", function () {
previous.addEventListener("click", function () {
show(current - 1);
showSlide(currentIndex - 1);
});
});


next.addEventListener("click", function () {
next.addEventListener("click", function () {
show(current + 1);
showSlide(currentIndex + 1);
});
});


Line 1,502: Line 1,541:
slider.addEventListener("mouseleave", function () {
slider.addEventListener("mouseleave", function () {
if (playing) {
if (playing) {
schedule();
scheduleNext();
}
}
});
});
Line 1,512: Line 1,551:
});
});


function loadCategoryMembers(continuation, collected) {
function buildQuery(params) {
var parameters = {
var parts = [];
action: "query",
format: "json",
formatversion: "2",
list: "categorymembers",
cmtitle: "Category:People",
cmnamespace: "0",
cmtype: "page",
cmlimit: "max"
};


if (continuation) {
Object.keys(params).forEach(function (key) {
parameters.cmcontinue = continuation;
parts.push(
}
encodeURIComponent(key) +
 
"=" +
return api(parameters).then(function (data) {
encodeURIComponent(params[key])
var members = collected.concat(
data.query && data.query.categorymembers
? data.query.categorymembers
: []
);
);
});


if (data.continue && data.continue.cmcontinue) {
return parts.join("&");
return loadCategoryMembers(
}
data.continue.cmcontinue,
members
);
}


return members;
function requestJson(params) {
return window
.fetch(apiPath + "?" + buildQuery(params), {
credentials: "same-origin"
})
.then(function (response) {
if (!response.ok) {
throw new Error("API HTTP " + response.status);
}
 
return response.json();
});
}
 
function copyParams(params) {
var copied = {};
 
Object.keys(params).forEach(function (key) {
copied[key] = params[key];
});
 
return copied;
}
 
function loadCategoryMembers(params, collectedPages) {
return requestJson(params).then(function (data) {
var nextPages =
data &&
data.query &&
Array.isArray(data.query.categorymembers)
? data.query.categorymembers
: [];
var allPages = collectedPages.concat(nextPages);
 
if (data && data.continue) {
var nextParams = copyParams(params);
 
Object.keys(data.continue).forEach(function (key) {
nextParams[key] = data.continue[key];
});
 
showStatus("Loading people... " + String(allPages.length));
return loadCategoryMembers(nextParams, allPages);
}
 
return allPages;
});
});
}
}


function loadPeopleDetails(members) {
function uniqueTitles(items) {
var selected = shuffle(members.slice()).slice(0, 18);
var seen = {};
var pageIds = selected.map(function (member) {
var titles = [];
return member.pageid;
 
items.forEach(function (item) {
if (!item || !item.title || seen[item.title]) {
return;
}
 
seen[item.title] = true;
titles.push(item.title);
});
});


if (!pageIds.length) {
return titles;
throw new Error("No people found");
}
 
function loadPageDetails(titles) {
var groups = [];
 
for (var index = 0; index < titles.length; index += 1) {
groups.push(titles.slice(index, index + 1));
}
}


return api({
return groups
action: "query",
.reduce(function (chain, group) {
format: "json",
return chain.then(function (pages) {
formatversion: "2",
return requestJson({
pageids: pageIds.join("|"),
action: "query",
prop: "extracts|pageimages|info",
titles: group.join("|"),
exintro: "1",
prop: "extracts|pageimages|info",
explaintext: "1",
exintro: 1,
exchars: "750",
explaintext: 1,
piprop: "thumbnail|original",
exchars: 700,
pithumbsize: "900",
piprop: "thumbnail|original",
inprop: "url"
pithumbsize: 1000,
}).then(function (data) {
inprop: "url",
var pages = data.query && data.query.pages
redirects: 1,
? data.query.pages
formatversion: 2,
: [];
format: "json"
 
}).then(function (data) {
return pages
var nextPages =
.filter(function (page) {
data &&
return page && page.title && !page.missing;
data.query &&
})
Array.isArray(data.query.pages)
.map(function (page) {
? data.query.pages
var pageImage = "";
: [];
 
 
if (page.thumbnail && page.thumbnail.source) {
if (
pageImage = page.thumbnail.source;
(pages.length + nextPages.length) % 10 === 0 ||
} else if (page.original && page.original.source) {
pages.length + nextPages.length === titles.length
pageImage = page.original.source;
) {
}
showStatus(
 
"Loading details... " +
return {
String(pages.length + nextPages.length) +
title: page.title,
" / " +
extract: page.extract || "",
String(titles.length)
image: pageImage,
);
url: page.fullurl || wikiUrl(page.title)
}
};
return pages.concat(nextPages);
});
});
});
});
}
}, Promise.resolve([]));
 
}
status.hidden = false;
 
 
var cachedPeople = readCache();
loadCategoryMembers("", [])
 
.then(loadPeopleDetails)
if (cachedPeople) {
.then(function (loadedPeople) {
activateSlides(cachedPeople);
if (!loadedPeople.length) {
}
throw new Error("No biographies loaded");
 
}
if (!window.fetch) {
 
showStatus("This browser cannot load the people list.");
people = shuffle(loadedPeople);
return;
status.hidden = true;
}
show(0);
 
})
showStatus("Loading people...");
.catch(function () {
loadCategoryMembers(
status.textContent = "The random biographies could not be loaded.";
{
people = [{
action: "query",
title: "Toonio Noord",
list: "categorymembers",
extract: extract.textContent,
cmtitle: "Category:People",
image: image.src,
cmnamespace: 0,
url: link.href
cmtype: "page",
}];
cmlimit: "max",
count.textContent = "1 / 1";
formatversion: 2,
setPlaying(false);
format: "json"
});
},
});
[]
})();
)
</script>
.then(function (members) {
var titles = uniqueTitles(members);
showStatus("Loading details... 0 / " + String(titles.length));
return loadPageDetails(titles);
})
.then(function (pages) {
var loadedPeople = normalizePages(pages);
 
saveCache(loadedPeople);
activateSlides(loadedPeople);
})
.catch(function () {
showStatus("Could not load people list.");
});
 
/*
* Raw HTML links need a separate existence check so missing pages
* keep MediaWiki's red-link appearance.
*/
if (window.mw && mw.loader) {
mw.loader
.using(["mediawiki.api", "mediawiki.util"])
.then(function () {
var api = new mw.Api();
var links = Array.prototype.slice.call(
root.querySelectorAll("a[data-wiki-title]")
);
 
var titles = links
.map(function (item) {
return item.getAttribute("data-wiki-title");
})
.filter(function (item, index, allItems) {
return item && allItems.indexOf(item) === index;
});
 
if (!titles.length) {
return;
}
 
return api
.get({
action: "query",
titles: titles.join("|"),
formatversion: 2
})
.then(function (data) {
var missing = {};
 
if (data.query && Array.isArray(data.query.pages)) {
data.query.pages.forEach(function (page) {
if (page.missing) {
missing[page.title] = true;
}
});
}
 
links.forEach(function (item) {
var pageTitle =
item.getAttribute("data-wiki-title");
 
if (!missing[pageTitle]) {
return;
}
 
item.classList.add("new");
item.href = mw.util.getUrl(pageTitle, {
action: "edit",
redlink: 1
});
item.title =
pageTitle + " (page does not exist)";
});
});
});
}
});
})();
</script>
 
</html>
</html>


Line 1,632: Line 1,792:


[[Category:Reference pages]]
[[Category:Reference pages]]
'''
path = Path("/mnt/data/VU_Main_Page_with_People_Slideshow.html")
path.write_text(content, encoding="utf-8")
print(f"Created {path} ({path.stat().st_size:,} bytes)")