// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw, OO */
/**
* Adds a jump box to category pages.
*
* Author(s): Surjection
* Last updated: 2024-11-25
*/
const mwApi = new mw.Api({ ajax: { headers: { "Api-User-Agent": "CategoryJumpTo gadget by ]" } } });
const strings = {
"en": {
wiktLangCode: "en",
jumpToPage: "Jump to page",
jump: "Jump",
jumpError: "There was an error while jumping. If this reoccurs, please report it on $1.",
greasePit: "the Grease Pit",
}
};
const NS_PROJECT = 4;
const GREASE_PIT_NAME = "Grease pit";
const userLanguage = mw.config.get("wgUserLanguage");
const REQUIRE_TOC = false;
const REQUIRE_CATEGORY_PAGING = true;
const resolveString = (strings, stringKey, defaultValue) => {
try {
if (defaultValue == null && stringKey)
defaultValue = stringKey;
if (strings == null)
return defaultValue;
// try to use user language first
if (strings != null) {
if (stringKey == null)
return strings;
else if (strings != null)
return strings;
}
// fall back to en
if (strings.en != null) {
if (stringKey == null)
return strings.en;
else if (strings.en != null)
return strings.en;
}
return defaultValue;
} catch (_) {
// invalid strings object, etc.
return defaultValue;
}
};
const createSearchBox = (jumpCallback) => {
const outerDiv = $('<div>');
outerDiv.attr("style", "display: inline-block; margin-top: 0.5em; border: solid 1px var(--border-color-base, gray); border-collapse: collapse;");
const table = $('<table>');
outerDiv.append(table);
const body = $('<div class="center"></div>');
table.append(body.wrap('<td></td>').parent().wrap('<tr></tr>').parent().wrap('<tbody></tbody>').parent());
const MAX_LENGTH = 256;
const textInput = new OO.ui.TextInputWidget({ value: "", maxLength: MAX_LENGTH });
const jumpButton = new OO.ui.ButtonWidget({ label: resolveString(strings, "jumpToPage", "Jump to page") });
const submit = () => {
textInput.setDisabled(true);
jumpCallback(textInput.getValue().slice(0, MAX_LENGTH)).finally(() => {
textInput.setDisabled(false);
});
};
textInput.on("enter", () => submit());
jumpButton.on("click", () => submit());
const fieldset = new OO.ui.FieldsetLayout({
items: [
new OO.ui.ActionFieldLayout(textInput, jumpButton)
]
});
body.append(fieldset.$element);
return outerDiv;
};
const htmlUnescape = (text) => new DOMParser().parseFromString(text, "text/html").body.textContent;
const getSortKey = (languageCode, text) =>
new Promise((resolve, reject) => {
const escapedText = text.replace(//g, (m) => `&#${m.charCodeAt(0)};`);
const wikitext = `{{sortkey|${languageCode}|${escapedText}}}`;
const params = {
action: "expandtemplates",
format: "json",
prop: "wikitext",
text: wikitext
};
mwApi.post(params).then(response => {
resolve(htmlUnescape(response.expandtemplates.wikitext));
}).catch((e) => {
reject(e);
});
});
const addJumpBox = () => {
const catfix = $("#catfix");
if (!catfix.length) return;
let languageCode = catfix.find("span");
if (!languageCode.length) return;
languageCode = languageCode.attr("lang");
// do not display if there are no pages
if (!$("#mw-pages").length) return;
const toc = $("#toc");
if (REQUIRE_TOC && !toc.length) return;
if (REQUIRE_CATEGORY_PAGING && !$('#mw-pages a, #mw-pages a').length) return;
const searchBox = createSearchBox((string) =>
new Promise((resolve, reject) => {
getSortKey(languageCode, string).then((sortKey) => {
const url = new URL(window.location.href);
url.searchParams.delete("pagefrom");
url.searchParams.set("from", sortKey);
resolve();
window.location.href = url.href;
}).catch(() => {
const greasePitUrl = new mw.Title(GREASE_PIT_NAME, NS_PROJECT).getUrl();
const greasePitLink = $("<a>").attr("href", greasePitUrl).text(resolveString(strings, "greasePit"));
const errorMessage = $("<span>").text(resolveString(strings, "jumpError"));
errorMessage.html(errorMessage.html().replace('\$1', greasePitLink.outerHTML));
mw.notification.notify(errorMessage, { "type": "error" });
reject();
});
})
);
const target = toc.length ? toc : catfix;
searchBox.wrap("<div>").parent().insertBefore(target.first());
};
addJumpBox();
// </nowiki>