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.

/**
* MIT Licensed - xem https://github.com/JohnsonLee01/WikiRCPatrol/blob/master/LICENSE
* 
* Bản quyền (c) 2020 Awesome Aasim và người đóng góp 
* 
* Quyền theo đây được cấp phép miễn phí cho bất kỳ người nào có được bản sao
* của phần mềm này và các tệp tài liệu liên quan ("Phần mềm"), để đối phó
* trong Phần mềm mà không có giới hạn, bao gồm nhưng không giới hạn các quyền
* để sử dụng, sao chép, sửa đổi, hợp nhất, xuất bản, phân phối, cấp phép phụ và/hoặc bán
* các bản sao của Phần mềm và cho phép những người có Phần mềm
* được trang bị để làm như vậy, tuân theo các điều kiện sau:
*
* Thông báo bản quyền ở trên và thông báo cho phép này sẽ bao gồm trong tất cả các 
* bản sao hoặc các phần quan trọng của Phần mềm.
* 
* PHẦN MỀM ĐƯỢC CUNG CẤP "NGUYÊN TRẠNG", KHÔNG BẢO HÀNH BẤT KỲ HÌNH THỨC NÀO, RÕ RÀNG HOẶC
* ĐƯỢC NGỤ Ý, BAO GỒM NHƯNG KHÔNG GIỚI HẠN ĐỐI VỚI CÁC BẢO ĐẢM VỀ KHẢ NĂNG LAO ĐỘNG,
* PHÙ HỢP VỚI MỤC ĐÍCH CỤ THỂ VÀ SỰ KHÔNG HỢP LỆ. KHÔNG CÓ VIỆC 
* TÁC GIẢ HOẶC CHỦ BẢN QUYỀN CHỊU TRÁCH NHIỆM PHÁP LÝ VỀ BẤT KỲ YÊU CẦU, THIỆT HẠI HOẶC CÁC VẤN ĐỀ KHÁC
* TRÁCH NHIỆM PHÁP LÝ ĐỐI VỚI MỘT HÀNH ĐỘNG HỢP ĐỒNG, KHAI THÁC HOẶC KHOẢN KHÁC, PHÁT SINH TỪ,
* NGOÀI HOẶC KẾT NỐI VỚI PHẦN MỀM HOẶC VIỆC SỬ DỤNG HOẶC KINH DOANH KHÁC TRONG
* PHẦN MỀM.

* Kịch bản này là một công việc đang được tiến hành. Sự giúp đỡ của bạn trong việc phát triển công cụ này được hoan nghênh tại https://github.com/JohnsonLee01/WikiRCPatrol.
* Các đóng góp và thay đổi đối với tập lệnh này nên được thực hiện tại kho lưu trữ GitHub ở trên.
* Tất cả các thay đổi khác sẽ bị mất nếu tệp này được tạo lại và lưu.
* Bằng cách đóng góp cho dự án này, bạn phải đồng ý phát hành tác phẩm của mình theo giấy phép MIT.
**/
// <nowiki>

mw.loader.using(['oojs-ui-core', 'oojs-ui.styles.icons-editing-core', 'oojs-ui.styles.icons-movement', 'oojs-ui.styles.icons-interactions', 'oojs-ui.styles.icons-layout', 'oojs-ui.styles.icons-alerts'], function () {
    if (!rcpatrol) { // stops multiple instances of RC patrol from running
        //necessary resources
        var rcpatrol = {};
        if (mw.config.get("wgPageName").toLowerCase() == "Special:RecentChanges".toLowerCase()) {
            rcpatrol = true;
            $(document).ready(function () {
                var rcpatrollocation = mw.config.get("wgArticlePath").replace("$1", "Special:BlankPage/RCPatrol")
                $("#mw-content-text").prepend('<a href="' + rcpatrollocation + '">RC patrol</a> (<a href="' + rcpatrollocation + '?oresreview=1">ORES</a>)');
            });
        }
        if ((mw.config.get("wgPageName").toLowerCase() == "Special:BlankPage/RCPatrol".toLowerCase())) {
            /**
             * Khởi tạo các biến liên quan đến RC patrol
             */
            if (mw.config.get("skin") == "vector" && !(new URL(location.href)).searchParams.get("useskinversion")) {
                window.location.replace((function () {
                    var url = (new URL(location.href));
                    url.searchParams.set("useskinversion", "2");
                    return url;
                })());
            }
            rcpatrol.changes = [];
            rcpatrol.currentChange = 0;
            window.setInterval(() => {
                $("#rcpatroldiff").find("a").attr("target", "_blank");
                $(".mw-rollback-link").hide();
                $(".ve-init-mw-diffPage-diffMode").hide();
                $(".mw-revslider-container").hide();
                if (rcpatrol.currentChange == 0) {
                    rcpatrol.previouseditbutton.setDisabled(true);
                } else if (!rcpatrol.isDisabled) {
                    rcpatrol.previouseditbutton.setDisabled(false);
                }
            }, 100);
            $(document).ready(function () {
                rcpatrol.fetch();
                $("#firstHeading, #section_0").html("Tuần tra thay đổi gần đây");
                $("title").text("Tuần tra thay đổi gần đây - " + mw.config.get("wgSiteName"));
                /*
                if (mw.config.get("skin") == "minerva") {
                    $("body").html($("main").html());
                    $("#siteNotice").prepend('<a id="rcpatrolexit" href="/">Exit</a>');
                    $("#rcpatrolexit").click(function () {
                        window.history.back();
                    })
                }
                */
                $("#mw-content-text").html("");
                $("#mw-content-text").append('<div id="rcpatrolbuttons"></div>');
                $("#mw-content-text").append('<div id="rcpatroldiff"></div>');
                $("#rcpatrolbuttons").prepend(rcpatrol.rcpatrolbar.$element);
                $("#rcpatrolbuttons").prepend(rcpatrol.rollbackbar.$element);
                $("#rcpatrolbuttons").prepend(rcpatrol.dropdownmenu.$element);
                $("#rcpatrolbuttons").prepend('<a href="/wiki/Special:BlankPage/RCPatrol?oresreview=1">Chỉ hiển thị các sửa đổi có thể cần được xem xét</a><br>');
                if (mw.config.get('wgUserGroups').includes('sysop')) {
                    $("#rcpatrolbuttons").append('Công cụ quản lý: <span id="rcpatroladmintools"></span>');
                }
                $("#rcpatrolbuttons").append('Công cụ trang: <span id="rcpatrolpagetools"></span>');
                $("#rcpatroldiff").css({
                    overflow: "auto"
                });
            })
        }
    }
    /**
     * Bắt đầu biên dịch tại đây
     */
    rcpatrol.i18n = {
        reasontorollback: {
            vi: 'Lý do lùi sửa (tuỳ chọn)'
        },
        rollback: {
            vi: 'Lùi sửa'
        },
        rollingback: {
            vi: 'Đang lùi sửa...'
        },
        rollbackfailed: {
            vi: 'Lùi sửa thất bại'
        },
        rollbacksuccess: {
            vi: 'Lùi sửa thành công'
        },
        rollbacksummary: {
            vi: '[[w:en:User:Awesome Aasim/rcpatrol|RCP]] đã lùi lại sửa đổi của [[Special:Contributions/$2|$2]] ([[User_talk:$2|thảo luận]]); quay về phiên bản cuối của [[Special:Contributions/$1|$1]]'
        },
        warnsummary: {
            vi: '[[w:en:User:Awesome Aasim/rcpatrol|RCP]] gửi cảnh báo đến $1 về [[$2]]'
        },
        reportsummary: {
            vi: '[[w:en:User:Awesome Aasim/rcpatrol|RCP]] báo cáo $1'
        },
        rollbacktitle: {
            vi: "Lùi lại sửa đổi của thành viên này",
        },
        previousedit: {
            vi: 'Sửa đổi trước đó'
        },
        previousedittitle: {
            vi: 'Tải các bản sửa đổi trước đó trong danh sách'
        },
        nextedit: {
            vi: 'Sửa đổi tiếp theo'
        },
        nextedittitle: {
            vi: 'Tải các bản sửa đổi tiếp theo trong danh sách'
        },
        refresh: {
            vi: 'Tải lại'
        },
        connectionlost: {
            vi: 'Mất kết nối'
        },
        connectionlostdiffmessage: {
            vi: "Không thể tải bản khác biệt. Xin vui lòng kiểm tra kết nối Internet của bạn. Bản khác biệt sẽ tự động tải lại khi kết nối Internet được thiết lập."
        },
        thank: {
            vi: 'Cảm ơn'
        },
        thanktitle: {
            vi: "Cảm ơn thành viên này vì các sửa đổi của họ"
        },
        rollbackandwarn: {
            vi: 'Lùi sửa và cảnh báo'
        },
        endoflist: {
            vi: 'Đã đến cuối danh sách. Đang tải danh sách tiếp theo...'
        },
        thankssent: {
            vi: 'Đã gửi lời cảm ơn!'
        },
        rcpatroltitle: {
            vi: 'Tuần tra thay đổi gần đây'
        },
        rcpatroltitlewithdiff: {
            vi: 'Tuần tra thay đổi gần đây "$1"'
        },
        delete: {
            vi: 'Xoá'
        },
        protect: {
            vi: 'Khoá'
        },
        block: {
            vi: 'Cấm'
        },
        history: {
            vi: 'Xem lịch sử trang'
        },
        diff: {
            vi: 'Xem khác biệt'
        }
    }
    /**
     * Kết thúc quá trình biên dịch.  KHÔNG ĐƯỢC PHÉP SỬA DÒNG BÊN DƯỚI.
     */
    rcpatrol.msgs = {};
    for (var i in rcpatrol.i18n) {
        rcpatrol.msgs[i] = rcpatrol.i18n[mw.config.get("wgUserLanguage")] ? rcpatrol.i18n[mw.config.get("wgUserLanguage")] : rcpatrol.i18n["en"]; //luôn trở ngôn ngữ lại tiếng Anh nếu bản dịch thư không hoàn chỉnh
    }
    /**
     * Cấu hình cho Wikipedia tiếng Việt 
     * Cuối cùng điều này sẽ được lưu trữ ở nơi khác trên một trang riêng biệt, có thể tại [[:en:Project:RC Patrol Script/config.js]]
     * Ý tưởng là bất kỳ mô-đun cụ thể nào cần được vô hiệu hóa hoặc cấu hình lại có thể được thực hiện như vậy trên cơ sở wiki của wiki bằng cách "lập trình lại" nó tại đây.
     * Tệp cấu hình phải được khóa để chỉ bảo quản viên mới có thể sửa đổi nó.
     */
    rcpatrol.reportpage = "Wikipedia:Tin nhắn cho bảo quản viên#Báo cáo thành viên phá hoại"; //trang mà thành viên sẽ được báo cáo
    rcpatrol.reportstring = "\n\n* {{vandal|1=$1}} - $2"; //mẫu chuỗi để sử dụng cho báo cáo
    rcpatrol.dropdown = [
        {
            keycode: 84, //t
            val: "Sửa đổi thử nghiệm",
            summary: "Sửa đổi thử nghiệm",
            template: "uw-test"
        },
        {
            keycode: 69, //e
            val: "Sửa đổi gây hại",
            summary: "[[Wikipedia:Sửa đổi gây hại|Sửa đổi gây hại]]",
            template: "uw-disruptive"
        },
        {
            keycode: 77, //m
            val: "Vi phạm cẩm nang biên soạn",
            summary: "Vi phạm [[Wikipedia:Cẩm nang biên soạn|cẩm nang biên soạn]]",
            template: "uw-mos"
        },
        {
            keycode: 65, //a
            val: "Tấn công cá nhân",
            summary: "[[WP:NPA|Sửa đổi gây tấn công cá nhân]]",
            template: "uw-npa"
        },
        {
            keycode: 76, //l
            val: "Vi phạm tiểu sử người đang sống",
            summary: "Vi phạm [[Wikipedia:Tiểu sử người đang sống|chính sách về tiểu sử người đang sống]]",
            template: "uw-biog"
        },
        {
            keycode: 78, //n
            val: "Vi phạm thái độ trung lập",
            summary: "Vi phạm [[Wikipedia:Thái độ trung lập|thái độ trung lập]]",
            template: "uw-npov"
        },
        {
            keycode: 85, //u
            val: "Không nguồn",
            summary: "[[Wikipedia:Thông tin kiểm chứng được|Thông tin không nguồn kiểm chứng]]",
            template: "uw-unsourced"
        },
        {
            keycode: 68, //d
            val: "Xoá nội dung không giải thích được",
            summary: "Xoá nội dung không giải thích được",
            template: "uw-delete"
        },
        {
            keycode: 66, //b
            val: "Tẩy trống trang",
            summary: "Tẩy trống trang",
            template: "uw-blank"
        },
        {
            keycode: 86, //v
            val: "Phá hoại",
            summary: "Sửa đổi phá hoại",
            template: "uw-vandalism"
        },
        {
            keycode: 83, //s
            val: "Liên kết spam",
            summary: "Liên kết ngoài không phù hợp",
            template: "uw-spam"
        },
        {
            keycode: 80, //p
            val: "Quảng cáo",
            summary: "Sử dụng ngôn từ quảng bá trong bài viết",
            template: "uw-advert"
        }
    ];
    /**
     * Tải tất cả các mục OOUI, bao gồm các nút nhấn, v.v...
     */

    rcpatrol.rcpatrolbar = new OO.ui.HorizontalLayout({ align: 'inline' });
    rcpatrol.rcpatrolbox = new OO.ui.TextInputWidget({
        autosize: true,
        placeholder: 'Lý do lùi sửa (tuỳ chọn)',
        icon: 'textSummary',
        align: 'inline'
    });
    rcpatrol.rcpatrolbutton = new OO.ui.ButtonWidget({
        autosize: true,
        label: 'Lùi sửa',
        flags: [
            'primary',
            'progressive'
        ],
        icon: "editUndo",
        align: 'inline'
    });
    rcpatrol.rollbackbar = new OO.ui.ActionFieldLayout(rcpatrol.rcpatrolbox, rcpatrol.rcpatrolbutton, { align: "inline" });
    rcpatrol.previouseditbutton = new OO.ui.ButtonWidget({
        autosize: true,
        label: 'Sửa đổi trước đó',
        icon: "previous",
        align: 'inline'
    });
    rcpatrol.nexteditbutton = new OO.ui.ButtonWidget({
        autosize: true,
        label: 'Sửa đổi tiếp theo',
        icon: "next",
        align: 'inline'
    });
    rcpatrol.fetchbutton = new OO.ui.ButtonWidget({
        autosize: true,
        label: 'Tải lại',
        icon: "reload",
        align: 'inline'
    });
    rcpatrol.thankbutton = new OO.ui.ButtonWidget({
        autosize: true,
        label: 'Cảm ơn',
        icon: "heart",
        align: 'inline'
    });
    rcpatrol.dropdownmenu = new OO.ui.DropdownWidget({
        label: "Lùi sửa và cảnh báo...",
        icon: "speechBubbleAdd",
        menu: {
            items: []
        }
    });
    rcpatrol.rcpatrolbar.addItems([
        rcpatrol.previouseditbutton,
        rcpatrol.nexteditbutton,
        rcpatrol.fetchbutton,
        rcpatrol.thankbutton
    ]);
    rcpatrol.rcpatrolbutton.$element.attr("title", "Lùi lại sửa đổi của thành viên này [ctrl-alt R]");
    rcpatrol.thankbutton.$element.attr("title", "Cảm ơn thành viên này vì các sửa đổi của họ [ctrl-alt =]");
    rcpatrol.nexteditbutton.$element.attr("title", "Tải các bản sửa đổi tiếp theo trong danh sách [ctrl-alt space]");
    rcpatrol.previouseditbutton.$element.attr("title", "Tải các bản sửa đổi trước đó trong danh sách [ctrl-alt ,]");
    for (var i in rcpatrol.dropdown) {
        var temp = new OO.ui.MenuOptionWidget({
            data: rcpatrol.dropdown[i].keycode,
            label: rcpatrol.dropdown[i].val
        });
        temp.$element.attr("title", rcpatrol.dropdown[i].val + (String.fromCharCode(rcpatrol.dropdown[i].keycode) ? " [ctrl-alt " + String.fromCharCode(rcpatrol.dropdown[i].keycode) + "]" : ""));
        rcpatrol.dropdownmenu.getMenu().addItems([temp]);
    }
    /**
     * Huỷ kích hoạt/kích hoạt bộ điều khiển RC patrol
     * @param {*} bool có tắt các điều khiển hay không
     */
    rcpatrol.setDisabled = function (bool) {
        rcpatrol.isDisabled = bool;
        rcpatrol.dropdownmenu.setDisabled(bool);
        rcpatrol.previouseditbutton.setDisabled(bool);
        rcpatrol.nexteditbutton.setDisabled(bool);
        rcpatrol.rcpatrolbutton.setDisabled(bool);
        rcpatrol.rcpatrolbox.setDisabled(bool);
        rcpatrol.thankbutton.setDisabled(bool);
        rcpatrol.fetchbutton.setDisabled(bool);
    };
    /**
     * Tìm nạp danh sách các thay đổi gần đây và tải RC patrol
     */
    rcpatrol.fetch = function () {
        if (!rcpatrol.fetchbutton.isDisabled()) {
            $("#rcpatroldiff").fadeOut();
            rcpatrol.setDisabled(true);
            $.get(mw.config.get("wgScriptPath") + "/api.php", {
                "action": "query",
                "format": "json",
                "list": "recentchanges",
                "rcprop": "title|timestamp|flags|loginfo|parsedcomment|user|ids|tags",
                "rcshow": "!bot" + ((new URL(window.location.href)).searchParams.get("oresreview") ? "|oresreview" : ""),
                "rctoponly": true,
                "rclimit": "max",
                "rctype": "edit|new",
                "uselang": mw.config.get("wgUserLanguage")
            }).done(function (result) {
                rcpatrol.changes = result.query.recentchanges;
                console.log(result.query.recentchanges);
                rcpatrol.setDisabled(false);
                rcpatrol.currentChange = 0;
                rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
            }).fail(function () {
                window.setTimeout(rcpatrol.fetch, 1000);
            });
        }
    };
    /**
     * Nạp một thay đổi và đặt nó vào đầu ra khác biệt của RC patrol.
     * @param {*} thay đổi những bản khác biệt của thay đổi để tải
     */
    rcpatrol.loadChange = function (change) {
        $("#rcpatroldiff").fadeOut();
        $("#rcpatroladmintools").fadeOut();
        $("#rcpatrolpagetools").fadeOut();
        rcpatrol.setDisabled(true);
        $.get(mw.config.get("wgScriptPath") + "/api.php", {
            "action": "query",
            "format": "json",
            "prop": "revisions",
            "titles": change.title,
            "rvlimit": "1",
            "uselang": mw.config.get("wgUserLanguage")
        }).done(function (result) {
            for (var pageid in result.query.pages) {
                change.revid = result.query.pages[pageid].revisions[0].revid;
                change.user = result.query.pages[pageid].revisions[0].user;
                break;
            }
            $.get(mw.config.get("wgScriptPath") + "/api.php", {
                "action": "query",
                "format": "json",
                "prop": "revisions",
                "titles": change.title,
                "rvexcludeuser": change.user,
                "rvlimit": "1",
                "uselang": mw.config.get("wgUserLanguage")
            }).done(function (result) {
                console.log(result);
                var oldid;
                try {
                    for (var pageid in result.query.pages) {
                        oldid = result.query.pages[pageid].revisions[0].revid;
                        break;
                    }
                } catch (Error) {
                    var temp = oldid;
                    oldid = change.revid;
                    change.revid = "";
                }
                console.log(mw.config.get("wgScriptPath") + "/index.php?oldid=" + oldid + "&diff=" + change.revid);
                var scriptpath = mw.config.get('wgScriptPath');
                var loadurl = mw.config.get("wgScriptPath") + "/index.php?oldid=" + oldid + (change.revid ? "&diff=" + change.revid : "");
                if (location.href.split(".").includes("m")) {
                    loadurl = mw.config.get("wgArticlePath").replace("$1", "Special:MobileDiff/" + oldid + (change.revid ? "..." + change.revid : ""));
                }
                $.get(loadurl, {
                    safemode: "1",
                    uselang: mw.config.get("wgUserLanguage"),
                    useskin: mw.config.get("skin"),
                    useskinversion: "2"
                }).done(function (result) {
                    var contenttextlocation;
                    switch (mw.config.get("skin")) {
                        case "timeless": contenttextlocation = '#mw-wrapper';
                            break;
                        case "vector": contenttextlocation = "#content, .mw-page-container";
                            break;
                        case "monobook": contenttextlocation = "#globalWrapper";
                            break;
                        case "minerva": contenttextlocation = "#mw-mf-viewport"
                            break;
                        case "modern": contenttextlocation = "#mw_main";
                            break;
                    }
                    var $r = $(result);
                    $("#rcpatroldiff").html($r);
                    $("#rcpatroldiff").find(contenttextlocation).html($("#rcpatroldiff").find("#mw-content-text").html());

                    $("#firstHeading, #section_0").html('Tuần tra thay đổi gần đây \"<a target=\"_blank\" href=\"' + scriptpath + '/index.php?title=' + change.title + '\">' + change.title + "</a>\"");
                    $("title").text("Tuần tra thay đổi gần đây \"" + change.title + "\" - " + mw.config.get("wgSiteName"));

                    $("#rcpatroldiff").fadeIn();
                    $("#rcpatroladmintools").fadeIn();
                    $("#rcpatrolpagetools").fadeIn();
                    rcpatrol.rcpatrolbox.setValue("");
                    rcpatrol.rcpatrolbutton.setLabel("Lùi sửa");
                    rcpatrol.setDisabled(false);
                    $("#rcpatroladmintools").html('');
                    $("#rcpatroladmintools").append('<a target="_blank" href="' + scriptpath + '/index.php?title=' + change.title + '&action=delete">Xoá</a>');
                    $("#rcpatroladmintools").append(' &bull; ');
                    $("#rcpatroladmintools").append('<a target="_blank" href="' + scriptpath + '/index.php?title=' + change.title + '&action=protect">Khoá</a>');
                    $("#rcpatroladmintools").append(' &bull; ');
                    $("#rcpatroladmintools").append('<a target="_blank" href="' + scriptpath + '/index.php?title=Special:Block/' + change.user + '">Cấm</a>');
                    $("#rcpatrolpagetools").html('');
                    $("#rcpatrolpagetools").append('<a target="_blank" href="' + scriptpath + '/index.php?title=' + change.title + '&action=history">Xem lịch sử trang</a>');
                    $("#rcpatrolpagetools").append(' &bull; ');
                    $("#rcpatrolpagetools").append('<a target="_blank" href="' + scriptpath + '/index.php?oldid=' + oldid + '&diff=' + change.revid + '">Xem khác biệt</a>');
                }).fail(function () {
                    $("#rcpatroldiff").fadeIn(1000);
                    $("#rcpatroldiff").text("Không thể tải bản khác biệt. Xin vui lòng kiểm tra kết nối Internet của bạn. Bản khác biệt sẽ tự động tải lại khi kết nối Internet được thiết lập.");
                    window.setTimeout(function () {
                        rcpatrol.loadChange(change);
                    }, 1000);
                });
                /*
                $("#rcpatroldiff").load(loadurl, function (response, status, xhr) {
                    if (status == "error") {
                    } else {
                        $("#rcpatroldiff").find("form").hide();
                        $("#rcpatroldiff").find("#firstHeading").hide();
                    }
                });
                */
            }).fail(function (result) {
                $("#rcpatroldiff").fadeIn(1000);
                $("#rcpatroldiff").text("Không thể tải bản khác biệt. Xin vui lòng kiểm tra kết nối Internet của bạn. Bản khác biệt sẽ tự động tải lại khi kết nối Internet được thiết lập.");
                window.setTimeout(function () {
                    rcpatrol.loadChange(change)
                }, 1000);
            });
        }).fail(function (result) {
            $("#rcpatroldiff").fadeIn(1000);
            $("#rcpatroldiff").text("Không thể tải bản khác biệt. Xin vui lòng kiểm tra kết nối Internet của bạn. Bản khác biệt sẽ tự động tải lại khi kết nối Internet được thiết lập.");
            window.setTimeout(function () {
                rcpatrol.loadChange(change)
            }, 1000);
        });
    };
    /**
     * Lùi lại sửa đổi trong bản khác biệt
     * @param {*} trang để lùi lại các sửa đổi 
     * @param {*} thành viên để lùi sửa 
     * @param {*} sau khi thành công phải làm gì sau khi lùi sửa thành công
     * @param {*} sau khi thất bại phải làm gì nếu lùi sửa không thành công
     */
    rcpatrol.revert = function (page, user, afterSuccess, afterFail) {
        if (!rcpatrol.rcpatrolbutton.isDisabled()) {
            var summary = rcpatrol.rcpatrolbox.getValue();
            rcpatrol.rcpatrolbutton.setLabel("Đang lùi sửa...");
            rcpatrol.setDisabled(true);
            $.get(mw.config.get("wgScriptPath") + "/api.php", {
                "action": "query",
                "format": "json",
                "meta": "tokens",
                "type": "rollback"
            }).done(function (result) {
                if (result.error) {
                    rcpatrol.rcpatrolbutton.setLabel("Lùi sửa thất bại");
                    alert(result.error.info);
                    afterFail();
                } else {
                    $.post(mw.config.get("wgScriptPath") + "/api.php", {
                        "action": "rollback",
                        "format": "json",
                        "title": page,
                        "token": result.query.tokens.rollbacktoken,
                        "user": user,
                        "summary": "[[User:Awesome Aasim/rcpatrol|RCP]] đã lùi lại sửa đổi của [[Special:Contributions/$2|$2]] ([[User_talk:$2|Talk]]); quay trở về phiên bản cuối của [[Special:Contributions/$1|$1]]" + (summary ? ": " + summary : "")
                    }).done(function (result) {
                        if (result.error) {
                            rcpatrol.rcpatrolbutton.setLabel("Lùi sửa thất bại");
                            alert(result.error.info);
                            afterFail();
                        } else {
                            rcpatrol.rcpatrolbutton.setLabel("Lùi sửa thành công");
                            afterSuccess();
                        }
                    }).fail(function () {
                        console.log(result);
                        alert("Lost connection.");
                        rcpatrol.rcpatrolbutton.setLabel("Lùi sửa thất bại");
                        afterFail();
                    });
                }
            });
        }
    };
    /**
     * Cảnh báo thành viên bằng bản mẫu cảnh báo cụ thể
     * @param {*} thành viên để cảnh báo
     * @param {*} tạo tiền tố của bản mẫu để sử dụng làm cảnh báo
     * @param {*} trang liên quan
     */
    rcpatrol.warn = function (user, template, page) {
        var date = new Date();
        var months = mw.config.get("wgMonthNames");
        var currentMonth = months[date.getUTCMonth() + 1];
        var year = date.getUTCFullYear();
        var header = currentMonth + " " + year;
        $.get(mw.config.get("wgScriptPath") + "/api.php", {
            "action": "query",
            "format": "json",
            "meta": "tokens",
            "type": "csrf",
            "uselang": mw.config.get("wgUserLanguage")
        }).done(function (result) {
            var token = result.query.tokens.csrftoken;
            $.get(mw.config.get("wgScriptPath") + "/api.php", {
                "action": "parse",
                "format": "json",
                "prop": "text",
                "page": "User_talk:" + user,
                "uselang": mw.config.get("wgUserLanguage")
            }).done(function (result) {
                if (result.error) {
                    if (result.error.code == "missingtitle") {
                        $.post(mw.config.get("wgScriptPath") + "/api.php", {
                            "action": "edit",
                            "section": "new",
                            "sectiontitle": header,
                            "format": "json",
                            "title": "User_talk:" + user,
                            "text": "{{subst:" + template + "1|1=" + page + "}} ~~" + "~~",
                            "summary": "[[User:Awesome Aasim/rcpatrol|RCP]] gửi cảnh báo tới " + user + " về [[" + page + "]]",
                            "token": token,
                            "uselang": mw.config.get("wgUserLanguage")
                        }).done(function (result) {
                            if (result.error) {
                                mw.notify("Chúng tôi không thể gửi cảnh báo tới " + user + ".");
                            } else {
                                mw.notify("Một cảnh báo đã được tự động gửi đến " + user + ".");
                            }
                        });
                    }
                } else {
                    var section = "new";
                    $(result.parse.text["*"]).find(".mw-headline").each(function (i) {
                        if ($(this).text() == header) {
                            section = i + 1;
                        }
                    });
                    if (section == "new") {
                        $.post(mw.config.get("wgScriptPath") + "/api.php", {
                            "action": "edit",
                            "section": "new",
                            "sectiontitle": header,
                            "format": "json",
                            "title": "User_talk:" + user,
                            "text": "{{subst:" + template + "1|1=" + page + "}} ~~" + "~~",
                            "summary": "[[User:Awesome Aasim/rcpatrol|RCP]] gửi cảnh báo tới " + user + " về [[" + page + "]]",
                            "token": token,
                            "uselang": mw.config.get("wgUserLanguage")
                        }).done(function (result) {
                            if (result.error) {
                                mw.notify("Chúng tôi không thể gửi cảnh báo tới " + user + ".");
                            } else {
                                mw.notify("Một cảnh báo đã được tự động gửi đến " + user + ".");
                            }
                        });
                    } else {
                        $.get(mw.config.get("wgScriptPath") + "/api.php", {
                            "action": "parse",
                            "section": section,
                            "format": "json",
                            "prop": "wikitext",
                            "page": "User_talk:" + user,
                            "uselang": mw.config.get("wgUserLanguage")
                        }).done(function (result) {
                            if (result.error) {
                                console.error(result.error.info);
                            } else {
                                console.log(result.parse.wikitext["*"]);
                                var warninglevelstrings = result.parse.wikitext["*"].match(/<!--( ){0,}Template:.*(1|2|3|4)(im)?( ){0,}-->/g);
                                console.log(warninglevelstrings);
                                var warninglevels = warninglevelstrings[warninglevelstrings.length - 1].match(/[(1|2|3|4)]/);
                                var warninglevel = parseInt(warninglevels[warninglevels.length - 1]) + 1;
                                var oldtimestamp = (new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) - 86400000)).toISOString();
                                $.get(mw.config.get("wgScriptPath") + "/api.php", {
                                    "action": "query",
                                    "format": "json",
                                    "prop": "revisions",
                                    "titles": "User_talk:" + user,
                                    "rvsection": section,
                                    "rvend": oldtimestamp,
                                    "uselang": mw.config.get("wgUserLanguage")
                                }).done(function (result) {
                                    var revisions = [];
                                    try {
                                        for (var pageid in result.query.pages) {
                                            revisions = result.query.pages[pageid].revisions;
                                        }
                                    } catch (Error) {

                                    }
                                    if (revisions) {
                                        if (warninglevel > 4) {
                                            rcpatrol.report(user);
                                        } else {
                                            $.post(mw.config.get("wgScriptPath") + "/api.php", {
                                                "action": "edit",
                                                "section": section,
                                                "sectiontitle": header,
                                                "format": "json",
                                                "title": "User_talk:" + user,
                                                "appendtext": "\n\n{{subst:" + template + warninglevel + "|1=" + page + "}} ~~" + "~~",
                                                "summary": "[[User:Awesome Aasim/rcpatrol|RCP]] gửi cảnh báo tới " + user + " về [[" + page + "]]",
                                                "token": token,
                                                "uselang": mw.config.get("wgUserLanguage")
                                            }).done(function (result) {
                                                if (result.error) {
                                                    mw.notify("Chúng tôi không thể gửi cảnh báo tới " + user + ".");
                                                } else {
                                                    mw.notify("Một cảnh báo đã được tự động gửi đến " + user + ".");
                                                }
                                            });
                                        }
                                    } else {
                                        $.post(mw.config.get("wgScriptPath") + "/api.php", {
                                            "action": "edit",
                                            "section": section,
                                            "sectiontitle": header,
                                            "format": "json",
                                            "title": "User_talk:" + user,
                                            "appendtext": "\n\n{{subst:" + template + "1|1=" + page + "}} ~~" + "~~",
                                            "summary": "[[User:Awesome Aasim/rcpatrol|RCP]] gửi cảnh báo tới " + user + " về [[" + page + "]]",
                                            "token": token,
                                            "uselang": mw.config.get("wgUserLanguage")
                                        }).done(function (result) {
                                            if (result.error) {
                                                mw.notify("Chúng tôi không thể gửi cảnh báo tới " + user + ".");
                                            } else {
                                                mw.notify("Một cảnh báo đã được tự động gửi đến " + user + ".");
                                            }
                                        });
                                    }
                                });
                            }
                        });
                    }
                }
            });
        });
    };
    /**
     * Báo cáo bất kỳ người dùng nào về trang dự án được định cấu hình trước
     * Nếu quản trị viên, hãy cấm thành viên trong thời gian đặt trước
     * @param {*} thành viên để báo cáo 
     */
    rcpatrol.report = function (user) {

        if (mw.config.get("wgUserGroups").includes("sysop")) {
            //LÀM:  nhận đề xuất cấm cho những thành viên phá hoại (24 giờ đối với lần cấm đầu tiên, 72 giờ đối với lần cấm thứ hai, 3^(n-1) ngày đối với (lần cấm thứ n))
            //cũng như:  cấm API
            var blockwindow = window.open(mw.config.get("wgArticlePath").replace("$1", "Special:Block/" + user));
            blockwindow.alert("Thành viên đã nhận được cảnh báo cuối cùng trong 24 giờ qua, vì vậy cửa sổ này đã được mở. Khi bạn hoàn tất việc cấm thành viên, bạn có thể đóng tab này và quay trở lại RC patrol.");
        } else {
            if (aivreportuser) {
                var reportinfo = prompt("Nhập thông tin về báo cáo tại đây: ");
                reportinfo = reportinfo ? reportinfo : "Phá hoại sau cảnh báo cuối cùng.";
                $.get(mw.config.get("wgScriptPath") + "/api.php", {
                    "action": "query",
                    "format": "json",
                    "meta": "tokens",
                    "type": "csrf",
                    "uselang": mw.config.get("wgUserLanguage")
                }).done(function (result) {
                    $.post(mw.config.get("wgScriptPath") + "/api.php", {
                        "action": "edit",
                        "format": "json",
                        "title": rcpatrol.reportpage,
                        "summary": "[[User:Awesome Aasim/rcpatrol|RCP]] báo cáo " + user,
                        "appendtext": rcpatrol.reportstring.replace("$1", user).replace("$2", reportinfo) + " ~~" + "~~",
                        "uselang": mw.config.get("wgUserLanguage")
                    }).done(function (result) {
                        if (result.error) {
                            alert(result.error.info);
                        } else {
                            mw.notify("Thành viên đã được báo cáo cho bảo quản viên.");
                        }
                    });
                });
            }
        }
    }
    /**
     * Xử lý các sự kiện toàn cục, bao gồm nhấp chuột, nhấn phím, v.v...
     */
    rcpatrol.fetchbutton.$element.click(rcpatrol.fetch);
    rcpatrol.rcpatrolbutton.$element.click(function (e) {
        rcpatrol.revert(rcpatrol.changes[rcpatrol.currentChange].title, rcpatrol.changes[rcpatrol.currentChange].user, function () {
            rcpatrol.currentChange++;
            rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
        },
            function () {
                rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
            });
    });
    rcpatrol.nexteditbutton.$element.click(function (e) {
        if (!rcpatrol.nexteditbutton.isDisabled()) {
            e.preventDefault();
            rcpatrol.currentChange++;
            if (rcpatrol.currentChange >= rcpatrol.changes.length) {
                mw.notify("Đã đến cuối danh sách. Đang tải các danh sách tiếp theo...")
                rcpatrol.fetch();
            } else {
                rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
            }
        }
    });
    rcpatrol.previouseditbutton.$element.click(function (e) {
        if (!rcpatrol.previouseditbutton.isDisabled()) {
            e.preventDefault();
            rcpatrol.currentChange--;
            if (rcpatrol.currentChange < 0) {
                rcpatrol.currentChange = 0;
            }
            rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
        }
    });
    rcpatrol.thankbutton.$element.click(function (e) {
        if (!rcpatrol.thankbutton.isDisabled()) {
            e.preventDefault();
            rcpatrol.thankbutton.setDisabled(true);
            $.get(mw.config.get("wgScriptPath") + "/api.php", {
                "action": "query",
                "format": "json",
                "meta": "tokens",
                "type": "csrf"
            }).done(function (result) {
                $.post(mw.config.get("wgScriptPath") + "/api.php", {
                    "action": "thank",
                    "format": "json",
                    "rev": rcpatrol.changes[rcpatrol.currentChange].revid,
                    "token": result.query.tokens.csrftoken
                }).done(function (result) {
                    if (result.error) {
                        alert(result.error.info);
                    } else {
                        mw.notify("Đã gửi lời cảm ơn!");
                    }
                })
            })
        }
    })
    rcpatrol.rcpatrolbox.$element.keypress(function (e) {

        if (e.which == 13) {
            rcpatrol.revert(rcpatrol.changes[rcpatrol.currentChange].title, rcpatrol.changes[rcpatrol.currentChange].user, function () {
                rcpatrol.currentChange++;
                rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
            },
                function () {
                    rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
                });
        }
    });

    rcpatrol.dropdownmenu.getMenu().on('select', function () {
        var val = rcpatrol.dropdownmenu.getLabel();
        for (var option of rcpatrol.dropdown) {
            if (option.val == val) {
                rcpatrol.rcpatrolbox.setValue(option.summary);
                rcpatrol.revert(rcpatrol.changes[rcpatrol.currentChange].title, rcpatrol.changes[rcpatrol.currentChange].user, function () {
                    rcpatrol.warn(rcpatrol.changes[rcpatrol.currentChange].user, option.template, rcpatrol.changes[rcpatrol.currentChange].title);
                    rcpatrol.currentChange++;
                    rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
                    rcpatrol.dropdownmenu.getMenu().unselectItem();
                    rcpatrol.dropdownmenu.setLabel("Lùi sửa và cảnh báo...");
                }, function () {
                    rcpatrol.dropdownmenu.getMenu().unselectItem();
                    rcpatrol.dropdownmenu.setLabel("Lùi sửa và cảnh báo...");
                    rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
                });
                break;
            } else {
                continue;
            }
        }
    });
    /**
     * Trang xử lý các tổ hợp phím
     */

    $(document).keydown(function (e) {
        if (e.ctrlKey && e.altKey) {
            switch (e.which) {
                case 82: e.preventDefault(); //rollback (r)
                    rcpatrol.revert(rcpatrol.changes[rcpatrol.currentChange].title, rcpatrol.changes[rcpatrol.currentChange].user, function () {
                        rcpatrol.currentChange++;
                        rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
                    },
                        function () {
                            rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
                        });
                    break;
                case 32: e.preventDefault(); //next edit ( )
                    rcpatrol.nexteditbutton.$element.click();
                    break;
                case 188: e.preventDefault(); //previous edit (<)
                    rcpatrol.previouseditbutton.$element.click();
                    break;
                case 187: e.preventDefault(); //thanks (+)
                    rcpatrol.thankbutton.$element.click();
                    break;
                default:
                    for (var option of rcpatrol.dropdown) {
                        if (option.keycode == e.which) {
                            rcpatrol.rcpatrolbox.setValue(option.summary);
                            rcpatrol.revert(rcpatrol.changes[rcpatrol.currentChange].title, rcpatrol.changes[rcpatrol.currentChange].user, function () {
                                rcpatrol.warn(rcpatrol.changes[rcpatrol.currentChange].user, option.template, rcpatrol.changes[rcpatrol.currentChange].title);
                                rcpatrol.currentChange++;
                                rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
                            }, function () {
                                rcpatrol.loadChange(rcpatrol.changes[rcpatrol.currentChange]);
                            });
                            break;
                        } else {
                            continue;
                        }
                    }
                    break;
            }
        }
    });
});
// </nowiki>