User:Peterxy12/AdvancedRollback/core.js: Difference between revisions
Jump to navigation
Jump to search
// Edited via InPageEdit |
m Peterxy12 moved page User:1F616EMO/AdvancedRollback/core.js to User:Peterxy12/AdvancedRollback/core.js without leaving a redirect |
| (One intermediate revision by the same user not shown) | |
(No difference)
| |
Latest revision as of 06:13, 22 February 2026
/*
* Advanced Rollback script for rollbackers
*
* This script asks the user to supply an optional rollback summary.
* After the rollback is complete, the user is taken to the diff page.
* The diff page will open in a new window if the user is on Special:RecentChanges.
*
* Do not load this script directly. Load a localization script instead.
* e.g. [[:zh:User:1F616EMO/AdvancedRollback/zh.js]].
*
* Use the following line to load this script:
* mw.loader.load( "https://zh.wikipedia.org/w/index.php?title=User:1F616EMO/AdvancedRollback/core.js&action=raw&ctype=text/javascript" );
*
* This script is made by 1F616EMO on zhwiki, licensed under CC BY-SA 4.0.
*
* 225-02-16 forked from https://zh.wikipedia.org/wiki/MediaWiki:Gadget-rollback-summary.js
*
*/
// <nowiki>
"use strict";
$.when(mw.loader.using([
'mediawiki.api',
'mediawiki.jqueryMsg',
'oojs-ui-core',
'oojs-ui-windows',
]), $.ready)
.then(() => new mw.Api({
userAgent: 'AdvancedRollback/1.0.0'
}).loadMessagesIfMissing([
'rollbacklinkcount',
'rollbacklinkcount-morethan',
'rollback',
'colon-separator',
'cancel',
'summary-preview',
]))
.then(() => {
const api = new mw.Api({
userAgent: 'AdvancedRollback/1.0.0'
});
const myname = mw.config.get('wgUserName');
const windowManager = new OO.ui.WindowManager();
$(document.body).append(windowManager.$element);
const rvlimit = Math.min((window.AdvancedRollBackRevisionLimit || 20) + 1, 500);
const rollbackTag = mw.message('advanced-rollback-tag').plain();
let diffMode = window.AdvancedRollBackDiffMode || 'default';
let ipe_quickdiff = null;
if (diffMode === 'inpageedit') {
ipe_quickdiff =
(window.InPageEdit && window.InPageEdit.quickDiff) ||
(window.ipe && window.ipe.quickDiff.comparePages && window.ipe.quickDiff.comparePages.bind(window.ipe.quickDiff)) || null;
if (!ipe_quickdiff) {
console.warn('InPageEdit is not available, fallback to default diff display mode.');
diffMode = 'default';
}
}
/* Rollback helpers */
async function rollbackUserEdits(params) {
const { title, user, summary } = params;
if (!title) {
throw new Error('Invalid parameters for rollbackUserEdits');
}
try {
const responce = await api.postWithToken('rollback', {
action: 'rollback',
title: title,
user: user,
summary: summary,
});
return {
success: true,
diff: responce.rollback.revid,
oldid: responce.rollback.old_revid,
}
} catch (error) {
console.error('Rollback failed:', error);
return {
success: false,
error: error,
};
}
}
async function undoEdits(params) {
const { title, undo, undoafter, summary } = params;
if (!title || !undo) {
throw new Error('Invalid parameters for undoEdits');
}
try {
const response = await api.postWithEditToken({
action: 'edit',
title: title,
baserevid: undo,
undo: undo,
undoafter: undoafter,
summary: summary,
});
if (response.edit && response.edit.nochange === "") {
return {
success: false,
error: 'nochange',
};
}
return {
success: true,
diff: response.edit.newrevid,
oldid: response.edit.oldrevid,
};
} catch (error) {
console.error('Undo failed:', error);
return {
success: false,
error: error,
};
}
}
/* Dialog Definition */
const rollbackDialog = window.AdvancedRollbackDialog = function (config) {
rollbackDialog.super.call(this, config);
}
OO.inheritClass(rollbackDialog, OO.ui.ProcessDialog);
rollbackDialog.static.name = 'rollbackDialog';
rollbackDialog.static.title = mw.message('rollback').plain();
rollbackDialog.static.actions = [
{
action: 'rollback',
label: mw.message('rollback').plain(),
flags: ['primary', 'progressive'],
},
{
flags: 'safe',
label: mw.message('cancel').plain(),
flags: ['safe', 'close']
}
];
rollbackDialog.prototype.initialize = function () {
rollbackDialog.super.prototype.initialize.apply(this, arguments);
this.panel = new OO.ui.PanelLayout({
padded: true,
expanded: false
});
const fieldset = this.content = new OO.ui.FieldsetLayout({
// label: () => mw.message('rollback-fieldset-label', this.data ? this.data.from : 'Example').parseDom(),
});
const menuItems = [];
menuItems.push({
data: 'custom',
label: mw.message('rollback-summary-custom').plain(),
});
window.AdvancedRollbackSummaryPresets = window.AdvancedRollbackSummaryPresets || {};
const OptionGroups = {};
for (const key in window.AdvancedRollbackSummaryPresets) {
if (typeof window.AdvancedRollbackSummaryPresets[key] === 'string') {
window.AdvancedRollbackSummaryPresets[key] = {
description: window.AdvancedRollbackSummaryPresets[key],
text: window.AdvancedRollbackSummaryPresets[key],
}
}
if (window.AdvancedRollbackSummaryPresets[key].optgroup) {
if (typeof window.AdvancedRollbackSummaryPresets[key].items !== 'object') {
console.warn('Invalid AdvancedRollbackSummaryPresets Option Group: ',
key, window.AdvancedRollbackSummaryPresets[key].optgroup);
continue;
}
if (typeof window.AdvancedRollbackSummaryPresets[key].optgroup !== 'string') {
window.AdvancedRollbackSummaryPresets[key].optgroup = key;
}
OptionGroups[key] = window.AdvancedRollbackSummaryPresets[key];
delete window.AdvancedRollbackSummaryPresets[key];
} else {
menuItems.push({
data: key,
label: window.AdvancedRollbackSummaryPresets[key].description,
});
}
}
// Due to how OOUI works, create option groups later here
for (const key in OptionGroups) {
menuItems.push({
optgroup: OptionGroups[key].optgroup,
});
const items = OptionGroups[key].items;
for (const itemKey in items) {
if (typeof items[itemKey] === 'string') {
items[itemKey] = {
description: items[itemKey],
text: items[itemKey],
};
}
window.AdvancedRollbackSummaryPresets[key + "-" + itemKey] = items[itemKey];
menuItems.push({
data: key + "-" + itemKey,
label: items[itemKey].description,
});
}
}
this.editWarWarningWidget = new OO.ui.MessageWidget({
type: 'warning',
label: mw.message('rollback-warn-3rr').parseDom(),
});
$(this.editWarWarningWidget.$element).css('margin-bottom', '12px');
// (1) Select intention (default: unspecified)
// Note: Not AGF cuz rollback is usually for combatting vandalism
// embarrasing if AGF'ed
// (2) Choose from summary presets (first element: custom -> blankern input)
// (3) Input custom summary
this.summaryDropdown = new OO.ui.DropdownInputWidget({
required: true,
value: 'custom',
options: menuItems,
});
// this.summaryDropdown.dropdownWidget.menu.getClosestScrollableElementContainer = () => $(document.body);
this.summaryInput = new OO.ui.MultilineTextInputWidget({
placeholder: mw.message('rollback-summary-prompt').plain(),
rows: 5,
allowLinebreaks: false,
});
this.useRollbackCheckbox = new OO.ui.CheckboxInputWidget({
selected: false,
});
this.hideUserNameCheckbox = new OO.ui.CheckboxInputWidget({
selected: false,
});
this.showTalkPageCheckbox = new OO.ui.CheckboxInputWidget({
selected: false,
});
this.intentionSelection = new OO.ui.ButtonSelectWidget({
items: [
new OO.ui.ButtonOptionWidget({
data: 'unspecified',
label: mw.message('rollback-intention-unspecified').plain(),
}).setSelected(true),
new OO.ui.ButtonOptionWidget({
icon: 'success',
data: 'good',
label: mw.message('rollback-intention-good').plain(),
}),
new OO.ui.ButtonOptionWidget({
icon: 'clear',
data: 'vandalism',
label: mw.message('rollback-intention-vandalism').plain(),
}),
],
});
this.summaryPreviewSpan = $('<span id="advancedrollback-summary-preview">');
const summaryPreviewShell = $('<span id="advancedrollback-summary-preview-shell">')
.append(mw.message('summary-preview').parseDom())
.append(this.summaryPreviewSpan);
fieldset.addItems([
new OO.ui.FieldLayout(this.intentionSelection, {
align: 'top',
}),
new OO.ui.FieldLayout(this.useRollbackCheckbox, {
label: mw.message('rollback-use-rollback').plain(),
align: 'inline',
}),
new OO.ui.FieldLayout(this.hideUserNameCheckbox, {
label: mw.message('rollback-summary-hide-user').plain(),
align: 'inline',
}),
new OO.ui.FieldLayout(this.showTalkPageCheckbox, {
label: mw.message('rollback-summary-show-talk-page').plain(),
align: 'inline',
}),
new OO.ui.FieldLayout(this.summaryDropdown, {
label: mw.message('rollback-summary-presets').plain(),
align: 'top',
}),
new OO.ui.FieldLayout(this.summaryInput, {
label: mw.message('rollback-summary-custom').plain(),
align: 'top',
}),
]);
this.panel.$element.append(this.editWarWarningWidget.$element);
this.panel.$element.append(fieldset.$element);
this.panel.$element.append(summaryPreviewShell);
this.$body.append(this.panel.$element);
this.summaryDropdown.on('change', this.onSummaryDropdownChange.bind(this));
this.summaryInput.on('change', this.onSummaryInputChange.bind(this));
};
rollbackDialog.prototype.getSetupProcess = function (data) {
data = data || {};
this.setData(data);
return rollbackDialog.super.prototype.getSetupProcess.call(this, data)
.next(() => {
this.intentionSelection.selectItemByData('unspecified');
this.summaryDropdown.setValue('custom');
this.summaryInput.setValue('');
if (!data.undo || !data.undoafter || !data.token) {
this.useRollbackCheckbox.setDisabled(true);
this.useRollbackCheckbox.setSelected(data.token ? true : false);
} else {
this.useRollbackCheckbox.setDisabled(false);
this.useRollbackCheckbox.setSelected(false);
}
this.hideUserNameCheckbox.setDisabled(!data.from);
this.showTalkPageCheckbox.setDisabled(!data.from);
this.hideUserNameCheckbox.setSelected(false);
this.showTalkPageCheckbox.setSelected(false);
if (data.warn3rr) {
this.editWarWarningWidget.toggle(true);
} else {
this.editWarWarningWidget.toggle(false);
}
});
};
rollbackDialog.prototype.onSummaryDropdownChange = function (value) {
if (value === 'custom') {
this.summaryInput.setValue('');
} else {
this.summaryInput.setValue(window.AdvancedRollbackSummaryPresets[value].text || '');
}
this.updateSummaryPreview();
};
rollbackDialog.prototype.updateSummaryPreview = function () {
delete this.updateSummaryPreviewTimeout;
const data = this.data;
const summary = this.summaryInput.getValue();
if (this.lastHandledSummary === summary)
return;
this.lastHandledSummary = summary;
this.summaryPreviewSpan.text('[...]');
api.get({
action: 'parse',
title: data.pagetitle,
text: '',
summary: summary,
}).then((rtnData) => {
console.log(rtnData);
const parsedHTML = rtnData.parse.parsedsummary["*"];
this.summaryPreviewSpan.html(parsedHTML);
this.updateSize();
});
};
rollbackDialog.prototype.onSummaryInputChange = function () {
if (this.updateSummaryPreviewTimeout)
clearTimeout(this.updateSummaryPreviewTimeout);
this.summaryPreviewSpan.text('[...]');
this.updateSummaryPreviewTimeout = setTimeout(this.updateSummaryPreview.bind(this), 500);
};
rollbackDialog.prototype.getActionProcess = function (action) {
if (action === 'rollback') {
const data = this.data;
const from = data.from;
const $this = data.elem;
if ($this) {
$this.addClass('advancedrollback-clicked');
$this.text(mw.message('rollback-processing'));
}
const summaryInput = this.summaryInput.getValue().trim();
const intentionItem = this.intentionSelection.findSelectedItem();
const intention = intentionItem ? intentionItem.data : 'unspecified';
const fromString = this.hideUserNameCheckbox.isSelected()
? mw.message('rollback-summary-inappropriate-user-string').plain() : (from
? mw.message('rollback-summary-user-string', from).plain()
: mw.message('rollback-summary-revdel-user-string').plain());
const useRollback = this.useRollbackCheckbox.isSelected();
const showTalkPage = this.showTalkPageCheckbox.isSelected();
let summaryTypePrefix;
let summaryParameters;
if (data.type === 'rollback') {
summaryTypePrefix = 'rollback-summary-';
summaryParameters = [fromString];
} else if (data.type === 'undo') {
summaryTypePrefix = 'rollback-summary-undo-';
summaryParameters = [data.undo, fromString];
} else if (data.type === 'undoseries') {
summaryTypePrefix = 'rollback-summary-undoseries-';
summaryParameters = [data.undo, data.undoafter];
} else {
throw new Error('Invalid rollback dialog type: ' + data.type);
}
let summary = mw.message(summaryTypePrefix + intention, ...summaryParameters).plain();
if (summaryInput !== '') {
summary += mw.message('colon-separator').plain() + summaryInput;
}
summary += " " + rollbackTag;
return rollbackDialog.super.prototype.getActionProcess.call(this, action)
.next(async function () {
let responce;
if (useRollback) {
responce = await rollbackUserEdits({
title: data.pagetitle,
user: from,
summary: summary,
});
} else {
responce = await undoEdits({
title: data.pagetitle,
undo: data.undo,
undoafter: data.undoafter,
summary: summary,
});
}
if (responce.success) {
if ($this)
$this.text(mw.message('rollback-done'));
/* Show user talk page and Twinkle integration */
if (showTalkPage) {
const url = mw.util.getUrl('User talk:' + from, {
'vanarticle': data.pagetitle,
});
window.open(url);
}
/* Display diff page */
let thisDiffMode = diffMode;
if (diffMode === 'inpageedit') {
ipe_quickdiff({
fromrev: responce.oldid,
torev: responce.diff,
});
} else if (diffMode === 'default') {
if (mw.config.get('wgCanonicalSpecialPageName') === 'Recentchanges'
|| mw.config.get('wgCanonicalSpecialPageName') === 'Watchlist') {
thisDiffMode = 'newtab';
} else {
thisDiffMode = 'this';
}
}
const diff_page = mw.util.getUrl('Special:Diff/' + responce.oldid + '/' + responce.diff);
if (thisDiffMode === 'newtab') {
window.open(diff_page);
} else if (thisDiffMode === 'this') {
window.location.href = diff_page;
}
} else {
console.warn("Rollback failed: ", responce.error);
if ($this)
$this.text(mw.message('rollback-failed', responce.error));
}
})
.next(() => this.close());
} else {
return rollbackDialog.super.prototype.getActionProcess.call(this, action);
}
};
let doRollbackDialog;
const openRollbackDialog = window.AdvancedRollbackOpenDialog = function (params) {
if (!doRollbackDialog) {
window.AdvancedRollbackDialogInstance = doRollbackDialog = new rollbackDialog();
windowManager.addWindows([doRollbackDialog]);
}
windowManager.openWindow(doRollbackDialog, params);
};
const onRollbackClick = function (e) {
e.preventDefault();
const $this = $(this);
if ($this.hasClass('advancedrollback-clicked'))
return;
const title = $this.attr('data-ar-title');
const from = $this.attr('data-ar-from');
const token = $this.attr('data-ar-token');
const action = $this.attr('data-ar-action') || 'rollback';
const undo = $this.attr('data-ar-undo');
const undoafter = $this.attr('data-ar-undoafter');
const warn3rr = $this.attr('data-warn-3rr') == '1';
if (!(title)) {
$this.text(mw.message('rollback-failed', mw.message('rolback-failed-href-error')));
return;
}
openRollbackDialog({
type: action,
title: mw.message('rollback-window-title', title, from).parseDom(),
pagetitle: title,
from: from,
token: token,
elem: $this,
undo: undo,
undoafter: undoafter,
warn3rr: warn3rr,
});
};
mw.util.addCSS('.mw-rollback-link .advancedrollback-processed { color: var(--color-subtle, #54595d) !important; }');
const createRollbackLink = function ($container, title, from, token) {
if (mw.util.isIPAddress(from)) {
from = mw.util.sanitizeIP(from);
}
let $rollbacklink = $container.find('a');
if ($rollbacklink.length === 0) {
$rollbacklink = $('<a>')
.addClass('mw-rollback-link advancedrollback-created')
.attr('href', '#')
.text(mw.message('rollback').plain())
.appendTo($container);
} else {
if ($rollbacklink.hasClass('advancedrollback-processed'))
return;
const href = $rollbacklink.attr('href');
token = mw.util.getParamValue('token', href);
}
$rollbacklink
.addClass('advancedrollback-processed')
.off('click')
.on('click', onRollbackClick);
$rollbacklink.attr('data-ar-title', title);
$rollbacklink.attr('data-ar-from', from != '' ? from : null);
$rollbacklink.attr('data-ar-token', token);
$rollbacklink.attr('data-ar-action', 'rollback');
const yesterday = new Date(Date.now() - 86400000);
api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'user|ids|tags|timestamp',
rvlimit: rvlimit,
}).then((data) => {
const page = Object.values(data.query.pages)[0];
if (!page)
return;
let edit_count = 0;
let undo = 0;
let undoafter = 0;
let revert_count = 0;
for (const rev of page.revisions) {
const user = mw.util.isIPAddress(rev.user) ? mw.util.sanitizeIP(rev.user) : rev.user;
if (undo === 0) {
undo = rev.revid;
}
if ((user !== from || rev.userhidden) && undoafter === 0) {
undoafter = rev.revid;
}
if (undoafter === 0) {
edit_count++;
}
if (
user == myname &&
new Date(rev.timestamp) > yesterday &&
rev.tags &&
(
rev.tags.includes('mw-undo') ||
rev.tags.includes('mw-manual-revert') ||
rev.tags.includes('mw-rollback')
)
) {
revert_count++;
}
}
if (
undo === undoafter || (
edit_count >= page.revisions.length &&
(
!token ||
page.revisions[page.revisions.length - 1].parentid === 0
)
)
) {
const $parent = $container.parent();
if ($parent.children().length <= 1) {
$parent.remove();
} else {
$container.remove();
}
return;
}
if (revert_count >= 3) {
$rollbacklink.attr('data-warn-3rr', 1);
}
$rollbacklink.attr('data-ar-undo', undo);
$rollbacklink.attr('data-ar-undoafter', undoafter);
if (!window.AdvancedRollbackNoCountRevision) {
if (edit_count >= rvlimit)
$rollbacklink.text(mw.message('rollbacklinkcount-morethan', edit_count - 1));
else
$rollbacklink.text(mw.message('rollbacklinkcount', edit_count));
}
})
}
const process = function ($content) {
// For rolling back changes made by the same user in a row
// We cannot assume .mw-rollback-link a is always present
// Where are we?
const SpecialPageName = mw.config.get('wgCanonicalSpecialPageName');
if (['Recentchanges', 'Watchlist'].includes(SpecialPageName)) {
let selector =
'.mw-changeslist-line.mw-changeslist-edit.mw-changeslist-last .mw-changeslist-line-inner' +
', .mw-changeslist-line.mw-changeslist-edit .mw-changeslist-last td.mw-enhanced-rc-nested';
const $lastChange = $content.find(selector);
$lastChange.each(function () {
const $this = $(this);
const title = $this.attr('data-target-page');
const from = $this.find('.mw-userlink bdi').text();
let $pagertools = $this.find('.mw-changeslist-links.mw-pager-tools');
if ($pagertools.length === 0) {
let $insertpoint = $this.find('.comment');
if ($insertpoint.length === 0)
$insertpoint = $this.find('.mw-changeslist-links.mw-usertoollinks');
$pagertools = $('<span class="mw-changeslist-links mw-pager-tools">')
.insertAfter($insertpoint);
$pagertools.before(' ');
}
let $rollbacklinkcontainer = $pagertools.find('.mw-rollback-link');
if ($rollbacklinkcontainer.length === 0) {
$rollbacklinkcontainer = $('<span class="mw-rollback-link">')
.appendTo($('<span>').prependTo($pagertools));
}
createRollbackLink($rollbacklinkcontainer, title, from);
});
$content.find('.mw-changeslist-groupdiff:not(.advancedrollback-processed)').each(function () {
const $this = $(this);
const title = $this.attr('title');
const href = $this.attr('href');
const undo = mw.util.getParamValue('diff', href);
const undoafter = mw.util.getParamValue('oldid', href);
$("<a>")
.addClass('rollback-summary-undoseries-groupdiff-button')
.attr('href', 'javascript:void(0);')
.text(mw.message('rollback-summary-undoseries-groupdiff-button').plain())
.click(function (e) {
e.preventDefault();
openRollbackDialog({
type: 'undoseries',
title: mw.message('rollback-window-title-nofrom', title).parseDom(),
pagetitle: title,
elem: $(this),
from: null,
undo: undo,
undoafter: undoafter,
});
})
.appendTo($("<span>").insertAfter($this.parent()))
$this.addClass('advancedrollback-processed');
});
} else if (SpecialPageName === 'Contributions') {
$(".mw-contributions-list .mw-contributions-current").each(function () {
const $this = $(this);
const title = $(this).find("bdi .mw-contributions-title").text();
const from = mw.config.get('wgRelevantUserName');
let $pagertools = $this.find('.mw-changeslist-links.mw-pager-tools');
if ($pagertools.length === 0) {
let $insertpoint = $this.find('.mw-uctop');
$pagertools = $('<span class="mw-changeslist-links mw-pager-tools">')
.insertAfter($insertpoint);
$pagertools.before(' ');
}
let $rollbacklinkcontainer = $pagertools.find('.mw-rollback-link');
if ($rollbacklinkcontainer.length === 0) {
$rollbacklinkcontainer = $('<span class="mw-rollback-link">')
.appendTo($('<span>').prependTo($pagertools));
}
createRollbackLink($rollbacklinkcontainer, title, from);
});
} else if (mw.config.get('wgAction') === 'history') {
// We are in history page, where the first .mw-contributions-list li *may* be the last change
// We have to check it against wgCurRevisionId
const $lastChange = $content.find('.mw-contributions-list li:first');
if ($lastChange.length <= 0)
return;
const revid = $lastChange.attr('data-mw-revid');
if (revid != mw.config.get('wgCurRevisionId'))
return;
// We don't have ot worry about the existance of .mw-pager-tools, as "revert" exists
const $pagertools = $lastChange.find('.mw-changeslist-links.mw-pager-tools');
let $rollbacklinkcontainer = $pagertools.find('.mw-rollback-link');
if ($rollbacklinkcontainer.length === 0) {
$rollbacklinkcontainer = $('<span class="mw-rollback-link">')
.appendTo($('<span>').prependTo($pagertools));
}
const title = mw.config.get('wgPageName');
const from = $lastChange.find('.history-user .mw-userlink bdi').text();
createRollbackLink($rollbacklinkcontainer, title, from);
// Handle range revert links
if (
$content.find('.mw-history-compareselectedversions .historysubmit').length > 0 &&
$content.find(".advancedrollback-undoseries-button").length === 0
) {
const compareForm = $content.find("#mw-history-compare")[0];
$("<button>")
.text(mw.message('rollback-summary-undoseries-button').plain())
.addClass("cdx-button advancedrollback-undoseries-button")
.click(function (e) {
const $this = $(this);
e.preventDefault();
const undo = compareForm.diff ? compareForm.diff.value : null;
const undoafter = compareForm.oldid ? compareForm.oldid.value : null;
if (undo && undoafter) {
openRollbackDialog({
type: 'undoseries',
title: mw.message('rollback-window-title-nofrom', title).parseDom(),
pagetitle: title,
from: from,
undo: undo,
undoafter: undoafter,
});
}
})
.insertBefore($content.find(".mw-history-revisionactions"));
}
} else if (mw.config.get('wgDiffOldId') !== null) {
// We're on diff page, where .diff-side-added *may* be the last edit
const $diffAdded = $content.find('.diff-side-added');
if ($diffAdded.length <= 0)
return;
const $userlink = $diffAdded.find('#mw-diff-ntitle2 a.mw-userlink:first');
const revid = $userlink.attr('data-mw-revid');
if (revid != mw.config.get('wgCurRevisionId'))
return;
// Find .mw-rollback-link or append it after .mw-usertoollinks
let $rollbacklinkcontainer = $content.find('.mw-rollback-link');
if ($rollbacklinkcontainer.length === 0) {
const $usertoollinks = $content.find('.mw-usertoollinks');
$rollbacklinkcontainer = $('<span class="mw-rollback-link">')
.appendTo($usertoollinks);
}
const title = mw.config.get('wgPageName');
const from = $userlink.find('bdi').text();
createRollbackLink($rollbacklinkcontainer, title, from);
}
// Handle .mw-history-undo, i.e. revert only one edit
$content.find(`
.mw-history-undo a:not(.advancedrollback-processed),
.mw-diff-undo a:not(.advancedrollback-processed)
`).each(function () {
const $this = $(this);
if ($this.length <= 0)
return;
$this.addClass('advancedrollback-processed');
(async function () {
const href = $this.attr('href');
const undo = mw.util.getParamValue('undo', href);
const undoafter = mw.util.getParamValue('undoafter', href);
if (!undo || !undoafter)
return;
const rev_data = await api.get({
action: 'query',
prop: 'revisions',
rvprop: 'user',
revids: undo,
});
if (!rev_data.query || !rev_data.query.pages)
return;
const rev_user = Object.values(rev_data.query.pages)[0].revisions[0].user;
if (!rev_user)
return;
$this.addClass('advancedrollback-processed');
const $quickRevertButtonFrame = $('<span>');
const $quickRevertButtonFrameInner = $('<span>').appendTo($quickRevertButtonFrame);
const $quickRevertButton = $('<a>').appendTo($quickRevertButtonFrameInner);
$quickRevertButton.text(mw.message('rollback-button-quick-revert').plain());
$quickRevertButton.attr('href', '#');
$quickRevertButton.attr('data-ar-undo', undo);
$quickRevertButton.attr('data-ar-undoafter', undoafter);
$quickRevertButton.attr('data-ar-title', mw.config.get('wgPageName'));
$quickRevertButton.attr('data-ar-from', rev_user);
$quickRevertButton.attr('data-ar-action', 'undo');
$quickRevertButton.click(onRollbackClick);
$quickRevertButton.addClass('advancedrollback-processed');
console.log($this.parent().parent());
$quickRevertButtonFrame.insertAfter($this.parent().parent().first());
})();
});
};
mw.hook('wikipage.content').add(process);
process($("#mw-content-text")); // HACK: Sometimes the above fail on Special:Diff
})
.fail(console.warn)
// </nowiki> Nya~!