QuickPatrol_v2

MediaWiki巡查工具 | A patrol tool for MediaWiki

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         QuickPatrol_v2
// @namespace    qp_tool_v2
// @version      2.55
// @description  MediaWiki巡查工具 | A patrol tool for MediaWiki
// @author       teaSummer
// @source       https://github.com/teaSummer/QuickPatrol_v2
// @match        *://*/wiki/*
// @match        *://*/w/*
// @match        *://*/index.php?*
// @match        *://*.wiki/*
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCI+PHBhdGggZD0iTTU2IDdjLTEuNDgyIDQuMTktMy41ODggNy41NjctNi4wNjMgMTEuMjVDNDEuNzEgMzAuNzk2IDM1LjIxOCA0My45NDYgMzAgNThjLTIuODIzLjIxLTUuMjI0LjM4NC04IDAtMi4wNjYtMS45MDYtMi4wNjYtMS45MDYtMy41NjMtNC41LTIuNzA3LTQuMzk4LTUuNi04LjA2OS05LjE1Mi0xMS44MDlDOCA0MCA4IDQwIDggMzdjMi45MDEtMS40NSA0Ljc4NS0xLjQwOCA4LTEgMi40ODQgMi4zNzEgMi40ODQgMi4zNzEgNC43NSA1LjQzOEMyMy4zODQgNDUuMjk0IDIzLjM4NCA0NS4yOTQgMjcgNDhsLjI4MS0xLjk3N2MuOTU3LTQuMDIyIDIuNTM3LTcuNjMgNC4yMTktMTEuMzk4bDEuMDYzLTIuMzgzYTE2NC41NjYgMTY0LjU2NiAwIDAgMSA4LjE4Ny0xNS42OGwxLjEwNC0xLjg3OEM0NS44MTkgOC4yMTUgNDguMzkyIDYuNTYxIDU2IDd6IiBmaWxsPSJncmVlbiIvPjxwYXRoIGQ9Ik01NSAxNmwzIDF2NGg0djJoLTRsLTEgNWgtMnYtNWgtNXYtMmg1di01eiIgZmlsbD0iI0YxRkYwRSIvPjwvc3ZnPg==
// ==/UserScript==

(() => {
    'use strict';

    const l10n = {
        'en': {
            un: 'This edit has not yet been patrolled',
            ing: 'Quick patrolling...',
            done: 'Quick patrolled',
            t_un: 'Unpatrolled',
            g_un: 'Unpatrolled',
            hl: 'Highlighted: ',
            add_summary: 'Additional Summary',
            undo: 'Undo All Changes',
            h_1: 'Apply',
            h_2: 'Edit',
            h_3: '[[Special:AbuseLog/$1|AbuseLog/$1]] by [[Special:Contribs/$2|$2]] ([[User talk:$2|talk]]), $3',
            h_4: 'the original summary is "$1"',
            h_5: 'no original summary',
            g_cr_1: 'Sure to rollback this edit?',
            g_er_1: 'Edit rollback summary',
            c: ': ',
            n_1: 'Applied',
            n_2: 'Failed to apply',
            n_3: '⚙️ Settings',
            n_4: 'Patrol all logs',
            p_1: 'Failed to patrol',
            p_2: 'Arguments',
            p_3: 'Cancel',
            r_1: 'Reverting...',
            r_2: 'Reverted',
            r_3: 'Failed to rollback',
            r_summary: 'Summary',
            r_confirm: 'Confirm',
            r_default: 'Default',
            r_markbot: 'Robotize',
            qp_patrol: 'Quick Patrol',
            qp_patrol_confirm: 'Quick Patrol Confirmation',
            qp_rollback: 'Quick Rollback',
            qp_rollback_mode: 'Rollback Mode',
            qp_rollback_show: 'Show Diff After Rollback',
            qp_rollback_show_tooltip: 'This defaults to off when the "Show a confirmation prompt when clicking on a rollback link" preference is enabled.',
            qp_abuse_edit: 'Helper that Filtered Edits',
            qp_jump_blank: 'Links Open in New Tabs',
            qp_ipe_undo_jump: 'Jump to Edit When Undo (InPageEdit)'
        },
        'zh-hans': {
            un: '该编辑尚未巡查',
            ing: '快速巡查中…',
            done: '已快速巡查',
            t_un: '未巡查',
            g_un: '尚未巡查',
            hl: '已高亮:',
            add_summary: '附加摘要',
            undo: '撤销所有更改',
            h_1: '应用',
            h_2: '编辑',
            h_3: '[[Special:AbuseLog/$1|滥用日志/$1]] — [[Special:Contribs/$2|$2]]([[User talk:$2|留言]]),$3',
            h_4: '原始摘要为“$1”',
            h_5: '没有原始摘要',
            g_cr_1: '确定要回退此编辑吗?',
            g_er_1: '编辑回退摘要',
            c: ':',
            n_1: '应用完成',
            n_2: '应用失败',
            n_3: '⚙️ 设置',
            n_4: '巡查所有日志',
            p_1: '巡查失败',
            p_2: '参数',
            p_3: '取消',
            r_1: '回退中…',
            r_2: '已回退',
            r_3: '回退失败',
            r_summary: '摘要',
            r_confirm: '确定',
            r_default: '默认',
            r_markbot: '机器化',
            qp_patrol: '快速巡查',
            qp_patrol_confirm: '确认快速巡查',
            qp_rollback: '快速回退',
            qp_rollback_mode: '回退模式',
            qp_rollback_show: '回退后显示差异',
            qp_rollback_show_tooltip: '已在参数设置启用“当点击回退链接时显示确认提示”,此项默认禁用。',
            qp_abuse_edit: '过滤内容编辑助手',
            qp_jump_blank: '在新标签页打开链接',
            qp_max_tries: '初始化最大尝试次数',
            qp_ipe_undo_jump: '撤销时跳转编辑(InPageEdit)'
        },
        'zh-hant': {
            un: '該編輯尚未巡查',
            ing: '快速巡查中…',
            done: '已快速巡查',
            t_un: '未巡查',
            g_un: '尚未巡查',
            hl: '已明顯標示:',
            add_summary: '附加摘要',
            undo: '復原所有更改',
            h_1: '應用',
            h_2: '編輯',
            h_3: '[[Special:AbuseLog/$1|濫用日誌/$1]] — [[Special:Contribs/$2|$2]]([[User talk:$2|留言]]),$3',
            h_4: '原始摘要為「$1」',
            h_5: '沒有原始摘要',
            g_cr_1: '確定要回退此編輯嗎?',
            g_er_1: '編輯回退摘要',
            c: ':',
            n_1: '應用完成',
            n_2: '應用失敗',
            n_3: '⚙️ 設定',
            n_4: '巡查所有日誌',
            p_1: '巡查失敗',
            p_2: '引數',
            p_3: '取消',
            r_1: '回退中…',
            r_2: '已回退',
            r_3: '回退失敗',
            r_summary: '摘要',
            r_confirm: '確定',
            r_default: '預設',
            r_markbot: '機器化',
            qp_patrol: '快速巡查',
            qp_patrol_confirm: '確認快速巡查',
            qp_rollback: '快速回退',
            qp_rollback_mode: '回退模式',
            qp_rollback_show: '回退後顯示差異',
            qp_rollback_show_tooltip: '已在偏好設定啟用「當點擊回退連結時顯示確認提示」,此項預設停用。',
            qp_abuse_edit: '過濾內容編輯助手',
            qp_jump_blank: '在新標籤頁開啟連結',
            qp_max_tries: '初始化最大嘗試次數',
            qp_ipe_undo_jump: '復原時跳轉編輯(InPageEdit)'
        }
    };

    // ES2017 -> ES2015
    function asyncGeneratorStep(ag_n, ag_t, ag_e, ag_r, ag_o, ag_a, ag_c) { let ag_i, ag_u; try { ag_i = ag_n[ag_a](ag_c); ag_u = ag_i.value; } catch (n) { return void ag_e(n); } return ag_i.done ? ag_t(ag_u) : Promise.resolve(ag_u).then(ag_r, ag_o); }
    function _asyncToGenerator(ag_n) { return function () { let ag_t = this, ag_e = arguments, ag_a; return new Promise(function (r, o) { ag_a = ag_n.apply(ag_t, ag_e); function _next(n) { asyncGeneratorStep(ag_a, r, o, _next, _throw, 'next', n); } function _throw(n) { asyncGeneratorStep(ag_a, r, o, _next, _throw, 'throw', n); } _next(void 0); }); }; }

    const $E = (e, f, r) => {
        if ($(e).length) return typeof f === 'function' ? f($(e)) : $(e);
        return typeof f === 'function' ? r : f;
    };
    const status = {};
    let mwApi, mwLang, rights, l, reverted, load, fails = 0;
    status.np = '.mw-changeslist-reviewstatus-unpatrolled:not(.mw-rcfilters-ui-highlights-enhanced-toplevel):not(.quickpatrol), .revisionpatrol-unpatrolled';
    status.load = _asyncToGenerator(function* (prefix, with_script) {
        for (let s of with_script ? ['.css', '.js'] : ['.css']) yield mw.loader.load(`https://fastly.jsdelivr.net/${prefix}${s}`, s === '.css' ? 'text/css' : void 0);
    });

    function helper() {
        if (!$E('.mw-abuselog-details') || $E('#mw-abusefilter-examine-editor')) return;
        const g = (e) => $E(`.mw-abuselog-details-${e} div.mw-abuselog-var-value`, (e) => e.text().replace(/^'|'$/g, ''));
        const v = {
            user: g('user_name'),
            action: g('action'),
            summary: g('summary'),
            new_text: g('new_wikitext'),
            page: g('page_prefixedtitle'),
            logid: new URL(location.href).pathname.split('/').slice(-1)[0]
        };
        if (v.action !== 'edit') return;
        const n_summary = mw.format(l.n_3, v.logid, v.user, (v.summary ? mw.format(l.h_4, v.summary) : l.h_5));
        $('#mw-content-text p').after(`<button class="quickpatrol-abuseedit-apply cdx-button mw-ui-button">${l.h_1}</button>`);
        $('[data-afl-log-id]').children().last().after(` | <a href="#quickpatrol-abuseedit-area" class="quickpatrol-abuseedit-jump">${l.h_2}</a>`);
        if (v.summary.trim()) {
            $('.quickpatrol-abuseedit-apply').after(`<div class="quickpatrol-abuseedit-summary"/>`);
            $('.quickpatrol-abuseedit-summary').text(v.summary);
        }
        $E('.diff-type-table', $('.quickpatrol-abuseedit-summary')).after('<span id="quickpatrol-abuseedit-area"/><textarea class="quickpatrol-abuseedit-editor" cols="120" rows="25"/>');
        $('.quickpatrol-abuseedit-editor').val(v.new_text).before('<input class="quickpatrol-abuseedit-title"/>');
        $('.quickpatrol-abuseedit-title').val(v.page).attr('placeholder', v.page);
        $('.quickpatrol-abuseedit-apply').click(function () {
            const me = $(this);
            const abuse_edit = (r) => {
                const outputs = {
                    page: $E('#quickpatrol-abuseedit-title', (e) => e.val() ? e.val() : v.page, v.page),
                    summary: r ? n_summary + l.c + r : n_summary,
                    online: navigator.onLine,
                    platform: navigator.platform
                };
                me.attr('disabled', true);
                $(window).on('beforeunload', () => me.attr('disabled'));
                console.debug(`[QuickPatrol] Trying - AbuseEdit - ${JSON.stringify(outputs)}`);
                mwApi.post({
                    action: 'query',
                    prop: 'revisions|info',
                    titles: outputs.page
                }).done((data) => {
                    const p = data.query.pages;
                    outputs.now = !p.length || p['-1'] ? new Date().toISOString() : null;
                    mwApi.postWithToken('csrf', {
                        action: 'edit',
                        basetimestamp: outputs.now || Object.values(p[0].revisions)[0].timestamp,
                        starttimestamp: outputs.now || p[0].touched,
                        text: outputs.text,
                        title: outputs.page,
                        summary: outputs.summary
                    }).done((data) => {
                        outputs.revids = {
                            old: data.edit.oldrevid,
                            new: data.edit.newrevid
                        };
                        console.debug(`[QuickPatrol] Succeeded - AbuseEdit - ${JSON.stringify(outputs)}`);
                        me.removeAttr('disabled').remove();
                        toast(l.n_1, outputs.summary.replace(/\[\[[^|[\]]*\|(.*?)]]/g, '$1'), 'success');
                    }).fail((msg, log) => {
                        outputs.error = msg;
                        console.warn(`[QuickPatrol] Failed - AbuseEdit - ${JSON.stringify(outputs)}`);
                        me.removeAttr('disabled');
                        mwApi.post({
                            action: 'parse',
                            page: `MediaWiki:${msg}`,
                            prop: 'text'
                        }).done((data) => Swal.fire({
                            title: l.n_2,
                            html: mw.format(data.parse.text['*'], log.error.abusefilter.description),
                            icon: 'error',
                            customClass: 'quickpatrol-output',
                            backdrop: '#0002',
                            showConfirmButton: true,
                            confirmButtonText: l.r_confirm,
                            customClass: { container: 'quickpatrol-swal quickpatrol-swal-menu' }
                        })).fail(() => toast(l.n_2, mw.msg(msg)));
                    });
                }).fail((err) => {
                    outputs.error = err;
                    console.warn(`[QuickPatrol] Failed - AbuseEdit - ${JSON.stringify(outputs)}`);
                    me.removeAttr('disabled');
                    toast(l.n_2, err);
                });
            };
            dialog('prompt', l.add_summary, abuse_edit);
        });
        new Promise(_asyncToGenerator(function* () {
            const me = $('.quickpatrol-abuseedit-editor');
            const require = yield mw.loader.using([
                'ext.wikiEditor',
                'ext.CodeMirror.WikiEditor',
                'ext.CodeMirror.mode.mediawiki'
            ]);
            mw.addWikiEditor(me);
            const CodeMirror = require('ext.CodeMirror.WikiEditor');
            const { mediawiki } = require('ext.CodeMirror.mode.mediawiki');
            const cmWe = new CodeMirror(me, mediawiki());
            cmWe.initialize();
            $('.wikiEditor-ui-text').outerHeight(400);
        }));
    }

    function g_cr() {
        for (let e of $('.mw-rollback-link a:not(.quickpatrol-rollback), .quickpatrol-rollback-button')) {
            const me = $(e);
            me.click((event) => {
                event.preventDefault();
                me.parent().find('.quickpatrol-rollback, .edit-rollback').addClass('quickpatrol-click');
                dialog('confirm', l.g_cr_1, () => rollback(me));
            });
            status.gen_rollback(me);
        }
    }

    function g_er(p) {
        let el = '.mw-rollback-link a, .quickpatrol-rollback-button';
        if (p) {
            if (p === 2) g_cr();
            for (let e of $(el)) {
                const me = $(e);
                if ($E(me.find('+.edit-rollback'))) continue;
                me.after(`<span class="edit-rollback" href="${me.attr('href')}" title="${l.g_er_1}"></span>`);
            }
            el = '.edit-rollback';
        }
        for (let e of $(el).not('.quickpatrol-rollback')) {
            const me = $(e);
            me.click((event) => {
                event.preventDefault();
                me.parent().find('.quickpatrol-rollback, .edit-rollback').addClass('quickpatrol-click');
                dialog('prompt', l.r_summary, (summary) => rollback(me, summary));
            });
            status.gen_rollback(me);
        }
    }

    function rollback_util() {
        if (GM_getValue('qp_rollback_mode') === 'd' || !load) return;
        load[0](load[1]);
    }

    function dialog(type, msg, f) {
        Swal.fire(Object.assign({
            width: '24em',
            text: msg,
            position: 'top',
            draggable: true,
            backdrop: '#0002',
            background: '#fffc',
            reverseButtons: true,
            showConfirmButton: true,
            confirmButtonText: l.r_confirm,
            showCancelButton: true,
            cancelButtonText: l.p_3,
            customClass: { container: `quickpatrol-swal quickpatrol-swal-${type}` }
        }, type === 'prompt' ? { input: 'text' } : {})).then((data) => {
            $('.quickpatrol-click').removeAttr('disabled').removeClass('quickpatrol-click');
            if (data.isConfirmed) f(data.value);
        });
    }

    function toast(title, msg, icon = 'error') {
        Swal.fire({
            toast: true,
            position: 'top-end',
            title: title,
            text: msg,
            icon: icon,
            timer: 5000,
            showConfirmButton: false,
            timerProgressBar: true,
            customClass: { container: 'quickpatrol-swal quickpatrol-swal-toast' }
        });
    }

    function init() {
        const default_settings = {
            qp_patrol: true,
            qp_patrol_confirm: false,
            qp_rollback: true,
            qp_rollback_mode: 's',
            qp_rollback_show: true,
            qp_abuse_edit: true,
            qp_jump_blank: false,
            qp_max_tries: 10,
            qp_ipe_undo_jump: true
        };
        Object.entries(default_settings).forEach(([k, v]) => {
            if (GM_getValue(k) === void 0) GM_setValue(k, v);
        });
        if (fails >= GM_getValue('qp_max_tries')) return;
        _asyncToGenerator(function* () {
            try {
                yield mw.loader.using(['jquery', 'mediawiki.base', 'mediawiki.api', 'mediawiki.user']);
                mwApi = new mw.Api();
                mwLang = mw.config.get('wgUserLanguage');
                if (['zh-cn', 'zh-hans', 'zh-hans-cn', 'zh'].includes(mwLang)) mwLang = 'zh-hans';
                else if (mwLang.startsWith('zh-')) mwLang = 'zh-hant';
                if (!Object.keys(l10n).includes(mwLang)) mwLang = 'en';
                l = l10n[mwLang];

                if (load) return;
                load = true;
                GM_registerMenuCommand(l.n_3, () => {
                    Swal.fire({
                        title: l.n_3,
                        html: `<div class="quickpatrol-settings">
                             <label>${l.qp_patrol}<input type="checkbox" id="quickpatrol-option-patrol" ${GM_getValue('qp_patrol') ? 'checked' : ''}></label>
                             <label>${l.qp_patrol_confirm}<input type="checkbox" id="quickpatrol-option-patrol-confirm" ${GM_getValue('qp_patrol_confirm') ? 'checked' : ''}></label>
                             <label>${l.qp_rollback}<input type="checkbox" id="quickpatrol-option-rollback" ${GM_getValue('qp_rollback') ? 'checked' : ''}></label>
                             <label>${l.qp_rollback_mode}<select id="quickpatrol-option-rollback-mode">
                             <option value="s" ${GM_getValue('qp_rollback_mode') === 's' ? 'selected' : ''}>${l.r_summary}</option>
                             <option value="c" ${GM_getValue('qp_rollback_mode') === 'c' ? 'selected' : ''}>${l.r_confirm}</option>
                             <option value="d" ${GM_getValue('qp_rollback_mode') === 'd' ? 'selected' : ''}>${l.r_default}</option>
                             <option value="c+s" ${GM_getValue('qp_rollback_mode') === 'c+s' ? 'selected' : ''}>${l.r_confirm}+${l.r_summary}</option>
                             <option value="d+s" ${GM_getValue('qp_rollback_mode') === 'd+s' ? 'selected' : ''}>${l.r_default}+${l.r_summary}</option>
                             <option value="b+c" ${GM_getValue('qp_rollback_mode') === 'b+c' ? 'selected' : ''}>${l.r_markbot}|${l.r_confirm}</option>
                             </select></label> ${mw.user.options.get('showrollbackconfirmation') ? `
                             <label>${l.qp_rollback_show}<input type="checkbox" id="quickpatrol-option-rollback-show" disabled></label><span style="color: #358cc6; font-size: 12px;">${l.qp_rollback_show_tooltip}</span>
                             `.trim() : `
                             <label>${l.qp_rollback_show}<input type="checkbox" id="quickpatrol-option-rollback-show" ${GM_getValue('qp_rollback_show') ? 'checked' : ''}></label>`.trim()}
                             <label>${l.qp_abuse_edit}<input type="checkbox" id="quickpatrol-option-abuse-edit" ${GM_getValue('qp_abuse_edit') ? 'checked' : ''}></label>
                             <label>${l.qp_jump_blank}<input type="checkbox" id="quickpatrol-option-jump-blank" ${GM_getValue('qp_jump_blank') ? 'checked' : ''}></label>
                             <label>${l.qp_max_tries}<input type="range" min="1" max="20" id="quickpatrol-option-max-tries" value="${GM_getValue('qp_max_tries')}"><span>${GM_getValue('qp_max_tries')}</span></label>
                             <label>${l.qp_ipe_undo_jump}<input type="checkbox" id="quickpatrol-option-ipe-undo-jump" ${GM_getValue('qp_ipe_undo_jump') ? 'checked' : ''}></label>
                        </div>`,
                        backdrop: '#0002',
                        reverseButtons: true,
                        showConfirmButton: true,
                        confirmButtonText: l.r_confirm,
                        showCancelButton: true,
                        cancelButtonText: l.p_3,
                        customClass: { container: 'quickpatrol-swal quickpatrol-swal-menu' }
                    }).then((res) => {
                        if (!res.isConfirmed) return;
                        Object.entries(default_settings).forEach(([k, v]) => {
                            const e = $('#quickpatrol-option-' + k.slice(3).replace(/_/g, '-'));
                            GM_setValue(k, typeof v === 'boolean' ? e.is(':checked') : e.val());
                        });
                        location.reload();
                    });
                    $('#quickpatrol-option-patrol').change(function () {
                        $('#quickpatrol-option-patrol-confirm').attr('disabled', !$(this).is(':checked'));
                    });
                    $('#quickpatrol-option-rollback').change(function () {
                        $('#quickpatrol-option-rollback-mode').attr('disabled', !$(this).is(':checked'));
                        if (!mw.user.options.get('showrollbackconfirmation')) $('#quickpatrol-option-rollback-show').attr('disabled', !$(this).is(':checked'));
                        $('#quickpatrol-option-local-summary').attr('disabled', !$(this).is(':checked') && !$('#quickpatrol-option-abuse-edit').is(':checked'));
                    });
                    $('#quickpatrol-option-max-tries').on('input', function () {
                        $(this).find('+span').text($(this).val());
                    });
                    $('#quickpatrol-option-abuse-edit').change(function () {
                        $('#quickpatrol-option-local-summary').attr('disabled', !$(this).is(':checked') && !$('#quickpatrol-option-rollback').is(':checked'));
                    });
                });
                $E('.mw-changeslist', () => GM_registerMenuCommand(l.n_4, () => dialog('confirm', l.n_4, () => $('[data-mw-logid] .unpatrolled').trigger('click'))));
                const oe = ['.mw-changeslist', '#mw-diff-ntitle1 strong', '#mw-diff-otitle1 strong', '.mw-contributions-list li:nth-child(20)', '.mw-contributions-list li:nth-last-child(1)'];
                const ob_IPE = new MutationObserver(with_IPE);
                const ob = new MutationObserver((ml) => {
                    ml.forEach((m) => {
                        const t = $(m.target);
                        if (t.attr('class').includes('revisionpatrol-') || t.hasClass('mw-changeslist')) return main();
                        $E(m.addedNodes, (e) => {
                            if (e.hasClass('quick-diff') || e.hasClass('ipe-modal-modal')) {
                                // InPageEdit NEXT <<< .ipe-modal-modal
                                $E(e.find('.ipe-progress'), (x) => ob_IPE.observe(x[0], { attributes: true }));
                                $E(e.find('.ipe-modal-modal__title'), (x) => ob_IPE.observe(x[0], { childList: true }));
                                $('.quick-diff .quickpatrol-rollback').remove();
                                $('.quick-diff .quickpatrol-undo').off('click');
                            }
                        });
                    });
                });
                ob.observe(document.body, { childList: true });
                for (let x of oe) $E(x, (e) => ob.observe(e[0], { childList: true, attributes: true }));
                status.load('gh/teaSummer/QuickPatrol_v2@main/styles');
                status.load('npm/sweetalert2/dist/sweetalert2.min', true);
                status.load(`gh/teaSummer/QuickPatrol_v2@minecraft-wiki/patrol/${mwLang}/Gadget-revisionPatrol`, true);
                load = ((m) => m === 's' ? [g_er] : m === 'b+c' || m === 'c' ? [g_cr] : m === 'd+s' ? [g_er, 1] : m === 'c+s' ? [g_er, 2] : null)(GM_getValue('qp_rollback_mode'));
                if (load.length === 2) status.load(`gh/teaSummer/QuickPatrol_v2@minecraft-wiki/rollback/${mwLang}/Gadget-editableRollback`);
            } catch (e) {
                fails = fails + 1;
                if (fails < GM_getValue('qp_max_tries')) console.warn(`[QuickPatrol] Failed to call MediaWiki. Retrying... (${fails}/${GM_getValue('qp_max_tries')})`);
                else console.error(`[QuickPatrol] Failed to call MediaWiki. (${fails}/${GM_getValue('qp_max_tries')})`);
                return new Promise(() => setTimeout(init, 2000));
            }

            console.log('[QuickPatrol] Checking rights...');
            rights = (yield mwApi.post({
                action: 'query',
                meta: 'userinfo',
                uiprop: 'rights'
            })).query.userinfo.rights;
            Object.assign(status, {
                patrol: rights.includes('patrol') && GM_getValue('qp_patrol'),
                rollback: rights.includes('rollback') && GM_getValue('qp_rollback'),
                abuselog: rights.includes('abusefilter-log-detail') && GM_getValue('qp_abuse_edit'),
                gen_rollback: (me) => me.addClass(`quickpatrol-rollback ${mwLang.startsWith('zh-') ? 'consolas' : ''}`).removeAttr('data-mw')
            });

            if (GM_getValue('qp_jump_blank')) $('.interlanguage-link-target, .vector-menu-content .selected a, #p-personal a, #mw-panel a:not([href^="#"]), a:not([href^="#"],[href$="/doc"],[href*="section="],:has(span))').attr('target', '_blank');
            main();
        })();
    }

    function with_IPE() {
        const diff = $E('.quick-diff');
        if (!diff) return;
        for (let e of diff.find('.mw-diff-title--title')) {
            // InPageEdit NEXT
            const me = $(e);
            me.contents().wrap('<span/>');
            const [a, b] = me.find('span');
            me.parent().attr('data-page', $(a).text());
            $(a).text(`${$(a).text()} (`).contents().unwrap();
            $(b).text($(b).text().slice(2, -1)).after(')').addClass('diff-version');
        }
        const diff_versions = diff.find('.diff-version');
        for (let e of diff_versions) {
            const me = $(e);
            if (me.attr('title')) return;
            const parent = me.parent();
            if (!parent.hasClass('mw-diff-title--title')) parent.attr('data-page', parent.find('a').first().text());
            me.attr('data-quickpatrol', me.text().replace(/[^\d]/g, ''));
            if (!status.patrol || me.hasClass('diff-hidden-history')) continue;
            me.click(() => {
                me.addClass('patrolling').attr('title', l.ing);
                patrol(me, me.data('quickpatrol'));
            });
        }
        if (!GM_getValue('qp_rollback')) return;
        diff.find('.quickpatrol-rollback, .quickpatrol-undo').remove();
        const revids = {
            last: diff_versions.first().data('quickpatrol'),
            old: diff_versions.last().data('quickpatrol')
        };
        if (!revids.last) return;
        // InPageEdit NEXT <<< .mw-diff-title--navigation>td+td>i
        const next = diff.find('.nextVersion[style], .mw-diff-title--navigation>td+td>i');
        diff.find('.btn-secondary').last().before(`<button class="btn btn-secondary quickpatrol-undo" type="button">${l.undo}</button>`);
        if ($E('.mw-diff-title--navigation')) diff.find('.quickpatrol-undo').addClass('ipe-modal-btn');
        else diff.find('.quickpatrol-undo').addClass('ssi-modalBtn');
        diff.find('.quickpatrol-undo').click(function () {
            const page = diff.find(`[data-page]:has([data-quickpatrol=${revids.old}])`).data('page');
            if (GM_getValue('qp_ipe_undo_jump')) {
                location.href = mw.format(mw.config.get('wgArticlePath'), `${page}?action=edit&undo=${revids.old}&undoafter=${revids.last}`);
                return;
            }
            const me = $(this);
            me.addClass('quickpatrol-click');
            $(window).on('beforeunload', () => me.attr('disabled'));
            dialog('prompt', l.r_summary, (summary) => {
                const outputs = {
                    page: page,
                    revids: revids,
                    summary: summary,
                    online: navigator.onLine,
                    platform: navigator.platform
                };
                console.debug(`[QuickPatrol] Trying - Undo - ${JSON.stringify(outputs)}`);
                me.attr('disabled', true);
                mwApi.post({
                    action: 'query',
                    prop: 'revisions|info',
                    titles: page,
                }).done((data) => {
                    const p = data.query.pages;
                    outputs.now = !p.length || p['-1'] ? new Date().toISOString() : null;
                    mwApi.postWithToken('csrf', {
                        action: 'edit',
                        basetimestamp: outputs.now || Object.values(p[0].revisions)[0].timestamp,
                        starttimestamp: outputs.now || p[0].touched,
                        undo: revids.old,
                        undoafter: revids.last,
                        title: page,
                        summary: summary
                    }).done((data) => {
                        outputs.revids.new = data.edit.newrevid;
                        console.debug(`[QuickPatrol] Succeeded - Undo - ${JSON.stringify(outputs)}`);
                        me.removeAttr('disabled').remove();
                        toast(l.r_2, outputs.summary.replace(/\[\[[^|[\]]*\|(.*?)]]/g, '$1').trim() || l.h_5, 'success');
                    }).fail((msg, log) => {
                        outputs.error = msg;
                        console.warn(`[QuickPatrol] Failed - Undo - ${JSON.stringify(outputs)}`);
                        me.removeAttr('disabled');
                        mwApi.post({
                            action: 'parse',
                            page: `MediaWiki:${msg}`,
                            prop: 'text'
                        }).done((data) => Swal.fire({
                            title: l.r_3,
                            html: mw.format(data.parse.text['*'], log.error.abusefilter.description),
                            icon: 'error',
                            customClass: 'quickpatrol-output',
                            backdrop: '#0002',
                            showConfirmButton: true,
                            confirmButtonText: l.r_confirm,
                            customClass: { container: 'quickpatrol-swal quickpatrol-swal-menu' }
                        })).fail(() => toast(l.r_3, mw.msg(msg)));
                    });
                }).fail((err) => {
                    outputs.error = err;
                    console.warn(`[QuickPatrol] Failed - Undo - ${JSON.stringify(outputs)}`);
                    me.removeAttr('disabled');
                    toast(l.r_3, err);
                });
            });
        });
        if (!status.rollback || !$E(next)) return;
        $E(`[data-mw-revid=${revids.old}] .quickpatrol-rollback`, (e) => {
            diff.find('.btn-secondary').last().before(`<button class="btn btn-secondary quickpatrol-rollback-button" type="button">${e.text()}</button>`);
            if ($E('.mw-diff-title--navigation')) diff.find('.quickpatrol-rollback-button').addClass('ipe-modal-btn');
            else diff.find('.quickpatrol-rollback-button').addClass('ssi-modalBtn');
            rollback_util();
        });
    }

    function clean_rcfilters(revid, start) {
        const that = $(`[data-mw-revid=${revid}]`);
        const clear = (e) => {
            $E(`.mw-rcfilters-ui-filterTagItemWidget:contains(${l.t_un}) .mw-rcfilters-ui-tagItemWidget-highlight`, (c) => {
                e.removeClass(`mw-rcfilters-highlight-color-${c.data('color')}`);
            });
            e.removeClass('mw-changeslist-reviewstatus-unpatrolled');
            if (e.attr('title')) e.attr('title', e.attr('title').replace(new RegExp(`${l.t_un}(, |、\u200B)|(, |、\u200B)?${l.t_un}`, 'g'), ''));
            if (e.attr('title') === l.hl) e.removeClass('mw-rcfilters-ui-changesListWrapperWidget-enhanced-grey').removeAttr('title');
            e.find('.unpatrolled').text('✔').addClass('quickpatrol').removeClass('unpatrolled').attr('title', l.done).off('click');
        };
        const top = $E(that.parent().find('.mw-rcfilters-ui-highlights-enhanced-toplevel'));
        if (!top) return clear(that);
        if (start) {
            for (let u of top.siblings().find('.unpatrolled')) {
                const e = $(u);
                if (e.data('mw-revid') <= revid) break;
                clear(e);
            }
        } else clear(that);
        if (!$E(top.siblings().find('.unpatrolled, .patrolling'))) clear(top);
    }

    function main() {
        console.debug('[QuickPatrol] Triggered main method.');
        if (!rights && (fails >= GM_getValue('qp_max_tries') || !fails)) return init();
        if (status.abuselog) helper();
        if (status.rollback) {
            reverted = false;
            rollback_util();
        }

        if (!status.patrol) return;
        for (let e of $(status.np)) {
            const that = $(e);
            that.find('.revisionpatrol-icon-unpatrolled').after(`<span class="unpatrolled quickpatrol-icon-unpatrolled" title="${l.g_un}">!</span>`);
            that.find('.revisionpatrol-icon-unpatrolled').remove();
            that.find('.unpatrolled').addClass('quickpatrol').click(_asyncToGenerator(function* () {
                const me = $(this);
                if (me.text() !== '!') return;
                me.addClass('patrolling').removeClass('unpatrolled');
                me.text('#').attr('title', l.ing);
                let revid = that.data('mw-revid');
                if (that.data('mw-logid')) {
                    yield mwApi.post({
                        action: 'query',
                        list: 'logevents',
                        leprop: 'ids',
                        letitle: that.find('td.mw-enhanced-rc-nested').data('target-page'),
                        letype: that.data('mw-logaction').split('/')[0],
                        lelimit: 'max'
                    }).done((data) => {
                        const q = data.query.logevents.find((i) => i.logid === that.data('mw-logid'));
                        if (q) revid = q.revid;
                    });
                }
                if (!revid) return me.text('!').addClass('unpatrolled').removeClass('patrolling').attr('title', l.un);
                that.attr('data-mw-revid', revid);
                patrol(me, revid, void 0, that.data('mw-logid'));
            }));
        }
    }

    function patrol(me, revid, rcid, logid, isConfirmed) {
        const target_page = $(`[data-mw-revid=${revid}] [data-target-page]`).data('target-page');
        const outputs = {
            revid: revid,
            rcid: rcid,
            logid: logid,
            ts: $(`[data-mw-revid=${revid}]`).data('mw-ts'),
            page: target_page || mw.config.get('wgPageName'),
            online: navigator.onLine,
            platform: navigator.platform
        };
        if (!isConfirmed && GM_getValue('qp_patrol_confirm')) {
            me.text('?').addClass('quickpatrol-confirm unpatrolled').removeClass('patrolling').attr('title', l.qp_patrol_confirm);
            me.click(() => {
                me.addClass('patrolling').attr('title', l.ing);
                patrol(me, revid, rcid, logid, true);
            });
            return;
        }
        const params = { action: 'patrol' };
        if (rcid) params.rcid = rcid;
        else params.revid = revid;
        console.debug(`[QuickPatrol] Trying - Patrol - ${JSON.stringify(outputs)}`);
        mwApi.postWithToken('patrol', params).done(() => {
            console.debug(`[QuickPatrol] Succeeded - Patrol - ${JSON.stringify(outputs)}`);
            me.text('✔').addClass('quickpatrol').removeClass('patrolling unpatrolled').attr('title', l.done).off('click');
            if (revid) clean_rcfilters(revid);
        }).fail((err) => {
            const fail = () => {
                outputs.error = err;
                console.warn(`[QuickPatrol] Failed - Patrol - ${JSON.stringify(outputs)}`);
                toast(l.p_1, l.p_2 + l.c + JSON.stringify(outputs));
                me.text('!').addClass('unpatrolled').removeClass('patrolling').attr('title', l.un);
                if (me.hasClass('quickpatrol-icon-unpatrolled')) me.attr('title', l.g_un);
            };
            if (!target_page) return fail();
            mwApi.post({
                action: 'query',
                list: 'recentchanges',
                rcprop: 'ids',
                rctitle: outputs.page,
                rclimit: 'max'
            }).done((data) => {
                const q = data.query.recentchanges.find((i) => i.revid === revid);
                if (!q) return fail();
                patrol(me, revid, q.rcid, void 0, true);
            }).fail(fail);
        });
    }

    function rollback(me, summary) {
        const href = me.attr('href');
        if (GM_getValue('qp_rollback_show') && !mw.user.options.get('showrollbackconfirmation')) {
            location.href = href;
            return;
        }
        const temp = { text: me.text(), title: me.attr('title') };
        const search = new URL(`THIS.IS.A.FOOL:${href}`).searchParams;
        me.text(l.r_1).addClass('reverting').attr('data-href', href).removeAttr('title href').removeClass('quickpatrol-rollback');
        const outputs = {
            page: $(`[data-target-page]:has(.reverting[data-href="${href}"])`).data('target-page') || mw.config.get('wgPageName'),
            user: search.get('from'),
            summary: summary,
            markbot: GM_getValue('qp_rollback_mode') === 'b+c' || void 0,
            refresh: reverted,
            online: navigator.onLine,
            platform: navigator.platform
        };
        const params = {
            action: 'rollback',
            title: outputs.page,
            user: outputs.user,
            summary: summary,
            markbot: outputs.markbot,
            token: search.get('token')
        };
        let action = (params) => mwApi.post(params);
        if (reverted) {
            delete params.token;
            action = (params) => mwApi.postWithToken('rollback', params);
        }
        reverted = true;
        console.debug(`[QuickPatrol] Trying - Rollback - ${JSON.stringify(outputs)}`);
        action(params).done((data) => {
            outputs.revids = {
                old: data.rollback.old_revid,
                last: data.rollback.last_revid,
                new: data.rollback.revid
            };
            console.debug(`[QuickPatrol] Succeeded - Rollback - ${JSON.stringify(outputs)}`);
            me.parent().text(l.r_2);
            clean_rcfilters(outputs.revids.last, true);
        }).fail((err) => {
            outputs.error = err;
            console.warn(`[QuickPatrol] Failed - Rollback - ${JSON.stringify(outputs)}`);
            me.text(temp.text).attr('title', temp.title).attr('href', href).addClass('quickpatrol-rollback').removeClass('reverting');
            toast(l.r_3, l.p_2 + l.c + JSON.stringify(outputs));
        });
    }

    init();
})();