// <nowiki>// === Compiled with esbuild and Novem Linguae's publish.php script ======================(() => { var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // modules/GANReviewHTMLGenerator.js var GANReviewHTMLGenerator_exports = {}; __export(GANReviewHTMLGenerator_exports, { GANReviewHTMLGenerator: () => GANReviewHTMLGenerator }); var GANReviewHTMLGenerator; var init_GANReviewHTMLGenerator = __esm({ "modules/GANReviewHTMLGenerator.js"() { GANReviewHTMLGenerator = class { getHTML(gaTitle, wikicodeOfGASubPages) { let defaultDisplayText = this.getDefaultDisplayText(gaTitle); defaultDisplayText = this.escapeHtml(defaultDisplayText); const gaTopicComboBoxOptionsHTML = this.makeTopicComboBoxOptions(wikicodeOfGASubPages); return `<style>#GANReviewTool {border: 1px solid black;padding: 1em;margin-bottom: 1em;}#GANReviewTool h2 {margin-top: 0;}#GANReviewTool strong {text-decoration: underline;}#GANReviewTool code {/* font-family: monospace; */}#GANReviewTool input[type="text"] {width: 50em;}#GANReviewTool p {margin-top: 1.5em;margin-bottom: 1.5em;line-height: 1.5em;}#GANReviewTool option:disabled {font-weight: bold;color: green;}#GANReviewTool-ProcessingMessage {display: none;}.GANReviewTool-ValidationError {display: none;color: red;font-weight: bold;}.GANReviewTool-ErrorNotice {color: red;font-weight: bold;}#GANReviewTool-MainForm {display: none;}</style><div><div><h2>GAN Review Tool</h2><p><a>Click here</a> to open GANReviewTool.</p><div><p><strong>Action</strong><br /><input type="radio" name="GANReviewTool-PassOrFail" value="pass" checked /> Pass<br /><input type="radio" name="GANReviewTool-PassOrFail" value="fail" /> Fail<br /><input type="radio" name="GANReviewTool-PassOrFail" value="placeOnHold" /> Place On Hold<br /><input type="radio" name="GANReviewTool-PassOrFail" value="askSecondOpinion" /> Ask 2nd Opinion<br /><input type="radio" name="GANReviewTool-PassOrFail" value="answerSecondOpinion" /> Answer 2nd Opinion<br /></p><!-- if pass or fail --><div><p><input type="checkbox" name="GANReviewTool-ATOPYesNo" value="1" checked /> Place {{<a href="/wiki/Template:Archive_top">Archive top</a>}} and {{Archive bottom}} templates on GA review page</p><!-- if pass --><div><p><strong>Topic, subtopic, and sub-subtopic:</strong><br /><select name="GANReviewTool-Topic"><option></option>${gaTopicComboBoxOptionsHTML}</select></p><p><strong>Wikicode to display when adding this to the list of good articles at [[<a href="/wiki/Wikipedia:Good_articles">WP:GA</a>]]</strong><br />People should be in format: <code>Lastname, Firstname</code><br />Albums, television shows, <a href="/wiki/Genus">genus</a>, <a href="/wiki/Binomial_nomenclature">species</a>, court cases should be italicized: <code>''Jeopardy''</code><br />Television episodes should be surrounded by double quotes: <code>"Episode name"</code><br />Parentheses at the end should not be formatted: <code>''Revolver'' (Beatles album)</code><br />Artwork, poetry, etc. may also require special formatting<br />More info at [[<a href="/wiki/Wikipedia:Manual_of_Style/Titles_of_works#Italics">MOS:TITLE#Italics</a>]] and [[<a href="/wiki/Wikipedia:Manual_of_Style/Titles_of_works#Quotation_marks">MOS:TITLE#Quotation marks</a>]]<br /><input type="text" name="GANReviewTool-DisplayWikicode" value="${defaultDisplayText}" /></p></div><!-- endif --></div><!-- endif --><p><button>Submit</button></p><div>You must select a topic from the combo box above.</div><div>"Wikicode to display" should not contain a pipe "|"</div></div></div><div><p>Processing...</p></div></div>`; } /** * @copyright bjornd, CC BY-SA 4.0, https://stackoverflow.com/a/6234804/3480193 */ escapeHtml(unsafe) { return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"); } getDefaultDisplayText(gaTitle) { const endsWithParentheticalDisambiguator = gaTitle.match(/^.+ \(.+\)$/); if (!endsWithParentheticalDisambiguator) { return gaTitle; } const suffixesThatTriggerItalics = [ "album", "book", "comic", "comics", "film series", "film", "magazine", "manga", "novel", "painting", "poem", "sculpture", "season 1", "season 10", "season 2", "season 3", "season 4", "season 5", "season 6", "season 7", "season 8", "season 9", "series 1", "series 10", "series 2", "series 3", "series 4", "series 5", "series 6", "series 7", "series 8", "series 9", "soundtrack" ]; const suffixesThatTriggerDoubleQuotes = [ "song" ]; const suffixesThatTriggerDoubleQuotesAndItalics = [ "30 Rock", "Family Guy", "Fringe", "Glee", "Lost", "Parks and Recreation", "South Park", "Star Trek: Enterprise", "Star Trek: The Next Generation", "The Office", "The Simpsons", "The Walking Dead", "The X-Files" ]; const firstHalf = gaTitle.match(/^(.+) \((.+)\)$/)[1]; const secondHalf = gaTitle.match(/^(.+) \((.+)\)$/)[2]; for (const suffixToCheck of suffixesThatTriggerItalics) { if (gaTitle.endsWith(suffixToCheck + ")")) { return `''${firstHalf}'' (${secondHalf})`; } } for (const suffixToCheck of suffixesThatTriggerDoubleQuotes) { if (gaTitle.endsWith(suffixToCheck + ")")) { return `"${firstHalf}" (${secondHalf})`; } } for (const suffixToCheck of suffixesThatTriggerDoubleQuotesAndItalics) { if (gaTitle.endsWith(suffixToCheck + ")")) { return `"${firstHalf}" (''${secondHalf}'')`; } } return gaTitle; } makeTopicComboBoxOptions(wikicodeOfGASubPages) { let html = ""; for (const key in wikicodeOfGASubPages) { const topic = key.replace(/^Wikipedia:Good articles\//, ""); const wikicode = wikicodeOfGASubPages[key]; html += this.makeTopicComboBoxOptionGroup(wikicode, topic); } return html; } makeTopicComboBoxOptionGroup(wikicode, topic) { wikicode = wikicode.replace(/\s*\[\[File:[^\]]+\]\]\s*/gi, ""); wikicode = wikicode.replace(/\{\{(?!#invoke)[^}]+\}\}\s*\n/gi, ""); wikicode = wikicode.replace(/={2,}\s*Contents\s*={2,}\s*\n/gi, ""); wikicode = wikicode.replace(/<!--[^>]+>\s*\n/gi, ""); wikicode = wikicode.replace( /(={2,}\s*[^=]+\s*={2,})\n(?!\{\{#invoke)/gi, `<option value="${topic}" disabled>$1</option>` ); wikicode = wikicode.replace( /(={2,}\s*[^=]+\s*={2,})\n(?=\{\{#invoke)/gi, `<option value="${topic}">$1</option>` ); wikicode = wikicode.replace(/^(?!\t{7}<option).*\n/gim, ""); wikicode = wikicode.replace(/\s+=/gi, "="); wikicode = wikicode.replace(/=\s+/gi, "="); wikicode += "\n"; return wikicode; } }; } }); // node_modules/wikiparser-template/bundle/bundle.min.js var require_bundle_min = __commonJS({ "node_modules/wikiparser-template/bundle/bundle.min.js"(exports, module) { "use strict"; var ie = Object.defineProperty; var ne = Object.defineProperties; var ae = Object.getOwnPropertyDescriptors; var X = Object.getOwnPropertySymbols; var oe = Object.prototype.hasOwnProperty; var le = Object.prototype.propertyIsEnumerable; var Q = (e) => { throw TypeError(e); }; var H = (e, t, s) => t in e ? ie(e, t, { enumerable: true, configurable: true, writable: true, value: s }) : e[t] = s; var q = (e, t) => { for (var s in t || (t = {})) oe.call(t, s) && H(e, s, t[s]); if (X) for (var s of X(t)) le.call(t, s) && H(e, s, t[s]); return e; }; var W = (e, t) => ne(e, ae(t)); var I = (e, t, s) => H(e, typeof t != "symbol" ? t + "" : t, s); var D = (e, t, s) => t.has(e) || Q("Cannot " + s); var f = (e, t, s) => (D(e, t, "read from private field"), s ? s.call(e) : t.get(e)); var v = (e, t, s) => t.has(e) ? Q("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, s); var x = (e, t, s, i) => (D(e, t, "write to private field"), i ? i.call(e, s) : t.set(e, s), s); var F = (e, t, s) => (D(e, t, "access private method"), s); var K = (e, t, s, i) => ({ set _(r) { x(e, t, r, s); }, get _() { return f(e, t, i); } }); var __create2 = Object.create; var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropNames2 = Object.getOwnPropertyNames; var __hasOwnProp2 = Object.prototype.hasOwnProperty; var __knownSymbol = (e, t) => (t = Symbol[e]) ? t : Symbol.for("Symbol." + e); var __typeError = (e) => { throw TypeError(e); }; var __defNormalProp2 = (e, t, s) => t in e ? __defProp2(e, t, { enumerable: true, configurable: true, writable: true, value: s }) : e[t] = s; var __name = (e, t) => __defProp2(e, "name", { value: t, configurable: true }); var __esm2 = (e, t) => function() { return e && (t = (0, e[__getOwnPropNames2(e)[0]])(e = 0)), t; }; var __commonJS2 = (e, t) => function() { return t || (0, e[__getOwnPropNames2(e)[0]])((t = { exports: {} }).exports, t), t.exports; }; var __export2 = (e, t) => { for (var s in t) __defProp2(e, s, { get: t[s], enumerable: true }); }; var __copyProps2 = (e, t, s, i) => { if (t && typeof t == "object" || typeof t == "function") for (let r of __getOwnPropNames2(t)) !__hasOwnProp2.call(e, r) && r !== s && __defProp2(e, r, { get: () => t[r], enumerable: !(i = __getOwnPropDesc2(t, r)) || i.enumerable }); return e; }; var __toCommonJS2 = (e) => __copyProps2(__defProp2({}, "__esModule", { value: true }), e); var __decoratorStart = (e) => { var t; return [, , , __create2((t = e == null ? void 0 : e[__knownSymbol("metadata")]) != null ? t : null)]; }; var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; var __expectFn = (e) => e !== void 0 && typeof e != "function" ? __typeError("Function expected") : e; var __decoratorContext = (e, t, s, i, r) => ({ kind: __decoratorStrings[e], name: t, metadata: i, addInitializer: (n) => s._ ? __typeError("Already initialized") : r.push(__expectFn(n || null)) }); var __decoratorMetadata = (e, t) => __defNormalProp2(t, __knownSymbol("metadata"), e[3]); var __runInitializers = (e, t, s, i) => { for (var r = 0, n = e[t >> 1], o = n && n.length; r < o; r++) t & 1 ? n[r].call(s) : i = n[r].call(s, i); return i; }; var __decorateElement = (e, t, s, i, r, n) => { var o, c, u, p, l, a = t & 7, h = !!(t & 8), d = !!(t & 16), g = a > 3 ? e.length + 1 : a ? h ? 1 : 2 : 0, _ = __decoratorStrings[a + 5], k = a > 3 && (e[g - 1] = []), E = e[g] || (e[g] = []), b = a && (!d && !h && (r = r.prototype), a < 5 && (a > 3 || !d) && __getOwnPropDesc2(a < 4 ? r : { get [s]() { return __privateGet(this, n); }, set [s](m) { return __privateSet(this, n, m); } }, s)); a ? d && a < 4 && __name(n, (a > 2 ? "set " : a > 1 ? "get " : "") + s) : __name(r, s); for (var A = i.length - 1; A >= 0; A--) p = __decoratorContext(a, s, u = {}, e[3], E), a && (p.static = h, p.private = d, l = p.access = { has: d ? (m) => __privateIn(r, m) : (m) => s in m }, a ^ 3 && (l.get = d ? (m) => (a ^ 1 ? __privateGet : __privateMethod)(m, r, a ^ 4 ? n : b.get) : (m) => m[s]), a > 2 && (l.set = d ? (m, T) => __privateSet(m, r, T, a ^ 4 ? n : b.set) : (m, T) => m[s] = T)), c = (0, i[A])(a ? a < 4 ? d ? n : b[_] : a > 4 ? void 0 : { get: b.get, set: b.set } : r, p), u._ = 1, a ^ 4 || c === void 0 ? __expectFn(c) && (a > 4 ? k.unshift(c) : a ? d ? n = c : b[_] = c : r = c) : typeof c != "object" || c === null ? __typeError("Object expected") : (__expectFn(o = c.get) && (b.get = o), __expectFn(o = c.set) && (b.set = o), __expectFn(o = c.init) && k.unshift(o)); return a || __decoratorMetadata(e, r), b && __defProp2(r, s, b), d ? a ^ 4 ? n : b : r; }; var __accessCheck = (e, t, s) => t.has(e) || __typeError("Cannot " + s); var __privateIn = (e, t) => Object(t) !== t ? __typeError('Cannot use the "in" operator on this value') : e.has(t); var __privateGet = (e, t, s) => (__accessCheck(e, t, "read from private field"), s ? s.call(e) : t.get(e)); var __privateSet = (e, t, s, i) => (__accessCheck(e, t, "write to private field"), i ? i.call(e, s) : t.set(e, s), s); var __privateMethod = (e, t, s) => (__accessCheck(e, t, "access private method"), s); var require_minimum = __commonJS2({ "config/minimum.json"(e, t) { t.exports = { ext: [], namespaces: { 0: "", 6: "File", 10: "Template" }, nsid: { "": 0, file: 6, template: 10 }, functionHook: [], variable: ["!", "="], parserFunction: [{}, { "!": "!", "=": "=" }, ["msg", "raw"], ["subst", "safesubst"]] }; } }); var MAX_STAGE; var minConfig; var init_constants = __esm2({ "util/constants.ts"() { "use strict"; MAX_STAGE = 2, minConfig = require_minimum(); } }); function restore(e, t) { return e.replace(/\0(\d+)\x7F/gu, (s, i) => t[i]); } var trimLc; var factory; var tidy; var removeComment; var text; var names; var decodeHtmlBasic; var decodeHtml; var init_string = __esm2({ "util/string.ts"() { "use strict"; trimLc = (e) => e.trim().toLowerCase(), factory = (e, t) => (s) => s.replace(e, t), tidy = factory(/[\0\x7F]|\r$/gmu, ""), removeComment = factory(/\0\d+[cn]\x7F/gu, ""), text = (e, t = "") => e.map((s) => typeof s == "string" ? s : s.text()).join(t), names = { lt: "<", gt: ">", lbrack: "[", rbrack: "]", lbrace: "{", rbrace: "}", nbsp: " ", amp: "&", quot: '"' }, decodeHtmlBasic = factory(/&(?:#(\d+|[Xx][\da-fA-F]+)|([lg]t|[LG]T|[lr]brac[ke]|nbsp|amp|AMP|quot|QUOT));/gu, (e, t, s) => t ? String.fromCodePoint(+((/^x/iu.test(t) ? "0" : "") + t)) : names[s.toLowerCase()]), decodeHtml = (e) => decodeHtmlBasic(e); } }); function getRegex(e) { const t = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new WeakMap(); return (i) => { const r = typeof i == "string" ? t : s; if (r.has(i)) { const o = r.get(i); return o.lastIndex = 0, o; } const n = e(i); return r.set(i, n), n; }; } var rawurldecode; var getObjRegex; var init_dist = __esm2({ "../common/dist/index.mjs"() { "use strict"; rawurldecode = (e) => decodeURIComponent(e.replace(/%(?![\da-f]{2})/giu, "%25")), getObjRegex = getRegex; } }); var title_exports = {}; __export2(title_exports, { Title: () => Title }); var Title; var init_title = __esm2({ "lib/title.ts"() { "use strict"; var e, t, s, i; init_dist(), init_string(), Title = (i = class { constructor(r, n, o, { temporary: c, decode: u, selfLink: p } = {}) { v(this, e); v(this, t); v(this, s); I(this, "valid"); const l = r.trim().startsWith("../"); if (u && r.includes("%")) try { r = rawurldecode(r); } catch (h) { } if (r = decodeHtml(r).replace(/[_ ]+/gu, " ").trim(), l) x(this, s, 0); else { let h = n; r.startsWith(":") && (h = 0, r = r.slice(1).trim()); const d = r.split(":"); if (d.length > 1) { const g = trimLc(d[0]), _ = Object.prototype.hasOwnProperty.call(o.nsid, g) && o.nsid[g]; _ && (h = _, r = d.slice(1).join(":").trim()); } x(this, s, h); } const a = r.indexOf("#"); a !== -1 && (r = r.slice(0, a).trim()), this.valid = !!r && decodeHtml(r) === r && !/^:|\0\d+[eh!+-]\x7F|[<>[\]{}|\n]|%[\da-f]{2}|(?:^|\/)\.{1,2}(?:$|\/)/iu.test(l ? /^(?:\.\.\/)+(.*)/u.exec(r)[1] : r), this.main = r, x(this, t, o.namespaces); } get ns() { return f(this, s); } get main() { return f(this, e); } set main(r) { r = r.replace(/_/gu, " ").trim(), x(this, e, r && r[0].toUpperCase() + r.slice(1)); } get prefix() { const r = f(this, t)[this.ns]; return r + (r && ":"); } get title() { return this.getRedirection()[1]; } getRedirection() { return [false, (this.prefix + this.main).replace(/ /gu, "_")]; } }, e = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap(), s = /* @__PURE__ */ new WeakMap(), i); } }); var setChildNodes; var init_debug = __esm2({ "util/debug.ts"() { "use strict"; setChildNodes = (e, t, s, i = []) => { var o, c; let r = e.getChildNodes(), n; r.length === s ? (n = r, r = i) : n = r.splice(t, s, ...i); for (let u = 0; u < i.length; u++) { const p = i[u]; p.setAttribute("parentNode", e), p.setAttribute("nextSibling", r[t + u + 1]), p.setAttribute("previousSibling", r[t + u - 1]); } return (o = r[t - 1]) == null || o.setAttribute("nextSibling", r[t]), (c = r[t + i.length]) == null || c.setAttribute("previousSibling", r[t + i.length - 1]), r === i && e.setAttribute("childNodes", r), n; }; } }); var nodeLike; var init_nodeLike = __esm2({ "mixin/nodeLike.ts"() { "use strict"; nodeLike = (e) => { class t extends e { get firstChild() { return this.childNodes[0]; } get lastChild() { return this.childNodes[this.childNodes.length - 1]; } } return t; }; } }); var _AstNode_decorators; var _init; var AstNode; var init_node = __esm2({ "lib/node.ts"() { "use strict"; var e, t, s, i; init_nodeLike(), _AstNode_decorators = [nodeLike], AstNode = (i = class { constructor() { I(this, "childNodes", []); v(this, e); v(this, t); v(this, s); } get parentNode() { return f(this, e); } get nextSibling() { return f(this, t); } get previousSibling() { return f(this, s); } getChildNodes() { const { childNodes: r } = this; return r; } getAttribute(r) { return this[r]; } setAttribute(r, n) { switch (r) { case "parentNode": x(this, e, n), n || (x(this, t, void 0), x(this, s, void 0)); break; case "nextSibling": x(this, t, n); break; case "previousSibling": x(this, s, n); break; default: this[r] = n; } } insertAdjacent(r, n) { const { parentNode: o } = this; if (!o) throw new Error("There is no parent node!"); const c = o.childNodes.indexOf(this) + n; for (let u = 0; u < r.length; u++) o.insertAt(r[u], c + u); } after(...r) { this.insertAdjacent(r, 1); } before(...r) { this.insertAdjacent(r, 0); } remove(r) { const { parentNode: n, nextSibling: o, previousSibling: c } = this, u = n == null ? void 0 : n.childNodes.indexOf(this); n == null || n.removeAt(u), r && (o == null ? void 0 : o.type) === "text" && (c == null ? void 0 : c.type) === "text" && o.data.startsWith(``) && c.data.endsWith(``) && o.deleteData(0, 1); } }, e = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap(), s = /* @__PURE__ */ new WeakMap(), i), _init = __decoratorStart(null), AstNode = __decorateElement(_init, 0, "AstNode", _AstNode_decorators, AstNode), __runInitializers(_init, 1, AstNode); } }); var basic; var getCondition; var init_selector = __esm2({ "parser/selector.ts"() { "use strict"; basic = (e, t, s) => { if (e.includes("#")) { const i = e.indexOf("#"); return (i === 0 || e.slice(0, i) === t) && e.slice(i + 1) === s; } return !e || e === t; }, getCondition = (e, t, s) => { const i = e.split(","); return ({ type: r, name: n }) => i.some((o) => basic(o.trim(), r, n)); }; } }); var elementLike; var init_elementLike = __esm2({ "mixin/elementLike.ts"() { "use strict"; init_selector(), elementLike = (e) => { var s, V; class t extends e { constructor() { super(...arguments); v(this, s); } getElementBy(o) { for (const c of this.childNodes) { if (c.type === "text") continue; if (o(c)) return c; const u = c.getElementBy(o); if (u) return u; } } querySelector(o) { return this.getElementBy(F(this, s, V).call(this, o)); } getElementsBy(o, c = []) { for (const u of this.childNodes) u.type !== "text" && (o(u) && c.push(u), u.getElementsBy(o, c)); return c; } querySelectorAll(o) { return this.getElementsBy(F(this, s, V).call(this, o)); } } return s = /* @__PURE__ */ new WeakSet(), V = function(o) { return getCondition(o, this); }, t; }; } }); var _AstElement_decorators; var _init2; var _a; var AstElement; var init_element = __esm2({ "lib/element.ts"() { "use strict"; init_string(), init_debug(), init_node(), init_elementLike(), _AstElement_decorators = [elementLike], AstElement = class extends (_a = AstNode) { get length() { return this.childNodes.length; } text(e) { return text(this.childNodes, e); } removeAt(e) { return setChildNodes(this, e, 1)[0]; } insertAt(e, t = this.length) { return setChildNodes(this, t, 0, [e]), e; } append(...e) { this.safeAppend(e); } safeAppend(e) { for (const t of e) this.insertAt(t); } safeReplaceChildren(e) { for (let t = this.length - 1; t >= 0; t--) this.removeAt(t); this.safeAppend(e); } setText(e, t = 0) { t += t < 0 ? this.length : 0; const s = this.childNodes[t], { data: i } = s; return s.replaceData(e), i; } toString(e, t = "") { return this.childNodes.map((s) => s.toString(e)).join(t); } replaceChildren(...e) { this.safeReplaceChildren(e); } }, _init2 = __decoratorStart(_a), AstElement = __decorateElement(_init2, 0, "AstElement", _AstElement_decorators, AstElement), __runInitializers(_init2, 1, AstElement); } }); var AstText; var init_text = __esm2({ "lib/text.ts"() { "use strict"; var e, U, s; init_node(), AstText = (s = class extends AstNode { constructor(r) { super(); v(this, e); I(this, "data", ""); this.data = r; } get type() { return "text"; } toString(r) { return this.data; } text() { return this.data; } replaceData(r) { F(this, e, U).call(this, r); } deleteData(r, n = 1 / 0) { F(this, e, U).call(this, this.data.slice(0, r) + (r < 0 && r + n >= 0 ? "" : this.data.slice(r + n))); } }, e = /* @__PURE__ */ new WeakSet(), U = function(r) { this.setAttribute("data", r); }, s); } }); var OnlyincludeToken; var init_onlyinclude = __esm2({ "src/onlyinclude.ts"() { "use strict"; init_src(), OnlyincludeToken = class extends Token { get type() { return "onlyinclude"; } toString(e) { return `<onlyinclude>${super.toString(e)}</onlyinclude>`; } isPlain() { return true; } }; } }); var hiddenToken; var init_hidden = __esm2({ "mixin/hidden.ts"() { "use strict"; hiddenToken = (e = true, t = true) => (s) => { class i extends s { text() { return ""; } } return i; }; } }); var NowikiBaseToken; var init_base = __esm2({ "src/nowiki/base.ts"() { "use strict"; init_src(), NowikiBaseToken = class extends Token { get innerText() { return this.firstChild.data; } constructor(e = "", t, s) { super(e, t, s); } }; } }); var _NoincludeToken_decorators; var _init3; var _a2; var NoincludeToken; var init_noinclude = __esm2({ "src/nowiki/noinclude.ts"() { "use strict"; init_hidden(), init_base(), _NoincludeToken_decorators = [hiddenToken()], NoincludeToken = class extends (_a2 = NowikiBaseToken) { get type() { return "noinclude"; } toString(e) { return e ? "" : super.toString(); } }, _init3 = __decoratorStart(_a2), NoincludeToken = __decorateElement(_init3, 0, "NoincludeToken", _NoincludeToken_decorators, NoincludeToken), __runInitializers(_init3, 1, NoincludeToken); } }); var TagPairToken; var init_tagPair = __esm2({ "src/tagPair/index.ts"() { "use strict"; var e, t; init_src(), TagPairToken = (t = class extends Token { constructor(i, r, n, o, c, u = []) { super(void 0, c); v(this, e); I(this, "closed"); I(this, "selfClosing"); x(this, e, [i, o || i]), this.closed = o !== "", this.selfClosing = o === void 0, this.append(r, n); const p = typeof r == "string" ? -1 : u.indexOf(r); u.splice(p === -1 ? 1 / 0 : p, 0, this); } toString(i) { const { selfClosing: r, firstChild: n, lastChild: o } = this, [c, u] = f(this, e); return r ? `<${c}${n.toString(i)}/>` : `<${c}${n.toString(i)}>${o.toString(i)}${this.closed ? `</${u}>` : ""}`; } text() { const [i, r] = f(this, e); return this.selfClosing ? `<${i}${this.firstChild.text()}/>` : `<${i}${super.text(">")}${this.closed ? `</${r}>` : ""}`; } }, e = /* @__PURE__ */ new WeakMap(), t); } }); var _IncludeToken_decorators; var _init4; var _a3; var IncludeToken; var init_include = __esm2({ "src/tagPair/include.ts"() { "use strict"; init_hidden(), init_tagPair(), _IncludeToken_decorators = [hiddenToken(false)], IncludeToken = class extends (_a3 = TagPairToken) { get type() { return "include"; } constructor(e, t = "", s, i, r, n) { super(e, t, s != null ? s : "", s === void 0 || i != null ? i : "", r, n); } toString(e) { return e ? "" : super.toString(); } }, _init4 = __decoratorStart(_a3), IncludeToken = __decorateElement(_init4, 0, "IncludeToken", _IncludeToken_decorators, IncludeToken), __runInitializers(_init4, 1, IncludeToken); } }); var AttributeToken; var init_attribute = __esm2({ "src/attribute.ts"() { "use strict"; var e, t, s, i; init_index(), init_src(), AttributeToken = (i = class extends Token { constructor(n, o, c, u = "", p, l = [], a = index_default.getConfig(), h = []) { const d = c; let g; if (o === "gallery" && c === "caption") { const _ = W(q({}, a), { excludes: [...a.excludes, "heading"] }); g = new Token(p, _, h, {}), g.type = "attr-value", g.setAttribute("stage", 1); } else g = p != null ? p : ""; super(void 0, a, h); v(this, e); v(this, t); v(this, s); x(this, e, n), this.append(d, g), x(this, t, u), x(this, s, [...l]); } get type() { return f(this, e); } toString(n) { const [o = "", c = ""] = f(this, s); return f(this, t) ? super.toString(n, f(this, t) + o) + c : this.firstChild.toString(n); } text() { return f(this, t) ? `${super.text(`${f(this, t).trim()}"`)}"` : this.firstChild.text(); } }, e = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap(), s = /* @__PURE__ */ new WeakMap(), i); } }); var toAttributeType; var AttributesToken; var init_attributes = __esm2({ "src/attributes.ts"() { "use strict"; var e, t; init_string(), init_src(), init_attribute(), toAttributeType = (s) => s.slice(0, -1), AttributesToken = (t = class extends Token { constructor(i, r, n, o, c = []) { super(void 0, o, c, {}); v(this, e); if (x(this, e, r), i) { const u = /([^\s/][^\s/=]*)(?:(\s*=\s*)(?:(["'])([\s\S]*?)(\3|$)|(\S*)))?/gu; let p = "", l = u.exec(i), a = 0; const h = () => { p && (super.insertAt(p), p = ""); }; for (; l; ) { const { index: d, 0: g, 1: _, 2: k, 3: E, 4: b, 5: A, 6: m } = l; if (p += i.slice(a, d), /^[\w:][\w:.-]*$/u.test(removeComment(_).trim())) { const T = b != null ? b : m, $2 = [E, A], S = new AttributeToken(toAttributeType(r), n, _, k, T, $2, o, c); h(), super.insertAt(S); } else p += g; ({ lastIndex: a } = u), l = u.exec(i); } p += i.slice(a), h(); } } get type() { return f(this, e); } }, e = /* @__PURE__ */ new WeakMap(), t); } }); var AtomToken; var init_atom = __esm2({ "src/atom.ts"() { "use strict"; var e, t; init_src(), AtomToken = (t = class extends Token { constructor(i, r, n, o, c) { super(i, n, o, c); v(this, e); x(this, e, r); } get type() { return f(this, e); } }, e = /* @__PURE__ */ new WeakMap(), t); } }); var HeadingToken; var init_heading = __esm2({ "src/heading.ts"() { "use strict"; var e, t, J, i; init_src(), init_atom(), HeadingToken = (i = class extends Token { constructor(n, o, c, u = []) { super(void 0, c, u); v(this, t); v(this, e); x(this, e, n); const p = new Token(o[0], c, u); p.type = "heading-title", p.setAttribute("stage", 2); const l = new AtomToken(o[1], "heading-trail", c, u); this.append(p, l); } get type() { return "heading"; } get level() { return f(this, e); } toString(n) { const o = F(this, t, J).call(this); return o + this.firstChild.toString(n) + o + this.lastChild.toString(n); } text() { const n = F(this, t, J).call(this); return n + this.firstChild.text() + n; } }, e = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakSet(), J = function() { return "=".repeat(this.level); }, i); } }); var ParameterToken; var init_parameter = __esm2({ "src/parameter.ts"() { "use strict"; init_string(), init_src(), ParameterToken = class extends Token { get type() { return "parameter"; } get anon() { return this.firstChild.length === 0; } constructor(e, t, s, i = []) { super(void 0, s, i); const r = new Token(typeof e == "number" ? void 0 : e, s, i, {}), n = new Token(t, s, i); r.type = "parameter-key", r.setAttribute("stage", 2), n.type = "parameter-value", n.setAttribute("stage", 2), this.append(r, n); } trimName(e, t = true) { const s = e.toString(true).replace(/^[ \t\n\0\v]+|([^ \t\n\0\v])[ \t\n\0\v]+$/gu, "$1"); return this.setAttribute("name", s), s; } afterBuild() { if (!this.anon) { const { parentNode: e, firstChild: t } = this, s = this.trimName(t); e && e.getArgs(s, false, false).add(this); } super.afterBuild(); } toString(e) { return this.anon ? this.lastChild.toString(e) : super.toString(e, "="); } text() { return this.anon ? this.lastChild.text() : super.text("="); } getValue() { var t; const e = removeComment(this.lastChild.text()); return this.anon && ((t = this.parentNode) == null ? void 0 : t.isTemplate()) !== false ? e : e.trim(); } setValue(e) { this.lastChild.replaceChildren(e); } }; } }); var TranscludeToken; var init_transclude = __esm2({ "src/transclude.ts"() { "use strict"; var e, t, s, i, r, n, Y, Z, u; init_string(), init_src(), init_parameter(), init_atom(), TranscludeToken = (u = class extends Token { constructor(l, a, h, d = []) { var j, z; let g; const _ = /^(?:\s|\0\d+[cn]\x7F)*\0(\d+)h\x7F(?:\s|\0\d+[cn]\x7F)*/u.exec(l); _ && (g = Number(_[1]), l = l.replace(`\0${g}h`, d[g].toString().replace(/^\n/u, ""))); super(void 0, h, d, {}); v(this, n); I(this, "modifier", ""); v(this, e, "template"); v(this, t, ":"); v(this, s, false); v(this, i, /* @__PURE__ */ new Map()); v(this, r); const { parserFunction: [k, E], variable: b, functionHook: A } = h, m = (j = /^(?:\s|\0\d+[cn]\x7F)*\0\d+s\x7F/u.exec(l)) == null ? void 0 : j[0]; if (m) this.setAttribute("modifier", m), l = l.slice(m.length); else if (l.includes(":")) { const [w, ...y] = l.split(":"), [C] = /^(?:\s|\0\d+[cn]\x7F)*/u.exec((z = y[0]) != null ? z : ""); this.setModifier(`${w}:${C}`) && (l = y.join(":").slice(C.length)); } const T = l.search(/[::]/u), $2 = l[T] === ":", S = T !== -1; if (S || a.length === 0 && !f(this, s)) { const w = S ? l.slice(0, T) : l, y = S && l.slice(T + 1), C = removeComment(w), O = S ? C.slice(C.search(/\S/u)) + ($2 ? ":" : "") : C.trim(), L = O.toLowerCase(), M = Object.prototype.hasOwnProperty.call(E, O), P = M ? E[O] : Object.prototype.hasOwnProperty.call(k, L) && k[L], R = !("functionHook" in h) || A.includes(P), G = b.includes(P); if (S ? P && R : G) { this.setAttribute("name", P || L.replace(/^#|:$/u, "")), x(this, e, "magic-word"), $2 && x(this, t, ":"); const re = new AtomToken(w, "magic-word-name", h, d); super.insertAt(re), y !== false && a.unshift([y]); } } if (this.type === "template") { const w = removeComment(l).trim(); if (!this.normalizeTitle(w, 10, { halfParsed: true, temporary: true }).valid) throw d.pop(), new SyntaxError("Invalid template name"); const y = new AtomToken(l, "template-name", h, d, {}); super.insertAt(y); } typeof g == "number" && (d[g] = void 0); const N = this.isTemplate(); let B = 1; for (let w = 0; w < a.length; w++) { const y = a[w]; N || (y[0] = y.join("="), y.length = 1), y.length === 1 && (y.unshift(B), B++), this.insertAt(new ParameterToken(...y, h, d)); } } get type() { return f(this, e); } setModifier(l) { const { parserFunction: [, , a, h] } = this.getAttribute("config"), g = removeComment(l).trim().slice(0, -1).toLowerCase(), _ = a.includes(g), k = h.includes(g); return _ || k || !l ? (this.setAttribute("modifier", l), x(this, s, _), !!l) : false; } isTemplate() { return this.type === "template"; } afterBuild() { this.modifier.includes("\0") && this.setAttribute("modifier", this.buildFromStr(this.modifier, 0)), super.afterBuild(), this.isTemplate() && (x(this, r, F(this, n, Y).call(this)), this.setAttribute("name", f(this, r).title)); } toString(l) { const { childNodes: a, length: h, firstChild: d, modifier: g, type: _ } = this; return `{{${g}${_ === "magic-word" ? d.toString(l) + (h === 1 ? "" : f(this, t)) + a.slice(1).map((k) => k.toString(l)).join("|") : super.toString(l, "|")}}}`; } text() { const { childNodes: l, length: a, firstChild: h, modifier: d, type: g } = this; return `{{${d}${g === "magic-word" ? h.text() + (a === 1 ? "" : f(this, t)) + text(l.slice(1), "|") : super.text("|")}}}`; } insertAt(l, a = this.length) { return super.insertAt(l, a), l.anon ? F(this, n, Z).call(this, l) : l.name && this.getArgs(l.name, false, false).add(l), l; } getAllArgs() { return this.childNodes.slice(1); } getAnonArgs() { return this.getAllArgs().filter(({ anon: l }) => l); } getArgs(l, a, h = true) { const d = String(l).replace(/^[ \t\n\0\v]+|([^ \t\n\0\v])[ \t\n\0\v]+$/gu, "$1"); let g; return f(this, i).has(d) ? g = f(this, i).get(d) : (g = new Set(this.getAllArgs().filter(({ name: _ }) => d === _)), f(this, i).set(d, g)), g; } getArg(l) { const { childNodes: a } = this; return [...this.getArgs(l)].sort((h, d) => a.indexOf(d) - a.indexOf(h))[0]; } getValue(l) { var a; return (a = this.getArg(l)) == null ? void 0 : a.getValue(); } setValue(l, a) { const h = this.getArg(l); h ? h.setValue(a) : this.insertAt(new ParameterToken(String(l), a, this.getAttribute("config"))); } }, e = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap(), s = /* @__PURE__ */ new WeakMap(), i = /* @__PURE__ */ new WeakMap(), r = /* @__PURE__ */ new WeakMap(), n = /* @__PURE__ */ new WeakSet(), Y = function() { return this.normalizeTitle(this.childNodes[0].text(), 10, { temporary: true }); }, Z = function(l) { const a = this.getAnonArgs(), h = l, d = String(a.length); h.setAttribute("name", d), this.getArgs(d, false, false).add(h); }, u); } }); var _HiddenToken_decorators; var _init5; var _a4; var HiddenToken; var init_hidden2 = __esm2({ "src/hidden.ts"() { "use strict"; init_hidden(), init_src(), _HiddenToken_decorators = [hiddenToken()], HiddenToken = class extends (_a4 = Token) { get type() { return "hidden"; } }, _init5 = __decoratorStart(_a4), HiddenToken = __decorateElement(_init5, 0, "HiddenToken", _HiddenToken_decorators, HiddenToken), __runInitializers(_init5, 1, HiddenToken); } }); var ArgToken; var init_arg = __esm2({ "src/arg.ts"() { "use strict"; init_string(), init_src(), init_atom(), init_hidden2(), ArgToken = class extends Token { get type() { return "arg"; } constructor(e, t, s = []) { super(void 0, t, s, {}); for (let i = 0; i < e.length; i++) { const r = e[i]; if (i === 0) { const n = new AtomToken(r, "arg-name", t, s, {}); super.insertAt(n); } else if (i > 1) { const n = new HiddenToken(r, t, s); super.insertAt(n); } else { const n = new Token(r, t, s); n.type = "arg-default", n.setAttribute("stage", 2), super.insertAt(n); } } } toString(e) { return `{{{${super.toString(e, "|")}}}}`; } text() { return `{{{${text(this.childNodes.slice(0, 2), "|")}}}}`; } }; } }); var braces_exports = {}; __export2(braces_exports, { parseBraces: () => parseBraces }); var closes; var lbrack; var newline; var openBraces; var marks; var getExecRegex; var reReplace; var getSymbol; var parseBraces; var init_braces = __esm2({ "parser/braces.ts"() { "use strict"; init_dist(), init_string(), init_heading(), init_transclude(), init_arg(), closes = { "=": String.raw`\n(?!(?:[^\S\n]|\0\d+[cn]\x7F)*\n)`, "{": String.raw`\}{2,}|\|`, "-": String.raw`\}-`, "[": String.raw`\]\]` }, lbrack = String.raw`\[(?!\[)`, newline = String.raw`\n(?![=\0])`, openBraces = String.raw`|\{{2,}`, marks = /* @__PURE__ */ new Map([["!", "!"], ["!!", "+"], ["(!", "{"], ["!)", "}"], ["!-", "-"], ["=", "~"], ["server", "m"]]), getExecRegex = getRegex((e) => new RegExp(e, "gmu")), reReplace = new RegExp(String.raw`\{\{((?:[^\n{}[]|${lbrack}|${newline})*)\}\}(?!\})` + "|" + String.raw`\[\[(?:[^\n[\]{]|${newline})*\]\]` + "|" + String.raw`-\{(?:[^\n{}[]|${lbrack}|${newline})*\}-`, "gu"), getSymbol = (e) => { const t = trimLc(removeComment(e)); return marks.has(t) ? marks.get(t) : /^(?:filepath|(?:full|canonical)urle?):./u.test(t) ? "m" : "t"; }, parseBraces = (e, t, s) => { var g, _, k, E, b; const i = String.raw`${t.excludes.includes("heading") ? "" : String.raw`^((?:\0\d+[cno]\x7F)*)={1,6}|`}\[\[|-\{(?!\{)`, { parserFunction: [, , , r] } = t, n = [], o = [], c = (A, m, T, $2) => { m[m.length - 1].push(restore(A.slice(T, $2), o)); }; let u; do u !== void 0 && (e = u), u = e.replace(reReplace, (A, m, T) => { if (m !== void 0 || typeof T == "string") try { const { length: $2 } = s, S = (m != null ? m : T).split("|"); return new TranscludeToken(restore(S[0], o), S.slice(1).map((N) => { const B = N.indexOf("="); return (B === -1 ? [N] : [N.slice(0, B), N.slice(B + 1)]).map((j) => restore(j, o)); }), t, s), `\0${$2}${getSymbol(S[0])}`; } catch ($2) { if (!($2 instanceof SyntaxError) || $2.message !== "Invalid template name") throw $2; } return o.push(restore(A, o)), `\0${o.length - 1}`; }); while (u !== e); e = u; const p = e.lastIndexOf("}}") - e.length; let l = p + e.length !== -1, a = getExecRegex(i + (l ? openBraces : "")), h = a.exec(e), d; for (; h || d !== void 0 && d <= e.length && ((_ = (g = n[n.length - 1]) == null ? void 0 : g[0]) != null && _.startsWith("=")); ) { if (h != null && h[1]) { const [, { length: y }] = h; h[0] = h[0].slice(y), h.index += y; } const { 0: A, index: m } = h != null ? h : { 0: ``, index: e.length }, T = (k = n.pop()) != null ? k : {}, { 0: $2, index: S, parts: N, findEqual: B, pos: j } = T, z = A === "=" && B; if (A === "]]" || A === "}-") d = m + 2; else if (A === ``) { d = m + 1; const { pos: y, findEqual: C } = (E = n[n.length - 1]) != null ? E : {}; if (y === void 0 || C || removeComment(e.slice(y, S)) !== "") { const O = /^(={1,6})(.+)\1((?:\s|\0\d+[cn]\x7F)*)$/u.exec(e.slice(S, m)); O && (O[2] = restore(O[2], o), O[2].includes(``) || (e = `${e.slice(0, S)}\0${s.length}h${e.slice(m)}`, d = S + 4 + String(s.length).length, new HeadingToken(O[1].length, O.slice(2), t, s))); } } else if (A === "|" || z) d = m + 1, c(e, N, j, m), A === "|" && N.push([]), T.pos = d, T.findEqual = A === "|", n.push(T); else if (A.startsWith("}}")) { const y = A.slice(0, Math.min($2.length, 3)), C = $2.length - y.length, { length: O } = s; d = m + y.length, c(e, N, j, m); let L = false, M = "t"; if (y.length === 3) { const P = N.map((G) => G.join("=")), R = P.length > 1 && removeComment(P[1]).trim(); new ArgToken(P, t, s), R && R.endsWith(":") && r.includes(R.slice(0, -1).toLowerCase()) && (M = "s"); } else try { new TranscludeToken(N[0][0], N.slice(1), t, s), M = getSymbol(N[0][0]); } catch (P) { if (P instanceof SyntaxError && P.message === "Invalid template name") L = true; else throw P; } L || (e = `${e.slice(0, S + C)}\0${O}${M}${e.slice(d)}`, d = S + C + 3 + String(O).length, C > 1 ? n.push({ 0: $2.slice(0, C), index: S, pos: S + C, parts: [[]] }) : C === 1 && e[S - 1] === "-" && n.push({ 0: "-{", index: S - 1, pos: S + 1, parts: [[]] })); } else d = m + A.length, A.startsWith("{") && (h.pos = d, h.parts = [[]]), n.push(..."0" in T ? [T] : [], h); let w = n[n.length - 1]; if (l && p + e.length < d) for (l = false; (b = w == null ? void 0 : w[0]) != null && b.startsWith("{"); ) n.pop(), w = n[n.length - 1]; a = getExecRegex(i + (l ? openBraces : "") + (w ? `|${closes[w[0][0]]}${w.findEqual ? "|=" : ""}` : "")), a.lastIndex = d, h = a.exec(e); } return restore(e, o); }; } }); var nested_exports = {}; __export2(nested_exports, { NestedToken: () => NestedToken }); var NestedToken; var init_nested = __esm2({ "src/nested.ts"() { "use strict"; init_commentAndExt(), init_braces(), init_src(), NestedToken = class extends Token { get type() { return "ext-inner"; } constructor(e, t, s, i, r = []) { const n = Symbol("NestedToken"), { length: o } = r; r.push(n), e && (e = parseCommentAndExt(e, i, r, t)), e && (e = parseBraces(e, i, r)), r.splice(o, 1), super(e, i, r); } }; } }); var multiLine; var init_multiLine = __esm2({ "mixin/multiLine.ts"() { "use strict"; multiLine = (e) => { class t extends e { toString(i) { return super.toString(i, ``); } text() { return super.text(``).replace(/\n\s*\n/gu, ``); } } return t; }; } }); var LinkBaseToken; var init_base2 = __esm2({ "src/link/base.ts"() { "use strict"; var e, t; init_src(), LinkBaseToken = (t = class extends Token { constructor(i, r, n, o = [], c = "|") { super(void 0, n, o, {}); v(this, e); this.insertAt(i), x(this, e, c); } toString(i) { return super.toString(i, f(this, e)); } text() { return super.text("|"); } }, e = /* @__PURE__ */ new WeakMap(), t); } }); var ImageParameterToken; var init_imageParameter = __esm2({ "src/imageParameter.ts"() { "use strict"; init_src(), ImageParameterToken = class extends Token { get type() { return "image-parameter"; } }; } }); var FileToken; var init_file = __esm2({ "src/link/file.ts"() { "use strict"; init_base2(), init_imageParameter(), FileToken = class extends LinkBaseToken { constructor(e, t, s, i = [], r = "|") { super(e, void 0, s, i, r), t !== void 0 && this.append(new ImageParameterToken(t, s, i)); } }; } }); var GalleryImageToken; var init_galleryImage = __esm2({ "src/link/galleryImage.ts"() { "use strict"; init_constants(), init_src(), init_file(), GalleryImageToken = class extends FileToken { constructor(t, s, i, r, n = []) { let o; if (i !== void 0) { const { length: c } = n; o = new Token(i, r, n); for (let u = 0; u < MAX_STAGE; u++) o.parseOnce(); n.splice(c, 1); } super(s, o == null ? void 0 : o.toString(), r, n); I(this, "privateType", "imagemap-image"); this.privateType = `${t}-image`; } get type() { return this.privateType; } }; } }); var gallery_exports = {}; __export2(gallery_exports, { GalleryToken: () => GalleryToken }); var _GalleryToken_decorators; var _init6; var _a5; var GalleryToken; var init_gallery = __esm2({ "src/gallery.ts"() { "use strict"; var e, ee, s; init_multiLine(), init_src(), init_galleryImage(), _GalleryToken_decorators = [multiLine], GalleryToken = (s = class extends (_a5 = Token) { constructor(r, n, o = []) { var c; super(void 0, n, o, {}); v(this, e); for (const u of (c = r == null ? void 0 : r.split(``)) != null ? c : []) { const p = /^([^|]+)(?:\|(.*))?/u.exec(u); if (!p) { super.insertAt(u); continue; } const [, l, a] = p; F(this, e, ee).call(this, l) ? super.insertAt(new GalleryImageToken("gallery", l, a, n, o)) : super.insertAt(u); } } get type() { return "ext-inner"; } }, e = /* @__PURE__ */ new WeakSet(), ee = function(r) { return this.normalizeTitle(r, 6, { halfParsed: true, temporary: true, decode: true }).valid; }, s), _init6 = __decoratorStart(_a5), GalleryToken = __decorateElement(_init6, 0, "GalleryToken", _GalleryToken_decorators, GalleryToken), __runInitializers(_init6, 1, GalleryToken); } }); var imagemap_exports = {}; __export2(imagemap_exports, { ImagemapToken: () => ImagemapToken }); var _ImagemapToken_decorators; var _init7; var _a6; var ImagemapToken; var init_imagemap = __esm2({ "src/imagemap.ts"() { "use strict"; init_multiLine(), init_index(), init_src(), init_galleryImage(), _ImagemapToken_decorators = [multiLine], ImagemapToken = class extends (_a6 = Token) { get type() { return "ext-inner"; } constructor(e, t = index_default.getConfig(), s = []) { if (super(void 0, t, s, {}), !e) return; const i = e.split(``); let r = true, n = false; for (const o of i) { const c = o.trim(); if (!(n || !c || c.startsWith("#"))) { if (r) { const u = o.indexOf("|"), p = u === -1 ? o : o.slice(0, u), { valid: l, ns: a } = this.normalizeTitle(p, 0, { halfParsed: true, temporary: true }); if (l && a === 6) { const h = new GalleryImageToken("imagemap", p, u === -1 ? void 0 : o.slice(u + 1), t, s); super.insertAt(h), r = false; continue; } else n = true; } } super.insertAt(o); } } }, _init7 = __decoratorStart(_a6), ImagemapToken = __decorateElement(_init7, 0, "ImagemapToken", _ImagemapToken_decorators, ImagemapToken), __runInitializers(_init7, 1, ImagemapToken); } }); var nowiki_exports = {}; __export2(nowiki_exports, { NowikiToken: () => NowikiToken }); var NowikiToken; var init_nowiki = __esm2({ "src/nowiki/index.ts"() { "use strict"; init_base(), NowikiToken = class extends NowikiBaseToken { get type() { return "ext-inner"; } }; } }); var ExtToken; var init_ext = __esm2({ "src/tagPair/ext.ts"() { "use strict"; init_index(), init_src(), init_tagPair(), init_attributes(), ExtToken = class extends TagPairToken { get type() { return "ext"; } constructor(e, t, s, i, r = index_default.getConfig(), n = false, o = []) { const c = e.toLowerCase(), u = new AttributesToken(!t || /^\s/u.test(t) ? t : ` ${t}`, "ext-attrs", c, r, o), p = W(q({}, r), { ext: r.ext.filter((a) => a !== c), excludes: [...r.excludes] }); let l; switch (c) { case "indicator": case "poem": case "ref": case "seo": case "langconvert": case "phonos": c === "poem" && p.excludes.push("heading"), l = new Token(s, p, o); break; case "references": { const { NestedToken: a } = (init_nested(), __toCommonJS2(nested_exports)); p.excludes.push("heading"), l = new a(s, n, ["ref"], p, o); break; } case "gallery": { const { GalleryToken: a } = (init_gallery(), __toCommonJS2(gallery_exports)); l = new a(s, p, o); break; } case "imagemap": { const { ImagemapToken: a } = (init_imagemap(), __toCommonJS2(imagemap_exports)); l = new a(s, p, o); break; } default: { const { NowikiToken: a } = (init_nowiki(), __toCommonJS2(nowiki_exports)); l = new a(s, p, o); } } l.setAttribute("name", c), l.type === "plain" && (l.type = "ext-inner"), super(e, u, l, i, r, o); } }; } }); var _CommentToken_decorators; var _init8; var _a7; var CommentToken; var init_comment = __esm2({ "src/nowiki/comment.ts"() { "use strict"; init_hidden(), init_base(), _CommentToken_decorators = [hiddenToken(false)], CommentToken = class extends (_a7 = NowikiBaseToken) { constructor(t, s, i, r) { super(t, i, r); I(this, "closed"); this.closed = s; } get type() { return "comment"; } toString(t) { return t ? "" : `<!--${this.innerText}${this.closed ? "-->" : ""}`; } }, _init8 = __decoratorStart(_a7), CommentToken = __decorateElement(_init8, 0, "CommentToken", _CommentToken_decorators, CommentToken), __runInitializers(_init8, 1, CommentToken); } }); var commentAndExt_exports = {}; __export2(commentAndExt_exports, { parseCommentAndExt: () => parseCommentAndExt }); var onlyincludeLeft; var onlyincludeRight; var length; var getRegex2; var update; var parseCommentAndExt; var init_commentAndExt = __esm2({ "parser/commentAndExt.ts"() { "use strict"; init_dist(), init_onlyinclude(), init_noinclude(), init_include(), init_ext(), init_comment(), onlyincludeLeft = "<onlyinclude>", onlyincludeRight = "</onlyinclude>", { length } = onlyincludeLeft, getRegex2 = [false, true].map((e) => { const t = e ? "includeonly" : "(?:no|only)include", s = e ? "noinclude" : "includeonly"; return getObjRegex((i) => new RegExp(String.raw`<!--[\s\S]*?(?:-->|$)|<${t}(?:\s[^>]*)?/?>|</${t}\s*>|<(${i.join("|")})(\s[^>]*?)?(?:/>|>([\s\S]*?)</(${"\\1"}\s*)>)|<(${s})(\s[^>]*?)?(?:/>|>([\s\S]*?)(?:</(${s}\s*)>|$))`, "giu")); }), update = (e) => { const t = e.indexOf(onlyincludeLeft); return { i: t, j: e.indexOf(onlyincludeRight, t + length) }; }, parseCommentAndExt = (e, t, s, i) => { if (i) { let { i: c, j: u } = update(e); if (c !== -1 && u !== -1) { let p = ""; const l = (a) => { new NoincludeToken(a, t, s), p += `\0${s.length - 1}n`; }; for (; c !== -1 && u !== -1; ) { const a = `\0${s.length}e`; new OnlyincludeToken(e.slice(c + length, u), t, s), c > 0 && l(e.slice(0, c)), p += a, e = e.slice(u + length + 1), { i: c, j: u } = update(e); } return e && l(e), p; } } const { ext: r } = t, n = r.filter((c) => c !== "translate" && c !== "tvar"), o = W(q({}, t), { ext: n }); return e.replace(getRegex2[i ? 1 : 0](n), (c, u, p, l, a, h, d, g, _) => { const k = s.length; let E = "n"; if (u) E = "e", new ExtToken(u, p, l, a, o, h, s); else if (c.startsWith("<!--")) { E = "c"; const b = c.endsWith("-->"); new CommentToken(c.slice(4, b ? -3 : void 0), b, t, s); } else h ? new IncludeToken(h, d, g, _, t, s) : new NoincludeToken(c, t, s); return `\0${k}${E}`; }); }; } }); var src_exports = {}; __export2(src_exports, { Token: () => Token }); var Token; var init_src = __esm2({ "src/index.ts"() { "use strict"; var e, t, s, i, r, n, te, se, u; init_string(), init_constants(), init_debug(), init_index(), init_element(), init_text(), Token = (u = class extends AstElement { constructor(a, h = index_default.getConfig(), d = [], g) { super(); v(this, n); v(this, e, "plain"); v(this, t, 0); v(this, s); v(this, i); v(this, r); typeof a == "string" && this.insertAt(a), x(this, s, h), x(this, i, d), d.push(this); } get type() { return f(this, e); } set type(a) { x(this, e, a); } parseOnce(a = f(this, t), h = false, d) { if (a < f(this, t) || this.length === 0 || !this.isPlain()) return this; if (f(this, t) >= MAX_STAGE) return this; switch (a) { case 0: this.type === "root" && f(this, i).pop(), x(this, r, h), F(this, n, te).call(this, h); break; case 1: F(this, n, se).call(this); } if (this.type === "root") for (const g of f(this, i)) g == null || g.parseOnce(a, h, d); return K(this, t)._++, this; } buildFromStr(a, h) { const d = a.split(/[\0\x7F]/u).map((g, _) => { if (_ % 2 === 0) return new AstText(g); if (isNaN(g.slice(-1))) return f(this, i)[Number(g.slice(0, -1))]; throw new Error(`Failed to build! Unrecognized token: ${g}`); }); return h === 0 ? d.map(String).join("") : h === 1 ? text(d) : d; } build() { x(this, t, MAX_STAGE); const { length: a, firstChild: h } = this, d = h == null ? void 0 : h.toString(); if (a === 1 && h.type === "text" && d.includes("\0") && (setChildNodes(this, 0, 1, this.buildFromStr(d)), this.type === "root")) for (const g of f(this, i)) g == null || g.build(); } afterBuild() { if (this.type === "root") for (const a of f(this, i)) a == null || a.afterBuild(); } parse(a = MAX_STAGE, h, d) { for (a = Math.min(a, MAX_STAGE); f(this, t) < a; ) this.parseOnce(f(this, t), h, d); return a && (this.build(), this.afterBuild()), this; } isPlain() { return this.constructor === u; } getAttribute(a) { switch (a) { case "config": return f(this, s); default: return super.getAttribute(a); } } setAttribute(a, h) { switch (a) { case "stage": f(this, t) === 0 && this.type === "root" && f(this, i).shift(), x(this, t, h); break; default: super.setAttribute(a, h); } } insertAt(a, h = this.length) { const d = typeof a == "string" ? new AstText(a) : a; super.insertAt(d, h); const { type: g } = d; return g === "root" && (d.type = "plain"), d; } normalizeTitle(a, h = 0, d) { return index_default.normalizeTitle(a, h, f(this, r), f(this, s), d); } }, e = /* @__PURE__ */ new WeakMap(), t = /* @__PURE__ */ new WeakMap(), s = /* @__PURE__ */ new WeakMap(), i = /* @__PURE__ */ new WeakMap(), r = /* @__PURE__ */ new WeakMap(), n = /* @__PURE__ */ new WeakSet(), te = function(a) { const { parseCommentAndExt: h } = (init_commentAndExt(), __toCommonJS2(commentAndExt_exports)); this.setText(h(this.firstChild.toString(), f(this, s), f(this, i), a)); }, se = function() { const { parseBraces: a } = (init_braces(), __toCommonJS2(braces_exports)), h = this.type === "root" ? this.firstChild.toString() : `\0${this.firstChild.toString()}`, d = a(h, f(this, s), f(this, i)); this.setText(this.type === "root" ? d : d.slice(1)); }, u); } }); var index_exports = {}; __export2(index_exports, { default: () => index_default }), module.exports = __toCommonJS2(index_exports); var Parser2; var index_default; var init_index = __esm2({ "index.ts"() { init_constants(), init_string(), Parser2 = { getConfig(e) { const t = e != null ? e : this.config; return W(q(q({}, minConfig), t), { excludes: [] }); }, normalizeTitle(e, t = 0, s, i = Parser2.getConfig(), r) { const { Title: n } = (init_title(), __toCommonJS2(title_exports)); let o; if (r != null && r.halfParsed) o = new n(e, t, i, r); else { const { Token: c } = (init_src(), __toCommonJS2(src_exports)); o = (() => { const u = new c(e, i); u.type = "root", u.parseOnce(0, s).parseOnce(); const p = new n(u.toString(), t, i, r); u.build(); const l = p.main; if (l.includes("\0")) { const a = u.buildFromStr(l, 1); p.main = a; } return p; })(); } return o; }, parse(e, t, s = MAX_STAGE, i = Parser2.getConfig()) { e = tidy(e); const { Token: r } = (init_src(), __toCommonJS2(src_exports)); return (() => { const o = new r(e, i); return o.type = "root", o.parse(s, t); })(); } }, index_default = Parser2; } }); init_index(); } }); // modules/Parser.js var import_wikiparser_template, Parser_default; var init_Parser = __esm({ "modules/Parser.js"() { import_wikiparser_template = __toESM(require_bundle_min()); import_wikiparser_template.default.config = { ext: [ "pre", "nowiki", "gallery", "indicator", "langconvert", "graph", "timeline", "hiero", "charinsert", "ref", "references", "inputbox", "imagemap", "source", "syntaxhighlight", "poem", "categorytree", "section", "score", "templatestyles", "templatedata", "math", "ce", "chem", "maplink", "mapframe", "page-collection", "phonos" ] }; Parser_default = import_wikiparser_template.default; } }); // modules/TemplateFinder.js var TemplateFinder; var init_TemplateFinder = __esm({ "modules/TemplateFinder.js"() { init_Parser(); TemplateFinder = class _TemplateFinder { constructor(wikicode) { this.wikiPage = Parser_default.parse(wikicode, false, 2); } static removePrefix(templateName) { return templateName.replace(/^Template:/, ""); } getWikitext() { return String(this.wikiPage); } firstTemplate(templateNameRegExOrArrayCaseInsensitive) { let filter; if (!templateNameRegExOrArrayCaseInsensitive) { filter = () => true; } else if (Array.isArray(templateNameRegExOrArrayCaseInsensitive)) { const templateNameArray = templateNameRegExOrArrayCaseInsensitive.map((name) => name.toLowerCase().replace(/\s/g, "_")); filter = ({ name }) => templateNameArray.includes(_TemplateFinder.removePrefix(name).toLowerCase()); } else { const regEx = new RegExp(`^Template:${templateNameRegExOrArrayCaseInsensitive}$`, "i"); filter = ({ name }) => regEx.test(name.replace(/_/g, " ")); } return this.wikiPage.querySelectorAll("template").find(filter); } firstTemplateInsertCode(templateNameRegExOrArrayCaseInsensitive, codeToInsert) { const template = this.firstTemplate(templateNameRegExOrArrayCaseInsensitive); if (template) { template.append(`${codeToInsert.replace(/^\|/, "")}`); } } firstTemplateGetParameterValue(templateNameRegExOrArrayCaseInsensitive, parameter) { const template = this.firstTemplate(templateNameRegExOrArrayCaseInsensitive); if (!template) { return null; } const value = template.getValue(parameter); return value === void 0 ? null : value; } firstTemplateDeleteParameter(templateNameRegExOrArrayCaseInsensitive, parameter) { const template = this.firstTemplate(templateNameRegExOrArrayCaseInsensitive); if (template) { for (const token of template.getAllArgs()) { if (token.name.toLowerCase() === parameter) { token.remove(); } } } } }; } }); // modules/GANReviewWikicodeGenerator.js var GANReviewWikicodeGenerator_exports = {}; __export(GANReviewWikicodeGenerator_exports, { GANReviewWikicodeGenerator: () => GANReviewWikicodeGenerator }); var GANReviewWikicodeGenerator; var init_GANReviewWikicodeGenerator = __esm({ "modules/GANReviewWikicodeGenerator.js"() { init_TemplateFinder(); GANReviewWikicodeGenerator = class { getPassWikicodeForGANPage(reviewWikicode) { return this.placeATOP(reviewWikicode, "Passed. ~~~~", "green"); } getPassWikicodeForTalkPage(talkWikicode, reviewTitle, topic, oldid) { const gaPageNumber = this.getTemplateParameter(talkWikicode, "GA nominee", "page"); talkWikicode = this.deleteGANomineeTemplate(talkWikicode); const boolHasArticleHistoryTemplate = this.hasArticleHistoryTemplate(talkWikicode); if (boolHasArticleHistoryTemplate) { talkWikicode = this.updateArticleHistory(talkWikicode, topic, reviewTitle, "listed", oldid); } else { talkWikicode = this.addGATemplate(talkWikicode, topic, gaPageNumber, oldid); } talkWikicode = this.changeWikiProjectArticleClassToGA(talkWikicode); return talkWikicode; } getPassWikicodeForGAListPage(gaSubpageHeading, gaSubpageWikicode, gaTitle, gaDisplayTitle) { gaDisplayTitle = gaDisplayTitle.trim(); this.findSectionStartAndEnd(gaSubpageHeading, gaSubpageWikicode); const insertPosition = this.findAlphabeticalInsertPosition(gaSubpageWikicode, gaDisplayTitle); const wikicodeToInsert = this.getWikicodeToInsert(gaTitle, gaDisplayTitle); return this.insertStringIntoStringAtPosition(gaSubpageWikicode, wikicodeToInsert, insertPosition); } getFailWikicodeForGANPage(reviewWikicode) { return this.placeATOP(reviewWikicode, "Unsuccessful. ~~~~", "red"); } getFailWikicodeForTalkPage(talkWikicode, reviewTitle, oldid) { const topic = this.getTopicFromGANomineeTemplate(talkWikicode); const gaPageNumber = this.getTemplateParameter(talkWikicode, "GA nominee", "page"); talkWikicode = this.deleteGANomineeTemplate(talkWikicode); const boolHasArticleHistoryTemplate = this.hasArticleHistoryTemplate(talkWikicode); if (boolHasArticleHistoryTemplate) { talkWikicode = this.updateArticleHistory(talkWikicode, topic, reviewTitle, "failed", oldid); } else { talkWikicode = this.addFailedGATemplate(talkWikicode, topic, gaPageNumber, oldid); } return talkWikicode; } getOnHoldWikicodeForTalkPage(talkWikicode) { return this.changeGANomineeTemplateStatus(talkWikicode, "onhold"); } getAskSecondOpinionWikicodeForTalkPage(talkWikicode) { return this.changeGANomineeTemplateStatus(talkWikicode, "2ndopinion"); } getAnswerSecondOpinionWikicodeForTalkPage(talkWikicode) { return this.changeGANomineeTemplateStatus(talkWikicode, "onreview"); } findSectionStartAndEnd(gaSubpageHeading, gaSubpageWikicode) { const headingStartPosition = this.getGASubpageHeadingPosition(gaSubpageHeading, gaSubpageWikicode); this.subsectionStartPosition = this.findFirstStringAfterPosition("|subsection|\n", gaSubpageWikicode, headingStartPosition) + 13; this.headingEndPosition = this.findFirstStringAfterPosition("\n}}", gaSubpageWikicode, headingStartPosition) + 1; if (this.subsectionStartPosition > this.headingEndPosition) { throw new Error("getPassWikicodeForGAListPage: Unable to find |subheading|\\n"); } } findAlphabeticalInsertPosition(gaSubpageWikicode, gaDisplayTitle) { let insertPosition; let startOfLine = this.subsectionStartPosition; while (startOfLine < this.headingEndPosition) { const endOfLine = this.findFirstStringAfterPosition("\n", gaSubpageWikicode, startOfLine); const line = gaSubpageWikicode.slice(startOfLine, endOfLine); const lineWithSomeFormattingRemoved = this.removeFormattingThatInterferesWithSort(line); const displayTitleWithSomeFormattingRemoved = this.removeFormattingThatInterferesWithSort(gaDisplayTitle); if (!this.aSortsLowerThanB(lineWithSomeFormattingRemoved, displayTitleWithSomeFormattingRemoved)) { insertPosition = startOfLine; break; } startOfLine = endOfLine + 1; } if (!insertPosition) { insertPosition = this.headingEndPosition; } return insertPosition; } changeGANomineeTemplateStatus(talkWikicode, newStatus) { const regex = new RegExp(`({{GA nominee[^\\}]*\\|\\s*status\\s*=\\s*${newStatus})`, "i"); const alreadyHasCorrectStatus = talkWikicode.match(regex); if (alreadyHasCorrectStatus) { return talkWikicode; } const hasStatus = talkWikicode.match(/({{GA nominee[^}]*\|\s*status\s*=\s*)[^}|]*/i); if (hasStatus) { return talkWikicode.replace(/({{GA nominee[^}]*\|\s*status\s*=\s*)[^}|]*/i, `$1${newStatus}`); } return talkWikicode.replace(/({{GA nominee[^}]*)(}})/i, `$1|status=${newStatus}$2`); } getLogMessageToAppend(username, action, reviewTitle, reviewRevisionID, talkRevisionID, gaRevisionID, error) { let textToAppend = "\n* "; if (error) { textToAppend += `<span>ERROR:</span> ${error}. `; } let verb = ""; switch (action) { case "pass": verb = "passed"; break; case "fail": verb = "failed"; break; case "placeOnHold": verb = "placed on hold"; break; case "askSecondOpinion": verb = "asked second opinion regarding"; break; case "answerSecondOpinion": verb = "answered second opinion regarding"; break; } textToAppend += `[[User:${username}|${username}]] ${verb} [[${reviewTitle}]] at ~~~~~. `; if (reviewRevisionID) { textToAppend += `[[Special:Diff/${reviewRevisionID}|[Atop]]]`; } if (talkRevisionID) { textToAppend += `[[Special:Diff/${talkRevisionID}|[Talk]]]`; } if (gaRevisionID) { textToAppend += `[[Special:Diff/${gaRevisionID}|[List]]]`; } return textToAppend; } getWikicodeToInsert(gaTitle, gaDisplayTitle) { if (gaDisplayTitle === gaTitle) { return `[[${gaTitle}]]`; } else if (gaDisplayTitle === `''${gaTitle}''`) { return `''[[${gaTitle}]]''`; } else if (gaDisplayTitle === `"${gaTitle}"`) { return `"[[${gaTitle}]]"`; } else { return `[[${gaTitle}|${gaDisplayTitle}]]`; } } placeATOP(wikicode, result, color) { let colorCode = ""; switch (color) { case "green": colorCode = "g"; break; case "red": colorCode = "r"; break; } const prependText = `{{atop${colorCode}| status = | result = ${result}}}`; const hasH2 = wikicode.match(/^==[^=]+==$/m); if (hasH2) { wikicode = wikicode.replace(new RegExp("^(.*?==[^=]+==\\n)(.*)$", "s"), "$1" + prependText + "\n$2"); } else { wikicode = prependText + "\n" + wikicode; } const appendText = "{{abot}}"; wikicode = wikicode.trim(); wikicode += `${appendText}`; return wikicode; } getTopicFromGANomineeTemplate(talkWikicode) { let topic = this.getTemplateParameter(talkWikicode, "GA nominee", "topic"); if (!topic) { topic = this.getTemplateParameter(talkWikicode, "GA nominee", "subtopic"); } return topic; } getTemplateParameter(wikicode, templateName, parameterName) { templateName = this.regExEscape(templateName); parameterName = this.regExEscape(parameterName); const regex = new RegExp(`\\{\\{${templateName}[^\\}]+\\|\\s*${parameterName}\\s*=\\s*([^\\}\\|]+)\\s*[^\\}]*\\}\\}`, "i"); const parameterValue = wikicode.match(regex); if (Array.isArray(parameterValue) && parameterValue[1] !== void 0) { return parameterValue[1].trim(); } else { return null; } } /** * @copyright coolaj86, CC BY-SA 4.0, https://stackoverflow.com/a/6969486/3480193 */ regExEscape(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } deleteGANomineeTemplate(talkWikicode) { return talkWikicode.replace(/\{\{GA nominee[^}]+\}\}\n?/i, ""); } addGATemplate(talkWikicode, topic, gaPageNumber, oldid) { const codeToAdd = `{{GA|~~~~~|topic=${topic}|page=${gaPageNumber}|oldid=${oldid}}}`; return this.addTemplateInCorrectMOSTalkOrderPosition(talkWikicode, codeToAdd); } addFailedGATemplate(talkWikicode, topic, gaPageNumber, oldid) { const codeToAdd = `{{FailedGA|~~~~~|topic=${topic}|page=${gaPageNumber}|oldid=${oldid}}}`; return this.addTemplateInCorrectMOSTalkOrderPosition(talkWikicode, codeToAdd); } addTemplateInCorrectMOSTalkOrderPosition(talkWikicode, codeToAdd) { const templateName = this.getFirstTemplateNameFromWikicode(codeToAdd); let templatesThatGoBefore; switch (templateName) { case "FailedGA": case "GA": templatesThatGoBefore = ["GA nominee", "Featured article candidates", "Peer review", "Skip to talk", "Talk header", "Talkheader", "Talk page header", "Talkpage", "Ds/talk notice", "Gs/talk notice", "BLP others", "Calm", "Censor", "Controversial", "Not a forum", "FAQ", "Round in circles", "American English", "British English"]; break; default: throw new Error("addTemplateInCorrectMOSTalkOrderPosition: Supplied template is not in dictionary. Unsure where to place it."); } return this.addWikicodeAfterTemplates(talkWikicode, templatesThatGoBefore, codeToAdd); } getFirstTemplateNameFromWikicode(wikicode) { const templateFinder = new TemplateFinder(wikicode); const template = templateFinder.firstTemplate(); if (!template) { throw new Error("getFirstTemplateNameFromWikicode: No template found in Wikicode."); } return TemplateFinder.removePrefix(template.name); } /** * Search algorithm looks for \n after the searched templates. If not present, it will not match. * * @param {string} wikicode * @param {string[]} templates * @param {string} codeToAdd */ addWikicodeAfterTemplates(wikicode, templates, codeToAdd) { let insertPosition = 0; for (const template of templates) { const regex = new RegExp(`{{${this.regExEscape(template)}[^\\}]*}}\\n`, "ig"); const endOfTemplatePosition = this.getEndOfStringPositionOfLastMatch(wikicode, regex); if (endOfTemplatePosition > insertPosition) { insertPosition = endOfTemplatePosition; } } return this.insertStringIntoStringAtPosition(wikicode, codeToAdd, insertPosition); } /** * @param {string} haystack * @param {RegExp} regex /g flag must be set * @return {number} endOfStringPosition Returns zero if not found */ getEndOfStringPositionOfLastMatch(haystack, regex) { const matches = [...haystack.matchAll(regex)]; const hasMatches = matches.length; if (hasMatches) { const lastMatch = matches[matches.length - 1]; const lastMatchStartPosition = lastMatch.index; const lastMatchStringLength = lastMatch[0].length; const lastMatchEndPosition = lastMatchStartPosition + lastMatchStringLength; return lastMatchEndPosition; } return 0; } changeWikiProjectArticleClassToGA(talkWikicode) { talkWikicode = talkWikicode.replace(/(\|\s*class\s*=\s*)(a|b|c|start|stub|list|fa|fl)?(?=[}\s|])/gi, "$1GA"); return talkWikicode; } /** * Determine next |action= number in {{Article history}} template. This is so we can insert an action. */ determineNextActionNumber(talkWikicode) { let i = 1; while (true) { const regex = new RegExp(`\\|\\s*action${i}\\s*=`, "i"); const hasAction = talkWikicode.match(regex); if (!hasAction) { return i; } i++; } } updateArticleHistory(talkWikicode, topic, nominationPageTitle, listedOrFailed, oldid) { const nextActionNumber = this.determineNextActionNumber(talkWikicode); if (listedOrFailed !== "listed" && listedOrFailed !== "failed") { throw new Error("InvalidArgumentException"); } talkWikicode = this.firstTemplateDeleteParameter(talkWikicode, "Article ?history", "topic"); const topicString = `|topic = ${topic}`; const existingStatus = this.firstTemplateGetParameterValue(talkWikicode, "Article ?history", "currentstatus"); talkWikicode = this.firstTemplateDeleteParameter(talkWikicode, "Article ?history", "currentstatus"); const currentStatusString = this.getArticleHistoryNewStatus(existingStatus, listedOrFailed); let addToArticleHistory = `|action${nextActionNumber} = GAN|action${nextActionNumber}date = ~~~~~|action${nextActionNumber}link = ${nominationPageTitle}|action${nextActionNumber}result = ${listedOrFailed}|action${nextActionNumber}oldid = ${oldid}`; addToArticleHistory += currentStatusString + topicString; talkWikicode = this.firstTemplateInsertCode(talkWikicode, "Article ?history", addToArticleHistory); return talkWikicode; } getArticleHistoryNewStatus(existingStatus, listedOrFailed) { if (listedOrFailed === "listed") { switch (existingStatus) { case "FFA": return "\n|currentstatus = FFA/GA"; case "FFAC": return "\n|currentstatus = FFAC/GA"; default: return "\n|currentstatus = GA"; } } else { switch (existingStatus) { case "FFA": return "\n|currentstatus = FFA"; case "FFAC": return "\n|currentstatus = FFAC"; case "DGA": return "\n|currentstatus = DGA"; default: return "\n|currentstatus = FGAN"; } } } firstTemplateInsertCode(wikicode, templateNameRegExNoDelimiters, codeToInsert) { const templateFinder = new TemplateFinder(wikicode); templateFinder.firstTemplateInsertCode(templateNameRegExNoDelimiters, codeToInsert); return templateFinder.getWikitext(); } firstTemplateGetParameterValue(wikicode, templateRegEx, parameter) { const templateFinder = new TemplateFinder(wikicode); return templateFinder.firstTemplateGetParameterValue(templateRegEx, parameter); } firstTemplateDeleteParameter(wikicode, templateRegEx, parameter) { const templateFinder = new TemplateFinder(wikicode); templateFinder.firstTemplateDeleteParameter(templateRegEx, parameter); return templateFinder.getWikitext(); } removeFormattingThatInterferesWithSort(str) { return str.replace(/^[^[]*\[\[(?:[^|]+\|)?/, "").replace(/\]\][^\]]*$/, "").replace(/"/g, "").replace(/''/g, "").replace(/^A /gi, "").replace(/^An /gi, "").replace(/^The /gi, ""); } aSortsLowerThanB(a, b) { a = a.toLowerCase(); b = b.toLowerCase(); a = this.removeDiacritics(a); b = this.removeDiacritics(b); const arr1 = [a, b]; const arr2 = [a, b]; const sortNumerically = (a2, b2) => a2.localeCompare(b2, "en", { numeric: true }); return JSON.stringify(arr1.sort(sortNumerically)) === JSON.stringify(arr2); } /** * @copyright Jeroen Ooms, CC BY-SA 3.0, https://stackoverflow.com/a/18123985/3480193 */ removeDiacritics(str) { const defaultDiacriticsRemovalMap = [ { base: "A", letters: /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g }, { base: "AA", letters: /[\uA732]/g }, { base: "AE", letters: /[\u00C6\u01FC\u01E2]/g }, { base: "AO", letters: /[\uA734]/g }, { base: "AU", letters: /[\uA736]/g }, { base: "AV", letters: /[\uA738\uA73A]/g }, { base: "AY", letters: /[\uA73C]/g }, { base: "B", letters: /[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g }, { base: "C", letters: /[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g }, { base: "D", letters: /[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g }, { base: "DZ", letters: /[\u01F1\u01C4]/g }, { base: "Dz", letters: /[\u01F2\u01C5]/g }, { base: "E", letters: /[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g }, { base: "F", letters: /[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g }, { base: "G", letters: /[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g }, { base: "H", letters: /[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g }, { base: "I", letters: /[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g }, { base: "J", letters: /[\u004A\u24BF\uFF2A\u0134\u0248]/g }, { base: "K", letters: /[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g }, { base: "L", letters: /[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g }, { base: "LJ", letters: /[\u01C7]/g }, { base: "Lj", letters: /[\u01C8]/g }, { base: "M", letters: /[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g }, { base: "N", letters: /[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g }, { base: "NJ", letters: /[\u01CA]/g }, { base: "Nj", letters: /[\u01CB]/g }, { base: "O", letters: /[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g }, { base: "OI", letters: /[\u01A2]/g }, { base: "OO", letters: /[\uA74E]/g }, { base: "OU", letters: /[\u0222]/g }, { base: "P", letters: /[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g }, { base: "Q", letters: /[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g }, { base: "R", letters: /[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g }, { base: "S", letters: /[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g }, { base: "T", letters: /[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g }, { base: "TZ", letters: /[\uA728]/g }, { base: "U", letters: /[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g }, { base: "V", letters: /[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g }, { base: "VY", letters: /[\uA760]/g }, { base: "W", letters: /[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g }, { base: "X", letters: /[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g }, { base: "Y", letters: /[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g }, { base: "Z", letters: /[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g }, { base: "a", letters: /[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g }, { base: "aa", letters: /[\uA733]/g }, { base: "ae", letters: /[\u00E6\u01FD\u01E3]/g }, { base: "ao", letters: /[\uA735]/g }, { base: "au", letters: /[\uA737]/g }, { base: "av", letters: /[\uA739\uA73B]/g }, { base: "ay", letters: /[\uA73D]/g }, { base: "b", letters: /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g }, { base: "c", letters: /[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g }, { base: "d", letters: /[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g }, { base: "dz", letters: /[\u01F3\u01C6]/g }, { base: "e", letters: /[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g }, { base: "f", letters: /[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g }, { base: "g", letters: /[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g }, { base: "h", letters: /[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g }, { base: "hv", letters: /[\u0195]/g }, { base: "i", letters: /[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g }, { base: "j", letters: /[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g }, { base: "k", letters: /[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g }, { base: "l", letters: /[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g }, { base: "lj", letters: /[\u01C9]/g }, { base: "m", letters: /[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g }, { base: "n", letters: /[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g }, { base: "nj", letters: /[\u01CC]/g }, { base: "o", letters: /[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g }, { base: "oi", letters: /[\u01A3]/g }, { base: "ou", letters: /[\u0223]/g }, { base: "oo", letters: /[\uA74F]/g }, { base: "p", letters: /[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g }, { base: "q", letters: /[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g }, { base: "r", letters: /[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g }, { base: "s", letters: /[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g }, { base: "t", letters: /[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g }, { base: "tz", letters: /[\uA729]/g }, { base: "u", letters: /[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g }, { base: "v", letters: /[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g }, { base: "vy", letters: /[\uA761]/g }, { base: "w", letters: /[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g }, { base: "x", letters: /[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g }, { base: "y", letters: /[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g }, { base: "z", letters: /[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g } ]; for (let i = 0; i < defaultDiacriticsRemovalMap.length; i++) { str = str.replace(defaultDiacriticsRemovalMap[i].letters, defaultDiacriticsRemovalMap[i].base); } return str; } getGASubpageHeadingPosition(shortenedVersionInComboBox, wikicode) { const needle = /^={2,5}\s*(.*?)\s*={2,5}$/gm.exec(shortenedVersionInComboBox)[1]; const equalsSignsOnOneSide = /^(={2,5})/gm.exec(shortenedVersionInComboBox)[1]; const regex = new RegExp(`^${equalsSignsOnOneSide}\\s*(?:\\[\\[File:[^\\]]*\\]\\]\\s*)?${this.regExEscape(needle)}\\s*(?:<!--[^\\-]*-->)?\\s*${equalsSignsOnOneSide}$`, "gm"); const result = regex.exec(wikicode); const resultNotFound = result === null; if (resultNotFound) { throw new Error(`WP:GA subpage heading insert location not found. GANReviewHTMLGenerator.js may need updating. Please add this article to the correct WP:GA subpage manually. Problematic heading: ${shortenedVersionInComboBox}`); } else { const headingPosition = result.index; return headingPosition; } } findFirstStringAfterPosition(needle, haystack, position) { const len = haystack.length; for (let i = position; i < len; i++) { const buffer = haystack.slice(i, len); if (buffer.startsWith(needle)) { return i; } } return -1; } /** * @copyright jAndy, CC BY-SA 4.0, https://stackoverflow.com/a/4364902/3480193 */ insertStringIntoStringAtPosition(bigString, insertString, position) { return [ bigString.slice(0, position), insertString, bigString.slice(position) ].join(""); } hasArticleHistoryTemplate(wikicode) { return Boolean(wikicode.match(/\{\{Article ?history/i)); } }; } }); // modules/GANReviewController.js var GANReviewController_exports = {}; __export(GANReviewController_exports, { GANReviewController: () => GANReviewController }); var GANReviewController; var init_GANReviewController = __esm({ "modules/GANReviewController.js"() { init_GANReviewHTMLGenerator(); init_GANReviewWikicodeGenerator(); GANReviewController = class { /** * @param {jQuery} $ jQuery * @param {mw} mw mediawiki, https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw * @param {Location} location https://developer.mozilla.org/en-US/docs/Web/API/Window/location * @param {GANReviewWikicodeGenerator} wg * @param {GANReviewHTMLGenerator} hg */ async execute($2, mw2, location2, wg, hg) { this.$ = $2; this.mw = mw2; this.location = location2; this.wg = wg; this.hg = hg; this.ganReviewPageTitle = this.mw.config.get("wgPageName"); this.ganReviewPageTitle = this.ganReviewPageTitle.replace(/_/g, " "); if (!this.shouldRunOnThisPageQuickChecks(this.ganReviewPageTitle)) { return; } if (!await this.shouldRunOnThisPageSlowChecks()) { return; } await this.displayForm(); await this.listenForUncollapse(); this.handleUserChangingFormType(); this.$("#GANReviewTool-Submit").on("click", async () => { await this.clickSubmit(); }); } async clickSubmit() { this.readFormAndSetVariables(); const hasFormValidationErrors = this.validateForm(); if (hasFormValidationErrors) { return; } this.$("#GANReviewTool-Form").hide(); this.$("#GANReviewTool-ProcessingMessage").show(); this.editSummarySuffix = " ([[User:Novem Linguae/Scripts/GANReviewTool|GANReviewTool]])"; this.reviewTitle = this.ganReviewPageTitle; this.error = false; try { if (this.action === "pass") { await this.doPass(); } else if (this.action === "fail") { await this.doFail(); } else if (this.action === "placeOnHold") { await this.placeOnHold(); } else if (this.action === "askSecondOpinion") { await this.askSecondOpinion(); } else if (this.action === "answerSecondOpinion") { await this.answerSecondOpinion(); } await this.writeToLog(); this.pushStatus("Script complete. Refreshing page."); location.reload(); } catch (err) { this.pushStatus(`<span>An error occurred :( Details: ${err}</span>`); this.error = err; await this.writeToLog(); } } async placeOnHold() { this.editSummary = `placed [[${this.gaTitle}]] GAN nomination on hold` + this.editSummarySuffix; await this.processOnHoldForTalkPage(); } async askSecondOpinion() { this.editSummary = `asked for a 2nd opinion regarding [[${this.gaTitle}]] GAN nomination` + this.editSummarySuffix; await this.processAskSecondOpinionForTalkPage(); } async answerSecondOpinion() { this.editSummary = `answered 2nd opinion request regarding [[${this.gaTitle}]] GAN nomination` + this.editSummarySuffix; await this.processAnswerSecondOpinionForTalkPage(); } async processOnHoldForTalkPage() { this.pushStatus('Marking article talk page status as "on hold"'); let talkWikicode = await this.getWikicode(this.gaTalkTitle); talkWikicode = this.wg.getOnHoldWikicodeForTalkPage(talkWikicode); this.talkRevisionID = await this.makeEdit(this.gaTalkTitle, this.editSummary, talkWikicode); } async processAskSecondOpinionForTalkPage() { this.pushStatus('Marking article talk page status as "asking for a second opinion"'); let talkWikicode = await this.getWikicode(this.gaTalkTitle); talkWikicode = this.wg.getAskSecondOpinionWikicodeForTalkPage(talkWikicode); this.talkRevisionID = await this.makeEdit(this.gaTalkTitle, this.editSummary, talkWikicode); } async processAnswerSecondOpinionForTalkPage() { this.pushStatus('Marking article talk page status as "answered second opinion request (onreview)"'); let talkWikicode = await this.getWikicode(this.gaTalkTitle); talkWikicode = this.wg.getAnswerSecondOpinionWikicodeForTalkPage(talkWikicode); this.talkRevisionID = await this.makeEdit(this.gaTalkTitle, this.editSummary, talkWikicode); } /** * @return {boolean} hasFormValidationErrors */ validateForm() { this.$(".GANReviewTool-ValidationError").hide(); let hasFormValidationErrors = false; if (this.action === "pass" && !this.detailedTopic) { this.$("#GANReviewTool-NoTopicMessage").show(); hasFormValidationErrors = true; } if (this.$('[name="GANReviewTool-DisplayWikicode"]').val().includes("|")) { this.$("#GANReviewTool-NoPipesMessage").show(); hasFormValidationErrors = true; } return hasFormValidationErrors; } async doPass() { this.editSummary = `promote [[${this.gaTitle}]] to good article` + this.editSummarySuffix; this.gaSubpageShortTitle = this.$('[name="GANReviewTool-Topic"]').val(); if (this.needsATOP) { await this.processPassForGANPage(); } await this.processPassForTalkPage(); await this.processPassForGASubPage(); } async doFail() { this.editSummary = `close [[${this.gaTitle}]] good article nomination as unsuccessful` + this.editSummarySuffix; if (this.needsATOP) { await this.processFailForGANPage(); } await this.processFailForTalkPage(); } async processFailForGANPage() { this.pushStatus("Placing {{atop}} and {{abot}} on GA review page."); let reviewWikicode = await this.getWikicode(this.ganReviewPageTitle); reviewWikicode = this.wg.getFailWikicodeForGANPage(reviewWikicode); this.reviewRevisionID = await this.makeEdit(this.reviewTitle, this.editSummary, reviewWikicode); } async processFailForTalkPage() { this.pushStatus("Deleting {{GA nominee}} from article talk page."); this.pushStatus("Adding {{FailedGA}} or {{Article history}} to article talk page."); let talkWikicode = await this.getWikicode(this.gaTalkTitle); let oldid; try { oldid = await this.getRevisionIDOfNewestRevision(this.gaTitle); } catch (err) { throw new Error("Unable to get main article's newest revision ID for placement in the |oldid= parameter of the talk page template. Is the main article created yet?"); } talkWikicode = this.wg.getFailWikicodeForTalkPage(talkWikicode, this.reviewTitle, oldid); this.talkRevisionID = await this.makeEdit(this.gaTalkTitle, this.editSummary, talkWikicode); } async processPassForTalkPage() { this.pushStatus("Deleting {{GA nominee}} from article talk page."); this.pushStatus("Adding {{GA}} or {{Article history}} to article talk page."); this.pushStatus("Changing WikiProject template class parameters to GA on article talk page."); let talkWikicode = await this.getWikicode(this.gaTalkTitle); let oldid; try { oldid = await this.getRevisionIDOfNewestRevision(this.gaTitle); } catch (err) { throw new Error("Unable to get main article's newest revision ID for placement in the |oldid= parameter of the talk page template. Is the main article created yet?"); } talkWikicode = this.wg.getPassWikicodeForTalkPage(talkWikicode, this.reviewTitle, this.gaSubpageShortTitle, oldid); this.talkRevisionID = await this.makeEdit(this.gaTalkTitle, this.editSummary, talkWikicode); } async processPassForGASubPage() { this.pushStatus("Adding to appropriate subpage of [[WP:GA]]"); const gaSubpageLongTitle = "Wikipedia:Good articles/" + this.gaSubpageShortTitle; const gaDisplayTitle = this.$('[name="GANReviewTool-DisplayWikicode"]').val(); let gaSubpageWikicode; try { gaSubpageWikicode = await this.getWikicode(gaSubpageLongTitle); } catch (err) { throw new Error("Error getting GA subpage wikicode. Is this GA subpage created yet?"); } gaSubpageWikicode = this.wg.getPassWikicodeForGAListPage(this.detailedTopic, gaSubpageWikicode, this.gaTitle, gaDisplayTitle); const gaSubPageEditSummary = this.getGASubPageEditSummary(this.editSummary, this.detailedTopic); this.gaRevisionID = await this.makeEdit(gaSubpageLongTitle, gaSubPageEditSummary, gaSubpageWikicode); } async processPassForGANPage() { this.pushStatus("Placing {{atop}} and {{abot}} on GA review page."); let reviewWikicode = await this.getWikicode(this.ganReviewPageTitle); reviewWikicode = this.wg.getPassWikicodeForGANPage(reviewWikicode); this.reviewRevisionID = await this.makeEdit(this.reviewTitle, this.editSummary, reviewWikicode); } async getRevisionIDOfNewestRevision(pageTitle) { const api = new this.mw.Api(); const params = { action: "query", format: "json", prop: "revisions", titles: pageTitle, formatversion: "2", rvlimit: "1", rvdir: "older" }; const result = await api.post(params); const revisionID = result.query.pages[0].revisions[0].revid; return revisionID; } readFormAndSetVariables() { this.action = this.$('[name="GANReviewTool-PassOrFail"]:checked').val(); this.needsATOP = this.$('[name="GANReviewTool-ATOPYesNo"]').is(":checked"); this.detailedTopic = document.querySelector('[name="GANReviewTool-Topic"]'); this.detailedTopic = this.detailedTopic.options[this.detailedTopic.selectedIndex]; this.detailedTopic = this.detailedTopic.text; } /** * Show or hide different parts of the form depending on whether the user clicks pass or fail. */ handleUserChangingFormType() { this.$('[name="GANReviewTool-PassOrFail"]').on("change", () => { if (this.$('[name="GANReviewTool-PassOrFail"]:checked').val() === "pass") { this.$("#GANReviewTool-PassDiv").show(); this.$("#GANReviewTool-PassFailDiv").show(); } else if (this.$('[name="GANReviewTool-PassOrFail"]:checked').val() === "fail") { this.$("#GANReviewTool-PassDiv").hide(); this.$("#GANReviewTool-NoTopicMessage").hide(); this.$("#GANReviewTool-PassFailDiv").show(); } else { this.$("#GANReviewTool-PassDiv").hide(); this.$("#GANReviewTool-NoTopicMessage").hide(); this.$("#GANReviewTool-PassFailDiv").hide(); } }); } async listenForUncollapse() { this.$("#GANReviewTool-Uncollapse").on("click", () => { this.$(".GANReviewTool-Collapsed").hide(); this.$("#GANReviewTool-MainForm").show(); }); } async displayForm() { const obj1 = await this.getWikicodeForMultiplePages([ "Wikipedia:Good articles/Agriculture, food and drink", "Wikipedia:Good articles/Art and architecture", "Wikipedia:Good articles/Engineering and technology", "Wikipedia:Good articles/Geography and places", "Wikipedia:Good articles/History", "Wikipedia:Good articles/Language and literature", "Wikipedia:Good articles/Mathematics" ]); const obj2 = await this.getWikicodeForMultiplePages([ "Wikipedia:Good articles/Media and drama", "Wikipedia:Good articles/Music", "Wikipedia:Good articles/Natural sciences", "Wikipedia:Good articles/Philosophy and religion", "Wikipedia:Good articles/Social sciences and society", "Wikipedia:Good articles/Sports and recreation", "Wikipedia:Good articles/Video games", "Wikipedia:Good articles/Warfare" ]); const wikicodeOfGASubPages = __spreadValues(__spreadValues({}, obj1), obj2); const html = this.hg.getHTML(this.gaTitle, wikicodeOfGASubPages); this.$("#mw-content-text").prepend(html); } async shouldRunOnThisPageSlowChecks() { const reviewWikicode = await this.getWikicode(this.ganReviewPageTitle); if (reviewWikicode.match(/\{\{atop/i)) { return false; } this.gaTitle = this.getGATitle(this.ganReviewPageTitle); this.gaTalkTitle = this.getGATalkTitle(this.gaTitle); const talkWikicode = await this.getWikicode(this.gaTalkTitle); if (!talkWikicode.match(/\{\{GA nominee/i)) { return false; } return true; } async writeToLog() { this.pushStatus("Adding to log"); const username = this.mw.config.get("wgUserName"); const textToAppend = this.wg.getLogMessageToAppend(username, this.action, this.reviewTitle, this.reviewRevisionID, this.talkRevisionID, this.gaRevisionID, this.error); await this.appendToPage("User:Novem Linguae/Scripts/GANReviewTool/GANReviewLog", this.editSummary, textToAppend); } async getWikicode(title) { const api = new this.mw.Api(); const params = { action: "parse", page: title, prop: "wikitext", format: "json" }; const result = await api.post(params); if (result.error) { return ""; } const wikicode = result.parse.wikitext["*"]; return wikicode; } /** * @param {Array} listOfTitles * @return {Promise<Object>} {'Page title': 'Page wikicode', 'Page title2': 'Page wikicode2'} Maximum 12MB of result wikicode. Will cut off after that. */ async getWikicodeForMultiplePages(listOfTitles) { const api = new this.mw.Api(); const params = { action: "query", format: "json", prop: "revisions", titles: listOfTitles.join("|"), formatversion: "2", rvprop: "content" }; const result = await api.post(params); if (result.error) { return ""; } const simplified = this.simplifyQueryRevisionsObject(result); return simplified; } /** * convert from the complex format returned by API action=query&prop=revisions, to * {'Page title': 'Page wikicode', 'Page title2': 'Page wikicode2'} */ simplifyQueryRevisionsObject(queryRevisionsObject) { const pages = queryRevisionsObject.query.pages; let newFormat = {}; for (const page of pages) { if (page.missing) { continue; } const key = page.title; const value = page.revisions[0].content; newFormat[key] = value; } newFormat = this.alphabetizeObjectByKeys(newFormat); return newFormat; } /** * @copyright Mathias Bynens, CC BY-SA 4.0, https://stackoverflow.com/a/31102605/3480193 */ alphabetizeObjectByKeys(unordered) { return Object.keys(unordered).sort().reduce( (obj, key) => { obj[key] = unordered[key]; return obj; }, {} ); } async makeEdit(title, editSummary, wikicode) { const api = new this.mw.Api(); const params = { action: "edit", format: "json", title, text: wikicode, summary: editSummary }; const result = await api.postWithToken("csrf", params); const revisionID = result.edit.newrevid; return revisionID; } /** * Lets you append without getting the Wikicode first. Saves an API query. */ async appendToPage(title, editSummary, wikicodeToAppend) { const api = new this.mw.Api(); const params = { action: "edit", format: "json", title, appendtext: wikicodeToAppend, summary: editSummary }; const result = await api.postWithToken("csrf", params); const revisionID = result.edit.newrevid; return revisionID; } pushStatus(statusToAdd) { this.$("#GANReviewTool-ProcessingMessage > p").append("<br />" + statusToAdd); } shouldRunOnThisPageQuickChecks(title) { const action = this.mw.config.get("wgAction"); if (action !== "view") { return false; } const isDiff = this.mw.config.get("wgDiffNewId"); if (isDiff) { return false; } const isDeletedPage = !this.mw.config.get("wgCurRevisionId"); if (isDeletedPage) { return false; } const namespace = this.mw.config.get("wgNamespaceNumber"); const isTalkNamespace = namespace === 1; if (!isTalkNamespace) { return false; } if (!this.isGASubPage(title)) { return false; } return true; } isGASubPage(title) { return Boolean(title.match(/\/GA\d{1,2}$/)); } getGATitle(title) { title = title.replace("Talk:", ""); title = title.replace(/_/g, " "); title = title.replace(/\/[^/]+$/, ""); return title; } getGATalkTitle(gaTitle) { return "Talk:" + gaTitle; } /** * @param {string} editSummary * @param {string} detailedTopic The heading name, with leading and trailing === to denote it as a heading */ getGASubPageEditSummary(editSummary, detailedTopic) { detailedTopic = detailedTopic.match(/={2,6} ?(.+?) ?={2,6}/)[1]; detailedTopic = detailedTopic.replace(/'{2,}/g, ""); editSummary = `/* ${detailedTopic} */ ${editSummary}`; return editSummary; } }; } }); // modules/GARCloserHTMLGenerator.js var GARCloserHTMLGenerator_exports = {}; __export(GARCloserHTMLGenerator_exports, { GARCloserHTMLGenerator: () => GARCloserHTMLGenerator }); var GARCloserHTMLGenerator; var init_GARCloserHTMLGenerator = __esm({ "modules/GARCloserHTMLGenerator.js"() { GARCloserHTMLGenerator = class { getHTML() { return `<style>#GARCloserTool {border: 1px solid black;padding: 1em;margin-bottom: 1em;}#GARCloserTool h2 {margin-top: 0;}#GARCloserTool strong {text-decoration: underline;}#GARCloserTool p {margin-top: 1.5em;margin-bottom: 1.5em;line-height: 1.5em;}#GARCloserTool-Status {display: none;}.GARCloserTool-ErrorNotice {color: red;font-weight: bold;}#GARCloserTool textarea {height: auto;}</style><div><div><h2>GAR Closer Tool</h2><p><strong>Closing message</strong><br />If you leave this blank, it will default to "Keep" or "Delist"<textarea rows="4"></textarea></p><p><button>Keep</button><button>Delist</button></p></div><div><p>Processing...</p></div></div>`; } }; } }); // modules/GARCloserWikicodeGenerator.js var GARCloserWikicodeGenerator_exports = {}; __export(GARCloserWikicodeGenerator_exports, { GARCloserWikicodeGenerator: () => GARCloserWikicodeGenerator }); var GARCloserWikicodeGenerator; var init_GARCloserWikicodeGenerator = __esm({ "modules/GARCloserWikicodeGenerator.js"() { init_TemplateFinder(); GARCloserWikicodeGenerator = class { processKeepForGARPage(garPageWikicode, message, isCommunityAssessment) { return this.processGARPage(garPageWikicode, message, isCommunityAssessment, "Kept.", "green"); } processKeepForTalkPage(wikicode, garPageTitle, talkPageTitle, oldid) { wikicode = this.removeTemplate("GAR/link", wikicode); wikicode = this.convertGATemplateToArticleHistoryIfPresent(talkPageTitle, wikicode); wikicode = this.updateArticleHistory("keep", wikicode, garPageTitle, oldid); return wikicode; } makeCommunityAssessmentLogEntry(garTitle, wikicode, newArchive, archiveTitle) { let output = ""; if (newArchive) { const archiveNumber = this.getArchiveNumber(archiveTitle); output += `{||-| [[Image:Filing cabinet icon.svg|50px|Archive]]| This is an '''[[Wikipedia:How to archive a talk page|archive]]''' of past discussions. Its contents should be preserved in their current form. If you wish to start a new discussion or revive an old one, please do so on the <span>[{{FULLURL:{{TALKSPACE}}:{{BASEPAGENAME}}}} current talk page]</span>.<!-- Template:Talkarchive -->|}{{Template:Process header green | title = Good article reassessment | section = (archive) | previous = ([[Wikipedia:Good article reassessment/Archive ${archiveNumber - 1}|Page ${archiveNumber - 1}]]) | next = ([[Wikipedia:Good article reassessment/Archive ${archiveNumber + 1}|Page ${archiveNumber + 1}]]) | shortcut = | notes =}}__TOC__`; } else { output += wikicode; } output += `{{${garTitle}}}`; return output; } setGARArchiveTemplate(newArchiveTitle, wikicode) { const archiveNumber = this.getArchiveNumber(newArchiveTitle); return wikicode.replace(/^\d{1,}/, archiveNumber); } /** * @param {'keep'|'delist'} keepOrDelist * @todo too many params. factor the RevisionIDs into their own class */ makeScriptLogEntryToAppend(username, keepOrDelist, reviewTitle, garRevisionID, talkRevisionID, articleRevisionID, gaListRevisionID, garLogRevisionID, garArchiveTemplateRevisionID, error, categoryRevisionID) { let textToAppend = "\n* "; if (error) { textToAppend += `<span>ERROR:</span> ${error}. `; } const keepOrDelistPastTense = this.getKeepOrDelistPastTense(keepOrDelist); textToAppend += `[[User:${username}|${username}]] ${keepOrDelistPastTense} [[${reviewTitle}]] at ~~~~~. `; if (garRevisionID) { textToAppend += `[[Special:Diff/${garRevisionID}|[Atop]]]`; } if (talkRevisionID) { textToAppend += `[[Special:Diff/${talkRevisionID}|[Talk]]]`; } if (articleRevisionID) { textToAppend += `[[Special:Diff/${articleRevisionID}|[Article]]]`; } if (gaListRevisionID) { textToAppend += `[[Special:Diff/${gaListRevisionID}|[List]]]`; } if (garLogRevisionID) { textToAppend += `[[Special:Diff/${garLogRevisionID}|[Log]]]`; } if (garArchiveTemplateRevisionID) { textToAppend += `[[Special:Diff/${garArchiveTemplateRevisionID}|[Tmpl]]]`; } if (categoryRevisionID) { textToAppend += `[[Special:Diff/${categoryRevisionID}|[Cat]]]`; } return textToAppend; } processDelistForGARPage(garPageWikicode, message, isCommunityAssessment) { return this.processGARPage(garPageWikicode, message, isCommunityAssessment, "Delisted.", "red"); } processDelistForTalkPage(wikicode, garPageTitle, talkPageTitle, oldid) { wikicode = this.removeTemplate("GAR/link", wikicode); wikicode = this.removeTemplate("GAR request", wikicode); wikicode = this.convertGATemplateToArticleHistoryIfPresent(talkPageTitle, wikicode); wikicode = this.updateArticleHistory("delist", wikicode, garPageTitle, oldid); wikicode = this.removeGAStatusFromWikiprojectBanners(wikicode); return wikicode; } processDelistForArticle(wikicode) { const gaTemplateNames = ["ga icon", "ga article", "good article"]; for (const templateName of gaTemplateNames) { let regex = new RegExp("\\n\\n\\{\\{" + templateName + "\\}\\}\\n\\n", "i"); wikicode = wikicode.replace(regex, "\n\n"); regex = new RegExp("\\{\\{" + templateName + "\\}\\}\\n?", "i"); wikicode = wikicode.replace(regex, ""); } return wikicode; } processDelistForGAList(wikicode, articleToRemove) { const regex = new RegExp(`'{0,3}"?\\[\\[${this.regExEscape(articleToRemove)}(?:\\|[^\\]]+)?\\]\\]"?'{0,3}\\n`, "gi"); wikicode = wikicode.replace(regex, ""); return wikicode; } processGARPage(garPageWikicode, message, isCommunityAssessment, defaultText, atopColor) { message = this.setMessageIfEmpty(defaultText, message); message = this.addSignatureIfMissing(message); const messageForAtop = this.getMessageForAtop(isCommunityAssessment, message); let result = this.placeATOP(garPageWikicode, messageForAtop, atopColor); if (isCommunityAssessment) { result = this.replaceGARCurrentWithGARResult(message, result); } return result; } /** * Public. Used in GARCloserController. */ getGAListTitleFromTalkPageWikicode(wikicode) { const dictionary = { agriculture: "Wikipedia:Good articles/Agriculture, food and drink", "agriculture, food and drink": "Wikipedia:Good articles/Agriculture, food and drink", "agriculture, food, and drink": "Wikipedia:Good articles/Agriculture, food and drink", cuisine: "Wikipedia:Good articles/Agriculture, food and drink", cuisines: "Wikipedia:Good articles/Agriculture, food and drink", cultivation: "Wikipedia:Good articles/Agriculture, food and drink", drink: "Wikipedia:Good articles/Agriculture, food and drink", "farming and cultivation": "Wikipedia:Good articles/Agriculture, food and drink", farming: "Wikipedia:Good articles/Agriculture, food and drink", "food and drink": "Wikipedia:Good articles/Agriculture, food and drink", food: "Wikipedia:Good articles/Agriculture, food and drink", art: "Wikipedia:Good articles/Art and architecture", architecture: "Wikipedia:Good articles/Art and architecture", "art and architecture": "Wikipedia:Good articles/Art and architecture", engtech: "Wikipedia:Good articles/Engineering and technology", "applied sciences and technology": "Wikipedia:Good articles/Engineering and technology", "applied sciences": "Wikipedia:Good articles/Engineering and technology", computers: "Wikipedia:Good articles/Engineering and technology", "computing and engineering": "Wikipedia:Good articles/Engineering and technology", computing: "Wikipedia:Good articles/Engineering and technology", eng: "Wikipedia:Good articles/Engineering and technology", engineering: "Wikipedia:Good articles/Engineering and technology", "engineering and technology": "Wikipedia:Good articles/Engineering and technology", technology: "Wikipedia:Good articles/Engineering and technology", transport: "Wikipedia:Good articles/Engineering and technology", geography: "Wikipedia:Good articles/Geography and places", "geography and places": "Wikipedia:Good articles/Geography and places", places: "Wikipedia:Good articles/Geography and places", history: "Wikipedia:Good articles/History", archaeology: "Wikipedia:Good articles/History", heraldry: "Wikipedia:Good articles/History", nobility: "Wikipedia:Good articles/History", royalty: "Wikipedia:Good articles/History", "royalty, nobility and heraldry": "Wikipedia:Good articles/History", "world history": "Wikipedia:Good articles/History", langlit: "Wikipedia:Good articles/Language and literature", "language and literature": "Wikipedia:Good articles/Language and literature", "languages and linguistics": "Wikipedia:Good articles/Language and literature", "languages and literature": "Wikipedia:Good articles/Language and literature", languages: "Wikipedia:Good articles/Language and literature", linguistics: "Wikipedia:Good articles/Language and literature", lit: "Wikipedia:Good articles/Language and literature", literature: "Wikipedia:Good articles/Language and literature", math: "Wikipedia:Good articles/Mathematics", "mathematics and mathematicians": "Wikipedia:Good articles/Mathematics", mathematics: "Wikipedia:Good articles/Mathematics", maths: "Wikipedia:Good articles/Mathematics", drama: "Wikipedia:Good articles/Media and drama", ballet: "Wikipedia:Good articles/Media and drama", dance: "Wikipedia:Good articles/Media and drama", film: "Wikipedia:Good articles/Media and drama", films: "Wikipedia:Good articles/Media and drama", "media and drama": "Wikipedia:Good articles/Media and drama", media: "Wikipedia:Good articles/Media and drama", opera: "Wikipedia:Good articles/Media and drama", television: "Wikipedia:Good articles/Media and drama", theater: "Wikipedia:Good articles/Media and drama", theatre: "Wikipedia:Good articles/Media and drama", "theatre, film and drama": "Wikipedia:Good articles/Media and drama", music: "Wikipedia:Good articles/Music", albums: "Wikipedia:Good articles/Music", "classical compositions": "Wikipedia:Good articles/Music", "other music articles": "Wikipedia:Good articles/Music", songs: "Wikipedia:Good articles/Music", natsci: "Wikipedia:Good articles/Natural sciences", astronomy: "Wikipedia:Good articles/Natural sciences", astrophysics: "Wikipedia:Good articles/Natural sciences", "atmospheric science": "Wikipedia:Good articles/Natural sciences", "biology and medicine": "Wikipedia:Good articles/Natural sciences", biology: "Wikipedia:Good articles/Natural sciences", "chemistry and materials science": "Wikipedia:Good articles/Natural sciences", chemistry: "Wikipedia:Good articles/Natural sciences", cosmology: "Wikipedia:Good articles/Natural sciences", "earth science": "Wikipedia:Good articles/Natural sciences", "earth sciences": "Wikipedia:Good articles/Natural sciences", geology: "Wikipedia:Good articles/Natural sciences", geophysics: "Wikipedia:Good articles/Natural sciences", medicine: "Wikipedia:Good articles/Natural sciences", "meteorology and atmospheric sciences": "Wikipedia:Good articles/Natural sciences", meteorology: "Wikipedia:Good articles/Natural sciences", mineralogy: "Wikipedia:Good articles/Natural sciences", "natural science": "Wikipedia:Good articles/Natural sciences", "natural sciences": "Wikipedia:Good articles/Natural sciences", "physics and astronomy": "Wikipedia:Good articles/Natural sciences", physics: "Wikipedia:Good articles/Natural sciences", philrelig: "Wikipedia:Good articles/Philosophy and religion", mysticism: "Wikipedia:Good articles/Philosophy and religion", myth: "Wikipedia:Good articles/Philosophy and religion", mythology: "Wikipedia:Good articles/Philosophy and religion", phil: "Wikipedia:Good articles/Philosophy and religion", "philosophy and religion": "Wikipedia:Good articles/Philosophy and religion", philosophy: "Wikipedia:Good articles/Philosophy and religion", relig: "Wikipedia:Good articles/Philosophy and religion", religion: "Wikipedia:Good articles/Philosophy and religion", "religion, mysticism and mythology": "Wikipedia:Good articles/Philosophy and religion", socsci: "Wikipedia:Good articles/Social sciences and society", "business and economics": "Wikipedia:Good articles/Social sciences and society", business: "Wikipedia:Good articles/Social sciences and society", "culture and society": "Wikipedia:Good articles/Social sciences and society", culture: "Wikipedia:Good articles/Social sciences and society", "culture, society and psychology": "Wikipedia:Good articles/Social sciences and society", "economics and business": "Wikipedia:Good articles/Social sciences and society", economics: "Wikipedia:Good articles/Social sciences and society", education: "Wikipedia:Good articles/Social sciences and society", gov: "Wikipedia:Good articles/Social sciences and society", government: "Wikipedia:Good articles/Social sciences and society", "journalism and media": "Wikipedia:Good articles/Social sciences and society", journalism: "Wikipedia:Good articles/Social sciences and society", law: "Wikipedia:Good articles/Social sciences and society", "magazines and print journalism": "Wikipedia:Good articles/Social sciences and society", "media and journalism": "Wikipedia:Good articles/Social sciences and society", "politics and government": "Wikipedia:Good articles/Social sciences and society", politics: "Wikipedia:Good articles/Social sciences and society", psychology: "Wikipedia:Good articles/Social sciences and society", "social science": "Wikipedia:Good articles/Social sciences and society", "social sciences and society": "Wikipedia:Good articles/Social sciences and society", "social sciences": "Wikipedia:Good articles/Social sciences and society", society: "Wikipedia:Good articles/Social sciences and society", sports: "Wikipedia:Good articles/Sports and recreation", "everyday life": "Wikipedia:Good articles/Sports and recreation", everydaylife: "Wikipedia:Good articles/Sports and recreation", games: "Wikipedia:Good articles/Sports and recreation", recreation: "Wikipedia:Good articles/Sports and recreation", "sport and recreation": "Wikipedia:Good articles/Sports and recreation", sport: "Wikipedia:Good articles/Sports and recreation", "sports and recreation": "Wikipedia:Good articles/Sports and recreation", "video games": "Wikipedia:Good articles/Video games", "video and computer games": "Wikipedia:Good articles/Video games", war: "Wikipedia:Good articles/Warfare", aircraft: "Wikipedia:Good articles/Warfare", "battles and exercises": "Wikipedia:Good articles/Warfare", battles: "Wikipedia:Good articles/Warfare", "decorations and memorials": "Wikipedia:Good articles/Warfare", military: "Wikipedia:Good articles/Warfare", "military people": "Wikipedia:Good articles/Warfare", units: "Wikipedia:Good articles/Warfare", "war and military": "Wikipedia:Good articles/Warfare", warfare: "Wikipedia:Good articles/Warfare", warships: "Wikipedia:Good articles/Warfare", "weapons and buildings": "Wikipedia:Good articles/Warfare", weapons: "Wikipedia:Good articles/Warfare" }; let topic = wikicode.match(new RegExp("(?:\\{\\{Article ?history|\\{\\{GA\\s*(?=\\|)).*?\\|\\s*(?:sub)?topic\\s*=\\s*([^|}\\n]+)", "is"))[1]; topic = topic.toLowerCase().trim(); const gaListTitle = dictionary[topic]; return gaListTitle; } addSignatureIfMissing(message) { if (!message.includes("~~~~")) { message += " ~~~~"; } return message; } setMessageIfEmpty(defaultText, message) { if (message === "") { message = defaultText; } return message; } getMessageForAtop(isCommunityAssessment, message) { let messageForAtop = message; if (isCommunityAssessment) { messageForAtop = ""; } return messageForAtop; } /** * {{GAR/current}} and {{GAR/result}} are templates used in community reassessment GARs. The first needs to be swapped for the second when closing community reassessment GARs. */ replaceGARCurrentWithGARResult(message, wikicode) { message = message.replace(/ ?~~~~/g, ""); return wikicode.replace(/\{\{GAR\/current\}\}/i, `{{subst:GAR/result|result=${this.escapeTemplateParameter(message)}}} ~~~~`); } escapeTemplateParameter(parameter) { return parameter; } /** * Takes a Wikipedia page name with a number on the end, and returns that number. */ getArchiveNumber(title) { return parseInt(title.match(/\d{1,}$/)); } placeATOP(wikicode, result, color) { let colorCode = ""; switch (color) { case "green": colorCode = "g"; break; case "red": colorCode = "r"; break; } const resultText = result ? `| result = ${result}` : ""; const prependText = `{{atop${colorCode}${resultText}}}`; const hasH2OrH3 = wikicode.match(/^===?[^=]+===?$/m); if (hasH2OrH3) { wikicode = wikicode.replace(new RegExp("^(.*?===?[^=]+===?\\n)\\n*(.*)$", "s"), "$1" + prependText + "\n$2"); } else { wikicode = prependText + "\n" + wikicode; } const appendText = "{{abot}}"; wikicode = wikicode.trim(); wikicode += `${appendText}`; return wikicode; } /** * @copyright coolaj86, CC BY-SA 4.0, https://stackoverflow.com/a/6969486/3480193 */ regExEscape(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } removeTemplate(templateName, wikicode) { const regex = new RegExp(`\\{\\{${this.regExEscape(templateName)}[^\\}]*\\}\\}\\n?`, "i"); return wikicode.replace(regex, ""); } regexGetFirstMatchString(regex, haystack) { const matches = haystack.match(regex); if (matches !== null && matches[1] !== void 0) { return matches[1]; } return null; } /** * There's a {{GA}} template that some people use instead of {{Article history}}. If this is present, replace it with {{Article history}}. */ convertGATemplateToArticleHistoryIfPresent(talkPageTitle, wikicode) { const hasArticleHistory = Boolean(wikicode.match(/\{\{Article ?history([^}]*)\}\}/gi)); const gaTemplateWikicode = this.regexGetFirstMatchString(/(\{\{GA[^}]*\}\})/i, wikicode); if (!hasArticleHistory && gaTemplateWikicode) { wikicode = wikicode.replace(/\{\{GA[^}]*\}\}\n?/i, ""); wikicode = wikicode.trim(); const parameters = this.getParametersFromTemplateWikicode(gaTemplateWikicode); const noPageSpecified = parameters.page === void 0; if (noPageSpecified) { parameters.page = 1; } let topicString = ""; if (parameters.topic !== void 0) { topicString = `|topic = ${parameters.topic}`; } else if (parameters.subtopic !== void 0) { topicString = `|topic = ${parameters.subtopic}`; } let oldIDString = ""; if (parameters.oldid !== void 0) { oldIDString = `|action1oldid = ${parameters.oldid}`; } if (parameters.date === void 0 && parameters[1] !== void 0) { parameters.date = parameters[1]; } const addToTalkPageAboveWikiProjects = `{{Article history|currentstatus = GA${topicString}|action1 = GAN|action1date = ${parameters.date}|action1link = ${talkPageTitle}/GA${parameters.page}|action1result = listed${oldIDString}}}`; wikicode = this.addToTalkPageAboveWikiProjects(wikicode, addToTalkPageAboveWikiProjects); } return wikicode; } /** * Adds wikicode right above {{WikiProject X}} or {{WikiProject Banner Shell}} if present, or first ==Header== if present, or at bottom of page. Treat {{Talk:abc/GA1}} as a header. */ addToTalkPageAboveWikiProjects(talkPageWikicode, wikicodeToAdd) { if (!talkPageWikicode) { return wikicodeToAdd; } let wikiProjectLocation = false; const dictionary = ["wikiproject", "wpb", "wpbs", "wpbannershell", "wp banner shell", "bannershell", "scope shell", "project shell", "multiple wikiprojects", "football"]; for (const value of dictionary) { const location2 = talkPageWikicode.toUpperCase().indexOf("{{" + value.toUpperCase()); if (location2 !== -1) { if (wikiProjectLocation === false || wikiProjectLocation > location2) { wikiProjectLocation = location2; } } } const headingLocation = talkPageWikicode.indexOf("=="); const gaTemplateLocation = this.preg_position(new RegExp("\\{\\{[^}]*\\/GA\\d{1,2}\\}\\}", "gis"), talkPageWikicode); let insertPosition; if (wikiProjectLocation !== false) { insertPosition = wikiProjectLocation; } else if (headingLocation !== -1) { insertPosition = headingLocation; } else if (gaTemplateLocation !== false) { insertPosition = gaTemplateLocation; } else { insertPosition = talkPageWikicode.length; } if (headingLocation !== -1 && gaTemplateLocation !== false && gaTemplateLocation < insertPosition) { insertPosition = gaTemplateLocation; } let deleteTopPosition = false; let deleteBottomPosition = false; let pos = insertPosition <= 0 ? 0 : insertPosition - 1; let i = 1; while (pos != 0) { const char = talkPageWikicode.substr(pos, 1); if (char == "\n") { if (i != 1 && i != 2) { deleteTopPosition = pos; if (i == 3) { deleteBottomPosition = insertPosition; } } insertPosition = pos; i++; pos--; } else { break; } } if (deleteTopPosition !== false) { talkPageWikicode = this.deleteMiddleOfString(talkPageWikicode, deleteTopPosition, deleteBottomPosition); } const lengthOfRightHalf = talkPageWikicode.length - insertPosition; const leftHalf = talkPageWikicode.substr(0, insertPosition); const rightHalf = talkPageWikicode.substr(insertPosition, lengthOfRightHalf); if (insertPosition == 0) { return wikicodeToAdd + "\n" + talkPageWikicode; } else { return leftHalf + "\n" + wikicodeToAdd + rightHalf; } } /** * @param {RegExp} regex */ preg_position(regex, haystack) { const matches = [...haystack.matchAll(regex)]; const hasMatches = matches.length; if (hasMatches) { return matches[0].index; } return false; } deleteMiddleOfString(string, deleteStartPosition, deleteEndPosition) { const part1 = string.substr(0, deleteStartPosition); const part2 = string.substr(deleteEndPosition); const final_str = part1 + part2; return final_str; } /** * @return {Object} Parameters, with keys being equivalent to the template parameter names. Unnamed parameters will be 1, 2, 3, etc. */ getParametersFromTemplateWikicode(wikicodeOfSingleTemplate) { wikicodeOfSingleTemplate = wikicodeOfSingleTemplate.slice(2, -2); const strings = wikicodeOfSingleTemplate.split("|"); const parameters = {}; let unnamedParameterCount = 1; let i = 0; for (const string of strings) { i++; if (i == 1) { continue; } const hasEquals = string.indexOf("="); if (hasEquals === -1) { parameters[unnamedParameterCount] = string; unnamedParameterCount++; } else { const matches = string.match(new RegExp("^([^=]*)=(.*)", "s")); const paramName = matches[1].trim().toLowerCase(); const paramValue = matches[2].trim(); parameters[paramName] = paramValue; } } return parameters; } /** * @param {'keep'|'delist'} keepOrDelist */ updateArticleHistory(keepOrDelist, wikicode, garPageTitle, oldid) { const nextActionNumber = this.determineNextActionNumber(wikicode); if (keepOrDelist !== "keep" && keepOrDelist !== "delist") { throw new Error("InvalidArgumentException"); } const topic = this.firstTemplateGetParameterValue(wikicode, "Article ?history", "topic"); let topicString = ""; if (!topic) { topicString = `|topic = ${topic}`; } const existingStatus = this.firstTemplateGetParameterValue(wikicode, "Article ?history", "currentstatus"); wikicode = this.firstTemplateDeleteParameter(wikicode, "Article ?history", "currentstatus"); const currentStatusString = this.getArticleHistoryNewStatus(existingStatus, keepOrDelist); const result = this.getKeepOrDelistPastTense(keepOrDelist); let addToArticleHistory = `|action${nextActionNumber} = GAR|action${nextActionNumber}date = ~~~~~|action${nextActionNumber}link = ${garPageTitle}|action${nextActionNumber}result = ${result}|action${nextActionNumber}oldid = ${oldid}`; addToArticleHistory += currentStatusString + topicString; wikicode = this.firstTemplateInsertCode(wikicode, ["Article history", "ArticleHistory"], addToArticleHistory); return wikicode; } getKeepOrDelistPastTense(keepOrDelist) { switch (keepOrDelist) { case "keep": return "kept"; case "delist": return "delisted"; } } /** * Determine next |action= number in {{Article history}} template. This is so we can insert an action. */ determineNextActionNumber(wikicode) { let i = 1; while (true) { const regex = new RegExp(`\\|\\s*action${i}\\s*=`, "i"); const hasAction = wikicode.match(regex); if (!hasAction) { return i; } i++; } } firstTemplateGetParameterValue(wikicode, templateRegEx, parameter) { const templateFinder = new TemplateFinder(wikicode); return templateFinder.firstTemplateGetParameterValue(templateRegEx, parameter); } getArticleHistoryNewStatus(existingStatus, keepOrDelist) { if (keepOrDelist === "keep") { return `|currentstatus = ${existingStatus}`; } else { return "\n|currentstatus = DGA"; } } /** * @param {Array} templateNameArrayCaseInsensitive */ firstTemplateInsertCode(wikicode, templateNameArrayCaseInsensitive, codeToInsert) { const templateFinder = new TemplateFinder(wikicode); templateFinder.firstTemplateInsertCode(templateNameArrayCaseInsensitive, codeToInsert); return templateFinder.getWikitext(); } removeGAStatusFromWikiprojectBanners(wikicode) { return wikicode.replace(/(\|\s*class\s*=\s*)([^}|\s]*)/gi, "$1"); } firstTemplateDeleteParameter(wikicode, template, parameter) { const templateFinder = new TemplateFinder(wikicode); templateFinder.firstTemplateDeleteParameter(template, parameter); return templateFinder.getWikitext(); } }; } }); // modules/GARCloserController.js var GARCloserController_exports = {}; __export(GARCloserController_exports, { GARCloserController: () => GARCloserController }); var GARCloserController; var init_GARCloserController = __esm({ "modules/GARCloserController.js"() { init_GARCloserHTMLGenerator(); init_GARCloserWikicodeGenerator(); GARCloserController = class { /** * @param {jQuery} $ jQuery * @param {mw} mw mediawiki, https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw * @param {Location} location https://developer.mozilla.org/en-US/docs/Web/API/Window/location * @param {GARCloserWikicodeGenerator} wg * @param {GARCloserHTMLGenerator} hg */ async execute($2, mw2, location2, wg, hg) { this.$ = $2; this.mw = mw2; this.location = location2; this.wg = wg; this.hg = hg; this.scriptLogTitle = "User:Novem Linguae/Scripts/GANReviewTool/GARLog"; this.editSummarySuffix = " ([[User:Novem Linguae/Scripts/GANReviewTool|GANReviewTool]])"; this.garPageTitle = this.mw.config.get("wgPageName"); this.garPageTitle = this.garPageTitle.replace(/_/g, " "); if (!this.shouldRunOnThisPageQuickChecks()) { return; } this.parentArticle = await this.confirmGARAndGetArticleName(); if (!this.parentArticle) { return; } this.talkPageTitle = `Talk:${this.parentArticle}`; const hasGARLinkTemplate = await this.hasGARLinkTemplate(this.talkPageTitle); const hasATOP = await this.hasATOP(this.garPageTitle); if (!hasGARLinkTemplate || hasATOP) { return; } this.$("#mw-content-text").prepend(hg.getHTML()); this.$("#GARCloser-Keep").on("click", async () => { await this.clickKeep(); }); this.$("#GARCloser-Delist").on("click", async () => { await this.clickDelist(); }); } async clickKeep() { try { this.editSummary = `close GAR [[${this.garPageTitle}]] as keep` + this.editSummarySuffix; this.deactivateBothButtons(); this.message = this.$("#GARCloser-Message").val(); await this.processKeepForGARPage(); await this.processKeepForTalkPage(); if (this.isCommunityAssessment()) { await this.makeCommunityAssessmentLogEntry(); await this.makeSureCategoryPageHasWikitext(); } await this.makeScriptLogEntry("keep"); this.pushStatus("Done! Reloading..."); location.reload(); } catch (err) { this.error = err; console.error(err); await this.makeScriptLogEntry("keep"); this.pushStatus(`<span>An error occurred :( Details: ${this.error}</span>`); } } async clickDelist() { try { this.editSummary = `close GAR [[${this.garPageTitle}]] as delist` + this.editSummarySuffix; if (!this.apiMode) { this.deactivateBothButtons(); this.message = this.$("#GARCloser-Message").val(); } await this.processDelistForGARPage(); await this.processDelistForTalkPage(); await this.processDelistForArticle(); await this.processDelistForGAList(); if (this.isCommunityAssessment()) { await this.makeCommunityAssessmentLogEntry(); await this.makeSureCategoryPageHasWikitext(); } await this.makeScriptLogEntry("delist"); if (!this.apiMode) { this.pushStatus("Done! Reloading..."); location.reload(); } } catch (err) { this.error = err; console.error(err); await this.makeScriptLogEntry("delist"); this.pushStatus(`<span>An error occurred :( Details: ${this.error}</span>`); } if (this.apiMode && this.error) { throw new Error(this.error); } } /** * Used by MassGARController. Does the same thing as this.clickDelist(), but with JQuery calls fixed to target MassGARController, no refresh of the page at the end of the task, 10 second edit delay for API etiquette reasons, and re-throwing any caught errors. */ async delistAPI(reassessmentPageTitle, editSummarySuffix, editThrottleInSeconds, message, $2, mw2, wg) { this.apiMode = true; this.editThrottleInSeconds = editThrottleInSeconds; this.editSummarySuffix = ` - ${editSummarySuffix}`; this.garPageTitle = reassessmentPageTitle; this.message = message; this.$ = $2; this.mw = mw2; this.wg = wg; this.parentArticle = this.getIndividualReassessmentParentArticle(this.garPageTitle); this.talkPageTitle = `Talk:${this.parentArticle}`; this.scriptLogTitle = "User:Novem Linguae/Scripts/GANReviewTool/GARLog"; await this.clickDelist(); } async getRevisionIDOfNewestRevision(pageTitle) { const api = new this.mw.Api(); const params = { action: "query", format: "json", prop: "revisions", titles: pageTitle, formatversion: "2", rvlimit: "1", rvdir: "older" }; const result = await api.post(params); if (result.query.pages[0].missing) { throw new Error("getRevisionIDOfNewestRevision: Page appears to have zero revisions"); } const revisionID = result.query.pages[0].revisions[0].revid; return revisionID; } async hasGARLinkTemplate(title) { const wikicode = await this.getWikicode(title); return Boolean(wikicode.match(/\{\{GAR\/link/i)); } async hasATOP(title) { const wikicode = await this.getWikicode(title); return Boolean(wikicode.match(/\{\{Atop/i)); } deactivateBothButtons() { this.$("#GARCloser-Keep").prop("disabled", true); this.$("#GARCloser-Delist").prop("disabled", true); } async processKeepForGARPage() { this.pushStatus("Place {{atop}} on GAR page. Replace {{GAR/current}} if present."); let wikicode = await this.getWikicode(this.garPageTitle); wikicode = this.wg.processKeepForGARPage(wikicode, this.message, this.isCommunityAssessment()); this.garPageRevisionID = await this.makeEdit(this.garPageTitle, this.editSummary, wikicode); if (this.garPageRevisionID === void 0) { throw new Error("Generated wikicode and page wikicode were identical, resulting in a null edit."); } } async processDelistForGARPage() { this.pushStatus("Place {{atop}} on GAR page"); let wikicode = await this.getWikicode(this.garPageTitle); wikicode = this.wg.processDelistForGARPage(wikicode, this.message, this.isCommunityAssessment()); this.garPageRevisionID = await this.makeEdit(this.garPageTitle, this.editSummary, wikicode); if (this.garPageRevisionID === void 0) { throw new Error("Generated wikicode and page wikicode were identical, resulting in a null edit."); } } async processKeepForTalkPage() { this.pushStatus("Remove {{GAR/link}} from talk page, and update {{Article history}}"); let wikicode = await this.getWikicode(this.talkPageTitle); const oldid = await this.getRevisionIDOfNewestRevision(this.parentArticle); wikicode = this.wg.processKeepForTalkPage(wikicode, this.garPageTitle, this.talkPageTitle, oldid); this.talkRevisionID = await this.makeEdit(this.talkPageTitle, this.editSummary, wikicode); if (this.talkRevisionID === void 0) { throw new Error("Generated wikicode and page wikicode were identical, resulting in a null edit."); } } isCommunityAssessment() { if (this.garPageTitle.startsWith("Wikipedia:Good article reassessment/")) { return true; } return false; } async makeCommunityAssessmentLogEntry() { this.pushStatus("Add entry to community assessment log"); this.archiveTitle = await this.getHighestNumberedPage("Wikipedia:Good article reassessment/Archive "); let archiveOldWikicode = await this.getWikicode(this.archiveTitle); const garTemplateCount = this.countGARTemplates(archiveOldWikicode); const maximumNumberOfHeadingsAllowedInArchive = 82; let isNewArchive = false; if (garTemplateCount >= maximumNumberOfHeadingsAllowedInArchive) { this.archiveTitle = this.incrementArchiveTitle(this.archiveTitle); isNewArchive = true; archiveOldWikicode = ""; await this.incrementGARArchiveTemplate(this.archiveTitle); } const archiveNewWikicode = this.wg.makeCommunityAssessmentLogEntry( this.garPageTitle, archiveOldWikicode, isNewArchive, this.archiveTitle ); this.garLogRevisionID = await this.makeEdit(this.archiveTitle, this.editSummary, archiveNewWikicode); if (this.garLogRevisionID === void 0) { throw new Error("Generated wikicode and page wikicode were identical, resulting in a null edit."); } } async makeSureCategoryPageHasWikitext() { const archiveNumber = this.archiveTitle.match(/\d+$/); const categoryTitle = `Category:GAR/${archiveNumber}`; const categoryWikicode = await this.getWikicodeAndDoNotThrowError(categoryTitle); if (!categoryWikicode) { const newWikicode = `{{Wikipedia category}}[[Category:Wikipedia good article reassessment]]`; this.categoryRevisionID = await this.makeEdit(categoryTitle, this.editSummary, newWikicode); } } async incrementGARArchiveTemplate(archiveTitle) { this.pushStatus("Update count at Template:GARarchive"); const wikicode = await this.getWikicode("Template:GARarchive"); const newTemplateWikicode = this.wg.setGARArchiveTemplate(archiveTitle, wikicode); this.garArchiveTemplateRevisionID = await this.makeEdit("Template:GARarchive", this.editSummary, newTemplateWikicode); if (this.garArchiveTemplateRevisionID === void 0) { throw new Error("Generated wikicode and page wikicode were identical, resulting in a null edit."); } } /** * Takes a Wikipedia page name with a number on the end, and returns that page name with the number on the end incremented by one. Example: "Wikipedia:Good article reassessment/Archive 67" -> "Wikipedia:Good article reassessment/Archive 68" */ incrementArchiveTitle(title) { let number = title.match(/\d{1,}$/); number++; const titleWithNoNumber = title.replace(/\d{1,}$/, ""); return titleWithNoNumber + number.toString(); } /** * Counts number of times "{{Wikipedia:Good article reassessment/" occurs in wikicode. */ countGARTemplates(wikicode) { return this.countOccurrencesInString(/\{\{Wikipedia:Good article reassessment\//g, wikicode); } /** * @copyright Lorenz Lo Sauer, CC BY-SA 4.0, https://stackoverflow.com/a/10671743/3480193 * @param {RegExp} needleRegEx Make sure to set the /g parameter. */ countOccurrencesInString(needleRegEx, haystack) { return (haystack.match(needleRegEx) || []).length; } /** * @param {'keep'|'delist'} keepOrDelist */ async makeScriptLogEntry(keepOrDelist) { this.pushStatus("Add entry to GARCloser debug log"); const username = this.mw.config.get("wgUserName"); const wikicode = this.wg.makeScriptLogEntryToAppend( username, keepOrDelist, this.garPageTitle, this.garPageRevisionID, this.talkRevisionID, this.articleRevisionID, this.gaListRevisionID, this.garLogRevisionID, this.garArchiveTemplateRevisionID, this.error, this.categoryRevisionID ); await this.appendToPage(this.scriptLogTitle, this.editSummary, wikicode); } async processDelistForTalkPage() { this.pushStatus("Remove {{GAR/link}} from talk page, update {{Article history}}, remove |class=GA"); let wikicode = await this.getWikicode(this.talkPageTitle); this.gaListTitle = this.wg.getGAListTitleFromTalkPageWikicode(wikicode); const oldid = await this.getRevisionIDOfNewestRevision(this.parentArticle); wikicode = this.wg.processDelistForTalkPage(wikicode, this.garPageTitle, this.talkPageTitle, oldid); this.talkRevisionID = await this.makeEdit(this.talkPageTitle, this.editSummary, wikicode); if (this.talkRevisionID === void 0) { throw new Error("Generated wikicode and page wikicode were identical, resulting in a null edit."); } } async processDelistForArticle() { this.pushStatus("Remove {{Good article}} from article"); let wikicode = await this.getWikicode(this.parentArticle); wikicode = this.wg.processDelistForArticle(wikicode); this.articleRevisionID = await this.makeEdit(this.parentArticle, this.editSummary, wikicode); } async processDelistForGAList() { this.pushStatus("Remove article from list of good articles"); if (!this.gaListTitle) { throw new Error("Unable to determine WP:GA subpage. Is the |topic= on the article's talk page correct?"); } let wikicode = await this.getWikicode(this.gaListTitle); wikicode = this.wg.processDelistForGAList(wikicode, this.parentArticle); this.gaListRevisionID = await this.makeEdit(this.gaListTitle, this.editSummary, wikicode); } /** * This also checks if GARCloser should run at all. A falsey result means that the supplied title is not a GAR page. */ async confirmGARAndGetArticleName() { let parentArticle = ""; const namespace = this.mw.config.get("wgNamespaceNumber"); const isTalkNamespace = namespace === 1; const isGASubPage = this.isGASubPage(this.garPageTitle); const garPageWikicode = await this.getWikicode(this.garPageTitle); const hasGAReassessmentHeading = garPageWikicode.match(/==GA Reassessment==/i); const couldBeIndividualReassessment = isTalkNamespace && isGASubPage && hasGAReassessmentHeading; if (couldBeIndividualReassessment) { parentArticle = this.getIndividualReassessmentParentArticle(this.garPageTitle); const parentArticleWikicode = await this.getWikicode(`Talk:${parentArticle}`); if (parentArticleWikicode.match(/\{\{GAR\/link/i)) { return parentArticle; } } const couldBeCommunityReassessment = this.garPageTitle.startsWith("Wikipedia:Good article reassessment/"); if (couldBeCommunityReassessment) { parentArticle = this.getCommunityReassessmentParentArticle(this.garPageTitle); const parentArticleWikicode = await this.getWikicode(`Talk:${parentArticle}`); if (parentArticleWikicode.match(/\{\{GAR\/link/i)) { return parentArticle; } } } getIndividualReassessmentParentArticle(title) { return title.match(/Talk:(.*)\/GA/)[1]; } getCommunityReassessmentParentArticle(title) { return title.match(/Wikipedia:Good article reassessment\/(.*)\/\d/)[1]; } async getWikicode(title) { const api = new this.mw.Api(); const params = { action: "parse", page: title, prop: "wikitext", format: "json" }; const result = await api.post(params); const wikicode = result.parse.wikitext["*"]; return wikicode; } async getWikicodeAndDoNotThrowError(title) { try { return await this.getWikicode(title); } catch (err) { } return ""; } async makeEdit(title, editSummary, wikicode) { if (this.apiMode) { await this.delay(this.editThrottleInSeconds); } const api = new this.mw.Api(); const params = { action: "edit", format: "json", title, text: wikicode, summary: editSummary }; const result = await api.postWithToken("csrf", params); const revisionID = result.edit.newrevid; return revisionID; } /** * Lets you append without getting the Wikicode first. Saves an API query. * * @private */ async appendToPage(title, editSummary, wikicodeToAppend) { if (this.apiMode) { await this.delay(this.editThrottleInSeconds); } const api = new this.mw.Api(); const params = { action: "edit", format: "json", title, appendtext: wikicodeToAppend, summary: editSummary }; const result = await api.postWithToken("csrf", params); const revisionID = result.edit.newrevid; return revisionID; } /** * Example: To get the latest archive of "Wikipedia:Good article reassessment/Archive ", use getHighestNumberedPage("Wikipedia:Good article reassessment/Archive "), which will return "Wikipedia:Good article reassessment/Archive 67" * * @private */ async getHighestNumberedPage(prefix) { const t = new this.mw.Title(prefix); const prefixNoNamespace = t.getMainText(); const namespaceNumber = t.getNamespaceId(); const api = new this.mw.Api(); const params = { action: "query", format: "json", list: "allpages", apprefix: prefixNoNamespace, apnamespace: namespaceNumber, aplimit: "1", apdir: "descending" }; const result = await api.post(params); const title = result.query.allpages[0].title; return title; } pushStatus(statusToAdd) { if (this.apiMode) { this.$("#MassGARTool-Status").show(); this.$("#MassGARTool-Status > p").append(`<br>${this.parentArticle}: ${statusToAdd}`); } else { this.$("#GARCloserTool-Status").show(); this.$("#GARCloserTool-Status > p").append(`<br>${statusToAdd}`); } } shouldRunOnThisPageQuickChecks() { const action = this.mw.config.get("wgAction"); if (action !== "view") { return false; } const isDiff = this.mw.config.get("wgDiffNewId"); if (isDiff) { return false; } const isDeletedPage = !this.mw.config.get("wgCurRevisionId"); if (isDeletedPage) { return false; } if (this.garPageTitle === "User:Novem_Linguae/sandbox") { return true; } return true; } isGASubPage(title) { return Boolean(title.match(/\/GA\d{1,2}$/)); } async delay(seconds) { const milliseconds = seconds * 1e3; return new Promise((res) => { setTimeout(res, milliseconds); }); } }; } }); // modules/MassGARWikicodeGenerator.js var MassGARWikicodeGenerator_exports = {}; __export(MassGARWikicodeGenerator_exports, { MassGARWikicodeGenerator: () => MassGARWikicodeGenerator }); var MassGARWikicodeGenerator; var init_MassGARWikicodeGenerator = __esm({ "modules/MassGARWikicodeGenerator.js"() { MassGARWikicodeGenerator = class { hasGoodArticleTemplate(mainArticleWikicode) { const gaTemplateNames = ["ga icon", "ga article", "good article"]; return this._wikicodeHasTemplate(mainArticleWikicode, gaTemplateNames); } talkPageIndicatesGA(talkPageWikicode) { const gaTemplateNames = ["GA"]; if (this._wikicodeHasTemplate(talkPageWikicode, gaTemplateNames)) { return true; } const matches = talkPageWikicode.match(/\|\s*currentstatus\s*=\s*GA\b/i); if (matches) { return true; } return false; } hasOpenGAR(talkPageWikicode) { const garTemplateNames = ["GAR/link"]; return this._wikicodeHasTemplate(talkPageWikicode, garTemplateNames); } /** * @param {string} wikicode * @param {Array} listOfTemplates Case insensitive. */ _wikicodeHasTemplate(wikicode, listOfTemplates) { const stringForRegEx = listOfTemplates.map((v) => this._regExEscape(v)).join("|"); const regex = new RegExp(`{{(?:${stringForRegEx})\\b`, "i"); const matches = wikicode.match(regex); if (matches) { return true; } return false; } /** * @copyright coolaj86, CC BY-SA 4.0 https://stackoverflow.com/a/6969486/3480193 */ _regExEscape(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } }; } }); // modules/MassGARController.js var MassGARController_exports = {}; __export(MassGARController_exports, { MassGARController: () => MassGARController }); var MassGARController; var init_MassGARController = __esm({ "modules/MassGARController.js"() { init_GARCloserController(); init_MassGARWikicodeGenerator(); init_GARCloserWikicodeGenerator(); MassGARController = class { /** * @param {jQuery} $ jQuery * @param {mw} mw mediawiki, https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw * @param {MassGARWikicodeGenerator} mgwg * @param {GARCloserController} gcc * @param {GARCloserWikicodeGenerator} gcwg */ async execute($2, mw2, mgwg, gcc, gcwg) { this.$ = $2; this.mw = mw2; this.mgwg = mgwg; this.gcc = gcc; this.gcwg = gcwg; this.editThrottleInSeconds = 10; if (!this.isCorrectPage()) { return; } if (!this.isAuthorizedUser()) { mw2.notify("Sorry. You are not currently authorized to run mass GARs."); return; } this.showHTMLForm(); this.$("#MassGARTool-Run").on("click", async () => { try { await this.clickRun(); } catch (err) { this.error = err; console.error(err); this.pushStatus(`<span>An error occurred :( Details: ${this.error}</span>`); } }); } async clickRun() { this.pushStatus("<br>Run button was clicked. Starting new run."); const listOfMainArticleTitles = this.$("#MassGARTool-ListOfGARs").val().trim().split("\n"); this.reassessmentPageWikicode = this.$("#MassGARTool-IndividualReassessmentPageWikicode").val(); this.editSummary = this.$("#MassGARTool-EditSummary").val(); for (const mainArticleTitle of listOfMainArticleTitles) { this.mainArticleTitle = mainArticleTitle; this.pushStatus(`${this.mainArticleTitle}: Started this article.`); this.mainArticleWikicode = await this.getWikicode(this.mainArticleTitle); this.talkPageTitle = new this.mw.Title(this.mainArticleTitle).getTalkPage().getPrefixedText(); this.talkPageWikicode = await this.getWikicode(this.talkPageTitle); this.verifyGoodArticleStatus(); this.verifyNoOpenGAR(); await this.placeGARTemplateOnTalkPage(); await this.createIndividualReassessmentPage(); await this.gcc.delistAPI(this.reassessmentPageTitle, this.editSummary, this.editThrottleInSeconds, "", this.$, this.mw, this.gcwg); this.pushStatus(`${this.mainArticleTitle}: Completed this article.`); } this.pushStatus("Run complete."); } verifyGoodArticleStatus() { this.pushStatus(`${this.mainArticleTitle}: Checking to make sure that it's a good article.`); if (!this.mgwg.hasGoodArticleTemplate(this.mainArticleWikicode)) { throw new Error(`${this.mainArticleTitle}: doesn't appear to be a good article. The main article page is missing a GA topicon.`); } if (!this.mgwg.talkPageIndicatesGA(this.talkPageWikicode)) { throw new Error(`${this.mainArticleTitle}: doesn't appear to be a good article. The article talk page does not indicate that this is a good article.`); } } verifyNoOpenGAR() { this.pushStatus(`${this.mainArticleTitle}: Checking to make sure that there isn't an open GAR.`); if (this.mgwg.hasOpenGAR(this.talkPageWikicode)) { throw new Error(`${this.mainArticleTitle}: someone appears to have already opened a GAR. The talk page contains the template {{GAR/link}}.`); } } async placeGARTemplateOnTalkPage() { this.pushStatus(`${this.mainArticleTitle}: Placing {{subst:GAR}} template on talk page, which will transform into {{GAR/link}}.`); const textToPrepend = "{{subst:GAR}}\n"; await this.prependEdit(this.talkPageTitle, this.editSummary, textToPrepend); } /** * This does not notify the nominator, notify the creator, or transclude the reassessment to the talk page. This only creates the individual reassessment page. */ async createIndividualReassessmentPage() { this.pushStatus(`${this.mainArticleTitle}: Creating an individual assessment page.`); const searchPrefixNoNamespace = this.mainArticleTitle + "/GA"; const listOfPages = await this.getAllSubpagesStartingWith(searchPrefixNoNamespace); this.reassessmentPageTitle = await this.getNextUnusedGASubpageTitle(listOfPages, this.mainArticleTitle); this.pushStatus(`${this.mainArticleTitle}: Decided to name the subpage ${this.reassessmentPageTitle}.`); await this.makeEdit(this.reassessmentPageTitle, this.editSummary, this.reassessmentPageWikicode); } /** * Manually tested. This is complicated but it works. * * @todo Could probably get rid of the complicated API call and math, and just read what the wikicode result of {{subst:GAR}} was in a previous step. Its |page= parameter either has the highest existing subpage # or the first empty subpage #. In other words, that template does the same calculation, so no reason to do it twice. */ getNextUnusedGASubpageTitle(listOfPages, mainArticleTitle) { listOfPages = listOfPages.map((v) => { let number = v.match(/(\d+)$/)[1]; number = parseInt(number); return number; }); listOfPages = this.sortNumerically(listOfPages); const highestSubpageNumber = listOfPages.length ? listOfPages[listOfPages.length - 1] : 0; const newSubpageNumber = highestSubpageNumber + 1; return `Talk:${mainArticleTitle}/GA${newSubpageNumber}`; } /** * @param {Array} listOfNumbers * @copyright aks, CC BY-SA 4.0, https://stackoverflow.com/a/1063027/3480193 */ sortNumerically(listOfNumbers) { return listOfNumbers.sort((a, b) => a - b); } /** * @return {Promise<Array>} listOfPages */ async getAllSubpagesStartingWith(searchPrefixNoNamespace) { const api = new this.mw.Api(); const params = { action: "query", format: "json", list: "allpages", formatversion: "2", apprefix: searchPrefixNoNamespace, apnamespace: "1", // article talk aplimit: "max" }; const result = await api.post(params); const allPages = result.query.allpages; const listOfPages = []; for (const key in allPages) { listOfPages.push(allPages[key].title); } return listOfPages; } showHTMLForm() { const formHTML = `<style>#MassGARTool {border: 1px solid black;padding: 1em;margin-bottom: 1em;}#MassGARTool h2 {margin-top: 0;}#MassGARTool strong {text-decoration: underline;}#MassGARTool p {margin-top: 1.5em;margin-bottom: 1.5em;line-height: 1.5em;}#MassGARTool-Status {display: none;}.MassGARTool-ErrorNotice {color: red;font-weight: bold;}#MassGARTool textarea {height: auto;}#MassGARTool input[type="text"] {width: 100%;}</style><div><div><h2>Mass GAR Tool</h2><p>This tool currently creates individual reassessment pages, then de-lists them all. It skips notifying creator, notifying nominator, and transcluding the assessment to the article talk page. Individual reassessment is deprecated, but is often better for mass delisting because then it won't spam the community reassessment GAR logs. Anyway, this code may need adjusting for future mass GARs.</p><p>To follow API etiquette, there is a 10 second edit throttle. So this page will go a bit slow. Please leave this tab open while the bot is running. Closing this tab will stop the bot.</p><p><strong>Edit summary</strong><br /><input type="text" /></p><p><strong>Individual reassessment page wikicode</strong><br /><textarea rows="5"></textarea></p><p><strong>List of GARs</strong><br />Separate with line breaks<textarea rows="10"></textarea></p><p><button>Run</button></p></div><div><p></p></div></div>`; const defaultEditSummary = "mass delist certain GAs per [[Wikipedia:Good article reassessment/February 2023]] (NovemBot Task 6)"; const defaultIndividualReassessmentPageWikicode = "{{subst:Wikipedia:Good article reassessment/February 2023/GAR notice}}"; const defaultListOfGARs = `Julius Kahn (inventor)Trussed Concrete Steel Company`; this.$(".mw-parser-output").after(formHTML); this.$("#MassGARTool-EditSummary").val(defaultEditSummary); this.$("#MassGARTool-IndividualReassessmentPageWikicode").val(defaultIndividualReassessmentPageWikicode); this.$("#MassGARTool-ListOfGARs").val(defaultListOfGARs); } isCorrectPage() { const currentPageTitle = this.mw.config.get("wgPageName").replace(/_/g, " "); if (currentPageTitle === "User:Novem Linguae/Scripts/GANReviewTool/MassGAR") { return true; } return false; } isAuthorizedUser() { const username = this.mw.config.get("wgUserName"); if (username === "Novem Linguae" || username === "NovemBot") { return true; } return false; } pushStatus(statusToAdd) { this.$("#MassGARTool-Status").show(); this.$("#MassGARTool-Status > p").append("<br />" + statusToAdd); } async getWikicode(title) { const api = new this.mw.Api(); const params = { action: "parse", page: title, prop: "wikitext", format: "json" }; let result; try { result = await api.post(params); } catch (e) { if (e === "missingtitle") { throw new Error(`${title}: does not appear to be created yet.`); } else { throw e; } } const wikicode = result.parse.wikitext["*"]; return wikicode; } async makeEdit(title, editSummary, wikicode) { await this.delay(this.editThrottleInSeconds); const api = new this.mw.Api(); const params = { action: "edit", format: "json", title, text: wikicode, summary: editSummary }; const result = await api.postWithToken("csrf", params); const revisionID = result.edit.newrevid; return revisionID; } async prependEdit(title, editSummary, wikicode) { await this.delay(this.editThrottleInSeconds); const api = new this.mw.Api(); const params = { action: "edit", format: "json", title, prependtext: wikicode, summary: editSummary }; const result = await api.postWithToken("csrf", params); const revisionID = result.edit.newrevid; return revisionID; } async delay(seconds) { const milliseconds = seconds * 1e3; return new Promise((res) => { setTimeout(res, milliseconds); }); } }; } }); // GANReviewTool.js var { GANReviewController: GANReviewController2 } = (init_GANReviewController(), __toCommonJS(GANReviewController_exports)); var { GANReviewHTMLGenerator: GANReviewHTMLGenerator2 } = (init_GANReviewHTMLGenerator(), __toCommonJS(GANReviewHTMLGenerator_exports)); var { GANReviewWikicodeGenerator: GANReviewWikicodeGenerator2 } = (init_GANReviewWikicodeGenerator(), __toCommonJS(GANReviewWikicodeGenerator_exports)); var { GARCloserController: GARCloserController2 } = (init_GARCloserController(), __toCommonJS(GARCloserController_exports)); var { GARCloserHTMLGenerator: GARCloserHTMLGenerator2 } = (init_GARCloserHTMLGenerator(), __toCommonJS(GARCloserHTMLGenerator_exports)); var { GARCloserWikicodeGenerator: GARCloserWikicodeGenerator2 } = (init_GARCloserWikicodeGenerator(), __toCommonJS(GARCloserWikicodeGenerator_exports)); var { MassGARController: MassGARController2 } = (init_MassGARController(), __toCommonJS(MassGARController_exports)); var { MassGARWikicodeGenerator: MassGARWikicodeGenerator2 } = (init_MassGARWikicodeGenerator(), __toCommonJS(MassGARWikicodeGenerator_exports)); $(async () => { await mw.loader.using(["mediawiki.api"], async () => { const ganController = new GANReviewController2(); await ganController.execute( $, mw, location, new GANReviewWikicodeGenerator2(), new GANReviewHTMLGenerator2() ); const garController = new GARCloserController2(); await garController.execute( $, mw, location, new GARCloserWikicodeGenerator2(), new GARCloserHTMLGenerator2() ); const massGARController = new MassGARController2(); await massGARController.execute( $, mw, new MassGARWikicodeGenerator2(), new GARCloserController2(), new GARCloserWikicodeGenerator2() ); }); });})();// </nowiki>