MediaWiki:Gadget-deletion.js
Note: After saving, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
/* eslint-disable require-atomic-updates */ // <pre> "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; $(function () { return (function () { return __awaiter(void 0, void 0, void 0, function () { var globalDeletionLock, categories, sleep, Thread, postMethods, api, container, node, portletLink, pages, isThatCategory, users_1, _a, _b, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: if (mw.config.get("wgNamespaceNumber") !== 14 || !mw.config.get("wgUserGroups").includes("sysop")) { return [2 /*return*/]; } globalDeletionLock = false; categories = { "zh.moegirl.org.cn": "即将删除的页面", "commons.moegirl.org.cn": "即将删除的页面", "en.moegirl.org.cn": "Pages awaiting deletion", "ja.moegirl.org.cn": "削除依頼中のページ", "library.moegirl.org.cn": "即将删除的页面" }; sleep = function (ms) { return new Promise(function (res) { return setTimeout(res, ms); }); }; Thread = /** @class */ (function () { function Thread(array, callback) { this.array = array; this.callback = callback; } Thread.prototype.generateThread = function () { return __awaiter(this, void 0, void 0, function () { var target, e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, sleep(Math.ceil(100 * Math.random()))]; case 1: _a.sent(); _a.label = 2; case 2: if (!(this.array.length > 0)) return [3 /*break*/, 7]; target = this.array.shift(); if (!target) { return [3 /*break*/, 2]; } _a.label = 3; case 3: _a.trys.push([3, 5, , 6]); return [4 /*yield*/, this.callback(target)]; case 4: _a.sent(); return [3 /*break*/, 6]; case 5: e_1 = _a.sent(); console.error("Thread ignored Error:", e_1); return [3 /*break*/, 6]; case 6: return [3 /*break*/, 2]; case 7: return [2 /*return*/]; } }); }); }; // temp throttle start Thread.prototype.generateThreads = function (length) { var _this = this; return Array.from({ length: 1 }, function () { return _this.generateThread(); }); }; return Thread; }()); return [4 /*yield*/, mw.loader.using(["mediawiki.util", "mediawiki.api"])]; case 1: _d.sent(); postMethods = ["post", "postWithToken"]; api = new Proxy(new mw.Api(), { get: function (t, p) { if (postMethods.includes(p)) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return sleep(200).then(function () { return t[p].apply(t, args); }); }; } return t[p]; } }); container = $(".mw-category-generated"); node = $("<p/>").attr("id", "deletionControl"); portletLink = $(mw.util.addPortletLink("p-cactions", "#", "批量删除本分类下页面", "startDeletion", "批量删除本分类下页面")); portletLink.attr("class", "sysop-show").on("click", function () { if ($("#deletionControl")[0] || globalDeletionLock) { return false; } container.before(node); node.text("请选择要删除的页面:").append('(已选:<span id="deletionSelectingNumber"> - </span>/总计:<span id="deletionTotalNumber"> - </span>)').append($("<input/>").attr({ type: "button", value: "全选", id: "selectAll" })).append($("<input/>").attr({ type: "button", value: "全不选", id: "selectNone" })).append($("<input/>").attr({ type: "button", value: "提交", id: "runDeletion" })).append($("<input/>").attr({ type: "button", value: "取消", id: "cancelDeletion" })); $("body").addClass("deletion"); $(".mw-category-generated li").prepend($("<input/>").attr({ type: "checkbox", "class": "selectBox" })).find(".stub").toggleClass("stub _stub"); $("#deletionTotalNumber").text($(".mw-category-generated li :checkbox").length); $(".mw-category-generated li :checkbox").on("change", function () { $("#deletionSelectingNumber").text($(".mw-category-generated li :checkbox:checked").length); }).change(); $(".mw-category-generated > div > p").each(function (_, ele) { $("<input/>").attr({ type: "button", value: "全选本类别页面", "class": "deletionControlButton" }).appendTo(ele).on("click", function (_, ele) { $(ele).closest(".mw-category-generated > div").find(":checkbox:not(:disabled)").prop("checked", "checked").first().change(); }); $("<input/>").attr({ type: "button", value: "全不选本类别页面", "class": "deletionControlButton" }).appendTo(ele).on("click", function (_, ele) { $(ele).closest(".mw-category-generated > div").find(":checkbox:not(:disabled)").removeAttr("checked").first().change(); }); }); return false; }); pages = []; isThatCategory = mw.config.get("wgTitle") === categories[location.hostname]; if (!isThatCategory) return [3 /*break*/, 5]; globalDeletionLock = true; portletLink.find("a").text("正在加载中……"); return [4 /*yield*/, (function () { return __awaiter(void 0, void 0, void 0, function () { var result, eol, aufrom, _result; return __generator(this, function (_a) { switch (_a.label) { case 0: result = []; eol = Symbol(); aufrom = undefined; _a.label = 1; case 1: if (!(aufrom !== eol)) return [3 /*break*/, 3]; return [4 /*yield*/, api.post({ action: "query", list: "allusers", aurights: "rollback", aulimit: "max", aufrom: aufrom })]; case 2: _result = _a.sent(); if (_result["continue"]) { aufrom = _result["continue"].aufrom; } else { aufrom = eol; } result.push.apply(result, _result.query.allusers.map(function (_a) { var name = _a.name; return name; })); return [3 /*break*/, 1]; case 3: return [2 /*return*/, result]; } }); }); })()]; case 2: users_1 = _d.sent(); _b = (_a = Promise).all; _c = Thread.bind; return [4 /*yield*/, (function () { return __awaiter(void 0, void 0, void 0, function () { var result, eol, cmcontinue, _result; return __generator(this, function (_a) { switch (_a.label) { case 0: result = []; eol = Symbol(); cmcontinue = undefined; _a.label = 1; case 1: if (!(cmcontinue !== eol)) return [3 /*break*/, 3]; return [4 /*yield*/, api.post({ action: "query", format: "json", list: "categorymembers", cmtitle: mw.config.get("wgPageName"), cmprop: "ids|title", cmtype: "page|subcat|file", cmlimit: "max", cmcontinue: cmcontinue })]; case 2: _result = _a.sent(); if (_result["continue"]) { cmcontinue = _result["continue"].cmcontinue; } else { cmcontinue = eol; } result.push.apply(result, _result.query.categorymembers); return [3 /*break*/, 1]; case 3: return [2 /*return*/, result.filter(function (_a) { var title = _a.title; return document.querySelector("a[href=\"/".concat(encodeURI(title), "\"], a[href=\"/").concat(mw.util.wikiUrlencode(title), "\"]")); })]; } }); }); })()]; case 3: return [4 /*yield*/, _b.apply(_a, [new (_c.apply(Thread, [void 0, _d.sent(), function (_a) { var title = _a.title, pageid = _a.pageid; return __awaiter(void 0, void 0, void 0, function () { var retryTimes, renderedHTML, root, reason, actor, link, data, user, isTrusted, e_2; return __generator(this, function (_b) { switch (_b.label) { case 0: retryTimes = 0; _b.label = 1; case 1: if (!(retryTimes < 3)) return [3 /*break*/, 9]; _b.label = 2; case 2: _b.trys.push([2, 7, , 8]); return [4 /*yield*/, $.get("".concat(mw.config.get("wgServer") + mw.config.get("wgScriptPath"), "/index.php?action=render&title=").concat(mw.util.rawurlencode(title), "&uselang=zh&_=").concat(Math.random().toString().substring(2)))]; case 3: renderedHTML = _b.sent(); root = $("<div/>").html(renderedHTML); reason = root.find(".mw-parser-output > .infoBox.will2Be2Deleted #reason"); actor = root.find(".mw-parser-output > .infoBox.will2Be2Deleted #actor a").first(); link = $("a[href=\"/".concat(encodeURI(title), "\"], a[href=\"/").concat(mw.util.wikiUrlencode(title), "\"]")); if (!(reason.length === 1 && actor.length === 1)) return [3 /*break*/, 5]; return [4 /*yield*/, api.post({ action: "query", rvprop: "user|content", prop: "revisions", titles: title })]; case 4: data = _b.sent(); user = data.query.pages[pageid].revisions[0].user; isTrusted = user === actor.text() && users_1.includes(user); pages.push({ title: title, user: user, isTrusted: isTrusted, reason: reason.text() }); link.addClass("checked"); if (!isTrusted) { link.after("<a href=\"/".concat(mw.util.wikiUrlencode(title), "\" target=\"_blank\" class=\"linksBlank external\">").concat(link.html(), "</a> \u7981\u6B62\u5220\u9664\uFF1A\u8BE5\u6B21\u6302\u5220\u4E0D\u53EF\u9760\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\uFF08").concat(user !== actor.text() ? "最后编辑者与挂删人不符" : "最后编辑者没有巡查权限", "\uFF09")).remove(); } else { link.after("<div style=\"clear: both; float: none\">\u6302\u5220\u4EBA\uFF1A<a href=\"/User:".concat(user, "\" class=\"mw-userlink bypass\"><bdi>").concat(user, "</bdi></a></div><div style=\"clear: both; float: none\">\u6302\u5220\u7406\u7531\uFF1A").concat(reason.text(), "</div>")); } return [3 /*break*/, 6]; case 5: pages.push({ title: title, user: actor.text(), isTrusted: false, reason: reason.text() }); link.after("<a href=\"/".concat(mw.util.wikiUrlencode(title), "\" target=\"_blank\" class=\"linksBlank bypass external\">").concat(link.html(), "</a> \u7981\u6B62\u5220\u9664\uFF1A\u8BE5\u6B21\u6302\u5220\u4E0D\u53EF\u9760\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\uFF08\u6302\u5220\u6A21\u677F\u672A\u7ED9\u51FA\u7406\u7531\u6216\u6302\u5220\u4EBA\uFF09")).remove(); _b.label = 6; case 6: return [2 /*return*/]; case 7: e_2 = _b.sent(); console.error("Deletion.js", e_2); return [3 /*break*/, 8]; case 8: retryTimes++; return [3 /*break*/, 1]; case 9: return [4 /*yield*/, oouiDialog.alert("出现错误,请刷新重试或联系维护人员!", { title: "批量删除分类下页面工具" })]; case 10: _b.sent(); return [2 /*return*/]; } }); }); }]))().generateThreads(3)])]; case 4: _d.sent(); container.find("li a").not(".bypass, .disabled, .undelectable, .checked").each(function (_, _link) { var link = $(_link); link.after("<a href=\"".concat(link.attr("href"), "\" target=\"_blank\" class=\"linksBlank bypass external\">").concat(link.html(), "</a> \u7981\u6B62\u5220\u9664\uFF1A\u8BE5\u9875\u9762\u672A\u88AB\u6302\u5220")).remove(); }); globalDeletionLock = false; mw.hook("wikipage.content").fire($(".mw-userlink.bypass")); portletLink.find("a").text("批量删除本分类下页面"); _d.label = 5; case 5: $("body").on("click", function (_a) { var target = _a.target; return __awaiter(void 0, void 0, void 0, function () { var self, statu, e_3; return __generator(this, function (_b) { switch (_b.label) { case 0: self = $(target); if (!self.is("#selectAll")) return [3 /*break*/, 1]; container.find("li :checkbox:not(:disabled)").prop("checked", "checked").first().change(); return [3 /*break*/, 10]; case 1: if (!self.is("#selectNone")) return [3 /*break*/, 2]; container.find("li :checkbox:not(:disabled)").removeAttr("checked").first().change(); return [3 /*break*/, 10]; case 2: if (!self.is("#cancelDeletion")) return [3 /*break*/, 3]; if (globalDeletionLock) { return [2 /*return*/, false]; } $("#deletionControl, .deletionControlButton").remove(); container.find("._stub").toggleClass("stub _stub"); container.find(".selectBox").remove(); $(".disabled").removeClass("disabled"); return [3 /*break*/, 10]; case 3: if (!self.is("#runDeletion")) return [3 /*break*/, 9]; if (globalDeletionLock) { return [2 /*return*/, false]; } return [4 /*yield*/, oouiDialog.confirm("\u60A8\u786E\u5B9A\u8981\u5220\u9664\u8FD9\u4E9B\u9875\u9762\u5417\uFF1F\uFF08\u9009\u4E2D\u4E86".concat($(".mw-category-generated li :checkbox:checked").length, "\u4E2A\u9875\u9762\uFF09"), { title: "批量删除分类下页面工具" })]; case 4: if (!(_b.sent())) { return [2 /*return*/]; } container.find(".deletionResult").remove(); container.find(".selectBox").attr("disabled", "disabled"); $("#deletionControl").append('<br><span id="result_text"><img src="https://img.moegirl.org.cn/common/d/d1/Windows_10_loading.gif" style="height: 1em; margin-top: -.25em;"><span id="deletionStatus"></span></span>'); statu = $("#deletionStatus"); globalDeletionLock = true; container.find("a:not(.bypass)").each(function (_, ele) { var self = $(ele); if (/User:AnnAngela\/SandBox/.test(self.text()) || !self.closest("li").find(":checked")[0]) { self.addClass("disabled"); } }); _b.label = 5; case 5: _b.trys.push([5, 7, , 8]); statu.text("正在删除,已完成删除的页面将会被删除线划去……"); return [4 /*yield*/, Promise.all(new Thread(container.find("a").not(".bypass, .disabled, .undelectable").toArray(), function (ele) { return __awaiter(void 0, void 0, void 0, function () { var self, link, page, e_4; return __generator(this, function (_a) { switch (_a.label) { case 0: self = $(ele); if (self.text().trim() === "") { return [2 /*return*/]; } self.css("margin-right", "2em"); link = decodeURIComponent(self.attr("href").replace("/", "")).replace(/_/g, " "); page = pages.filter(function (_a) { var title = _a.title; return title === link; })[0]; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, api.postWithToken("csrf", { action: "delete", format: "json", title: link, tags: "Automation tool", reason: "\u6279\u91CF\u5220\u9664\u3010".concat(mw.config.get("wgPageName"), "\u3011\u4E0B\u7684\u9875\u9762").concat(isThatCategory && page.isTrusted && page.reason && page.user ? "\uFF08[[User_talk:".concat(page.user, "|").concat(page.user, "]]\u7684\u6302\u5220\u7406\u7531\uFF1A").concat(page.reason, " \uFF09") : "") }, { timeout: 99999 })]; case 2: _a.sent(); self.css("text-decoration", "line-through").after('<span class="deletionResult"> 删除成功</span>'); return [3 /*break*/, 4]; case 3: e_4 = _a.sent(); self.after("<span class=\"deletionResult\"> \u5220\u9664\u5931\u8D25\uFF1A".concat(e_4 instanceof Error ? "".concat(e_4, " ").concat(e_4.stack.split("\n")[1].trim()) : JSON.stringify(e_4), "</span>")); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }).generateThreads(3))]; case 6: _b.sent(); $("#result_text").text("删除已完成!"); return [3 /*break*/, 8]; case 7: e_3 = _b.sent(); statu.text("\u53D1\u751F\u9519\u8BEF\uFF1A".concat(e_3 instanceof Error ? "".concat(e_3, " ").concat(e_3.stack.split("\n")[1].trim()) : JSON.stringify(e_3))); return [3 /*break*/, 8]; case 8: return [3 /*break*/, 10]; case 9: if (self.is("a") && globalDeletionLock) { window.open(self[0].href, "_blank"); return [2 /*return*/, false]; } _b.label = 10; case 10: return [2 /*return*/]; } }); }); }); return [2 /*return*/]; } }); }); })(); }); // </pre>