Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* AntiVandal — Bharatpedia patrol helper
*
* Original userscript for en.bharatpedia.org.
* Inspired by the general idea of icon-driven revert + notice tools,
* written from scratch for this wiki's API, templates, and noticeboards.
*
* Host: User:WikiDwarf/AntiVandal.js
* Install (Special:MyPage/common.js):
* mw.loader.load('/w/index.php?title=User:WikiDwarf/AntiVandal.js&action=raw&ctype=text/javascript');
*
* You are responsible for every action this script performs on your account.
*/
(function () {
'use strict';
if (window.AntiVandal && window.AntiVandal.loaded) {
return;
}
var RK = (window.AntiVandal = window.AntiVandal || {});
RK.loaded = true;
RK.version = '1.0.0';
var CONF = {
storageKey: 'antivandal-prefs-v1',
tag: 'AntiVandal',
aivPage: 'Bharatpedia:Administrator intervention against vandalism',
rfppPage: 'Bharatpedia:Requests for page protection',
docsPage: 'Bharatpedia:AntiVandal'
};
var PRESETS = [
{ id: 'vandalism', label: 'Vandalism', summary: 'Reverted unconstructive edit(s)', notice: 'uw-vandalism', level: true },
{ id: 'blanking', label: 'Blanking', summary: 'Reverted unexplained content removal', notice: 'uw-delete', level: true },
{ id: 'test', label: 'Test edit', summary: 'Reverted test edit', notice: 'uw-test', level: true },
{ id: 'unsourced', label: 'Unsourced', summary: 'Reverted unsourced or poorly sourced addition', notice: 'uw-unsourced', level: true },
{ id: 'spam', label: 'Spam / promo', summary: 'Reverted promotional or spam content', notice: 'uw-spam', level: true },
{ id: 'npov', label: 'POV / attack', summary: 'Reverted non-neutral or attack content', notice: 'uw-biog', level: true },
{ id: 'custom', label: 'Custom summary', summary: '', notice: '', level: false }
];
var NOTICES = [
{ id: 'uw-vandalism', label: 'Vandalism', leveled: true },
{ id: 'uw-test', label: 'Test edit', leveled: true },
{ id: 'uw-delete', label: 'Removing content', leveled: true },
{ id: 'uw-unsourced', label: 'Unsourced content', leveled: true },
{ id: 'uw-error', label: 'Factual error / hoax', leveled: true },
{ id: 'uw-spam', label: 'Spam / advertising', leveled: true },
{ id: 'uw-advert', label: 'Promotional tone', leveled: true },
{ id: 'uw-biog', label: 'BLP / biography issue', leveled: true },
{ id: 'uw-defamatory', label: 'Defamatory content', leveled: true },
{ id: 'uw-joke', label: 'Joke edit', leveled: true },
{ id: 'uw-notvoted', label: 'Not a vote / forum', leveled: false },
{ id: 'uw-chat', label: 'Talk page as chat', leveled: false },
{ id: 'welcome', label: 'Welcome (constructive new editor)', leveled: false },
{ id: 'welcometest', label: 'Welcome after a test edit', leveled: false }
];
function defaultPrefs() {
return {
barPosition: 'top',
useRollback: true,
autoNotice: false
};
}
function loadPrefs() {
try {
var raw = localStorage.getItem(CONF.storageKey);
return raw ? Object.assign(defaultPrefs(), JSON.parse(raw)) : defaultPrefs();
} catch (e) {
return defaultPrefs();
}
}
function savePrefs(p) {
try {
localStorage.setItem(CONF.storageKey, JSON.stringify(p));
} catch (e) { /* ignore quota */ }
}
var prefs = loadPrefs();
var api = null;
var hasRollback = false;
function toast(msg, kind) {
var el = document.getElementById('antivandal-toast');
if (!el) {
el = document.createElement('div');
el.id = 'antivandal-toast';
el.setAttribute('role', 'status');
document.body.appendChild(el);
}
el.className = 'rk-toast rk-toast-' + (kind || 'info');
el.textContent = msg;
el.style.display = 'block';
clearTimeout(toast._t);
toast._t = setTimeout(function () {
el.style.display = 'none';
}, 4200);
}
function pageName() {
return mw.config.get('wgPageName');
}
function relevantUser() {
return mw.config.get('wgRelevantUserName') || null;
}
function talkTitleFor(user) {
if (!user) {
return null;
}
if (/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d)$/.test(user) ||
user.indexOf(':') !== -1 && user.match(/^[0-9a-fA-F:.]+$/)) {
return 'User talk:' + user;
}
return 'User talk:' + user;
}
function injectCss() {
if (document.getElementById('antivandal-css')) {
return;
}
var css = [
'#antivandal-bar{display:flex;align-items:center;gap:2px;flex-wrap:wrap;',
'margin:4px 0 8px;height:auto;line-height:1;z-index:3;position:relative;clear:both;}',
'.page-actions #antivandal-bar,.citizen-page-heading #antivandal-bar{margin:0 0 0 8px;display:inline-flex;}',
'#antivandal-bar button.rk-ico{background:transparent;border:0;cursor:pointer;width:28px;height:28px;',
'border-radius:50%;padding:0;font-size:16px;line-height:28px;color:#202122;}',
'#antivandal-bar button.rk-ico:hover{background:rgba(0,0,0,.08);}',
'#antivandal-bar button.rk-ico.rk-on{background:#2a4a33;color:#fff;}',
'.rk-tip{position:absolute;bottom:-1.8em;left:50%;transform:translateX(-50%);background:#323232;color:#fff;',
'font:11px sans-serif;padding:3px 6px;border-radius:2px;white-space:nowrap;display:none;z-index:20;}',
'#antivandal-bar button.rk-ico:hover .rk-tip{display:block;}',
'#antivandal-more{position:absolute;right:0;top:32px;background:#fff;border:1px solid #a2a9b1;',
'box-shadow:0 2px 8px rgba(0,0,0,.18);min-width:200px;z-index:50;display:none;padding:4px 0;}',
'#antivandal-more button{display:block;width:100%;text-align:left;border:0;background:none;',
'padding:8px 12px;cursor:pointer;font:13px sans-serif;}',
'#antivandal-more button:hover{background:#eaecf0;}',
'#antivandal-ctx{position:fixed;z-index:1300;background:#fff;border:1px solid #a2a9b1;',
'box-shadow:0 2px 8px rgba(0,0,0,.2);min-width:180px;padding:4px 0;display:none;}',
'#antivandal-ctx button{display:block;width:100%;text-align:left;border:0;background:none;',
'padding:8px 12px;cursor:pointer;font:13px sans-serif;}',
'#antivandal-ctx button:hover{background:#eaecf0;}',
'.rk-modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1100;display:flex;',
'align-items:flex-start;justify-content:center;padding:8vh 16px 16px;}',
'.rk-modal{background:#fff;color:#1b1b1b;max-width:560px;width:100%;border-radius:8px;',
'box-shadow:0 12px 40px rgba(0,0,0,.28);overflow:hidden;}',
'.rk-modal h2{margin:0;padding:12px 16px;background:#1f2a22;color:#f4efe4;font-size:16px;}',
'.rk-modal .rk-body{padding:14px 16px;}',
'.rk-modal label{display:block;font-weight:600;margin:10px 0 4px;}',
'.rk-modal input[type=text],.rk-modal select,.rk-modal textarea{width:100%;box-sizing:border-box;',
'padding:7px 8px;font:14px sans-serif;}',
'.rk-modal textarea{min-height:90px;}',
'.rk-modal .rk-actions{display:flex;justify-content:flex-end;gap:8px;padding:10px 16px;background:#f3f1ea;}',
'.rk-modal .rk-actions button{padding:7px 14px;border-radius:4px;border:1px solid #bbb;cursor:pointer;}',
'.rk-modal .rk-actions .rk-primary{background:#1f2a22;color:#fff;border-color:#1f2a22;}',
'.rk-toast{position:fixed;right:16px;bottom:16px;z-index:1200;background:#1f2a22;color:#fff;',
'padding:10px 14px;border-radius:6px;max-width:360px;display:none;}',
'.rk-toast-err{background:#6b2a22;}',
'.rk-toast-ok{background:#2a4a33;}'
].join('');
var style = document.createElement('style');
style.id = 'antivandal-css';
style.textContent = css;
document.head.appendChild(style);
}
function closeModal() {
var bg = document.getElementById('antivandal-modal-bg');
if (bg) {
bg.parentNode.removeChild(bg);
}
}
function openModal(title, bodyNode, actions) {
closeModal();
var bg = document.createElement('div');
bg.id = 'antivandal-modal-bg';
bg.className = 'rk-modal-bg';
bg.addEventListener('click', function (e) {
if (e.target === bg) {
closeModal();
}
});
var box = document.createElement('div');
box.className = 'rk-modal';
var h = document.createElement('h2');
h.textContent = title;
box.appendChild(h);
var body = document.createElement('div');
body.className = 'rk-body';
body.appendChild(bodyNode);
box.appendChild(body);
var foot = document.createElement('div');
foot.className = 'rk-actions';
(actions || []).forEach(function (a) {
var b = document.createElement('button');
b.type = 'button';
b.textContent = a.label;
if (a.primary) {
b.className = 'rk-primary';
}
b.addEventListener('click', a.onClick);
foot.appendChild(b);
});
box.appendChild(foot);
bg.appendChild(box);
document.body.appendChild(bg);
return box;
}
function field(labelText, node) {
var wrap = document.createElement('div');
var lab = document.createElement('label');
lab.textContent = labelText;
wrap.appendChild(lab);
wrap.appendChild(node);
return wrap;
}
function getLastRev() {
return api.get({
action: 'query',
prop: 'revisions',
titles: pageName(),
rvlimit: 2,
rvprop: 'ids|user|comment|timestamp',
formatversion: 2
}).then(function (data) {
var page = data.query && data.query.pages && data.query.pages[0];
if (!page || page.missing || !page.revisions || !page.revisions.length) {
return null;
}
return {
page: page,
latest: page.revisions[0],
previous: page.revisions[1] || null
};
});
}
function revertWithSummary(summary, userToRevert) {
return getLastRev().then(function (info) {
if (!info) {
throw new Error('Could not read this page history.');
}
var latest = info.latest;
var targetUser = userToRevert || latest.user;
if (prefs.useRollback && hasRollback) {
return api.postWithToken('rollback', {
action: 'rollback',
title: pageName(),
user: targetUser,
summary: summary + ' ([[' + CONF.docsPage + '|AntiVandal]])',
formatversion: 2
}).then(function (res) {
if (res.error) {
throw new Error(res.error.info || res.error.code);
}
return { method: 'rollback', data: res };
});
}
if (!info.previous) {
throw new Error('No earlier revision to restore.');
}
return api.postWithEditToken({
action: 'edit',
title: pageName(),
undo: latest.revid,
undoafter: info.previous.revid,
summary: summary + ' ([[' + CONF.docsPage + '|AntiVandal]])',
formatversion: 2
}).then(function (res) {
if (res.error) {
throw new Error(res.error.info || res.error.code);
}
if (res.edit && res.edit.result !== 'Success') {
throw new Error('Edit did not succeed.');
}
return { method: 'undo', data: res };
});
});
}
function restoreRevision(revid) {
return api.get({
action: 'query',
prop: 'revisions',
revids: revid,
rvprop: 'ids|content|timestamp|user',
rvslots: 'main',
formatversion: 2
}).then(function (data) {
var page = data.query && data.query.pages && data.query.pages[0];
var rev = page && page.revisions && page.revisions[0];
var text = rev && rev.slots && rev.slots.main && rev.slots.main.content;
if (!text && text !== '') {
throw new Error('Could not load that revision.');
}
return api.postWithEditToken({
action: 'edit',
title: page.title || pageName(),
text: text,
summary: 'Restored revision ' + revid + ' ([[' + CONF.docsPage + '|AntiVandal]])',
basetimestamp: undefined,
formatversion: 2
});
});
}
function appendTalkNotice(user, templateName, article, extra) {
var talk = talkTitleFor(user);
var subst = '{{subst:' + templateName;
if (article) {
subst += '|1=' + article.replace(/_/g, ' ');
}
if (extra) {
subst += '|2=' + extra;
}
subst += '}} [[User:WikiDwarf|WikiDwarf]] ([[User talk:WikiDwarf|talk]]) 12:28, 21 September 2026 (IST)';
return api.postWithEditToken({
action: 'edit',
title: talk,
appendtext: '\n\n' + subst,
summary: 'Notice regarding [[' + (article || pageName()).replace(/_/g, ' ') + ']] ([[' + CONF.docsPage + '|AntiVandal]])',
formatversion: 2
});
}
function openRevertDialog() {
getLastRev().then(function (info) {
var user = info && info.latest ? info.latest.user : (relevantUser() || '');
var wrap = document.createElement('div');
var userInput = document.createElement('input');
userInput.type = 'text';
userInput.value = user || '';
var sel = document.createElement('select');
PRESETS.forEach(function (p) {
var o = document.createElement('option');
o.value = p.id;
o.textContent = p.label;
sel.appendChild(o);
});
var sum = document.createElement('input');
sum.type = 'text';
sum.value = PRESETS[0].summary;
sel.addEventListener('change', function () {
var p = PRESETS.filter(function (x) { return x.id === sel.value; })[0];
if (p && p.summary) {
sum.value = p.summary;
}
});
var alsoWarn = document.createElement('input');
alsoWarn.type = 'checkbox';
alsoWarn.id = 'rk-also-warn';
var warnLab = document.createElement('label');
warnLab.htmlFor = 'rk-also-warn';
warnLab.style.fontWeight = 'normal';
warnLab.textContent = ' Open notice dialog after revert';
var row = document.createElement('div');
row.style.marginTop = '10px';
row.appendChild(alsoWarn);
row.appendChild(warnLab);
wrap.appendChild(field('Editor to revert', userInput));
wrap.appendChild(field('Reason', sel));
wrap.appendChild(field('Edit summary', sum));
wrap.appendChild(row);
openModal('Revert with AntiVandal', wrap, [
{ label: 'Cancel', onClick: closeModal },
{
label: 'Revert',
primary: true,
onClick: function () {
var summary = (sum.value || 'Reverted edit').trim();
var who = userInput.value.trim();
if (!who) {
toast('Name the editor whose edit you are reverting.', 'err');
return;
}
toast('Reverting…');
revertWithSummary(summary, who).then(function (res) {
toast('Reverted with ' + res.method + '.', 'ok');
closeModal();
if (alsoWarn.checked) {
openWarnDialog(who, sel.value);
} else {
setTimeout(function () { location.reload(); }, 700);
}
}).catch(function (err) {
toast(err.message || String(err), 'err');
});
}
}
]);
}).catch(function (err) {
toast(err.message || String(err), 'err');
});
}
function openWarnDialog(prefillUser, presetId) {
var wrap = document.createElement('div');
var userInput = document.createElement('input');
userInput.type = 'text';
userInput.value = prefillUser || relevantUser() || '';
var articleInput = document.createElement('input');
articleInput.type = 'text';
articleInput.value = pageName().replace(/_/g, ' ');
var sel = document.createElement('select');
NOTICES.forEach(function (n) {
var o = document.createElement('option');
o.value = n.id;
o.textContent = n.label + (n.leveled ? ' (1–4)' : '');
sel.appendChild(o);
});
if (presetId) {
var mapped = {
vandalism: 'uw-vandalism',
blanking: 'uw-delete',
test: 'uw-test',
unsourced: 'uw-unsourced',
spam: 'uw-spam',
npov: 'uw-biog'
}[presetId];
if (mapped) {
sel.value = mapped;
}
}
var level = document.createElement('select');
[1, 2, 3, 4].forEach(function (n) {
var o = document.createElement('option');
o.value = String(n);
o.textContent = 'Level ' + n;
level.appendChild(o);
});
var extra = document.createElement('textarea');
extra.placeholder = 'Optional extra sentence for templates that accept a second parameter';
function currentTemplate() {
var meta = NOTICES.filter(function (n) { return n.id === sel.value; })[0];
if (meta && meta.leveled) {
return sel.value + level.value;
}
return sel.value;
}
wrap.appendChild(field('User', userInput));
wrap.appendChild(field('Related article', articleInput));
wrap.appendChild(field('Notice', sel));
wrap.appendChild(field('Level (if the template has levels)', level));
wrap.appendChild(field('Extra note', extra));
openModal('Post a talk-page notice', wrap, [
{ label: 'Cancel', onClick: closeModal },
{
label: 'Post notice',
primary: true,
onClick: function () {
var user = userInput.value.trim();
if (!user) {
toast('Enter a username or IP.', 'err');
return;
}
var tpl = currentTemplate();
toast('Posting {{' + tpl + '}}…');
appendTalkNotice(user, tpl, articleInput.value.trim(), extra.value.trim()).then(function (res) {
if (res.error) {
throw new Error(res.error.info || res.error.code);
}
toast('Notice posted on User talk:' + user, 'ok');
closeModal();
}).catch(function (err) {
toast(err.message || String(err), 'err');
});
}
}
]);
}
function openPrefs() {
var wrap = document.createElement('div');
var pos = document.createElement('select');
['top', 'bottom'].forEach(function (v) {
var o = document.createElement('option');
o.value = v;
o.textContent = v === 'top' ? 'Under the page title' : 'Stick to the bottom';
pos.appendChild(o);
});
pos.value = prefs.barPosition;
var rb = document.createElement('input');
rb.type = 'checkbox';
rb.checked = !!prefs.useRollback;
var rbLab = document.createElement('label');
rbLab.style.fontWeight = 'normal';
rbLab.textContent = ' Prefer software rollback when this account has the right';
var row = document.createElement('div');
row.appendChild(rb);
row.appendChild(rbLab);
wrap.appendChild(field('Toolbar placement', pos));
wrap.appendChild(row);
openModal('AntiVandal preferences', wrap, [
{ label: 'Close', onClick: closeModal },
{
label: 'Save',
primary: true,
onClick: function () {
prefs.barPosition = pos.value;
prefs.useRollback = rb.checked;
savePrefs(prefs);
closeModal();
toast('Preferences saved. Reload to move the bar.', 'ok');
}
}
]);
}
function goLatest() {
location.href = mw.util.getUrl(pageName(), { diff: 'cur', oldid: 'prev' });
}
function reportAiv() {
getLastRev().then(function (info) {
var user = (info && info.latest && info.latest.user) || relevantUser() || '';
var wrap = document.createElement('div');
var p = document.createElement('p');
p.textContent = 'This opens the AIV noticeboard. Paste the suggested line, or edit it first.';
var ta = document.createElement('textarea');
ta.value = '* {{vandal|' + user + '}} – repeated unconstructive editing on [[' +
pageName().replace(/_/g, ' ') + ']]. [[User:WikiDwarf|WikiDwarf]] ([[User talk:WikiDwarf|talk]]) 12:28, 21 September 2026 (IST)';
wrap.appendChild(p);
wrap.appendChild(ta);
openModal('Report at AIV', wrap, [
{ label: 'Cancel', onClick: closeModal },
{
label: 'Open AIV',
primary: true,
onClick: function () {
try {
sessionStorage.setItem('antivandal-aiv-draft', ta.value);
} catch (e) { /* ignore */ }
window.open(mw.util.getUrl(CONF.aivPage, { action: 'edit', section: 'new' }), '_blank');
}
}
]);
});
}
function requestProtection() {
window.open(mw.util.getUrl(CONF.rfppPage, { action: 'edit', section: 'new' }), '_blank');
}
function openNewMessage(prefillUser) {
var wrap = document.createElement('div');
var userInput = document.createElement('input');
userInput.type = 'text';
userInput.value = prefillUser || relevantUser() || '';
var subj = document.createElement('input');
subj.type = 'text';
var body = document.createElement('textarea');
body.placeholder = 'Wikitext of the new section';
wrap.appendChild(field('User', userInput));
wrap.appendChild(field('Subject', subj));
wrap.appendChild(field('Message', body));
openModal('New talk-page message', wrap, [
{ label: 'Cancel', onClick: closeModal },
{
label: 'Send',
primary: true,
onClick: function () {
var user = userInput.value.trim();
if (!user || !subj.value.trim()) {
toast('Need a user and a subject.', 'err');
return;
}
var text = '\n\n== ' + subj.value.trim() + ' ==\n' + body.value + '\n[[User:WikiDwarf|WikiDwarf]] ([[User talk:WikiDwarf|talk]]) 12:28, 21 September 2026 (IST)';
toast('Sending…');
api.postWithEditToken({
action: 'edit',
title: talkTitleFor(user),
appendtext: text,
summary: 'New message ([[' + CONF.docsPage + '|AntiVandal]])',
formatversion: 2
}).then(function (res) {
if (res.error) {
throw new Error(res.error.info || res.error.code);
}
toast('Message posted.', 'ok');
closeModal();
}).catch(function (err) {
toast(err.message || String(err), 'err');
});
}
}
]);
}
var alertTimer = null;
var alertLastRev = null;
function toggleAlert(btn) {
if (alertTimer) {
clearInterval(alertTimer);
alertTimer = null;
btn.classList.remove('rk-on');
toast('Alert on change off.');
return;
}
btn.classList.add('rk-on');
toast('Watching this page for new edits.');
getLastRev().then(function (info) {
alertLastRev = info && info.latest ? info.latest.revid : null;
});
alertTimer = setInterval(function () {
getLastRev().then(function (info) {
var id = info && info.latest ? info.latest.revid : null;
if (id && alertLastRev && id !== alertLastRev) {
alertLastRev = id;
if (window.Notification && Notification.permission === 'granted') {
new Notification('AntiVandal', { body: pageName().replace(/_/g, ' ') + ' was edited.' });
}
toast('New edit on this page.', 'ok');
goLatest();
} else if (id) {
alertLastRev = id;
}
});
}, 12000);
if (window.Notification && Notification.permission === 'default') {
Notification.requestPermission();
}
}
function openMoreMenu(anchor) {
var menu = document.getElementById('antivandal-more');
if (!menu) {
return;
}
menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
}
function bindUserContextMenu() {
document.addEventListener('contextmenu', function (e) {
if (!e.shiftKey) {
return;
}
var a = e.target.closest ? e.target.closest('a') : null;
if (!a || !a.href) {
return;
}
var m = a.href.match(/[?&]title=User:([^&#]+)/i) || a.href.match(/\/wiki\/User:([^?#]+)/);
if (!m) {
return;
}
e.preventDefault();
var user = decodeURIComponent(m[1]).replace(/_/g, ' ').split('/')[0];
var ctx = document.getElementById('antivandal-ctx');
if (!ctx) {
ctx = document.createElement('div');
ctx.id = 'antivandal-ctx';
document.body.appendChild(ctx);
}
ctx.innerHTML = '';
function item(label, fn) {
var b = document.createElement('button');
b.type = 'button';
b.textContent = label;
b.addEventListener('click', function () {
ctx.style.display = 'none';
fn();
});
ctx.appendChild(b);
}
item('User page', function () { location.href = mw.util.getUrl('User:' + user); });
item('Talk page', function () { location.href = mw.util.getUrl('User talk:' + user); });
item('Contributions', function () { location.href = mw.util.getUrl('Special:Contributions/' + user); });
item('New message', function () { openNewMessage(user); });
item('Notice', function () { openWarnDialog(user); });
ctx.style.left = e.pageX + 'px';
ctx.style.top = e.pageY + 'px';
ctx.style.display = 'block';
});
document.addEventListener('click', function () {
var ctx = document.getElementById('antivandal-ctx');
if (ctx) {
ctx.style.display = 'none';
}
var more = document.getElementById('antivandal-more');
if (more && more.style.display === 'block') {
more.style.display = 'none';
}
});
}
function addDiffRestore() {
if (mw.config.get('wgDiffNewId') || mw.config.get('wgDiffOldId') ||
mw.config.get('wgAction') === 'history') {
var oldid = mw.util.getParamValue('oldid') || mw.config.get('wgRevisionId');
if (!oldid) {
return;
}
var bar = document.getElementById('antivandal-bar');
if (!bar) {
return;
}
var b = document.createElement('button');
b.type = 'button';
b.textContent = 'Restore this revision';
b.addEventListener('click', function () {
if (!window.confirm('Replace the current page text with revision ' + oldid + '?')) {
return;
}
toast('Restoring…');
restoreRevision(oldid).then(function (res) {
if (res.error) {
throw new Error(res.error.info || res.error.code);
}
toast('Revision restored.', 'ok');
setTimeout(function () { location.href = mw.util.getUrl(pageName()); }, 600);
}).catch(function (err) {
toast(err.message || String(err), 'err');
});
});
bar.appendChild(b);
}
}
function buildBar() {
if (document.getElementById('antivandal-bar')) {
return;
}
if (mw.config.get('wgCanonicalNamespace') === 'Special') {
return;
}
var bar = document.createElement('div');
bar.id = 'antivandal-bar';
function ico(symbol, tip, fn) {
var b = document.createElement('button');
b.type = 'button';
b.className = 'rk-ico';
b.setAttribute('aria-label', tip);
b.appendChild(document.createTextNode(symbol));
var t = document.createElement('span');
t.className = 'rk-tip';
t.textContent = tip;
b.appendChild(t);
b.addEventListener('click', function (e) {
e.stopPropagation();
fn(b);
});
bar.appendChild(b);
return b;
}
ico('↶', 'Revert last editor', openRevertDialog);
ico('!', 'Post a notice', function () { openWarnDialog(); });
ico('✉', 'New talk-page message', function () { openNewMessage(); });
ico('⟳', 'Latest revision', goLatest);
var alertBtn = ico('◎', 'Alert on change', function () { toggleAlert(alertBtn); });
ico('⛨', 'Request protection', requestProtection);
var moreWrap = document.createElement('span');
moreWrap.style.position = 'relative';
var moreBtn = document.createElement('button');
moreBtn.type = 'button';
moreBtn.className = 'rk-ico';
moreBtn.setAttribute('aria-label', 'More');
moreBtn.appendChild(document.createTextNode('⋮'));
var moreTip = document.createElement('span');
moreTip.className = 'rk-tip';
moreTip.textContent = 'More';
moreBtn.appendChild(moreTip);
var menu = document.createElement('div');
menu.id = 'antivandal-more';
function menuItem(label, fn) {
var mb = document.createElement('button');
mb.type = 'button';
mb.textContent = label;
mb.addEventListener('click', function (e) {
e.stopPropagation();
menu.style.display = 'none';
fn();
});
menu.appendChild(mb);
}
menuItem('Report at AIV', reportAiv);
menuItem('Restore this revision', function () {
var oldid = mw.util.getParamValue('oldid') || mw.config.get('wgRevisionId');
if (!oldid) {
toast('Open a specific revision first.', 'err');
return;
}
if (!window.confirm('Replace the current page text with revision ' + oldid + '?')) {
return;
}
toast('Restoring…');
restoreRevision(oldid).then(function (res) {
if (res.error) {
throw new Error(res.error.info || res.error.code);
}
toast('Revision restored.', 'ok');
setTimeout(function () { location.href = mw.util.getUrl(pageName()); }, 600);
}).catch(function (err) {
toast(err.message || String(err), 'err');
});
});
menuItem('Preferences', openPrefs);
moreBtn.addEventListener('click', function (e) {
e.stopPropagation();
menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
});
moreWrap.appendChild(moreBtn);
moreWrap.appendChild(menu);
bar.appendChild(moreWrap);
var mount =
document.querySelector('.page-actions') ||
document.querySelector('.citizen-page-heading__actions') ||
document.querySelector('.vector-page-toolbar-container') ||
document.querySelector('#p-views') ||
null;
var heading = document.getElementById('firstHeading');
if (mount) {
mount.appendChild(bar);
} else if (heading && heading.parentNode) {
heading.parentNode.insertBefore(bar, heading.nextSibling);
} else {
var content = document.getElementById('mw-content-text') || document.getElementById('content');
if (content) {
content.parentNode.insertBefore(bar, content);
} else {
document.body.insertBefore(bar, document.body.firstChild);
}
}
bindUserContextMenu();
}
function init() {
if (!mw.config.get('wgUserName')) {
return;
}
injectCss();
api = new mw.Api({ parameters: { formatversion: 2 } });
var rights = mw.config.get('wgUserGroups') || [];
hasRollback = rights.indexOf('rollbacker') !== -1 ||
rights.indexOf('sysop') !== -1 ||
rights.indexOf('steward') !== -1;
buildBar();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(init);
});
} else {
mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(init);
}
}());