您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
MediaWiki巡查工具 | A patrol tool for MediaWiki
// ==UserScript== // @name QuickPatrol_v2 // @namespace qp_tool_v2 // @version 2.23 // @description MediaWiki巡查工具 | A patrol tool for MediaWiki // @author teaSummer // @match *://*/wiki/* // @match *://*/w/* // @match *://*/index.php?* // @license MIT // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @icon data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0Ij4KPHBhdGggZD0iTTAgMCBDLTEuNDgyMzI2MzcgNC4xOTA4NzAzNyAtMy41ODgxNjg3OSA3LjU2Njg2ODUzIC02LjA2MjUgMTEuMjUgQy0xNC4yOTA3MTcxNCAyMy43OTYzMTAyOCAtMjAuNzgzMjk3MDMgMzYuOTQ1NTg4NDYgLTI2IDUxIEMtMjguODIyNzg0MTcgNTEuMjEwMTc2MjggLTMxLjIyNDQxNTI3IDUxLjM4NDA1MTY3IC0zNCA1MSBDLTM2LjA2NjQwNjI1IDQ5LjA5Mzc1IC0zNi4wNjY0MDYyNSA0OS4wOTM3NSAtMzcuNTYyNSA0Ni41IEMtNDAuMjY5ODgxNjYgNDIuMTAyMDY0MzUgLTQzLjE2MjMzNzgyIDM4LjQzMTI5NzExIC00Ni43MTQ4NDM3NSAzNC42OTE0MDYyNSBDLTQ4IDMzIC00OCAzMyAtNDggMzAgQy00NS4wOTg5NjczNiAyOC41NDk0ODM2OCAtNDMuMjE0OTAyNzYgMjguNTkyMTk4MyAtNDAgMjkgQy0zNy41MTU2MjUgMzEuMzcxMDkzNzUgLTM3LjUxNTYyNSAzMS4zNzEwOTM3NSAtMzUuMjUgMzQuNDM3NSBDLTMyLjYxNjA1MTQxIDM4LjI5NDUyODk1IC0zMi42MTYwNTE0MSAzOC4yOTQ1Mjg5NSAtMjkgNDEgQy0yOC44NjA3ODEyNSA0MC4wMjE2MDE1NiAtMjguODYwNzgxMjUgNDAuMDIxNjAxNTYgLTI4LjcxODc1IDM5LjAyMzQzNzUgQy0yNy43NjIzOTM4MSAzNS4wMDA1MDQ0MSAtMjYuMTgyMDY0MDkgMzEuMzkzNjA5MjcgLTI0LjUgMjcuNjI1IEMtMjQuMTQ5MDUyNzMgMjYuODM4NTkxMzEgLTIzLjc5ODEwNTQ3IDI2LjA1MjE4MjYyIC0yMy40MzY1MjM0NCAyNS4yNDE5NDMzNiBDLTIwLjk3MzE4NTg5IDE5Ljg1MTM1Nzc4IC0xOC4yNzUwODM4OSAxNC42NTg3NzgyNyAtMTUuMjUgOS41NjI1IEMtMTQuODg1ODM5ODQgOC45NDI2MjIwNyAtMTQuNTIxNjc5NjkgOC4zMjI3NDQxNCAtMTQuMTQ2NDg0MzggNy42ODQwODIwMyBDLTEwLjE4MDg3NDY3IDEuMjE1NDk4MzcgLTcuNjA4MTM4NTQgLTAuNDM4OTMxMDcgMCAwIFogIiBmaWxsPSIjMDA4MDAwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1Niw3KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjk5IDAuMzMgMS45OCAwLjY2IDMgMSBDMyAyLjMyIDMgMy42NCAzIDUgQzQuMzIgNSA1LjY0IDUgNyA1IEM3IDUuNjYgNyA2LjMyIDcgNyBDNS42OCA3IDQuMzYgNyAzIDcgQzIuNjcgOC42NSAyLjM0IDEwLjMgMiAxMiBDMS4zNCAxMiAwLjY4IDEyIDAgMTIgQzAgMTAuMzUgMCA4LjcgMCA3IEMtMS42NSA3IC0zLjMgNyAtNSA3IEMtNSA2LjM0IC01IDUuNjggLTUgNSBDLTMuMzUgNSAtMS43IDUgMCA1IEMwIDMuMzUgMCAxLjcgMCAwIFogIiBmaWxsPSIjRjFGRjBFIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1NSwxNikiLz4KPC9zdmc+Cg== // ==/UserScript== (() => { 'use strict'; // 本地化 | Localization const w = { 'en': { un: 'This edit has not yet been patrolled', ing: 'Quick patrolling...', done: 'Quick patrolled', t_un: 'Unpatrolled', g_un: 'Unpatrolled', hl: 'Highlighted: ', s: 'Additional summary:', h_1: 'Apply', h_2: '[[Special:AbuseLog/$1|AbuseLog/$1]] by [[Special:Contribs/$2|$2]] ([[User talk:$2|talk]]), ', h_3: 'the original summary is "$1"', h_4: 'no original summary', g_cr_1: 'Sure to rollback this edit?', g_er_1: 'Revert edits by [[Special:Contribs/$1|$1]] ([[User talk:$1|talk]])', g_er_2: 'Edit rollback summary', c: ': ', n_1: '✔ Applied', n_2: '× Failed to apply', n_3: '⚙️ Settings', n_4: 'Patrol all logs (in emergency)', r_summary: 'Summary', r_confirm: 'Confirm', r_default: 'Default', qp_patrol: 'Quick Patrol', qp_rollback: 'Quick Rollback', qp_rollback_mode: 'Rollback Mode', qp_abuse_edit: 'Helper that Filtered Edits', qp_jump_blank: 'Links Open in New Tabs', qp_using_ooui: 'Using OOUI', qp_max_retries: 'Maximum Retries of Initialization', qp_local_summary: 'Localization Maps English Summary', map: ['unnecessary edit', 'unnecessary information|unnecessary content|unnecessary info|unnecessary|#UN|#U', 'false content', 'false information|false info|false|#F', 'outdated content', 'outdated information|outdated info|outdated?|#OUT|#OD|#O', 'machine translation', 'machine-trans|#MT', 'duplicated content', 'duplicated information|duplicated info|duplicated?|#DUP|#DP|#D', 'unambiguous content', 'unambig(uous)?|#NAM|#NA', 'ambiguous content', 'ambig(uous)?|#AM', 'WAI for content', '(sic|WAI)( content|[#-]C)|#SIC|#SC|#S', 'Works As Intended', '#WAI|WAI|#W', 'N-ST', '#NST|#NS', 'refuse moving', 'refuse deleting', 'refuse', 'keeping redirection after moving', 'keeping redirection', 'typo', 'sic', 'fixed', 'fix'] }, 'zh-hans': { un: '该编辑尚未巡查', ing: '快速巡查中…', done: '已快速巡查', t_un: '未巡查', g_un: '尚未巡查', hl: '已高亮:', s: '附加摘要:', h_1: '应用', h_2: '[[Special:AbuseLog/$1|滥用日志/$1]] — [[Special:Contribs/$2|$2]]([[User talk:$2|留言]]),', h_3: '原始摘要为“$1”', h_4: '没有原始摘要', g_cr_1: '确定要回退此编辑吗?', g_er_1: '回退[[Special:Contribs/$1|$1]]([[User talk:$1|留言]])所做的编辑', g_er_2: '编辑回退摘要', c: ':', n_1: '✔ 应用完成', n_2: '× 应用失败', n_3: '⚙️ 设置', n_4: '巡查所有日志(紧急时)', r_summary: '摘要', r_confirm: '确认', r_default: '默认', qp_patrol: '快速巡查', qp_rollback: '快速回退', qp_rollback_mode: '回退模式', qp_abuse_edit: '过滤内容编辑助手', qp_jump_blank: '在新标签页打开链接', qp_using_ooui: '使用OOUI', qp_max_retries: '初始化最大重试次数', qp_local_summary: '本地化映射英语摘要', map: ['不必要的编辑', '不必要的编辑', // '含不实内容', '含不实内容', // '含过时内容', '含过时内容', // '疑似使用机器翻译', '疑似使用机器翻译', // '内容重复', '内容重复', // '内容无歧义', '内容无歧义', // '内容含有歧义', '内容含有歧义', // '原文无误', '原文无误', // '有意为之', '有意为之', // '含非标准译名的编辑', '含非标准译名的编辑', // '拒绝移动', '拒绝删除', '拒绝', '移动后保留重定向', '保留重定向', '含错误拼写的编辑', '原文如此', '已修复', '修复'] }, 'zh-hant': { un: '該編輯尚未巡查', ing: '快速巡查中…', done: '已快速巡查', t_un: '未巡查', g_un: '尚未巡查', hl: '已明顯標示:', s: '附加摘要:', h_1: '應用', h_2: '[[Special:AbuseLog/$1|濫用日誌/$1]] — [[Special:Contribs/$2|$2]]([[User talk:$2|留言]]),', h_3: '原始摘要為「$1」', h_4: '沒有原始摘要', g_cr_1: '確定要回退此編輯嗎?', g_er_1: '回退[[Special:Contribs/$1|$1]]([[User talk:$1|留言]])所做的編輯', g_er_2: '編輯回退摘要', c: ':', n_1: '✔ 應用完成', n_2: '× 應用失敗', n_3: '⚙️ 設定', n_4: '巡查所有日誌(緊急時)', r_summary: '摘要', r_confirm: '確認', r_default: '預設', qp_patrol: '快速巡查', qp_rollback: '快速回退', qp_rollback_mode: '回退模式', qp_abuse_edit: '過濾內容編輯助手', qp_jump_blank: '在新標籤頁開啟連結', qp_using_ooui: '使用OOUI', qp_max_retries: '初始化最大重試次數', qp_local_summary: '本地化對映英語摘要', map: ['不必要的編輯', '不必要的編輯', // '含不實內容', '含不實內容', // '含過時內容', '含過時內容', // '疑似使用機器翻譯', '疑似使用機器翻譯', // '內容重複', '內容重複', // '內容無歧義', '內容無歧義', // '內容含有歧義', '內容含有歧義', // '原文無誤', '原文無誤', // '有意為之', '有意為之', // '含非標準譯名的編輯', '含非標準譯名的編輯', // '拒絕移動', '拒絕刪除', '拒絕', '移動後保留重定向', '保留重定向', '含錯誤拼寫的編輯', '原文如此', '已修復', '修復'] } } const $E = (e, f, r) => { if ($(e).length) return typeof f == 'function' ? f($(e)) : $(e); return typeof f == 'function' ? r : f; } let mwApi, mwLang, status = {}, rights, l, fail = 0, load = false; status.np = '.mw-changeslist-reviewstatus-unpatrolled:not(.mw-rcfilters-ui-highlights-enhanced-toplevel):not(.quickpatrol), .revisionpatrol-unpatrolled'; status.load = async (...l) => { for (const f of l) await mw.loader.load(`https://fastly.jsdelivr.net/gh/teaSummer/QuickPatrol_v2@${f}`, f.endsWith('.css') ? 'text/css' : void 0); } function helper() { const g = (e) => $E(`.mw-abuselog-details-${e} div.mw-abuselog-var-value`, (e) => e.text().replace(/^'|'$/g, '')); if ($E('.mw-abuselog-details')) { const v = { user_name: g('user_name'), action: g('action'), summary: g('summary'), new_wikitext: g('new_wikitext'), page_prefixedtitle: g('page_prefixedtitle'), lgid: location.href.replace(/\/+$/, '').split('/').slice(-1)[0].replace(/[^0-9]/g, ''), created: $E('fieldset .mw-usertoollinks+a.new') } if (v.action == 'edit') { let o_summary = l.h_2.replace(/\$1/g, v.lgid).replace(/\$2/g, v.user_name); $('#mw-content-text p').after(`<button class="quickpatrol-abuseedit">${l.h_1}</button>`); o_summary = o_summary + (v.summary ? l.h_3.replace('$1', v.summary) : l.h_4); if (v.summary.trim()) { $('.quickpatrol-abuseedit').after(`<div class="quickpatrol-abuseedit-summary"/>`); $('.quickpatrol-abuseedit-summary').text(v.summary); } $E('.diff-type-table', $('.quickpatrol-abuseedit-summary')).after('<textarea class="new_wikitext" cols="80" rows="25"/>'); $('.new_wikitext').val(v.new_wikitext).before('<input class="page_prefixedtitle"/>'); if (mw.addWikiEditor) mw.addWikiEditor($(".new_wikitext")); $('.page_prefixedtitle').val(v.page_prefixedtitle).attr('placeholder', v.page_prefixedtitle); $('.mw-abuselog-details').wrap(`<details/>`); $('.quickpatrol-abuseedit').click(function () { const me = $(this); const rb = (result) => { let act; const r = result.trim(); const title = $E('.page_prefixedtitle', (e) => e.val() ? e.val() : v.page_prefixedtitle, v.page_prefixedtitle); const summary = r ? o_summary + l.c + r : o_summary; const text = $E('.new_wikitext', (e) => e.val(), v.new_wikitext); const notice = summary.replace(/\[\[[^|[\]]*\|(.*?)]]/g, '$1'); me.attr('disabled', true); $(window).on('beforeunload', () => me.attr('disabled')); if (!v.created) { act = mwApi.edit(title, () => Object({ summary: summary, text: text, minor: true })); } else { act = mwApi.create(title, { summary: summary }, text); } act.done(() => { me.remove(); $(window).off('beforeunload'); mw.notify(notice, { title: l.n_1, type: 'success' }); }).fail(() => { me.removeAttr('disabled'); mw.notify(notice, { title: l.n_2, type: 'error' }); }); } if (status.ooui) OO.ui.prompt(l.s).then((result) => rb(result)); else { const result = prompt(l.s); if (result != null) rb(result); } }); } } } function g_cr() { $('.mw-rollback-link a:not(.quickpatrol-rollback)').each(function () { const href = $(this).attr('href'); $(this).click((e) => { e.preventDefault(); if (status.ooui) { OO.ui.confirm(l.g_cr_1).then((confirmed) => { if (confirmed) location.href = href; }); } else if (confirm(l.g_cr_1)) location.href = href; }); status.rf(this); }); } function g_er(p) { let el = '.mw-rollback-link a'; if (p) { if (p == 2) g_cr(); $(el).each(function () { $(this).after($(`<span class="edit-rollback" href="${$(this).attr('href')}" title="${l.g_er_2}"></span>`)); status.rf(this); }) el = '.edit-rollback'; } el = el + ':not(.quickpatrol-rollback)'; $(el).each(function () { const href = $(this).attr('href'); $(this).click((e) => { e.preventDefault(); const rb = (result) => { let r = result.trim(); if (GM_getValue('qp_local_summary')) for (const [k, v] of Object.entries(w)) for (let i = 0; i < v.map.length; i++) { r = r.replace(new RegExp('\\b' + v.map[i] + '\\b', 'gi'), l.map[i]); } location.href = href + '&summary=' + encodeURIComponent(r ? summary + l.c + r : summary); } const name = decodeURIComponent(href.match(/&from=(.+)&token/)[1].replace(/\+/g, ' ')); const summary = mw.format(l.g_er_1, name); if (status.ooui) OO.ui.prompt(l.s).then((result) => rb(result)); else { const result = prompt(l.s); if (result != null) rb(result); } }); status.rf(this); }); } async function rollback_gadget() { if (GM_getValue('qp_rollback_mode') == 'd' || !load) return; if (!(OO && OO.ui && OO.ui.confirm)) status.ooui = false; load[0](load[1]); } async function init() { const defaultValue = { qp_patrol: true, qp_rollback: true, qp_rollback_mode: 's', qp_abuse_edit: true, qp_jump_blank: false, qp_using_ooui: true, qp_max_retries: 10, qp_local_summary: false } for (const [k, v] of Object.entries(defaultValue)) { if (GM_getValue(k) === undefined) { GM_setValue(k, v); } } if (fail >= GM_getValue('qp_max_retries')) return; try { mwApi = await new mw.Api(); mwLang = Object.keys(await mw.language.data)[0]; 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(w).includes(mwLang)) mwLang = 'en'; l = w[mwLang]; if (!load) { load = true; status.ooui = GM_getValue('qp_using_ooui'); GM_registerMenuCommand(l.n_3, () => { let html = `<div class="quickpatrol-setting"> <label>${l.qp_patrol}<input type="checkbox" id="quickpatrol-option-patrol" ${GM_getValue('qp_patrol') ? '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> </select></label> <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_using_ooui}<input type="checkbox" id="quickpatrol-option-using-ooui" ${GM_getValue('qp_using_ooui') ? 'checked' : ''}></label> <label>${l.qp_max_retries}<input type="range" min="1" max="20" id="quickpatrol-option-max-retries" value="${GM_getValue('qp_max_retries')}"><span>${GM_getValue('qp_max_retries')}</span></label> <label>${l.qp_local_summary}<input type="checkbox" id="quickpatrol-option-local-summary" ${GM_getValue('qp_local_summary') ? 'checked' : ''}></label> </div>`; Swal.fire({ html, showCloseButton: true, confirmButtonText: l.h_1 }).then((res) => { if (res.isConfirmed) { for (const [k, v] of Object.entries(defaultValue)) { const e = $('#quickpatrol-option-' + k.slice(3).replace(/_/g, '-')); if (typeof v == 'boolean') { GM_setValue(k, e.is(':checked')); } else { GM_setValue(k, e.val()); } } location.reload(); } }); $('#quickpatrol-option-max-retries').on('input', function () { $(this).find('+span').text($(this).val()); }); $('#quickpatrol-option-rollback').change(function () { $('#quickpatrol-option-rollback-mode').attr('disabled', !$(this).is(':checked')); }); }); $E('.mw-changeslist', () => GM_registerMenuCommand(l.n_4, () => { $("[data-mw-logid] .unpatrolled").click(); })); const oe = ['.mw-changeslist', '#mw-diff-ntitle1 strong', '#mw-diff-otitle1 strong', '.mw-contributions-list li']; const ob_IPE = new MutationObserver(with_IPE); let z = false; const ob = new MutationObserver((ml) => { ml.forEach((m) => { const t = $(m.target); if (!z && t.attr('class').includes('revisionpatrol-')) { z = true; main(); } if (t.hasClass('mw-changeslist')) main(); $E(m.addedNodes, (e) => { if (e.hasClass('quick-diff')) ob_IPE.observe(e.find('.ipe-progress')[0], { attributes: true }); }); }); }); ob.observe(document.body, { childList: true }); for (const x of oe) $E(x, (e) => ob.observe(e[0], { childList: true, attributes: true })); status.load('main/styles.css', `minecraft-wiki/patrol/${mwLang}/Gadget-revisionPatrol.css`, `minecraft-wiki/patrol/${mwLang}/Gadget-revisionPatrol.js`); mw.loader.load('https://unpkg.com/[email protected]/dist/sweetalert2.min.js'); mw.loader.load('https://unpkg.com/[email protected]/dist/sweetalert2.min.css', 'text/css'); load = ((m) => m == 's' ? [g_er] : m == 'c' ? [g_cr] : m == 'd+s' ? [g_er, 1] : m == 'c+s' ? [g_er, 2] : [() => { }])(GM_getValue('qp_rollback_mode')); if (load.length == 2) status.load(`minecraft-wiki/rollback/${mwLang}/Gadget-editableRollback.css`); } } catch (e) { fail = fail + 1; if (fail < GM_getValue('qp_max_retries')) console.warn(`[QuickPatrol] Failed to call MediaWiki. Retrying... (${fail}/${GM_getValue('qp_max_retries')})`); else console.error(`[QuickPatrol] Failed to call MediaWiki. (${fail}/${GM_getValue('qp_max_retries')})`); new Promise(() => setTimeout(init, 2000)); return; } console.log('[QuickPatrol] Checking rights...'); rights = ( await mwApi.get({ 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'), rf: (e) => $(e).addClass('quickpatrol-rollback' + (mwLang.startsWith('zh-') ? ' consolas' : '')) }); 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() { if (!status.patrol) return; $('.diff-version:not(.diff-hidden-history)').click(function () { const me = $(this); if (me.attr('title')) return; const value = me.text().replace(/[^0-9]/g, ''); me.addClass('patrolling').attr('title', l.ing); patrol(me, value); }); } async function main() { if (!rights) { if (fail >= GM_getValue('qp_max_retries') || !fail) init(); return; } if (status.abuselog) helper(); if (status.rollback) rollback_gadget(); if (status.patrol) { $(status.np).attr('data-mw-revid', function (_i, value) { const that = $(this); 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(async function () { const me = $(this); me.addClass('patrolling').removeClass('unpatrolled'); if (me.text() == '!') { $(this).text('#').attr('title', l.ing); if (!value && that.attr('data-mw-logid')) { try { value = new RegExp(`"logid":${that.attr('data-mw-logid')},.+?,"revid":([0-9]+)`).exec(JSON.stringify((await mwApi .get({ action: 'query', list: 'logevents', leprop: 'ids', letitle: that.find('td.mw-enhanced-rc-nested').attr('data-target-page'), letype: that.attr('data-mw-logaction').split('/')[0], lelimit: 'max', format: 'json' }) )))[1]; } catch (e) { $(this).text('!').addClass('unpatrolled').removeClass('patrolling').attr('title', l.un); return; } } patrol(me, value, () => { $E(`.mw-rcfilters-ui-filterTagItemWidget:contains(${l.t_un}) .mw-rcfilters-ui-tagItemWidget-highlight`, (e) => { that.removeClass(`mw-rcfilters-highlight-color-${e.data('color')}`); }); that.removeClass('mw-changeslist-reviewstatus-unpatrolled'); that.attr('title', that.attr('title').replace(new RegExp(`(, |、\u200B)?${l.t_un}(、\u200B|, )?`, 'g'), '')); if (that.attr('title') == l.hl) that.removeAttr('title'); }); } }); }); } } function patrol(me, revid, success = () => { }, exFail = () => { }) { const fail = () => { console.warn(`[QuickPatrol] FAILED (revid: ${revid})`); me.text('!').addClass('unpatrolled').removeClass('patrolling').attr('title', l.un); if (me.hasClass('quickpatrol-icon-unpatrolled')) me.attr('title', l.g_un); exFail(); } console.debug(`[QuickPatrol] TRYING (revid: ${revid})`); mwApi.get({ action: 'query', meta: 'tokens', type: 'patrol', format: 'json' }).done((data) => { mwApi.post({ action: 'patrol', revid: revid, token: data.query.tokens.patroltoken, format: 'json' }).done(() => { console.debug(`[QuickPatrol] SUCCEEDED (revid: ${revid})`); me.text('✔').addClass('quickpatrol').removeClass('patrolling unpatrolled').attr('title', l.done).off('click'); success(); }).fail(fail); }).fail(fail); } init(); })();