User:Ioaxxere/fixTimeoutErrors.js
Note: You may have to bypass your browser’s cache to see the changes. In addition, after saving a sitewide CSS file such as MediaWiki:Common.css, it will take 5-10 minutes before the changes take effect, even if you clear your cache.
- Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
- Konqueror and Chrome: click Reload or press F5;
- Opera: clear the cache in Tools → Preferences;
- Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.
- This script lacks a documentation subpage. Please create it.
- Useful links: root page • root page’s subpages • links • redirects • your own
// Fixes timeout errors on long pages such as [[a]].
// Note: the script does not run other gadgets after replacing the HTML aside from [[MediaWiki:Gadget-defaultVisibilityToggles.js]]
// <nowiki>
(() => {
let actionAPI = new mw.Api({ ajax: { headers: { "Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]" } } });
let pageTitle = mw.config.values.wgTitle;
let mobileHTMLformat = Boolean(document.querySelector(".mw-parser-output > section"));
// Collect all the elements until reaching the next L2 header.
const L2_contentOf = L2_header => {
let contentElems = [];
let elem = L2_header.nextElementSibling;
while (elem && !elem.matches(".mw-heading2")) {
contentElems.push(elem);
elem = elem.nextElementSibling;
}
return contentElems;
}
// Check whether an element contains a timeout error.
// e.g. <span class="scribunto-error" id="mw-scribunto-error-3d8151e8">The time allocated for running scripts has expired.</span>
const hasTimeoutError = elem => Array.from(elem.querySelectorAll(".scribunto-error"))
.some(error => error.textContent === "The time allocated for running scripts has expired.");
let sections = Array.from(document.querySelectorAll(".mw-heading"));
let queries = [];
// Find the section number to use in the API.
for (let i = 0; i < sections.length; i++) {
// Check whether the section is a broken L2 containing a timeout error.
if (sections[i].matches(".mw-heading2") && L2_contentOf(sections[i]).some(hasTimeoutError)) {
// Sections are one-indexed (section 0 comes before the first header)
let params1 = {
action: "parse",
prop: "wikitext",
page: pageTitle,
section: i + 1,
format: "json"
}
// Query wikitext of that section (API query 1), then convert that wikitext into HTML (API query 2)
queries.push(actionAPI.get(params1).then(response1 => {
let params2 = {
action: "parse",
format: "json",
title: pageTitle,
prop: "text",
text: response1.parse.wikitext["*"]
};
return actionAPI.post(params2);
}).then(response2 => {
let responseDocument = new DOMParser().parseFromString(response2.parse.text["*"], "text/html");
// Remove TOC.
let TOC = responseDocument.querySelector("#toc");
if (TOC) TOC.remove();
// Replace original (error-filled) HTML with fixed HTML.
L2_contentOf(sections[i]).forEach(elem => elem.remove());
let nextL2 = sections[i].nextElementSibling; // possibly null
for (let elem of Array.from(responseDocument.querySelector(".mw-parser-output").children)) {
sections[i].parentNode.insertBefore(elem, nextL2);
elem.querySelectorAll("ol > li").forEach(window.setupHiddenQuotes);
elem.querySelectorAll(".NavFrame").forEach(window.createNavToggle);
elem.querySelectorAll(".vsSwitcher").forEach(window.viewSwitching);
}
// Remove extra heading element(s).
sections[i].nextElementSibling.remove();
if (mobileHTMLformat)
sections[i].nextElementSibling.remove();
// Sync up header open/close status with the content.
if (mobileHTMLformat) {
if (sections[i].matches(".open-block")) {
sections[i].nextElementSibling.classList.add("open-block");
} else {
sections[i].nextElementSibling.setAttribute("hidden", "until-found");
}
}
}));
}
}
Promise.all(queries).then(() => {
// Fix edit links.
let editlinks = Array.from(document.querySelectorAll(".mw-editsection"));
for (let i = 0; i < editlinks.length; i++) {
let editlink = editlinks[i].querySelector("a");
// Edit links added by the API don't have this class for some reason.
if (!editlink.matches(".edithere-target")) {
// One-indexed as before.
editlink.href = editlink.href.replace(/§ion=\d+$/, "§ion=" + (i + 1));
}
}
// Fix header IDs.
let existingHeaderIds = new Set();
for (let header of document.querySelectorAll(".mw-heading")) {
let headerID = header.firstElementChild.textContent.replaceAll(" ", "_");
if (!existingHeaderIds.has(headerID)) {
header.firstElementChild.setAttribute("id", headerID);
existingHeaderIds.add(headerID);
} else {
let counter = 2;
while (existingHeaderIds.has(headerID + "_" + counter))
counter++;
existingHeaderIds.add(headerID + "_" + counter);
header.firstElementChild.setAttribute("id", headerID + "_" + counter);
}
}
// Fix section classes and IDs, skipping section 0.
let sectionElems = document.querySelectorAll(".mw-parser-output > section");
for (let i = 1; i < sectionElems.length; i++) {
sectionElems[i].classList.remove("mf-section-1");
sectionElems[i].classList.add("mf-section-" + i);
sectionElems[i].setAttribute("id", "content-collapsible-block-" + (i - 1));
}
});
})();
// </nowiki>