Thành viên:Lê Song Vĩ/CHUCloser.js

Bách khoa toàn thư mở Wikipedia

Chú ý: Sau khi lưu thay đổi trang, bạn phải xóa bộ nhớ đệm của trình duyệt để nhìn thấy các thay đổi. Google Chrome, Firefox, Internet ExplorerSafari: Giữ phím ⇧ Shift và nhấn nút Reload/Tải lại trên thanh công cụ của trình duyệt. Để biết chi tiết và hướng dẫn cho các trình duyệt khác, xem Trợ giúp:Xóa bộ nhớ đệm.

/*
 * CHUCloser.js
 * Công cụ hỗ trợ đóng nhanh yêu cầu đổi tên
 *
 * Giới thiệu: Công cụ CHUCloser.js hỗ trợ đóng nhanh yêu cầu đổi tên bằng cách kiểm tra nội dung yêu cầu và thành viên đóng trong nhóm Steward, Global renamer hoặc là các thành viên uy tín của Wikipedia tiếng Việt. Từ đó có thể gợi ý cho người sử dụng nên đóng yêu cầu đổi tên với kết quả nào, giúp tiết kiệm thời gian trên trang [[Wikipedia:Yêu cầu đổi tên]] vốn không nhận được nhiều sự quan tâm.
 *
 * Sau khi thêm vào common.js. Bạn có thể sử dụng script này theo 3 cách:
 * - Cách 1: Đóng nhanh yêu cầu ngay trên trang [[Wikipedia:Yêu cầu đổi tên]], sẽ có 3 liên kết ở bên cạnh tiêu đề yêu cầu đổi tên, liên kết được gợi ý sẽ được tô đậm, có màu xanh nước biển kèm theo đoạn "(khả năng cao)". Các liên kết mặc định là "hoàn thành" (màu xanh lá), "từ chối" (màu đỏ) và "không xác định" (màu xám).
 * - Cách 2: Đóng yêu cầu nhanh trên trang sửa đổi chi tiết của yêu cầu (theo section), bạn có thể click vào nút sửa mã nguồn bên cạnh tiêu đề của 1 yêu cầu đổi tên, trang sửa đổi sẽ hiện ra trong đó sẽ được tải 2 phần là "Tự động nhận biết kết quả" và "Đóng với kết quả" tương ứng với gợi ý kết quả của yêu cầu hiện tại và các nút đóng nhanh.
 * - Cách 3: Đóng hàng loạt yêu cầu đổi tên bằng cách nhấp chọn từng yêu cầu, sau đó chọn kết quả chung cho tất cả yêu cầu đó. Khi bạn nhấp chọn ít nhất 1 yêu cầu, sẽ có một panel chứa các nút hành động ở giữa màn hình.
 *
 * Tác giả: [[Thành viên:Lê Song Vĩ]]
 *
 * <nowiki>
 */

/* globals mw */
/* jshint esversion: 9 */

mw.loader.using(["oojs-ui-core"], function () {
    let chucloser = {
        actionDialog: function (config) {
            chucloser.actionDialog.super.call(this, config);
        },
        api: new mw.Api(),
        bottomActionPrefix: "chucloser-fixed-bottom-",
        bottomPrefix: "chucloser-fixed-bottom",
        checkboxes: [],
        crats: [],
        earlyClosingWarn: " Thường thì nên dành ra một khoảng thời gian cho người yêu cầu đổi tên vào xem nội dung, kết quả. Nhưng nếu bạn vẫn muốn tiếp tục, hãy bỏ qua lưu ý này.",
        editPageActionPrefix: "chucloser-action-",
        init: () => {
            if (mw.config.get("wgAction") === "view") {
                chucloser.initView();
            } else if (mw.config.get("wgAction") === "edit" &&
                document.location.href.indexOf("section=") != -1
            ) {
                chucloser.initEditPageOptions();
            }
        },
        listRs: [
            ["done", "notdone", ""],
            ["hoàn thành", "bị từ chối", "không xác định"],
            ["green", "red", "gray"],
            ["progressive", "destructive", "progressive"],
            ["primary", "primary", ""],
            [
                "Dựa theo các điều kiện trong yêu cầu này, có thể yêu cầu này đã <span style=\"color:red\">được chấp nhận</span>.",
                "Dựa theo các điều kiện trong yêu cầu này, có thể yêu cầu này đã <span style=\"color:red\">bị từ chối</span>.",
                "<span style=\"color:gray\">Không thể xác định</span> được kết quả của yêu cầu này. Vui lòng kiểm tra lại thủ công, chú ý đừng đóng những yêu cầu đang chờ xử lý. Việc không thể gợi ý có thể xảy ra do yêu cầu này chưa được xử lý hoặc do nội dung yêu cầu này không đáp ứng các điều kiện để được gợi ý."
            ],
            ["success", "error", "warning"],
            ["đã hoàn thành", "bị từ chối", "không xác định"],
            ["done", "notdone", "none"],
            ["green", "red", ""]
        ],
        promise: null,
        summaryCHUID: "chucloser-total-summary",
        recentDay: 14,
        totalCHUID: "chucloser-total"
    };

    chucloser.restApiQuery = (params, wiki) => {
        let family = "wikipedia";
        if (wiki === "meta") {
            family = "wikimedia";
        }
        let fApi = new mw.ForeignApi(`https://${wiki}.${family}.org/w/api.php`);

        return fApi.get(params);
    };

    chucloser.apiQuery = (params, wiki = null) => {
        if (wiki) {
            return chucloser.restApiQuery(params, wiki);
        }
        return chucloser.api.get(params);
    };

    chucloser.getSteward = () => {
        return chucloser.apiQuery({
            action: "query",
            augroup: "steward",
            aulimit: 5000,
            format: "json",
            list: "allusers",
            rawcontinue: 1
        });
    };

    chucloser.getGlobalRenamer = () => {
        return chucloser.apiQuery({
            action: "query",
            augroup: "steward|global-renamer",
            aulimit: 5000,
            format: "json",
            list: "allusers",
            rawcontinue: 1
        }, "meta");
    };

    chucloser.getTrustedUsers = () => {
        return chucloser.apiQuery({
            action: "query",
            format: "json",
            prop: "revisions",
            rvprop: "content",
            titles: "Thành viên:Lê Song Vĩ/thanhvienuytin.js"
        });
    };

    chucloser.isDone = (content) => {
        return new Promise((resolve, reject) => {
            if (/\{\{\s*?(Đóng yêu cầu đổi tên|rnra)\|(.*?\|\s*green\s*|(?!(bottom|\|)).)*?\}\}/.test(content)) {
                resolve();
            }
            Promise.all(chucloser.crats.map((crat) => {
                crat = crat.replace(" ", "[ _]");
                let regex = "{{(tick(|\\d+)|done(|\\d+)|agree|.*?\\|rename(?:|\\|(?:d|xong|x|done)))}}.*?(Thành viên|User)?:" + crat;
                return new RegExp(regex, "gm").test(content);
            })).then((data) => {
                if (data.includes(true)) {
                    resolve();
                }
                reject();
            });
        });
    };

    chucloser.isNotDone = (content) => {
        return new Promise((resolve, reject) => {
            if (/\{\{\s*?(Đóng yêu cầu đổi tên|rnra)\|(.*?\|\s*red\s*|(?!(bottom|\|)).)*?\}\}/.test(content)) {
                resolve();
            }
            Promise.all(chucloser.crats.map((crat) => {
                crat = crat.replace(" ", "[ _]");
                let regex = "{{\\s*((cross|not\\s?|already\\s?)done|từ chối|denied|.*?\\|rename\\|(r|decline|từ chối|tc)\\|.*?)\\s*?}}.*?(Thành viên|User)?:" + crat;
                return new RegExp(regex, "gm").test(content);
            })).then((data) => {
                if (data.includes(true)) {
                    resolve();
                }
                reject();
            });
        });
    };

    chucloser.detectResult = (content) => {
        return new Promise((resolve, reject) => {
            if (/\{\{\s*?(Đóng yêu cầu đổi tên|rnra).*?\}\}/gm.test(content)) {
                reject();
                return;
            }
            Promise.all(chucloser.crats.length === 0 ? [chucloser.getSteward(), chucloser.getGlobalRenamer(), chucloser.getTrustedUsers()] : [Promise.resolve()])
                .then((data) => {
                    if (chucloser.crats.length === 0) {
                        let stewards = data[0].query.allusers;
                        for (let i = 0; i < stewards.length; i++) {
                            chucloser.crats.push(stewards[i].name);
                        }

                        glbrname = data[1].query.allusers;
                        for (let i = 0; i < glbrname.length; i++) {
                            chucloser.crats.push(glbrname[i].name);
                        }

                        let page = data[2].query.pages;
                        let pageid = Object.keys(page)[0];
                        if (pageid != -1) {
                            let pageContent = page[pageid].revisions[0]["*"];
                            let lines = pageContent.split("\n");
                            for (let i = 0; i < lines.length; i++) {
                                if (lines[i].charAt(0) != "#") {
                                    chucloser.crats.push(
                                        lines[i].replace("_", " ")
                                    );
                                }
                            }
                        }
                    }

                    chucloser.isDone(content).then(() => {
                        resolve(1);
                    }).catch(() => {
                        chucloser.isNotDone(content).then(() => {
                            resolve(0);
                        }).catch(() => {
                            resolve(-1);
                        });
                    });
                }).catch(() => {
                    resolve(-1);
                });
        });
    };

    chucloser.notify = msg => {
        mw.notify(msg);
    };

    chucloser.genActionHref = (result, section, sectionHashbang, isSuggest, pageContent) => {
        let a = document.createElement("a");
        a.onclick = function () {
            let clr = chucloser.mappingRs(result, 2);
            let summary = chucloser.genSummary(clr);
            let newpageContent = chucloser.addRnra(pageContent, clr);

            if (newpageContent != pageContent) {
                let forceClose = !chucloser.shouldClose(pageContent);

                chucloser.genConfirmDialog(
                    "Xác nhận đóng yêu cầu đổi tên",
                    `Bạn có chắc muốn đóng yêu cầu đổi tên này với tóm lược: "<span style="font-style: italic;">${summary}</span>"?${forceClose ? `<br /><br /><span style="font-weight:bold">Chú ý:</span> Yêu cầu này mới được mở chưa được <strong>${chucloser.recentDay}</strong> ngày.${chucloser.earlyClosingWarn}` : ""}`,
                    () => {
                        chucloser.notify("Đang đóng yêu cầu...");

                        chucloser.api.postWithToken("csrf", {
                            action: "edit",
                            title: mw.config.get("wgPageName"),
                            section: section,
                            summary: summary,
                            text: newpageContent,
                            minor: true,
                            nocreate: true,
                        }).done(() => {
                            chucloser.notify(`Đã đóng thành công yêu cầu đổi tên với tóm lược: ${summary}, tải lại trang sau 1 giây...`);

                            setTimeout(() => {
                                location.href = `${location.protocol}//${location.host}${mw.config.get("wgArticlePath").replace("$1", mw.config.get("wgPageName"))}#${sectionHashbang}`
                                location.reload();
                            }, 1000);
                        }).fail((err) => {
                            chucloser.notify("Đã có lỗi xảy ra khi cố gắng đóng yêu cầu đổi tên: " + err);
                        });
                    }, () => {
                        chucloser.notify("Đã hủy đóng yêu cầu đổi tên!");
                    });
            } else {
                chucloser.notify("Không có gì thay đổi");
            }
        };
        a.innerHTML = chucloser.mappingRs(result, 1);
        if (isSuggest) {
            a.style = "color:blue";
            a.style = "font-weight:bold";
            a.innerHTML += " (khả năng cao)";
        } else {
            a.style = `color:${chucloser.mappingRs(result, 2)}`;
        }

        return a;
    };

    chucloser.genBracket = isOpen => {
        let span = document.createElement("span");
        span.innerHTML = isOpen ? "[" : "]";
        span.classList = "mw-editsection-bracket";

        return span;
    };

    chucloser.genDivider = () => {
        let span = document.createElement("span");
        span.innerHTML = " | ";
        span.classList = "mw-editsection-divider";

        return span;
    };

    chucloser.genCheckbox = (data) => {
        let checkbox = new OO.ui.CheckboxInputWidget({
            data: data,
            selected: false,
        });

        return checkbox;
    };

    chucloser.toggleFixedBottom = toggle => {
        let fixedBottom = document.getElementById(chucloser.bottomPrefix);
        if (toggle) {
            fixedBottom.style.display = "block";
        } else {
            fixedBottom.style.display = "none";
        }
    };

    chucloser.genConfirmDialog = (title, content, handleAccept, handleReject) => {
        chucloser.actionDialog.prototype.getTitle = function () {
            return title;
        };

        chucloser.actionDialog.prototype.initialize = function () {
            chucloser.actionDialog.super.prototype.initialize.apply(this, arguments);
            this.content = new OO.ui.PanelLayout({
                padded: true,
                expanded: false,
                $content: content,
            });

            this.$body.append(this.content.$element);
        };

        chucloser.actionDialog.prototype.getActionProcess = function (action) {
            let dialog = this;
            if (action === "accept") {
                return new OO.ui.Process(function () {
                    handleAccept();
                    dialog.close({
                        action: action
                    });
                });
            } else if (action === "reject") {
                return new OO.ui.Process(function () {
                    handleReject();
                    dialog.close({
                        action: action
                    });
                });
            }
            return chucloser.actionDialog.super.prototype.getActionProcess.call(this, action);
        };

        chucloser.actionDialog.prototype.getBodyHeight = function () {
            let textLength = content.length;

            let height = Math.ceil(textLength / 69) * 15 + 35;
            return height;
        };

        let dialog = new chucloser.actionDialog();

        let windowManager = new OO.ui.WindowManager();
        document.body.appendChild(windowManager.$element[0]);
        windowManager.addWindows([dialog]);
        windowManager.openWindow(dialog);
    };

    chucloser.massClose = status => {
        status = chucloser.revertMappingRs(status, 8);

        chucloser.apiQuery({
            action: "query",
            prop: "revisions",
            rvprop: "content",
            titles: mw.config.get("wgPageName"),
        })
            .then((data) => {
                let page = data.query.pages[Object.keys(data.query.pages)[0]],
                    mainPageContent = page.revisions[0]["*"],
                    newMainPageContent = mainPageContent,
                    numOfRecent = 0;

                chucloser.checkboxes.forEach((checkbox) => {
                    if (!chucloser.shouldClose(checkbox.pageContent)) {
                        numOfRecent++;
                    }
                    newMainPageContent = newMainPageContent.replace(checkbox.pageContent, chucloser.addRnra(checkbox.pageContent, chucloser.mappingRs(status, 2)));
                });

                if (newMainPageContent != mainPageContent) {
                    let numOfClosed = chucloser.checkboxes.length;
                    let massSummary = "Đóng hàng loạt " + numOfClosed + " yêu cầu đổi tên " + chucloser.mappingRs(status, 7);
                    chucloser.genConfirmDialog(
                        `Xác nhận đóng hàng loạt ${numOfClosed} yêu cầu đổi tên`,
                        `Bạn có chắc muốn đóng hàng loạt ${numOfClosed} yêu cầu đổi tên này với tóm lược: "<span style="font-style: italic;">${massSummary}</span>"?${numOfRecent > 0 ? `<br /><br /><span style="font-weight:bold">Chú ý:</span> Trong đó có <strong>${numOfRecent}</strong> yêu cầu mới được mở chưa được ${chucloser.recentDay} ngày.${chucloser.earlyClosingWarn}` : ""}`, () => {
                            chucloser.notify("Đang đóng yêu cầu hàng loạt...");

                            chucloser.api.postWithToken("csrf", {
                                action: "edit",
                                title: mw.config.get("wgPageName"),
                                summary: massSummary,
                                text: newMainPageContent,
                                nocreate: true,
                            }).done(() => {
                                chucloser.notify("Đã đóng thành công " + numOfClosed + " yêu cầu đổi tên với tóm lược: " + massSummary + ", tải lại trang sau 1 giây...");

                                setTimeout(() => {
                                    location.reload();
                                }, 1000);
                            }).fail(() => {
                                chucloser.notify("Đã có lỗi xảy ra khi cố gắng đóng hàng loạt yêu cầu đổi tên!");
                            });
                        }, () => {
                            chucloser.notify("Đã hủy đóng hàng loạt yêu cầu đổi tên!");
                        });
                } else {
                    chucloser.notify("Không có gì thay đổi");
                }
            })
            .catch(() => {
                chucloser.notify("Đã có lỗi xảy ra khi cố gắng lấy nội dung trang Đổi tên người dùng!");
            });
    };

    chucloser.initFixedBottom = () => {
        const idPrefix = chucloser.bottomActionPrefix;
        let fixedBottom = document.createElement("div");
        fixedBottom.id = chucloser.bottomPrefix;
        fixedBottom.style = "display:none;color: rgb(0, 0, 0); position: fixed; bottom: 4px; left: 50%; cursor: pointer; transition: bottom 0.5s ease 0s; user-select: none; z-index: 99999; transform: translate(-50%, 0%);";

        let uuoiLabel = new OO.ui.LabelWidget({
            label: "Tổng số yêu cầu đổi tên đã chọn: ",
        });
        let total = new OO.ui.LabelWidget({
            label: "0",
            id: chucloser.totalCHUID,
        });
        let totalSummary = new OO.ui.LabelWidget({
            label: "",
            id: chucloser.summaryCHUID,
        });
        fixedBottom.appendChild(uuoiLabel.$element[0]);
        fixedBottom.appendChild(total.$element[0]);
        fixedBottom.appendChild(totalSummary.$element[0]);

        let fieldset = new OO.ui.FieldsetLayout({});

        [["done", "đã hoàn thành"], ["notdone", "bị từ chối"], ["none", "không xác định"]].forEach((i) => {
            fieldset.addItems([
                chucloser.genCloseBtn(i[1], i[0], { id: idPrefix + i[0] }, true),
            ]);
        });

        fixedBottom.appendChild(fieldset.$element[0]);

        document.body.appendChild(fixedBottom);

        ["done", "notdone", "none"].forEach((i) => {
            document.getElementById(idPrefix + i).addEventListener("click", () => {
                chucloser.massClose(i);
            });
        });
    };

    chucloser.initDialog = () => {
        OO.inheritClass(chucloser.actionDialog, OO.ui.ProcessDialog);
        chucloser.actionDialog.static.name = "chucloser.actionDialog";
        chucloser.actionDialog.static.title = "Xác nhận đóng nhanh yêu cầu";
        chucloser.actionDialog.static.actions = [{
            action: "accept",
            label: "Đồng ý",
            flags: "primary"
        },
        {
            action: "reject",
            label: "Hủy",
            flags: "safe"
        }];
    };

    chucloser.extractTime = (content) => {
        let regex = /\*\s*?Thời gian:\s*?((\d+:\d+),\s*?ngày\s*?(\d{1,2})\s*?tháng\s*?(\d{1,2})\s*?năm\s*?(\d{4})\s*?\(UTC\))/g;
        let match = regex.exec(content);

        if (match) {
            let hour = match[2],
                day = match[3],
                month = match[4],
                year = match[5];

            return new Date(year, month - 1, day, hour.split(":")[0], hour.split(":")[1]);
        }

        return null;
    }

    chucloser.shouldClose = content => {
        let requestDate = chucloser.extractTime(content);
        let now = new Date();

        return (now - requestDate > chucloser.recentDay * 24 * 60 * 60 * 1000);
    };

    chucloser.calculatePercentageByStatus = () => {
        const numOfStatus = chucloser.checkboxes.length;

        if (numOfStatus === 0) {
            return;
        }

        let total = document.getElementById(chucloser.totalCHUID),
            totalSummary = document.getElementById(chucloser.summaryCHUID),
            totalSummaryText = "";

        if (total) {
            total.innerHTML = `&nbsp;${numOfStatus}`;
        }

        chucloser.listRs[8].forEach((i) => {
            let status = chucloser.revertMappingRs(i, 8),
                btnTitle = chucloser.mappingRs(status, 7),
                percent = -1,
                statusShort = chucloser.mappingRs(status, 1);

            if (numOfStatus > 0) {
                let numByStt = chucloser.checkboxes.filter((j) => {
                    return j.result === status;
                }).length;

                percent = Math.round((numByStt / numOfStatus) * 100);

                if (percent > 0) {
                    if (totalSummaryText === "") {
                        totalSummaryText = `&nbsp;(${statusShort} chiếm ${percent}%`;
                    } else {
                        totalSummaryText += `, ${statusShort} chiếm ${percent}%`;
                    }
                }
            }

            let buttonWidget = document
                .getElementById(chucloser.bottomActionPrefix + i);
            let percentTxt = "";
            if (percent != -1) {
                percentTxt = ` (${percent}%)`;
            }
            buttonWidget.querySelector(".oo-ui-labelElement-label").innerText = `${btnTitle}${percentTxt}`;
        });

        if (totalSummaryText !== "") {
            totalSummaryText += ")";

            if (totalSummary) {
                totalSummary.innerHTML = totalSummaryText;
            }
        }
    };

    chucloser.onCheckboxChange = (checkbox, section, sectionHashbang, result, pageContent) => {
        if (checkbox.selected) {
            chucloser.checkboxes[section] = {
                section,
                sectionHashbang,
                result,
                pageContent,
            };
        } else {
            chucloser.checkboxes = chucloser.checkboxes.filter((i) => {
                return i.section != section;
            });
        }

        chucloser.checkboxes = chucloser.checkboxes.filter((i) => {
            return i;
        });

        chucloser.calculatePercentageByStatus();

        if (Object.keys(chucloser.checkboxes).length > 0) {
            chucloser.toggleFixedBottom(true);
        } else {
            chucloser.toggleFixedBottom(false);
        }
    };

    chucloser.initView = () => {
        chucloser.initDialog();
        chucloser.initFixedBottom();

        let mwHeadline = document.getElementsByClassName("mw-headline");
        for (let i = 0; i < mwHeadline.length; i++) {
            let mwEditSection = mwHeadline[i].parentNode.getElementsByClassName("mw-editsection");
            let mwEditSection_a = mwEditSection[0].getElementsByTagName("a");
            let editUrl = mwEditSection_a[mwEditSection_a.length - 1].getAttribute("href");
            let section = editUrl.match(/.*?section=(\d+)/)[1];
            let sectionHashbang = mwHeadline[i].getAttribute("id");

            let nMwEditSection = document.createElement("span");
            nMwEditSection.classList = "mw-editsection";

            chucloser.apiQuery({
                action: "query",
                prop: "revisions",
                rvprop: "content",
                format: "json",
                formatversion: "2",
                titles: mw.config.get("wgPageName"),
                rvsection: section
            })
                .then((data) => {
                    let page = data.query.pages;
                    let pageid = Object.keys(page)[0];
                    if (pageid != -1) {
                        let pageContent = page[pageid].revisions[0].content;

                        if (chucloser.promise == null) {
                            chucloser.promise = Promise.resolve();
                        }

                        chucloser.promise = chucloser.promise.then(() => {
                            return chucloser.detectResult(pageContent);
                        }).then((result) => {
                            let checkbox = chucloser.genCheckbox(section);
                            checkbox.on("change", (selected, _) => {
                                chucloser.onCheckboxChange({ selected }, section, sectionHashbang, result, pageContent);
                            });

                            mwHeadline[i].parentNode.insertBefore(checkbox.$element[0], mwHeadline[i]);

                            nMwEditSection.appendChild(chucloser.genBracket(1));
                            [1, 0, -1].forEach((item) => {
                                nMwEditSection.appendChild(chucloser.genActionHref(item, section, sectionHashbang, item == result, pageContent));
                                if (item != -1) {
                                    nMwEditSection.appendChild(chucloser.genDivider());
                                }
                            });
                            nMwEditSection.appendChild(chucloser.genBracket(!1));

                            mwHeadline[i].parentNode.appendChild(nMwEditSection);
                        })
                            .catch(() => { });
                    }
                })
                .catch(() => {
                    let span = document.createElement("span");
                    span.innerHTML = "không thể đóng nhanh yêu cầu đổi tên này!";
                    span.style.color = "red";
                    span.classList = "mw-editsection";
                    mwHeadline[i].parentNode.appendChild(span);
                });
        }
    };

    chucloser.genSummary = (action) => {
        let summary = "Đóng yêu cầu đổi tên";
        switch (action) {
            case "green":
                return `${summary} đã được chấp nhận`;
            case "red":
                return `${summary} đã bị từ chối`;
            default:
                return summary;
        }
    };

    chucloser.addRnra = (content, action) => {
        let locateHeader = content.match(/===\s*?<span(.*?)===/);
        if (locateHeader) {
            content = content.replace(locateHeader[0], "").trim()
            let actionTxt = "";
            if (action.trim().length > 0) {
                actionTxt = `|${action}`;
            }
            content = `${locateHeader[0]}\n{{rnra|top${actionTxt}}}\n${content}\n{{rnra|bottom}}`;
        }
        return content;
    };

    chucloser.startCloseReq = (action = "") => {
        let f = document.editform, t = f.wpTextbox1;
        let content = chucloser.addRnra(t.value, action);
        if (content != t.value) {
            t.value = content;
            f.wpSummary.value = chucloser.genSummary(action);
            document.getElementById("wpPreview").click();
        } else {
            chucloser.notify("Không tìm thấy header hợp lệ");
        }
    };

    chucloser.mappingRs = (status, idList) => chucloser.listRs[idList][[1, 0, -1].indexOf(status)];

    chucloser.revertMappingRs = (rs, idList) => [1, 0, -1][chucloser.listRs[idList].indexOf(rs)];

    chucloser.mappingStatusWithRS = (rs) => {
        switch (rs) {
            case "done":
                return chucloser.startCloseReq("green");
            case "notdone":
                return chucloser.startCloseReq("red");
            default:
                return chucloser.startCloseReq();
        }
    };

    chucloser.genCloseBtn = (text, status, args, noElem = false) => {
        let btn = new OO.ui.ButtonWidget({
            label: text,
            flags: [
                status === "done" || status === "notdone" ? "primary" : "",
                status === "done" ? "progressive" : (status === "notdone" ? "destructive" : "progressive"),
            ],
            icon: status === "done" ? "check" : (status === "notdone" ? "close" : "help"),
            title: text,
            ...args,
        });

        btn.$element[0].style = "margin: 0px 0px 10px 10px;";

        if (!noElem) {
            return btn.$element[0];
        }
        return btn;
    };

    chucloser.genMsg = (status, args = {}) => {
        let msg = new OO.ui.MessageWidget({
            type: args.type || chucloser.mappingRs(status, 6),
            inline: true,
            label: args.label || $(`<span>${chucloser.mappingRs(status, 5)}</span>`),
            ...args
        });

        return msg.$element[0].outerHTML;
    };

    chucloser.initEditPageOptions = () => {
        let content = document.editform.wpTextbox1.value;

        chucloser.detectResult(content).then((result) => {
            const span_ = document.createElement("span"),
                span_2_ = document.createElement("span"),
                actionId = chucloser.editPageActionPrefix,
                isEarly = !chucloser.shouldClose(result, content);
            span_.innerHTML = chucloser.genMsg(result);

            const f = document.editform;
            let top = document.createElement("div");

            top.innerHTML = "<h2>Tự động nhận biết kết quả và lưu ý khác:</h2>";
            top.appendChild(span_);
            if (isEarly) {
                span_2_.innerHTML = chucloser.genMsg(null, {
                    label: $(`<span>Yêu cầu đổi tên này mới được tạo chưa đến ${chucloser.recentDay} ngày.${chucloser.earlyClosingWarn}</span>`),
                    type: "warning"
                });
                top.appendChild(span_2_);
            }

            top.innerHTML += "<h2>Đóng với kết quả:</h2>";
            [["done", "đã hoàn thành"], ["notdone", "bị từ chối"], ["none", "không xác định"]].forEach((i) => {
                top.appendChild(chucloser.genCloseBtn(i[1], i[0], { id: actionId + i[0] }));
            });

            f.insertBefore(top, f.childNodes[0]);

            ["done", "notdone", "none"].forEach((i) => {
                document.getElementById(actionId + i).addEventListener("click", () => {
                    chucloser.mappingStatusWithRS(i);
                });
            });
        }).catch(() => {
            chucloser.notify("Yêu cầu đã được đóng");
        });
    };

    if (mw.config.get("wgPageName") === "Wikipedia:Đổi_tên_người_dùng") {
        chucloser.init();
    }
});

/* </nowiki> */