MediaWiki:Gadget-twinklefluff.js: Perbedaan antara revisi
Konten dihapus Konten ditambahkan
Hidayatsrf (bicara | kontrib) Perbaiki terjemahan |
Repo at b425e01: twinklefluff.js |
||
(10 revisi perantara oleh 3 pengguna tidak ditampilkan) | |||
Baris 1:
// <nowiki>
(function($) {
Baris 9:
*** twinklefluff.js: Revert/rollback module
****************************************
* Mode of invocation: Links on
* Active on: Diff pages, history pages,
and Special:Contributions
*/
Baris 18:
*/
Twinkle.fluff = function twinklefluff() {
// A list of usernames, usually only bots, that vandalism revert is jumped over; that is,
// if vandalism revert was chosen on such username, then its target is on the revision before.
// This is for handling quick bots that makes edits seconds after the original edit is made.
// This only affects vandalism rollback; for good faith rollback, it will stop, indicating a bot
// has no faith, and for normal rollback, it will rollback that edit.
Twinkle.fluff.trustedBots = [
'AnomieBOT',
'SineBot'
];
if (mw.config.get('wgIsProbablyEditable')) {
// Only proceed if the user can actually edit the page
// in question (ignored for contributions, see #632).
// wgIsProbablyEditable should take care of
// namespace/contentModel restrictions as well as
// explicit protections; it won't take care of
// cascading or TitleBlacklist restrictions
if (mw.config.get('wgDiffNewId') || mw.config.get('wgDiffOldId')) { // wgDiffOldId included for clarity in if else loop [[phab:T214985]]
mw.hook('wikipage.diff').add(function () { // Reload alongside the revision slider
Twinkle.fluff.addLinks.diff();
});
} else if (mw.config.get('wgAction') === 'view' && mw.config.get('wgCurRevisionId') !== mw.config.get('wgRevisionId')) {
Twinkle.fluff.addLinks.oldid();
} else if (mw.config.get('wgAction') === 'history') {
Twinkle.fluff.addLinks.history();
}
} else if (mw.config.get('wgNamespaceNumber') === -1) {
Twinkle.fluff.skipTalk = !Twinkle.getPref('openTalkPageOnAutoRevert');
Twinkle.fluff.rollbackInPlace = Twinkle.getPref('rollbackInPlace');
if (mw.config.get('wgCanonicalSpecialPageName') === 'Contributions') {
Twinkle.fluff.addLinks.contributions();
} else if (mw.config.get('wgCanonicalSpecialPageName') === 'Recentchanges' || mw.config.get('wgCanonicalSpecialPageName') === 'Recentchangeslinked') {
// Reload with recent changes updates
// structuredChangeFilters.ui.initialized is just on load
mw.hook('wikipage.content').add(function(item) {
if (item.is('div')) {
Twinkle.fluff.addLinks.recentchanges();
}
});
}
}
};
Twinkle.fluff.skipTalk = null;
Twinkle.fluff.rollbackInPlace = null;
// Consolidated construction of fluff links
Twinkle.fluff.linkBuilder = {
spanTag: function(color, content) {
var span = document.createElement('span');
span.style.color = color;
span.appendChild(document.createTextNode(content));
return span;
},
link.appendChild(Twinkle.fluff.linkBuilder.spanTag('Black', '['));
link.appendChild(Twinkle.fluff.linkBuilder.spanTag('Black', ']'));
link.href = '#';
return link;
},
/**
* @param {string} vandal - Username of the editor being reverted (required)
* @param {boolean} inline - True to create two links in a span, false
* to create three links in a div (optional)
* @param {number|string} [rev=wgCurRevisionId] - Revision ID being reverted (optional)
* @param {string} [page=wgPageName] - Page being reverted (optional)
*/
rollbackLinks: function(vandal, inline, rev, page) {
var elem = inline ? 'span' : 'div';
var revNode = document.createElement(elem);
rev = parseInt(rev, 10);
if (rev) {
} else {
}
var normLink = Twinkle.fluff.linkBuilder.buildLink('SteelBlue', 'kembalikan');
var vandLink = Twinkle.fluff.linkBuilder.buildLink('Red', 'vandalisme');
$(normLink).click(function() {
Twinkle.fluff.revert('norm', vandal, rev, page);
Twinkle.fluff.disableLinks(revNode);
$(vandLink).click(function() {
Twinkle.fluff.revert('vand', vandal, rev, page);
Twinkle.fluff.disableLinks(revNode);
});
vandNode.appendChild(vandLink);
normNode.appendChild(normLink);
var separator = inline ? ' ' : ' || ';
if (!inline) {
var agfNode = document.createElement('strong');
var agfLink = Twinkle.fluff.linkBuilder.buildLink('DarkOliveGreen', 'kembalikan (AGF)');
$(agfLink).click(function() {
Twinkle.fluff.revert('agf', vandal, rev, page);
// Twinkle.fluff.disableLinks(revNode); // rollbackInPlace not relevant for any inline situations
});
agfNode.appendChild(agfLink);
revNode.appendChild(agfNode);
}
revNode.appendChild(document.createTextNode(separator));
revNode.appendChild(normNode);
revNode.appendChild(document.createTextNode(separator));
revNode.appendChild(vandNode);
return revNode;
},
// Build [restore this revision] links
restoreThisRevisionLink: function(revisionRef, inline) {
// If not a specific revision number, should be wgDiffNewId/wgDiffOldId/wgRevisionId
revisionRef = typeof revisionRef === 'number' ? revisionRef : mw.config.get(revisionRef);
var elem = inline ? 'span' : 'div';
var revertToRevisionNode = document.createElement(elem);
revertToRevisionNode.setAttribute('id', 'tw-revert-to-' + revisionRef);
revertToRevisionNode.style.fontWeight = 'bold';
var revertToRevisionLink = Twinkle.fluff.linkBuilder.buildLink('SaddleBrown', 'kembalikan revisi ini');
$(revertToRevisionLink).click(function() {
Twinkle.fluff.revertToRevision(revisionRef);
});
if (inline) {
revertToRevisionNode.appendChild(document.createTextNode(' '));
}
revertToRevisionNode.appendChild(revertToRevisionLink);
return revertToRevisionNode;
}
};
Twinkle.fluff.addLinks = {
contributions: function() {
// $('sp-contributions-footer-anon-range') relies on the fmbox
// id in [[MediaWiki:Sp-contributions-footer-anon-range]] and
// is used to show rollback/vandalism links for IP ranges
if (mw.config.exists('wgRelevantUserName') || !!$('#sp-contributions-footer-anon-range')[0]) {
// Get the username these contributions are for
var username = mw.config.get('wgRelevantUserName');
if (Twinkle.getPref('showRollbackLinks').indexOf('contribs') !== -1 ||
(mw.config.get('wgUserName') !== username && Twinkle.getPref('showRollbackLinks').indexOf('others') !== -1) ||
(mw.config.get('wgUserName') === username && Twinkle.getPref('showRollbackLinks').indexOf('mine') !== -1)) {
var list = $('#mw-content-text').find('ul li:has(span.mw-uctop):has(.mw-changeslist-diff)');
list.each(function(key, current) {
// revid is also available in the href of both
// .mw-changeslist-date or .mw-changeslist-diff
var page = $(current).find('.mw-contributions-title').text();
current.appendChild(Twinkle.fluff.linkBuilder.rollbackLinks(username, true, current.dataset.mwRevid, page));
});
}
}
},
recentchanges: function() {
if (Twinkle.getPref('showRollbackLinks').indexOf('recent') !== -1) {
// Latest and revertable (not page creations, logs, categorizations, etc.)
var list = $('.mw-changeslist .mw-changeslist-last.mw-changeslist-src-mw-edit');
// Exclude top-level header if "group changes" preference is used
// and find only individual lines or nested lines
list = list.not('.mw-rcfilters-ui-highlights-enhanced-toplevel').find('.mw-changeslist-line-inner, td.mw-enhanced-rc-nested');
list.each(function(key, current) {
var
var
var rev = mw.util.getParamValue('diff', href);
var page = current.dataset.targetPage;
current.appendChild(Twinkle.fluff.linkBuilder.rollbackLinks(vandal, true, rev, page));
});
}
},
history: function() {
if (Twinkle.getPref('showRollbackLinks').indexOf('history') !== -1) {
// All revs
var histList = $('#pagehistory li').toArray();
// On first page of results, so add revert/rollback
// links to the top revision
if (!$('.mw-firstlink').length) {
var first = histList.shift();
var vandal = first.querySelector('.mw-userlink').text;
}
// oldid
histList.forEach(function(rev) {
// From restoreThisRevision, non-transferable
var href = rev.querySelector('.mw-changeslist-date').href;
var oldid = parseInt(mw.util.getParamValue('oldid', href), 10);
});
}
},
diff: function() {
// Autofill user talk links on diffs with vanarticle for easy warning, but don't autowarn
var warnFromTalk = function(xtitle) {
var talkLink = $('#mw-diff-' + xtitle + '2 .mw-usertoollinks a').first();
if (talkLink.length) {
var extraParams = 'vanarticle=' + mw.util.rawurlencode(Morebits.pageNameNorm) + '&' + 'noautowarn=true';
// diffIDs for vanarticlerevid
extraParams += '&vanarticlerevid=';
extraParams += xtitle === 'otitle' ? mw.config.get('wgDiffOldId') : mw.config.get('wgDiffNewId');
var href = talkLink.attr('href');
if (href.indexOf('?') === -1) {
talkLink.attr('href', href + '?' + extraParams);
} else {
talkLink.attr('href', href + '&' + extraParams);
}
}
};
// Older revision
warnFromTalk('otitle'); // Add quick-warn link to user talk link
// Don't load if there's a single revision or weird diff (cur on latest)
if (mw.config.get('wgDiffOldId') && (mw.config.get('wgDiffOldId') !== mw.config.get('wgDiffNewId'))) {
// Add a [restore this revision] link to the older revision
var oldTitle = document.getElementById('mw-diff-otitle1').parentNode;
oldTitle.insertBefore(Twinkle.fluff.linkBuilder.restoreThisRevisionLink('wgDiffOldId'), oldTitle.firstChild);
}
// Newer revision
warnFromTalk('ntitle'); // Add quick-warn link to user talk link
// Add either restore or rollback links to the newer revision
// Don't show if there's a single revision or weird diff (prev on first)
if (document.getElementById('differences-nextlink')) {
// Not latest revision, add [restore this revision] link to newer revision
var newTitle = document.getElementById('mw-diff-ntitle1').parentNode;
newTitle.insertBefore(Twinkle.fluff.linkBuilder.restoreThisRevisionLink('wgDiffNewId'), newTitle.firstChild);
} else if (Twinkle.getPref('showRollbackLinks').indexOf('diff') !== -1 && mw.config.get('wgDiffOldId') && (mw.config.get('wgDiffOldId') !== mw.config.get('wgDiffNewId') || document.getElementById('differences-prevlink'))) {
var vandal = $('#mw-diff-ntitle2').find('a').first().text();
var ntitle = document.getElementById('mw-diff-ntitle1').parentNode;
ntitle.insertBefore(Twinkle.fluff.linkBuilder.rollbackLinks(vandal), ntitle.firstChild);
}
},
oldid: function() { // Add a [restore this revision] link on old revisions
var revisionInfo = document.getElementById('mw-revision-info');
if (!revisionInfo) {
return;
}
var title = revisionInfo.parentNode;
title.insertBefore(Twinkle.fluff.linkBuilder.restoreThisRevisionLink('wgRevisionId'), title.firstChild);
}
};
Twinkle.fluff.
// Array.from not available in IE11 :(
$(parentNode).children().each(function(_ix, node) {
node.innerHTML = node.textContent; // Feels like cheating
$(node).css('font-weight', 'normal').css('color', 'darkgray');
});
};
Twinkle.fluff.revert = function revertPage(type, vandal, rev, page) {
if (mw.util.isIPv6Address(vandal)) {
vandal = Morebits.sanitizeIPv6(vandal);
Baris 209 ⟶ 306:
var revid = rev || mw.config.get('wgCurRevisionId');
if (Twinkle.fluff.rollbackInPlace) {
var notifyStatus = document.createElement('span');
mw.notify(notifyStatus, {
autoHide: false,
title: 'Rollback on ' + page,
tag: 'twinklefluff_' + rev // Shouldn't be necessary given disableLink
});
Morebits.status.init(notifyStatus);
} else {
Morebits.status.init(document.getElementById('mw-content-text'));
$('#catlinks').remove();
}
var params = {
Baris 216 ⟶ 323:
user: vandal,
pagename: pagename,
revid: revid
};
var query = {
Baris 223 ⟶ 329:
'prop': ['info', 'revisions', 'flagged'],
'titles': pagename,
'intestactions': 'edit',
'rvlimit': 50, // intentionally limited
'rvprop': [ 'ids', 'timestamp', 'user', 'comment' ],
'
'meta': 'tokens',
'type': 'csrf'
};
var wikipedia_api = new Morebits.wiki.api(
wikipedia_api.params = params;
wikipedia_api.post();
};
Twinkle.fluff.revertToRevision = function revertToRevision(
Morebits.status.init(
var query = {
'action': 'query',
'prop': ['info',
'titles': mw.config.get('wgPageName'),
'rvlimit': 1,
'rvstartid': oldrev,
'rvprop': [ 'ids', 'timestamp', 'user', 'comment' ],
'
'
'meta': 'tokens',
'type': 'csrf'
};
var wikipedia_api = new Morebits.wiki.api(
wikipedia_api.params = { rev: oldrev };
wikipedia_api.post();
};
Twinkle.fluff.userIpLink = function(
return (
};
Twinkle.fluff.callbacks = {
toRevision: function(apiobj) {
var xmlDoc = apiobj.responseXML;
apiobj.statelem.error('Dibatalkan oleh pengguna.');
return;
}
var summary = Twinkle.fluff.formatSummary('Dikembalikan ke revisi ' + revertToRevID + ' oleh $USER', revertToUser, optional_summary);
},
main: function(
var xmlDoc =
apiobj.statelem.error('Tidak bisa menyunting halaman, kemungkinan dilindungi.');
return;
}
var lastrevid = parseInt($(xmlDoc).find('page').attr('lastrevid'), 10);
var touched = $(xmlDoc).find('page').attr('touched');
var
var
var lastuser = $(xmlDoc).find('rev').attr('user');
var revs = $(xmlDoc).find('rev');
var statelem = apiobj.statelem;
var params = apiobj.params;
if (revs.length < 1) {
statelem.error('Tidak memiliki satu pun revisi tambahan, sehingga tidak mungkin untuk dikembalikan.');
return;
}
var top = revs[0];
if (
Morebits.status.error(
return;
}
var index = 1;
if (
Morebits.status.warn(
if (
switch (
case 'vand':
Morebits.status.info(
break;
case 'agf':
Morebits.status.warn(
return;
default:
Morebits.status.warn(
return;
}
} else if (params.type === 'vand' &&
Twinkle.fluff.trustedBots.indexOf(top.getAttribute('user')) !== -1 && revs.length > 1 &&
Morebits.status.info('Informasi', [ 'Revisi terakhir dibuat oleh ', Morebits.htmlNode('strong', lastuser), ', bot yang tepercaya, dan revisi sebelumnya dibuat oleh pengguna vandalisme, pengembalian dilanjutkan.' ]);
index = 2;
} else {
Morebits.status.error(
return;
}
Baris 350 ⟶ 465:
}
if
switch (
case 'vand':
Morebits.status.info(
index = 2;
break;
case 'agf':
Morebits.status.warn(
return;
case 'norm':
/* falls through */
default:
var cont = confirm(
if (
Morebits.status.info(
index = 2;
} else {
Morebits.status.warn(
}
break;
}
}
Baris 377 ⟶ 492:
var count = 0;
for
++count;
if
found = i;
break;
Baris 385 ⟶ 500:
}
if (
return;
}
if (
Morebits.status.error(
return;
}
var good_revision = revs[
var userHasAlreadyConfirmedAction = false;
if (
if (
Morebits.status.info(
return;
}
Baris 405 ⟶ 520:
}
var summary, extra_summary;
switch (
case 'agf':
extra_summary = prompt(
if (extra_summary === null) {
statelem.error('Dibatalkan oleh pengguna.');
return;
userHasAlreadyConfirmedAction = true;
summary = Twinkle.fluff.formatSummary(
break;
case 'vand':
summary =
break;
case 'norm':
/* falls through */
default:
if (
extra_summary = prompt(
if (extra_summary === null) {
statelem.error('Dibatalkan oleh pengguna.');
return;
userHasAlreadyConfirmedAction = true;
}
summary = Twinkle.fluff.formatSummary(
break;
}
if (Twinkle.getPref('confirmOnFluff') && !userHasAlreadyConfirmedAction && !confirm(
return;
}
// Decide whether to notify the user on success
if
mw.config.get('wgUserName') !== params.user) {
params.notifyUser = true;
}
// figure out whether we need to/can review the edit
var $flagged = $(xmlDoc).find('flagged');
if ((Morebits.userIsInGroup('reviewer') || Morebits.
$flagged.length &&
$flagged.attr(
$flagged.attr(
}
var query = {
'action': 'edit',
'title':
'summary': summary,
'token':
'undo': lastrevid,
'undoafter':
'basetimestamp': touched,
'starttimestamp':
'watchlist'
'minor': Twinkle.getPref('markRevertedPagesAsMinor').indexOf(
};
if (!Twinkle.fluff.rollbackInPlace) {
Morebits.wiki.actionCompleted.
}
Morebits.wiki.actionCompleted.notice = 'Pembatalan selesai';
var wikipedia_api = new Morebits.wiki.api(
wikipedia_api.params =
wikipedia_api.post();
},
complete: function (apiobj) {
// TODO Most of this is copy-pasted from Morebits.wiki.page#fnSaveSuccess. Unify it
var
var $edit = $(xml).find('edit');
if ($(xml).find('captcha').length > 0) {
apiobj.statelem.error('Could not rollback, because the wiki server wanted you to fill out a CAPTCHA.');
} else if ($edit.attr('nochange') === '') {
apiobj.statelem.
} else {
apiobj.statelem.info(
var params = apiobj.params;
if (params.notifyUser) { // Only from main, not from toRevision
Morebits.status.info('Info', [ 'Opening user talk page edit form for user ', Morebits.htmlNode('strong', params.user) ]);
var windowQuery = {
'title': 'User talk:' + params.user,
'action': 'edit',
'preview': 'yes',
'vanarticle': params.pagename.replace(/_/g, ' '),
'vanarticlerevid': params.revid,
'vanarticlegoodrevid': params.goodid,
'type': params.type,
'count': params.count
};
switch (Twinkle.getPref('userTalkPageMode')) {
case 'tab':
window.open(mw.util.getUrl('', windowQuery), '_blank');
break;
case 'blank':
window.open(mw.util.getUrl('', windowQuery), '_blank',
'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800');
break;
case 'window':
/* falls through */
default:
window.open(mw.util.getUrl('', windowQuery),
window.name === 'twinklewarnwindow' ? '_blank' : 'twinklewarnwindow',
'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800');
break;
}
}
// review the revert, if needed
Baris 540 ⟶ 659:
'action': 'review',
'revid': $edit.attr('newrevid'),
'token': apiobj.params.
'comment': Twinkle.getPref('summaryAd').trim()
};
Baris 555 ⟶ 674:
var result = builtInString;
// append user's custom reason
if (userString) {
result += ': ' + Morebits.string.toUpperCaseFirstChar(userString);
}
result += Twinkle.getPref('summaryAd');
Baris 569 ⟶ 683:
// a contributions or contributions+talk link if it doesn't push the edit summary
// over the 255-byte limit
var resultLen = unescape(encodeURIComponent(result.replace(
var contribsLink =
var contribsLen = unescape(encodeURIComponent(contribsLink)).length;
if (resultLen + contribsLen <= 255) {
var talkLink =
if (resultLen + contribsLen + unescape(encodeURIComponent(talkLink)).length <= 255) {
result =
} else {
result =
}
} else {
result =
}
return result;
};
})(jQuery);
// </nowiki>
|