Main Page: Difference between revisions
No edit summary |
No edit summary |
||
| Line 1,117: | 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"> | <div class="vu-people-status" id="vu-people-status" hidden></div> | ||
<div class="vu-people-stage"> | <div class="vu-people-stage"> | ||
| Line 1,139: | Line 1,139: | ||
id="vu-person-link" | id="vu-person-link" | ||
href="/wiki/Toonio_Noord"> | href="/wiki/Toonio_Noord"> | ||
Open | Open page | ||
</a> | </a> | ||
</div> | </div> | ||
| Line 1,233: | Line 1,233: | ||
"use strict"; | "use strict"; | ||
function | 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,241: | Line 1,241: | ||
} | } | ||
onReady(function () { | |||
var root = document.querySelector(".vu-main"); | var root = document.querySelector(".vu-main"); | ||
var slider = document.getElementById("vu-people-slider"); | var slider = document.getElementById("vu-people-slider"); | ||
var image = document.getElementById("vu-person-image"); | var image = document.getElementById("vu-person-image"); | ||
var title = document.getElementById("vu-person-title"); | var title = document.getElementById("vu-person-title"); | ||
var extract = document.getElementById("vu-person-extract"); | var extract = document.getElementById("vu-person-extract"); | ||
var | var pageLink = document.getElementById("vu-person-link"); | ||
var count = document.getElementById("vu-person-count"); | var count = document.getElementById("vu-person-count"); | ||
var previous = document.getElementById("vu-person-prev"); | var previous = document.getElementById("vu-person-prev"); | ||
var next = document.getElementById("vu-person-next"); | var next = document.getElementById("vu-person-next"); | ||
var toggle = document.getElementById("vu-person-toggle"); | var toggle = document.getElementById("vu-person-toggle"); | ||
if ( | |||
!root || | |||
return | !slider || | ||
!image || | |||
!title || | |||
!extract || | |||
!pageLink || | |||
!count || | |||
!previous || | |||
!next || | |||
!toggle | |||
) { | |||
return; | |||
} | } | ||
var fallbackImage = "/wiki/Special:Redirect/file/Vu-logo.png"; | |||
var cacheKey = "vu-main-people-slides-v4"; | |||
var cacheLifetime = 6 * 60 * 60 * 1000; | |||
var slideInterval = 9000; | |||
var people = []; | |||
var currentIndex = 0; | |||
var timer = null; | |||
var playing = false; | |||
previous.disabled = true; | |||
next.disabled = true; | |||
toggle.disabled = true; | |||
count.textContent = "1 / 1"; | |||
toggle.textContent = "Play"; | |||
function shuffle(items) { | function shuffle(items) { | ||
for (var | var result = items.slice(); | ||
var | |||
var temporary = | 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 result; | |||
} | } | ||
function | function cleanText(value) { | ||
return String(value || "") | |||
.replace(/\[\s*\d+(?:\.\d+)?\s*\]/g, "") | |||
.replace(/\s+/g, " ") | |||
.trim(); | |||
} | } | ||
function shorten(value, limit) { | |||
var cleaned = cleanText(value); | |||
if (!cleaned || cleaned.length <= limit) { | |||
return cleaned; | |||
} | |||
var cut = cleaned.slice(0, limit); | |||
var sentenceEnd = Math.max( | |||
cut.lastIndexOf(". "), | |||
cut.lastIndexOf("! "), | |||
cut.lastIndexOf("? ") | |||
); | |||
if (sentenceEnd > 180) { | |||
return cut.slice(0, sentenceEnd + 1); | |||
} | |||
if ( | |||
var finalSpace = cut.lastIndexOf(" "); | |||
return cut.slice(0, finalSpace > 0 ? finalSpace : limit) + "…"; | |||
} | } | ||
function | function getImage(page) { | ||
if (page.thumbnail && page.thumbnail.source) { | |||
return page.thumbnail.source; | |||
} | |||
if (page.original && page.original.source) { | |||
return page.original.source; | |||
if ( | } | ||
return ""; | return ""; | ||
} | } | ||
function | function normalizePages(data) { | ||
var | var pages = | ||
data && | |||
data.query && | |||
Array.isArray(data.query.pages) | |||
? data.query.pages | |||
: []; | |||
return shuffle( | |||
pages | |||
.filter(function (page) { | |||
return ( | |||
page && | |||
!page.missing && | |||
page.title && | |||
cleanText(page.extract).length >= 60 && | |||
getImage(page) | |||
); | |||
}) | |||
.map(function (page) { | |||
return { | |||
title: page.title, | |||
extract: shorten(page.extract, 650), | |||
image: getImage(page), | |||
url: | |||
page.fullurl || | |||
(window.mw && mw.util | |||
? mw.util.getUrl(page.title) | |||
: "/wiki/" + | |||
encodeURIComponent( | |||
page.title.replace(/ /g, "_") | |||
)) | |||
}; | |||
}) | |||
).slice(0, 12); | |||
} | } | ||
function | function readCache() { | ||
try { | |||
var cached = JSON.parse( | |||
window.sessionStorage.getItem(cacheKey) || "null" | |||
); | |||
if ( | |||
if ( | cached && | ||
cached.savedAt && | |||
Date.now() - cached.savedAt < cacheLifetime && | |||
Array.isArray(cached.people) && | |||
cached.people.length > 1 | |||
) { | |||
return cached.people; | |||
} | |||
} catch (error) { | |||
return null; | |||
} | |||
return null; | |||
} | } | ||
function | function saveCache(items) { | ||
try { | |||
window.sessionStorage.setItem( | |||
cacheKey, | |||
JSON.stringify({ | |||
savedAt: Date.now(), | |||
people: items | |||
}) | |||
); | |||
} catch (error) { | |||
return; | |||
} | |||
} | } | ||
function | function preloadNext() { | ||
if (people.length < 2) { | |||
return; | |||
return | |||
} | } | ||
var | |||
var nextIndex = (currentIndex + 1) % people.length; | |||
var preload = new Image(); | |||
preload.src = people[nextIndex].image; | |||
} | } | ||
function restartProgress() { | function restartProgress() { | ||
slider.classList.remove("is-playing"); | slider.classList.remove("is-playing"); | ||
void slider.offsetWidth; | void slider.offsetWidth; | ||
if (playing && people.length > 1) slider.classList.add("is-playing"); | |||
if (playing && people.length > 1) { | |||
slider.classList.add("is-playing"); | |||
} | |||
} | } | ||
function | function scheduleNext() { | ||
if (timer) window.clearTimeout(timer); | if (timer) { | ||
window.clearTimeout(timer); | |||
} | |||
restartProgress(); | restartProgress(); | ||
if (!playing || people.length < 2) return; | |||
timer = window.setTimeout(function () { | if (!playing || people.length < 2) { | ||
return; | |||
} | |||
timer = window.setTimeout(function () { | |||
showSlide(currentIndex + 1); | |||
}, slideInterval); | |||
} | } | ||
function | function showSlide(index) { | ||
if (!people.length) return; | if (!people.length) { | ||
return; | |||
var person = people[ | } | ||
currentIndex = (index + people.length) % people.length; | |||
var person = people[currentIndex]; | |||
slider.classList.add("is-changing"); | slider.classList.add("is-changing"); | ||
window.setTimeout(function () { | window.setTimeout(function () { | ||
image.src = person.image || fallbackImage; | image.src = person.image || fallbackImage; | ||
| Line 1,472: | Line 1,460: | ||
title.textContent = person.title; | title.textContent = person.title; | ||
extract.textContent = person.extract; | extract.textContent = person.extract; | ||
pageLink.href = person.url; | |||
count.textContent = ( | pageLink.textContent = "Open page"; | ||
window.setTimeout(function () { slider.classList.remove("is-changing"); }, 40); | count.textContent = | ||
}, | String(currentIndex + 1) + " / " + String(people.length); | ||
window.setTimeout(function () { | |||
slider.classList.remove("is-changing"); | |||
}, 40); | |||
preloadNext(); | |||
}, 140); | |||
scheduleNext(); | |||
} | } | ||
function setPlaying(value) { | function setPlaying(value) { | ||
playing = value && people.length > 1; | playing = Boolean(value && people.length > 1); | ||
toggle.textContent = playing ? "Pause" : "Play"; | toggle.textContent = playing ? "Pause" : "Play"; | ||
toggle.setAttribute("aria-label", playing ? "Pause slideshow" : "Play slideshow"); | toggle.setAttribute( | ||
if (playing) | "aria-label", | ||
else { | playing ? "Pause slideshow" : "Play slideshow" | ||
if (timer) window.clearTimeout(timer); | ); | ||
if (playing) { | |||
scheduleNext(); | |||
} else { | |||
if (timer) { | |||
window.clearTimeout(timer); | |||
} | |||
slider.classList.remove("is-playing"); | slider.classList.remove("is-playing"); | ||
} | } | ||
} | } | ||
previous.addEventListener("click", function () { | function activateSlides(items) { | ||
next.addEventListener("click", function () { | if (!Array.isArray(items) || items.length < 2) { | ||
toggle.addEventListener("click", function () { setPlaying(!playing); }); | return; | ||
} | |||
people = shuffle(items); | |||
currentIndex = 0; | |||
previous.disabled = false; | |||
next.disabled = false; | |||
toggle.disabled = false; | |||
showSlide(0); | |||
setPlaying(true); | |||
} | |||
previous.addEventListener("click", function () { | |||
showSlide(currentIndex - 1); | |||
}); | |||
next.addEventListener("click", function () { | |||
showSlide(currentIndex + 1); | |||
}); | |||
toggle.addEventListener("click", function () { | |||
setPlaying(!playing); | |||
}); | |||
slider.addEventListener("mouseenter", function () { | slider.addEventListener("mouseenter", function () { | ||
if (playing && timer) { window.clearTimeout(timer); slider.classList.remove("is-playing"); } | if (playing && timer) { | ||
window.clearTimeout(timer); | |||
slider.classList.remove("is-playing"); | |||
} | |||
}); | |||
slider.addEventListener("mouseleave", function () { | |||
if (playing) { | |||
scheduleNext(); | |||
} | |||
}); | |||
image.addEventListener("error", function () { | |||
if (image.src.indexOf("Vu-logo.png") === -1) { | |||
image.src = fallbackImage; | |||
} | |||
}); | }); | ||
var cachedPeople = readCache(); | |||
if (cachedPeople) { | |||
.then(function ( | activateSlides(cachedPeople); | ||
} | |||
return | |||
if (!window.mw || !mw.loader) { | |||
return; | |||
} | |||
mw.loader | |||
.using(["mediawiki.api", "mediawiki.util"]) | |||
.then(function () { | |||
var api = new mw.Api(); | |||
/* | |||
* One request obtains category members, lead extracts, | |||
* page images and page URLs. | |||
*/ | |||
return api.get({ | |||
action: "query", | |||
generator: "categorymembers", | |||
gcmtitle: "Category:People", | |||
gcmnamespace: 0, | |||
gcmtype: "page", | |||
gcmlimit: 120, | |||
prop: "extracts|pageimages|info", | |||
exintro: 1, | |||
explaintext: 1, | |||
exchars: 700, | |||
piprop: "thumbnail|original", | |||
pithumbsize: 1000, | |||
inprop: "url", | |||
redirects: 1, | |||
formatversion: 2 | |||
}); | |||
}) | |||
.then(function (data) { | |||
var loadedPeople = normalizePages(data); | |||
if (loadedPeople.length < 2) { | |||
return; | |||
} | |||
saveCache(loadedPeople); | |||
activateSlides(loadedPeople); | |||
}) | }) | ||
.then(function ( | .catch(function () { | ||
/* | |||
* The initial slide remains visible if the request fails. | |||
* No permanent loading message is shown. | |||
*/ | |||
return; | |||
}); | |||
/* | |||
* Raw HTML links need a separate existence check so missing pages | |||
* keep MediaWiki's red-link appearance. | |||
*/ | |||
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; | ||
} | } | ||
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)"; | |||
}); | |||
}); | |||
}) | }) | ||
.catch(function () { | .catch(function () { | ||
return; | |||
}); | }); | ||
}); | }); | ||