MediaWiki:Gadget-DisamAssist.js
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Internet Explorer或Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
- Opera:按 Ctrl-F5。
/**
* SPDX-License-Identifier: CC-BY-SA-4.0
* _addText: '{{Gadget Header|license=CC-BY-SA-4.0}}'
*
* @base {@link https://zh.wikipedia.org/wiki/User:Peacearth/DisamAssist.js}
* @base {@link https://es.wikipedia.org/wiki/Usuario:Qwertyytrewqqwerty/DisamAssist.js}
* @base {@link https://es.wikipedia.org/wiki/Usuario:Qwertyytrewqqwerty/DisamAssist.css}
* @source {@link https://git.qiuwen.net.cn/InterfaceAdmin/QiuwenGadgets/src/branch/master/src/DisamAssist}
* @license CC-BY-SA-4.0 {@link https://www.qiuwenbaike.cn/wiki/H:CC-BY-SA-4.0}
*/
/**
* +------------------------------------------------------------+
* | === WARNING: GLOBAL GADGET FILE === |
* +------------------------------------------------------------+
* | All changes should be made in the repository, |
* | otherwise they will be lost. |
* +------------------------------------------------------------+
* | Changes to this page may affect many users. |
* | Please discuss changes by opening an issue before editing. |
* +------------------------------------------------------------+
*/
/* <nowiki> */
(() => {
"use strict";
// dist/DisamAssist/DisamAssist.js
function _createForOfIteratorHelper(r, e) {
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (!t) {
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
t && (r = t);
var n = 0, F = function() {
};
return { s: F, n: function() {
return n >= r.length ? { done: true } : { done: false, value: r[n++] };
}, e: function(r2) {
throw r2;
}, f: F };
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var o, a = true, u = false;
return { s: function() {
t = t.call(r);
}, n: function() {
var r2 = t.next();
return a = r2.done, r2;
}, e: function(r2) {
u = true, o = r2;
}, f: function() {
try {
a || null == t.return || t.return();
} finally {
if (u) throw o;
}
} };
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
//! src/DisamAssist/modules/api.ts
var import_ext_gadget = require("ext.gadget.Util");
var api = (0, import_ext_gadget.initMwApi)("DisamAssist/1.1");
//! src/DisamAssist/modules/config.ts
var cfg = {
/* Categories where disambiguation pages are added (usually by a template like {{Disambiguation}} */
disamCategories: ["全部消歧义页面"],
/* "Canonical names" of the templates that may appear after ambiguous links and which should be removed when fixing those links */
disamLinkTemplates: ["Disambiguation needed", "Ambiguous link", "Amblink", "Dab needed", "Disamb-link", "Disambig needed", "Disambiguate", "Dn", "Needdab"],
/* "Canonical names" of the templates that designate intentional links to disambiguation pages */
disamLinkIgnoreTemplates: ["R from ambiguous page", "R to disambiguation page", "R from incomplete disambiguation"],
/* Format string for "Foo (disambiguation)"-style pages */
disamFormat: "$1(消歧义)",
/* Regular expression matching the titles of disambiguation pages (when they are different from the titles of the primary topics) */
disamRegExp: "^(.*)((消歧义|消歧義))$",
/* Text that will be inserted after the link if the user requests help. If the value is null, the option to request help won't be offered */
disamNeededText: "{{dn|date={{".concat("subst:", "CURRENTMONTHNAME", "}} {{", "subst:", "CURRENTYEAR}}}}"),
/* Content of the "Foo (disambiguation)" pages that will be created automatically when using DisamAssist from a "Foo" page */
redirectToDisam: "#重定向 [[$1]]".concat("{{R to disambiguation page}}"),
/* Whether intentional links to disambiguation pages can be explicitly marked by adding " (disambiguation)" */
intentionalLinkOption: false,
/* Namespaces that will be searched for incoming links to the disambiguation page (pages in other namespaces will be ignored) */
targetNamespaces: [6, 10, 14, 118, 0],
/* Number of backlinks that will be downloaded at once When using blredirect, the maximum limit is supposedly halved (see http://www.mediawiki.org/wiki/API:Backlinks) */
backlinkLimit: 250,
/* Number of titles we can query for at once */
queryTitleLimit: 50,
/* Number of characters before and after the incoming link that will be displayed */
radius: 300,
/* Height of the context box, in lines */
numContextLines: 4,
/* Number of pages that will be stored before saving, so that changes to them can be undone if need be */
historySize: 2,
/* Minimum time in seconds since the last change was saved before a new edit can be made. A negative value or 0 disables the cooldown. Users with the "bot" right won't be affected by the cooldown */
editCooldown: 0,
/* Specify how the watchlist is affected by DisamAssist edits. Possible values: "watch", "unwatch", "preferences", "nochange" */
watch: "nochange"
};
//! src/DisamAssist/modules/messages.js
var messages = {
start: "为链接消歧义",
startMain: "为链至主条目的链接消歧义",
startSame: "为链至消歧义页的链接消歧义",
close: "关闭",
undo: "复原",
omit: "跳过",
refresh: "重新整理",
titleAsText: "其它目标",
disamNeeded: "标示{{dn}}",
intentionalLink: "有意链到消歧义页的链接",
titleAsTextPrompt: "请输入新的链接目标:",
removeLink: "去除链接",
optionMarker: " [链接到这里]",
targetOptionMarker: " [当前目标]",
redirectOptionMarker: " [当前目标(重定向)]",
pageTitleLine: '<a href="$1">$2</a>:',
noMoreLinks: "没有需要消歧义的链接了。",
pendingEditCounter: "已保存$1个,已编辑$2个",
pendingEditBox: "DisamAssist正在储存您的编辑($1)。",
pendingEditBoxTimeEstimation: "$1;剩余时间:$2",
pendingEditBoxLimited: "在所有更改保存前,请不要关闭页面;你可以在另一个页面编辑求闻百科,但是请勿同时使用多个DisamAssist。",
error: "错误:$1",
fetchRedirectsError: '获取重定向失败:"$1".',
getBacklinksError: '下载反向链接失败:"$1".',
fetchRightsError: '获取用户权限失败:"$1",',
loadPageError: '加载$1失败:"$2".',
savePageError: '保存至$1失败:"$2".',
dismissError: "Dismiss",
pending: "DisamAssist尚有未储存的编辑。如欲储存之,请按“关闭”。",
editInProgress: "DisamAssist正在进行编辑。若您将本分页关闭,可能会丧失您的编辑。",
ellipsis: "……",
notifyCharacter: "✔",
summary: "使用[[MediaWiki:Gadget-DisamAssist.js|DisamAssist]]清理[[QW:DAB|消歧义]]链接:[[$1]]($2)。",
summaryChanged: "改链接至[[$1]]",
summaryOmitted: "链接已跳过",
summaryRemoved: "链接已移除",
summaryIntentional: "刻意链接至消歧义页面",
summaryHelpNeeded: "需要帮助",
summarySeparator: "; ",
redirectSummary: "使用[[MediaWiki:Gadget-DisamAssist.js|DisamAssist]]创建目标为[[$1]]的重定向。"
};
//! src/DisamAssist/modules/core.js
var startLink;
var ui;
var links;
var pageChanges;
var currentPageTitle;
var currentPageParameters;
var currentLink;
var possibleBacklinkDestinations;
var forceSamePage = false;
var running = false;
var choosing = false;
var canMarkIntentionalLinks = false;
var displayedPages = {};
var editCount = 0;
var editLimit;
var pendingSaves = [];
var pendingEditBox;
var pendingEditBoxText;
var lastEditMillis = 0;
var runningSaves = false;
var install = () => {
const {
wgAction
} = mw.config.get();
if (wgAction !== "view" || !isDisam()) {
return;
}
$(() => {
const portletId = document.querySelector("#p-cactions") ? "p-cactions" : "p-tb";
if (new RegExp(cfg.disamRegExp).test(getTitle())) {
const startMainLink = $(mw.util.addPortletLink(portletId, "#", messages.startMain, "ca-disamassist-main")).on("click", startMain);
const startSameLink = $(mw.util.addPortletLink(portletId, "#", messages.startSame, "ca-disamassist-same")).on("click", startSame);
startLink = startMainLink.add(startSameLink);
} else {
startLink = $(mw.util.addPortletLink(portletId, "#", messages.start, "ca-disamassist-page")).on("click", start);
}
});
};
var start = () => {
if (running) {
return;
}
running = true;
links = [];
pageChanges = [];
displayedPages = {};
ensureDABExists().then((canMark) => {
canMarkIntentionalLinks = canMark;
createUI();
addUnloadConfirm();
markDisamOptions();
checkEditLimit().then(() => {
togglePendingEditBox(false);
doPage();
});
});
};
var startSame = () => {
forceSamePage = true;
start();
};
var startMain = () => {
forceSamePage = false;
start();
};
var createUI = () => {
const $body = $("body");
ui = {
display: $("<div>").addClass("disamassist-box disamassist-mainbox"),
finishedMessage: $("<div>").text(messages.noMoreLinks).hide(),
pageTitleLine: $("<span>").addClass("disamassist-pagetitleline"),
pendingEditCounter: $("<div>").addClass("disamassist-editcounter"),
context: $("<span>").addClass("disamassist-context"),
undoButton: createButton(messages.undo, undo),
omitButton: createButton(messages.omit, omit),
endButton: createButton(messages.close, saveAndEnd),
refreshButton: createButton(messages.refresh, refresh),
titleAsTextButton: createButton(messages.titleAsText, chooseTitleFromPrompt),
intentionalLinkButton: canMarkIntentionalLinks ? createButton(messages.intentionalLink, chooseIntentionalLink) : $("<span>"),
disamNeededButton: cfg.disamNeededText ? createButton(messages.disamNeeded, chooseDisamNeeded) : $("<span>"),
removeLinkButton: createButton(messages.removeLink, chooseLinkRemoval)
};
const top = $("<div>").addClass("disamassist-top").append([ui.pendingEditCounter, ui.finishedMessage, ui.pageTitleLine]);
const leftButtons = $("<div>").addClass("disamassist-leftbuttons").append([ui.titleAsTextButton, ui.removeLinkButton, ui.intentionalLinkButton, ui.disamNeededButton, ui.omitButton]);
const rightButtons = $("<div>").addClass("disamassist-rightbuttons").append([ui.undoButton, ui.refreshButton, ui.endButton]);
const allButtons = $("<div>").addClass("disamassist-allbuttons").append([leftButtons, rightButtons]);
ui.display.append([top, ui.context, allButtons]);
updateEditCounter();
toggleActionButtons(false);
$body.find("#mw-content-text").before(ui.display);
ui.display.hide().fadeIn();
};
var addUnloadConfirm = () => {
$(window).on("beforeunload", () => {
if (running && checkActualChanges()) {
return messages.pending;
} else if (editCount !== 0) {
return messages.editInProgress;
}
});
};
var markDisamOptions = () => {
const optionPageTitles = [];
const optionMarkers = [];
getDisamOptions().each((_index, element) => {
const link = $(element);
const title = extractPageName(link);
const optionMarker = $("<a>").attr("href", "#").addClass("disamassist-optionmarker").text(messages.optionMarker).on("click", (ev) => {
ev.preventDefault();
chooseReplacement(title);
});
link.after(optionMarker);
optionMarkers[optionMarkers.length] = optionMarker;
optionPageTitles[optionPageTitles.length] = title;
});
const targetPage = getTargetPage();
fetchRedirects([...optionPageTitles, ...targetPage]).done((redirects) => {
const endTargetPage = resolveRedirect(targetPage, redirects);
var _iterator = _createForOfIteratorHelper(optionPageTitles.entries()), _step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done; ) {
const [ii, optionPageTitle] = _step.value;
const endOptionTitle = resolveRedirect(optionPageTitle, redirects);
if (isSamePage(optionPageTitle, targetPage)) {
optionMarkers[ii].text(messages.targetOptionMarker).addClass("disamassist-curroptionmarker");
} else if (isSamePage(endOptionTitle, endTargetPage)) {
optionMarkers[ii].text(messages.redirectOptionMarker).addClass("disamassist-curroptionmarker");
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}).fail(error);
};
var ensureDABExists = () => {
const deferred = new $.Deferred();
const title = getTitle();
if (!cfg.intentionalLinkOption) {
deferred.resolve(false);
} else if (new RegExp(cfg.disamRegExp).exec(title)) {
deferred.resolve(true);
} else {
const disamTitle = cfg.disamFormat.replace("$1", title);
loadPage(disamTitle).done((page) => {
if (page.missing) {
page.content = cfg.redirectToDisam.replace("$1", title);
const summary = messages.redirectSummary.replace("$1", title);
savePage(disamTitle, page, summary, false, true).done(() => {
deferred.resolve(true);
}).fail((description) => {
error(description);
deferred.resolve(false);
});
} else {
deferred.resolve(true);
}
}).fail((description) => {
error(description);
deferred.resolve(false);
});
}
return deferred.promise();
};
var checkEditLimit = () => {
const deferred = new $.Deferred();
if (cfg.editCooldown <= 0) {
editLimit = false;
deferred.resolve();
} else {
fetchRights().done((rights) => {
editLimit = !rights.includes("bot");
}).fail((description) => {
error(description);
editLimit = true;
}).always(() => {
deferred.resolve();
});
}
return deferred.promise();
};
var doPage = () => {
if (pageChanges.length > cfg.historySize) {
applyChange(pageChanges.shift());
}
if (links.length) {
currentPageTitle = links.shift();
displayedPages[currentPageTitle] = true;
toggleActionButtons(false);
loadPage(currentPageTitle).done((data) => {
currentPageParameters = data;
currentLink = void 0;
doLink();
}).fail(error);
} else {
const targetPage = getTargetPage();
getBacklinks(targetPage).done((backlinks, pageTitles) => {
const pending = {};
for (var _i = 0, _pendingSaves = pendingSaves; _i < _pendingSaves.length; _i++) {
const el = _pendingSaves[_i];
pending[el[0]] = true;
}
possibleBacklinkDestinations = pageTitles.filter((t) => {
if (t === targetPage) {
return true;
}
return removeDisam(t) !== targetPage;
});
links = backlinks.filter((el) => {
return !displayedPages[el] && !pending[el];
});
if (links.length) {
doPage();
} else {
updateContext();
}
}).fail(error);
}
};
var doLink = () => {
currentLink = extractLinkToPage(currentPageParameters.content, possibleBacklinkDestinations, currentLink ? currentLink.end : 0);
if (currentLink) {
updateContext();
} else {
doPage();
}
};
var chooseReplacement = (pageTitle, extra, summary) => {
if (choosing) {
choosing = false;
summary || (summary = pageTitle ? messages.summaryChanged.replace("$1", pageTitle) : messages.summaryOmitted);
addChange(currentPageTitle, currentPageParameters, currentPageParameters.content, currentLink, summary);
if (pageTitle && (pageTitle !== getTargetPage() || extra)) {
currentPageParameters.content = replaceLink(currentPageParameters.content, pageTitle, currentLink, extra || "");
}
doLink();
}
};
var chooseIntentionalLink = () => {
const disamTitle = cfg.disamFormat.replace("$1", getTargetPage());
chooseReplacement(disamTitle, "", messages.summaryIntentional);
};
var chooseTitleFromPrompt = () => {
const title = prompt(messages.titleAsTextPrompt);
if (title !== null) {
chooseReplacement(title);
}
};
var chooseLinkRemoval = () => {
if (choosing) {
const summary = messages.summaryRemoved;
addChange(currentPageTitle, currentPageParameters, currentPageParameters.content, currentLink, summary);
currentPageParameters.content = removeLink(currentPageParameters.content, currentLink);
doLink();
}
};
var chooseDisamNeeded = () => {
chooseReplacement(currentLink.title, cfg.disamNeededText, messages.summaryHelpNeeded);
};
var undo = () => {
if (pageChanges.length) {
const lastPage = pageChanges.at(-1);
if (currentPageTitle !== lastPage.title) {
links.unshift(currentPageTitle);
currentPageTitle = lastPage.title;
}
currentPageParameters = lastPage.page;
currentPageParameters.content = lastPage.contentBefore.pop();
currentLink = lastPage.links.pop();
lastPage.summary.pop();
if (!lastPage.contentBefore.length) {
pageChanges.pop();
}
updateContext();
}
};
var omit = () => {
chooseReplacement();
};
var refresh = () => {
saveAndEnd();
start();
};
var toggleActionButtons = (enabled) => {
const affectedButtons = [ui.omitButton, ui.titleAsTextButton, ui.removeLinkButton, ui.intentionalLinkButton, ui.disamNeededButton, ui.undoButton];
for (var _i2 = 0, _affectedButtons = affectedButtons; _i2 < _affectedButtons.length; _i2++) {
const button = _affectedButtons[_i2];
button.prop("disabled", !enabled);
}
};
var toggleFinishedMessage = (show) => {
toggleActionButtons(!show);
ui.undoButton.prop("disabled", !pageChanges.length);
ui.finishedMessage.toggle(show);
ui.pageTitleLine.toggle(!show);
ui.context.toggle(!show);
};
var togglePendingEditBox = (show) => {
const $body = $("body");
if (!pendingEditBox) {
pendingEditBox = $("<div>").addClass("disamassist-box disamassist-pendingeditbox");
pendingEditBoxText = $("<div>");
pendingEditBox.append(pendingEditBoxText).hide();
if (editLimit) {
pendingEditBox.append($("<div>").text(messages.pendingEditBoxLimited).addClass("disamassist-subtitle"));
}
$body.find("#mw-content-text").before(pendingEditBox);
updateEditCounter();
}
if (show) {
pendingEditBox.fadeIn();
} else {
pendingEditBox.fadeOut();
}
};
var notifyCompletion = () => {
const $body = $("body");
const oldTitle = document.title;
document.title = messages.notifyCharacter + document.title;
$body.one("mousemove", () => {
document.title = oldTitle;
});
};
var updateContext = () => {
updateEditCounter();
if (currentLink) {
ui.pageTitleLine.html(messages.pageTitleLine.replace("$1", mw.util.getUrl(currentPageTitle, {
redirect: "no"
})).replace("$2", mw.html.escape(currentPageTitle)));
const context = extractContext(currentPageParameters.content, currentLink);
ui.context.empty().append($("<span>").text(context[0])).append($("<span>").text(context[1]).addClass("disamassist-inclink")).append($("<span>").text(context[2]));
const numLines = Math.ceil(ui.context.height() / Number.parseFloat(ui.context.css("line-height")));
if (numLines < cfg.numContextLines) {
ui.context.append(Array.from({
length: cfg.numContextLines - numLines + 2
}).join("<br>"));
}
toggleFinishedMessage(false);
ui.undoButton.prop("disabled", !pageChanges.length);
ui.removeLinkButton.prop("disabled", currentPageParameters.redirect);
ui.intentionalLinkButton.prop("disabled", currentPageParameters.redirect);
ui.disamNeededButton.prop("disabled", currentPageParameters.redirect || currentLink.hasDisamTemplate);
choosing = true;
} else {
toggleFinishedMessage(true);
}
};
var updateEditCounter = () => {
if (ui.pendingEditCounter) {
ui.pendingEditCounter.text(messages.pendingEditCounter.replace("$1", editCount).replace("$2", countActuallyChangedFullyCheckedPages()));
}
if (pendingEditBox) {
if (editCount === 0 && !running) {
togglePendingEditBox(false);
notifyCompletion();
}
let textContent = editCount;
if (editLimit) {
textContent = messages.pendingEditBoxTimeEstimation.replace("$1", editCount).replace("$2", secondsToHHMMSS(cfg.editCooldown * editCount));
}
pendingEditBoxText.text(messages.pendingEditBox.replace("$1", textContent));
}
};
var applyChange = (pageChange) => {
if (pageChange.page.content !== pageChange.contentBefore[0]) {
editCount++;
const changeSummaries = pageChange.summary.join(messages.summarySeparator);
const summary = messages.summary.replace("$1", getTargetPage()).replace("$2", changeSummaries);
const save = editLimit ? saveWithCooldown : savePage;
save(pageChange.title, pageChange.page, summary, true, true).always(() => {
if (editCount > 0) {
editCount--;
}
updateEditCounter();
}).fail(error);
updateEditCounter();
}
};
var applyAllChanges = () => {
var _iterator2 = _createForOfIteratorHelper(pageChanges), _step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) {
const pageChange = _step2.value;
applyChange(pageChange);
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
pageChanges = [];
};
var addChange = (pageTitle, page, oldContent, link, summary) => {
if (!pageChanges.length || pageChanges.at(-1).title !== pageTitle) {
pageChanges[pageChanges.length] = {
title: pageTitle,
page,
contentBefore: [],
links: [],
summary: []
};
}
const lastPageChange = pageChanges.at(-1);
lastPageChange.contentBefore[lastPageChange.contentBefore.length] = oldContent;
lastPageChange.links[lastPageChange.links.length] = link;
lastPageChange.summary[lastPageChange.summary.length] = summary;
};
var checkActualChanges = () => {
return countActualChanges() !== 0;
};
var countActualChanges = () => {
let changeCount = 0;
var _iterator3 = _createForOfIteratorHelper(pageChanges), _step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done; ) {
const pageChange = _step3.value;
if (pageChange.page.content !== pageChange.contentBefore[0]) {
changeCount++;
}
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
return changeCount;
};
var countActuallyChangedFullyCheckedPages = () => {
let changeCount = countActualChanges();
if (pageChanges.length) {
const lastChange = pageChanges.at(-1);
if (lastChange.title === currentPageTitle && currentLink && lastChange.page.content !== lastChange.contentBefore[0]) {
changeCount--;
}
}
return changeCount;
};
var getDisamOptions = () => {
const $body = $("body");
return $body.find("#mw-content-text a").filter((_index, element) => {
return !!extractPageName($(element));
});
};
var saveAndEnd = () => {
applyAllChanges();
end();
};
var end = () => {
const $body = $("body");
const currentToolUI = ui.display;
choosing = false;
running = false;
startLink.removeClass("selected");
$body.find(".disamassist-optionmarker").remove();
currentToolUI.fadeOut({
complete() {
currentToolUI.remove();
if (editCount) {
togglePendingEditBox(true);
}
}
});
};
var error = (errorDescription) => {
const $body = $("body");
const errorBox = $("<div>").addClass("disamassist-box disamassist-errorbox");
errorBox.text(messages.error.replace("$1", errorDescription));
errorBox.append(createButton(messages.dismissError, () => {
errorBox.fadeOut();
}).addClass("disamassist-errorbutton"));
const uiIsInPlace = ui && $.contains(document.documentElement, ui.display[0]);
const nextElement = uiIsInPlace ? ui.display : $body.find("#mw-content-text");
nextElement.before(errorBox);
errorBox.hide().fadeIn();
};
var replaceLink = (text, title, link, extra) => {
let newContent;
if (isSamePage(title, link.description)) {
newContent = link.description;
} else {
newContent = "".concat(title, "|").concat(link.description);
}
const linkStart = text.slice(0, Math.max(0, link.start));
const linkEnd = text.slice(Math.max(0, link.end));
return "".concat(linkStart, "[[").concat(newContent, "]]").concat(link.afterDescription).concat(extra || "").concat(linkEnd);
};
var removeLink = (text, link) => {
const linkStart = text.slice(0, Math.max(0, link.start));
const linkEnd = text.slice(Math.max(0, link.end));
return linkStart + link.description + link.afterDescription + linkEnd;
};
var extractLink = (text, lastIndex) => {
const titleRegex = /\[\[(.*?)(?:\|(.*?))?]]/g;
const templateRegex = /^(\w*[\s!),.:;?}]*){{\s*([^{|}]+?)\s*(?:\|[^{]*?)?}}/;
titleRegex.lastIndex = lastIndex;
const match = titleRegex.exec(text);
if (match && match.index !== -1) {
var _match$;
let possiblyAmbiguous = true;
let hasDisamTemplate = false;
let _end = match.index + 4 + match[1].length + (match[2] ? 1 + match[2].length : 0);
let afterDescription = "";
const rest = text.slice(Math.max(0, _end));
const templateMatch = templateRegex.exec(rest);
if (templateMatch) {
const templateTitle = getCanonicalTitle(templateMatch[2]);
const {
disamLinkTemplates
} = cfg;
const {
disamLinkIgnoreTemplates
} = cfg;
if (disamLinkTemplates.includes(templateTitle)) {
_end += templateMatch[0].length;
afterDescription = templateMatch[1].replace(/\s$/, "");
hasDisamTemplate = true;
} else if (disamLinkIgnoreTemplates.includes(templateTitle)) {
possiblyAmbiguous = false;
}
}
return {
start: match.index,
end: _end,
possiblyAmbiguous,
hasDisamTemplate,
title: match[1],
description: (_match$ = match[2]) !== null && _match$ !== void 0 ? _match$ : match[1],
afterDescription
};
}
};
var extractLinkToPage = (text, destinations, lastIndex) => {
let link;
let title;
do {
link = extractLink(text, lastIndex);
if (link) {
lastIndex = link.end;
title = getCanonicalTitle(link.title);
}
} while (link && (!link.possiblyAmbiguous || !(destinations !== null && destinations !== void 0 && destinations.includes(title))));
return link;
};
var getTargetPage = () => {
const title = getTitle();
return forceSamePage ? title : removeDisam(title);
};
var getTitle = () => {
const {
wgPageName
} = mw.config.get();
return wgPageName.replace(/_/g, " ");
};
var removeDisam = (title) => {
var _match$2;
const match = new RegExp(cfg.disamRegExp).exec(title);
return (_match$2 = match === null || match === void 0 ? void 0 : match[1]) !== null && _match$2 !== void 0 ? _match$2 : title;
};
var isSamePage = (title1, title2) => {
return getCanonicalTitle(title1) === getCanonicalTitle(title2);
};
var getCanonicalTitle = (title) => {
try {
title = new mw.Title(title).getPrefixedText();
} catch {
}
return title;
};
var extractContext = (text, link) => {
const contextStart = link.start - cfg.radius;
const contextEnd = link.end + cfg.radius;
let contextPrev = text.slice(contextStart, link.start);
if (contextStart > 0) {
contextPrev = messages.ellipsis + contextPrev;
}
let contextNext = text.slice(link.end, contextEnd);
if (contextEnd < text.length) {
contextNext += messages.ellipsis;
}
return [contextPrev, text.slice(link.start, link.end), contextNext];
};
var extractPageName = (link) => {
let pageName = extractPageNameRaw(link);
if (pageName) {
const sectionPos = pageName.indexOf("#");
let section = "";
if (sectionPos !== -1) {
section = pageName.slice(Math.max(0, sectionPos));
pageName = pageName.slice(0, Math.max(0, sectionPos));
}
return getCanonicalTitle(pageName) + section;
}
};
var extractPageNameRaw = (link) => {
const {
wgScript,
wgArticlePath
} = mw.config.get();
if (!link.hasClass("image")) {
const href = link.attr("href");
if (link.hasClass("new")) {
if (href.includes(wgScript)) {
return mw.util.getParamValue("title", href);
}
} else {
const regex = wgArticlePath.replace("$1", "(.*)");
const regexResult = new RegExp("^".concat(regex, "$")).exec(href);
if (Array.isArray(regexResult) && regexResult.length) {
return decodeURIComponent(regexResult[1]);
}
}
}
};
var isDisam = () => {
const {
wgCategories
} = mw.config.get();
const categories = wgCategories !== null && wgCategories !== void 0 ? wgCategories : [];
var _iterator4 = _createForOfIteratorHelper(categories), _step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done; ) {
const category = _step4.value;
const {
disamCategories
} = cfg;
if (disamCategories.includes(category)) {
return true;
}
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
return false;
};
var secondsToHHMMSS = (totalSeconds) => {
let hhmmss = "";
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor(totalSeconds % 3600 / 60);
const seconds = Math.floor(totalSeconds % 3600 % 60);
if (hours >= 1) {
hhmmss = "".concat(pad(hours, "0", 2), ":");
}
hhmmss += "".concat(pad(minutes, "0", 2), ":").concat(pad(seconds, "0", 2));
return hhmmss;
};
var pad = (str, z, width) => {
str = str.toString();
if (str.length >= width) {
return str;
}
return Array.from({
length: width - str.length + 1
}).join(z) + str;
};
var createButton = (text, onClick) => {
const button = $("<input>").attr({
type: "button",
value: text
});
button.addClass("disamassist-button").on("click", onClick);
return button;
};
var resolveRedirect = (pageTitle, possibleRedirects) => {
let appliedRedirect = true;
const visitedPages = {};
let currentPage = getCanonicalTitle(pageTitle);
while (appliedRedirect) {
appliedRedirect = false;
var _iterator5 = _createForOfIteratorHelper(possibleRedirects), _step5;
try {
for (_iterator5.s(); !(_step5 = _iterator5.n()).done; ) {
const possibleRedirect = _step5.value;
if (possibleRedirect.from === currentPage) {
if (visitedPages[possibleRedirect.to]) {
return pageTitle;
}
visitedPages[currentPage] = true;
appliedRedirect = true;
currentPage = possibleRedirect.to;
}
}
} catch (err) {
_iterator5.e(err);
} finally {
_iterator5.f();
}
}
return currentPage;
};
var getBacklinks = (page) => {
const deferred = new $.Deferred();
const params = {
action: "query",
list: "backlinks",
bltitle: page,
blredirect: true,
bllimit: cfg.backlinkLimit,
blnamespace: cfg.targetNamespaces.join("|")
};
api.get(params).done(({
query
}) => {
const backlinks = [];
const linkTitles = [getCanonicalTitle(page)];
const backlinksQuery = query.backlinks;
var _iterator6 = _createForOfIteratorHelper(backlinksQuery), _step6;
try {
for (_iterator6.s(); !(_step6 = _iterator6.n()).done; ) {
const element = _step6.value;
backlinks[backlinks.length] = element.title;
if (!element.redirlinks) {
continue;
}
linkTitles[linkTitles.length] = element.title;
const {
redirlinks
} = element;
var _iterator7 = _createForOfIteratorHelper(redirlinks), _step7;
try {
for (_iterator7.s(); !(_step7 = _iterator7.n()).done; ) {
const {
title
} = _step7.value;
backlinks[backlinks.length] = title;
}
} catch (err) {
_iterator7.e(err);
} finally {
_iterator7.f();
}
}
} catch (err) {
_iterator6.e(err);
} finally {
_iterator6.f();
}
deferred.resolve(backlinks, linkTitles);
}).fail((code) => {
deferred.reject(messages.getBacklinksError.replace("$1", code));
});
return deferred.promise();
};
var fetchRedirects = (pageTitles) => {
const deferred = new $.Deferred();
const currentTitles = pageTitles.slice(0, cfg.queryTitleLimit);
const restTitles = pageTitles.slice(cfg.queryTitleLimit);
const params = {
action: "query",
titles: currentTitles.join("|"),
redirects: true
};
api.get(params).done(({
query
}) => {
var _query$redirects;
const theseRedirects = (_query$redirects = query.redirects) !== null && _query$redirects !== void 0 ? _query$redirects : [];
if (restTitles.length) {
fetchRedirects(restTitles).done((redirects) => {
deferred.resolve([...theseRedirects, ...redirects]);
}).fail((description) => {
deferred.reject(description);
});
} else {
deferred.resolve(theseRedirects);
}
}).fail((code) => {
deferred.reject(messages.fetchRedirectsError.replace("$1", code));
});
return deferred.promise();
};
var fetchRights = () => {
const deferred = $.Deferred();
const params = {
action: "query",
meta: "userinfo",
uiprop: "rights"
};
api.get(params).done(({
query
}) => {
deferred.resolve(query.userinfo.rights);
}).fail((code) => {
deferred.reject(messages.fetchRightsError.replace("$1", code));
});
return deferred.promise();
};
var loadPage = (pageTitle) => {
const deferred = new $.Deferred();
const params = {
action: "query",
format: "json",
formatversion: "2",
titles: pageTitle,
prop: "revisions",
rvprop: "timestamp|content",
meta: "tokens",
type: "csrf"
};
api.get(params).done(({
query
}) => {
const [rawPage] = query.pages;
const page = {};
page.redirect = rawPage.redirect !== void 0;
page.missing = rawPage.missing !== void 0;
if (rawPage.revisions) {
page.content = rawPage.revisions[0].content;
page.baseTimeStamp = rawPage.revisions[0].timestamp;
} else {
page.content = "";
page.baseTimeStamp = void 0;
}
page.startTimeStamp = rawPage.starttimestamp;
page.editToken = query.tokens.csrftoken;
deferred.resolve(page);
}).fail((code) => {
deferred.reject(messages.loadPageError.replace("$1", pageTitle).replace("$2", code));
});
return deferred.promise();
};
var saveWithCooldown = (...args) => {
const deferred = new $.Deferred();
pendingSaves[pendingSaves.length] = {
args,
dfd: deferred
};
if (!runningSaves) {
checkAndSave();
}
return deferred.promise();
};
var checkAndSave = function() {
if (!pendingSaves.length) {
runningSaves = false;
return;
}
runningSaves = true;
const millisSinceLast = Date.now() - lastEditMillis;
if (millisSinceLast < cfg.editCooldown * 1e3) {
setTimeout(checkAndSave, cfg.editCooldown * 1e3 - millisSinceLast);
} else {
const save = pendingSaves.shift();
savePage.apply(this, save.args).done(() => {
checkAndSave();
save.dfd.resolve();
}).fail((description) => {
checkAndSave();
save.dfd.reject(description);
});
lastEditMillis = Date.now();
}
};
var savePage = (pageTitle, {
editToken,
content,
baseTimeStamp,
startTimeStamp
}, summary, minorEdit, botEdit) => {
const deferred = new $.Deferred();
const params = {
action: "edit",
title: pageTitle,
token: editToken,
text: content,
basetimestamp: baseTimeStamp,
starttimestamp: startTimeStamp,
summary,
watchlist: cfg.watch,
minor: minorEdit,
bot: botEdit
};
api.post(params).done(() => {
deferred.resolve();
}).fail((code) => {
deferred.reject(messages.savePageError.replace("$1", pageTitle).replace("$2", code));
});
return deferred.promise();
};
//! src/DisamAssist/DisamAssist.ts
$(install);
})();