利用者:Syunsyunminmin/Twinkle/twinklefluff.js
表示
お知らせ: 保存した後、ブラウザのキャッシュをクリアしてページを再読み込みする必要があります。
多くの Windows や Linux のブラウザ
- Ctrl を押しながら F5 を押す。
Mac における Safari
Mac における Chrome や Firefox
- ⌘ Cmd と ⇧ Shift を押しながら R を押す。
詳細についてはWikipedia:キャッシュを消すをご覧ください。
// <nowiki>
(function($) {
/*
****************************************
*** twinklefluff.js: Revert/rollback module
****************************************
* Mode of invocation: Links on contributions, recent changes, history, and diff pages
* Active on: Diff pages, history pages, Special:RecentChanges(Linked),
and Special:Contributions
*/
/**
Twinklefluff revert and antivandalism utility
*/
Twinkle.fluff = function twinklefluff() {
// Only proceed if the user can actually edit the page in question
// (see #632 for contribs issue). wgIsProbablyEditable should take
// care of namespace/contentModel restrictions as well as explicit
// protections; it won't take care of cascading or TitleBlacklist.
if (mw.config.get('wgIsProbablyEditable')) {
// wgDiffOldId included for clarity in if else loop [[phab:T214985]]
if (mw.config.get('wgDiffNewId') || mw.config.get('wgDiffOldId')) {
// Reload alongside the revision slider
mw.hook('wikipage.diff').add(function () {
Twinkle.fluff.addLinks.diff();
});
} else if (mw.config.get('wgAction') === 'view' && mw.config.get('wgRevisionId') && mw.config.get('wgCurRevisionId') !== mw.config.get('wgRevisionId')) {
Twinkle.fluff.addLinks.oldid();
} else if (mw.config.get('wgAction') === 'history' && mw.config.get('wgArticleId')) {
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();
}
});
}
}
};
// 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', 'MajavahBot'];
Twinkle.fluff.skipTalk = null;
Twinkle.fluff.rollbackInPlace = null;
// String to insert when a username is hidden
Twinkle.fluff.hiddenName = '不明なユーザー';
var ad_jawiki = ' ([[Help:Twinkle|Twinkle]]使用)';
var vand_user_summary = Twinkle.getPref( 'vandusersummary' );
console.log(vand_user_summary);
// 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;
},
buildLink: function(color, text) {
var link = document.createElement('a');
link.appendChild(Twinkle.fluff.linkBuilder.spanTag('Black', '['));
link.appendChild(Twinkle.fluff.linkBuilder.spanTag(color, text));
link.appendChild(Twinkle.fluff.linkBuilder.spanTag('Black', ']'));
link.href = '#';
return link;
},
/**
* @param {string} [vandal=null] - Username of the editor being reverted
* Provide a falsey value if the username is hidden, defaults to null
* @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) {
vandal = vandal || null;
var elem = inline ? 'span' : 'div';
var revNode = document.createElement(elem);
rev = parseInt(rev, 10);
if (rev) {
revNode.setAttribute('id', 'tw-revert' + rev);
} else {
revNode.setAttribute('id', 'tw-revert');
}
var separator = inline ? ' ' : ' || ';
var sepNode1 = document.createElement('span');
var sepText = document.createTextNode(separator);
sepNode1.setAttribute('class', 'tw-rollback-link-separator');
sepNode1.appendChild(sepText);
var sepNode2 = sepNode1.cloneNode(true);
var normNode = document.createElement('span');
var vandNode = document.createElement('span');
var normLink = Twinkle.fluff.linkBuilder.buildLink('SteelBlue', '差し戻し');
var vandLink = Twinkle.fluff.linkBuilder.buildLink('Red', '荒らし');
normLink.style.fontWeight = 'bold';
vandLink.style.fontWeight = 'bold';
$(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);
});
normNode.setAttribute('class', 'tw-rollback-link-normal');
vandNode.setAttribute('class', 'tw-rollback-link-vandalism');
// normNode.appendChild(sepNode1);
vandNode.appendChild(sepNode2);
normNode.appendChild(normLink);
vandNode.appendChild(vandLink);
revNode.appendChild(normNode);
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', 'この版を復元');
$(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
var isRange = !!$('#sp-contributions-footer-anon-range')[0];
if (mw.config.exists('wgRelevantUserName') || isRange) {
// 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();
// Get username for IP ranges (wgRelevantUserName is null)
if (isRange) {
// The :not is possibly unnecessary, as it appears that
// .mw-userlink is simply not present if the username is hidden
username = $(current).find('.mw-userlink:not(.history-deleted)').text();
}
// It's unlikely, but we can't easily check for revdel'd usernames
// since only a strong element is provided, with no easy selector [[phab:T255903]]
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) {
// The :not is possibly unnecessary, as it appears that
// .mw-userlink is simply not present if the username is hidden
var vandal = $(current).find('.mw-userlink:not(.history-deleted)').text();
var href = $(current).find('.mw-changeslist-diff').attr('href');
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).find('.mw-userlink:not(.history-deleted)').text();
// Check for first username different than the top user,
// only apply rollback links if/when found
// for faster than every
for (var i = 0; i < histList.length; i++) {
if ($(histList[i]).find('.mw-userlink').text() !== vandal) {
first.appendChild(Twinkle.fluff.linkBuilder.rollbackLinks(vandal, true));
break;
}
}
}
// oldid
histList.forEach(function(rev) {
// From restoreThisRevision, non-transferable
// If the text has been revdel'd, it gets wrapped in a span with .history-deleted,
// and href will be undefined (and thus oldid is NaN)
var href = rev.querySelector('.mw-changeslist-date').href;
var oldid = parseInt(mw.util.getParamValue('oldid', href), 10);
if (!isNaN(oldid)) {
rev.appendChild(Twinkle.fluff.linkBuilder.restoreThisRevisionLink(oldid, true));
}
});
}
},
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'))) {
// Normally .mw-userlink is a link, but if the
// username is hidden, it will be a span with
// .history-deleted as well. When a sysop views the
// hidden content, the span contains the username in a
// link element, which will *just* have
// .mw-userlink. The below thus finds the first
// instance of the class, which if hidden is the span
// and thus text returns undefined. Technically, this
// is a place where sysops *could* have more
// information available to them (as above, via
// &unhide=1), since the username will be available by
// checking a.mw-userlink instead, but revert() will
// need reworking around userHidden
var vandal = $('#mw-diff-ntitle2').find('.mw-userlink')[0];
// See #1337
vandal = vandal ? vandal.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) {
var title = revisionInfo.parentNode;
title.insertBefore(Twinkle.fluff.linkBuilder.restoreThisRevisionLink('wgRevisionId'), title.firstChild);
}
}
};
Twinkle.fluff.disableLinks = function disablelinks(parentNode) {
// 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.ip.sanitizeIPv6(vandal);
}
var pagename = page || mw.config.get('wgPageName');
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 = {
type: type,
user: vandal,
userHidden: !vandal, // Keep track of whether the username was hidden
pagename: pagename,
revid: revid
};
var query = {
action: 'query',
prop: ['info', 'revisions', 'flagged'],
titles: pagename,
inprop: 'watched',
intestactions: 'edit',
rvlimit: Twinkle.getPref('revertMaxRevisions'),
rvprop: [ 'ids', 'timestamp', 'user' ],
curtimestamp: '',
meta: 'tokens',
type: 'csrf',
format: 'json'
};
var wikipedia_api = new Morebits.wiki.api('以前の版のデータを取得する', query, Twinkle.fluff.callbacks.main);
wikipedia_api.params = params;
wikipedia_api.post();
};
Twinkle.fluff.revertToRevision = function revertToRevision(oldrev) {
Morebits.status.init(document.getElementById('mw-content-text'));
var query = {
action: 'query',
prop: ['info', 'revisions'],
titles: mw.config.get('wgPageName'),
inprop: 'watched',
rvlimit: 1,
rvstartid: oldrev,
rvprop: [ 'ids', 'user' ],
curtimestamp: '',
meta: 'tokens',
type: 'csrf',
format: 'json'
};
var wikipedia_api = new Morebits.wiki.api('以前の版のデータを取得する', query, Twinkle.fluff.callbacks.toRevision);
wikipedia_api.params = { rev: oldrev };
wikipedia_api.post();
};
Twinkle.fluff.callbacks = {
toRevision: function(apiobj) {
var response = apiobj.getResponse();
var loadtimestamp = response.curtimestamp;
var csrftoken = response.query.tokens.csrftoken;
var page = response.query.pages[0];
var lastrevid = parseInt(page.lastrevid, 10);
var touched = page.touched;
var rev = page.revisions[0];
var revertToRevID = parseInt(rev.revid, 10);
var revertToUser = rev.user;
var revertToUserHidden = !!rev.userhidden;
if (revertToRevID !== apiobj.params.rev) {
apiobj.statelem.error('取得した版は、要求された版と一致しません。 差し戻すのを停止します。');
return;
}
var optional_summary = prompt('元に戻す理由を指定してください: ', ''); // padded out to widen prompt in Firefox
if (optional_summary === null) {
apiobj.statelem.error('ユーザにより中断されました。');
return;
}
var summary = Twinkle.fluff.formatSummary('$USERさんによる第' + revertToRevID + '版を復元',
revertToUserHidden ? null : revertToUser, optional_summary + ad_jawiki);
var query = {
action: 'edit',
title: mw.config.get('wgPageName'),
summary: summary,
token: csrftoken,
undo: lastrevid,
undoafter: revertToRevID,
basetimestamp: touched,
starttimestamp: loadtimestamp,
minor: Twinkle.getPref('markRevertedPagesAsMinor').indexOf('torev') !== -1 ? true : undefined,
format: 'json'
};
// Handle watching, possible expiry
if (Twinkle.getPref('watchRevertedPages').indexOf('torev') !== -1) {
var watchOrExpiry = Twinkle.getPref('watchRevertedExpiry');
if (!watchOrExpiry || watchOrExpiry === 'no') {
query.watchlist = 'nochange';
} else if (watchOrExpiry === 'default' || watchOrExpiry === 'preferences') {
query.watchlist = 'preferences';
} else {
query.watchlist = 'watch';
// number allowed but not used in Twinkle.config.watchlistEnums
if ((!page.watched || page.watchlistexpiry) && typeof watchOrExpiry === 'string' && watchOrExpiry !== 'yes') {
query.watchlistexpiry = watchOrExpiry;
}
}
}
Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
Morebits.wiki.actionCompleted.notice = '復元完了';
var wikipedia_api = new Morebits.wiki.api('復元したコンテンツを保存する', query, Twinkle.fluff.callbacks.complete, apiobj.statelem);
wikipedia_api.params = apiobj.params;
wikipedia_api.post();
},
main: function(apiobj) {
var response = apiobj.getResponse();
var loadtimestamp = response.curtimestamp;
var csrftoken = response.query.tokens.csrftoken;
var page = response.query.pages[0];
if (!page.actions.edit) {
apiobj.statelem.error("ページを編集できません。おそらく保護されています。");
return;
}
var lastrevid = parseInt(page.lastrevid, 10);
var touched = page.touched;
var revs = page.revisions;
var statelem = apiobj.statelem;
var params = apiobj.params;
if (revs.length < 1) {
statelem.error('追加の版は1つ未満であるため、元に戻すことはできません。');
return;
}
var top = revs[0];
var lastuser = top.user;
if (lastrevid < params.revid) {
Morebits.status.error('エラー', [ 'サーバーから取得した最新のリビジョンID, ', Morebits.htmlNode('strong', lastrevid), ', は、表示されているリビジョンIDよりも小さいです。 これは、現在の版が削除されたか、サーバーが遅れているか、または不正なデータが受信されたことを示している可能性があります。差し戻すのを停止します。' ]);
return;
}
// Used for user-facing alerts, messages, etc., not edits or summaries
var userNorm = params.user || Twinkle.fluff.hiddenName;
var index = 1;
if (params.revid !== lastrevid) {
Morebits.status.warn('Warning', [ 'Latest revision ', Morebits.htmlNode('strong', lastrevid), ' doesn\'t equal our revision ', Morebits.htmlNode('strong', params.revid) ]);
// Treat ipv6 users on same 64 block as the same
if (lastuser === params.user || (mw.util.isIPv6Address(params.user) && Morebits.ip.get64(lastuser) === Morebits.ip.get64(params.user))) {
switch (params.type) {
case 'vand':
var diffUser = lastuser !== params.user;
Morebits.status.info('情報', [ '最新の版は ' + (diffUser ? '' : '同様に ') + 'made by ', Morebits.htmlNode('strong', userNorm),
diffUser ? ', 同じ/64サブネット上にあります' : '', '. 破壊行為を想定しているので、元に戻します。' ]);
break;
default:
Morebits.status.warn('通知', [ '最新の版の作成者は ', Morebits.htmlNode('strong', userNorm), 'です。ただし、元に戻すのは停止します。' ]);
return;
}
} else if (params.type === 'vand' &&
// Okay to test on user since it will either fail or sysop will correctly access it
// Besides, none of the trusted bots are going to be revdel'd
Twinkle.fluff.trustedBots.indexOf(top.user) !== -1 && revs.length > 1 &&
revs[1].revid === params.revid) {
Morebits.status.info('Info', [ 'Latest revision was made by ', Morebits.htmlNode('strong', lastuser), ', a trusted bot, and the revision before was made by our vandal, so we will proceed with the revert.' ]);
index = 2;
} else {
Morebits.status.error('エラー', [ '最新の版の作成者は', Morebits.htmlNode('strong', lastuser), 'です。既に差し戻されている可能性があるため元に戻すのを停止します。']);
return;
}
} else {
// Expected revision is the same, so the users must match;
// this allows sysops to know whether the users are the same
params.user = lastuser;
userNorm = params.user || Twinkle.fluff.hiddenName;
}
if (Twinkle.fluff.trustedBots.indexOf(params.user) !== -1) {
switch (params.type) {
case 'vand':
Morebits.status.info('情報', [ 'Vandalism revert was chosen on ', Morebits.htmlNode('strong', userNorm), '. As this is a trusted bot, we assume you wanted to revert vandalism made by the previous user instead.' ]);
index = 2;
params.user = revs[1].user;
params.userHidden = !!revs[1].userhidden;
break;
case 'norm':
/* falls through */
default:
var cont = confirm('Normal revert was chosen, but the most recent edit was made by a trusted bot (' + userNorm + '). Do you want to revert the revision before instead?');
if (cont) {
Morebits.status.info('Info', [ 'Normal revert was chosen on ', Morebits.htmlNode('strong', userNorm), '. This is a trusted bot, and per confirmation, we\'ll revert the previous revision instead.' ]);
index = 2;
params.user = revs[1].user;
params.userHidden = !!revs[1].userhidden;
userNorm = params.user || Twinkle.fluff.hiddenName;
} else {
Morebits.status.warn('Notice', [ 'Normal revert was chosen on ', Morebits.htmlNode('strong', userNorm), '. This is a trusted bot, but per confirmation, revert on selected revision will proceed.' ]);
}
break;
}
}
var found = false;
var count = 0;
var seen64 = false;
for (var i = index; i < revs.length; ++i) {
++count;
if (revs[i].user !== params.user) {
// Treat ipv6 users on same 64 block as the same
if (mw.util.isIPv6Address(revs[i].user) && Morebits.ip.get64(revs[i].user) === Morebits.ip.get64(params.user)) {
if (!seen64) {
new Morebits.status('注', '同じ/64内の連続するIPv6アドレスを同じユーザーとして処理する');
seen64 = true;
}
continue;
}
found = i;
break;
}
}
if (!found) {
statelem.error([ '以前の版が見つかりません。おそらく', Morebits.htmlNode('strong', userNorm), 'が唯一の貢献者であるか、差し戻す版が連続' + mw.language.convertNumber(Twinkle.getPref('revertMaxRevisions')) + '版以上かもしれません。' ]);
return;
}
if (!count) {
Morebits.status.error('エラー', 'As it is not possible to revert zero revisions, we will stop this revert. It could be that the edit has already been reverted, but the revision ID was still the same.');
return;
}
var good_revision = revs[found];
var userHasAlreadyConfirmedAction = false;
if (params.type !== 'vand' && count > 1) {
if (!confirm(userNorm + 'は連続' + mw.language.convertNumber(count) + '版の編集を行いました。 全ての編集を差し戻しますか?')) {
Morebits.status.info('通知', '差し戻しを中止します。');
return;
}
userHasAlreadyConfirmedAction = true;
}
params.count = count;
params.goodid = good_revision.revid;
params.gooduser = good_revision.user;
params.gooduserHidden = !!good_revision.userhidden;
statelem.status([ ' revision ', Morebits.htmlNode('strong', params.goodid), ' that was made ', Morebits.htmlNode('strong', mw.language.convertNumber(count)), ' revisions ago by ', Morebits.htmlNode('strong', params.gooduserHidden ? Twinkle.fluff.hiddenName : params.gooduser) ]);
var summary, extra_summary;
switch (params.type) {
case 'vand':
summary = Twinkle.fluff.formatSummary( vand_user_summary + ad_jawiki );
break;
case 'norm':
/* falls through */
default:
if (Twinkle.getPref('offerReasonOnNormalRevert')) {
extra_summary = prompt('要約欄の追加のコメント: ', ''); // padded out to widen prompt in Firefox
if (extra_summary === null) {
statelem.error('ユーザーにより中断されました。');
return;
}
userHasAlreadyConfirmedAction = true;
}
summary = Twinkle.fluff.formatSummary('$USERさんによる' + params.count + '版を差し戻し', params.userHidden ? null : params.user, extra_summary + ad_jawiki);
break;
}
if ((Twinkle.getPref('confirmOnFluff') ||
// Mobile user agent taken from [[en:MediaWiki:Gadget-confirmationRollback-mobile.js]]
(Twinkle.getPref('confirmOnMobileFluff') && /Android|webOS|iPhone|iPad|iPod|BlackBerry|Mobile|Opera Mini/i.test(navigator.userAgent))) &&
!userHasAlreadyConfirmedAction && !confirm('ページを復元します: よろしいですか?')) {
statelem.error('ユーザーにより中断されました。');
return;
}
// Decide whether to notify the user on success
if (!Twinkle.fluff.skipTalk && Twinkle.getPref('openTalkPage').indexOf(params.type) !== -1 &&
!params.userHidden && mw.config.get('wgUserName') !== params.user) {
params.notifyUser = true;
// Pass along to the warn module
params.vantimestamp = top.timestamp;
}
// figure out whether we need to/can review the edit
var flagged = page.flagged;
if ((Morebits.userIsInGroup('reviewer') || Morebits.userIsSysop) &&
!!flagged &&
flagged.stable_revid >= params.goodid &&
!!flagged.pending_since) {
params.reviewRevert = true;
params.csrftoken = csrftoken;
}
var query = {
action: 'edit',
title: params.pagename,
summary: summary,
token: csrftoken,
undo: lastrevid,
undoafter: params.goodid,
basetimestamp: touched,
starttimestamp: loadtimestamp,
minor: Twinkle.getPref('markRevertedPagesAsMinor').indexOf(params.type) !== -1 ? true : undefined,
format: 'json'
};
// Handle watching, possible expiry
if (Twinkle.getPref('watchRevertedPages').indexOf(params.type) !== -1) {
var watchOrExpiry = Twinkle.getPref('watchRevertedExpiry');
if (!watchOrExpiry || watchOrExpiry === 'no') {
query.watchlist = 'nochange';
} else if (watchOrExpiry === 'default' || watchOrExpiry === 'preferences') {
query.watchlist = 'preferences';
} else {
query.watchlist = 'watch';
// number allowed but not used in Twinkle.config.watchlistEnums
if ((!page.watched || page.watchlistexpiry) && typeof watchOrExpiry === 'string' && watchOrExpiry !== 'yes') {
query.watchlistexpiry = watchOrExpiry;
}
}
}
if (!Twinkle.fluff.rollbackInPlace) {
Morebits.wiki.actionCompleted.redirect = params.pagename;
}
Morebits.wiki.actionCompleted.notice = '復元完了';
var wikipedia_api = new Morebits.wiki.api('元に戻したコンテンツを保存する', query, Twinkle.fluff.callbacks.complete, statelem);
wikipedia_api.params = params;
wikipedia_api.post();
},
complete: function (apiobj) {
// TODO Most of this is copy-pasted from Morebits.wiki.page#fnSaveSuccess. Unify it
var response = apiobj.getResponse();
var edit = response.edit;
if (edit.captcha) {
apiobj.statelem.error('ウィキのサーバーがあなたにCAPTCHAを要求しているため差し戻せません。');
} else if (edit.nochange) {
apiobj.statelem.error('差し戻す版は現在の版と同じため、差し戻すのを停止します。');
} else {
apiobj.statelem.info('完了');
var params = apiobj.params;
if (params.notifyUser && !params.userHidden) { // notifyUser only from main, not from toRevision
Morebits.status.info('情報', [ '利用者の会話ページ編集フォームを開きます', Morebits.htmlNode('strong', params.user) ]);
var windowQuery = {
title: 'User talk:' + params.user,
action: 'edit',
preview: 'yes',
vanarticle: params.pagename.replace(/_/g, ' '),
vanarticlerevid: params.revid,
vantimestamp: params.vantimestamp,
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
if (apiobj.params.reviewRevert) {
var query = {
action: 'review',
revid: edit.newrevid,
token: apiobj.params.csrftoken,
comment: 'Automatically reviewing reversion' + Twinkle.summaryAd // until the below
// 'tags': Twinkle.changeTags // flaggedrevs tag support: [[phab:T247721]]
};
var wikipedia_api = new Morebits.wiki.api('Automatically accepting your changes', query);
wikipedia_api.post();
}
}
}
};
// If builtInString contains the string "$USER", it will be replaced
// by an appropriate user link if a user name is provided
Twinkle.fluff.formatSummary = function(builtInString, userName, customString) {
var result = builtInString;
// append user's custom reason
if (customString) {
result += ': ' + Morebits.string.toUpperCaseFirstChar(customString);
}
// find number of UTF-8 bytes the resulting string takes up, and possibly add
// a contributions or contributions+talk link if it doesn't push the edit summary
// over the 499-byte limit
if (/\$USER/.test(builtInString)) {
if (userName) {
var resultLen = unescape(encodeURIComponent(result.replace('$USER', ''))).length;
var contribsLink = '[[Special:Contributions/' + userName + '|' + userName + ']]';
var contribsLen = unescape(encodeURIComponent(contribsLink)).length;
if (resultLen + contribsLen <= 499) {
var talkLink = ' ([[User talk:' + userName + '|会話]])';
if (resultLen + contribsLen + unescape(encodeURIComponent(talkLink)).length <= 499) {
result = Morebits.string.safeReplace(result, '$USER', contribsLink + talkLink);
} else {
result = Morebits.string.safeReplace(result, '$USER', contribsLink);
}
} else {
result = Morebits.string.safeReplace(result, '$USER', userName);
}
} else {
result = Morebits.string.safeReplace(result, '$USER', Twinkle.fluff.hiddenName);
}
}
return result;
};
Twinkle.addInitCallback(Twinkle.fluff, 'fluff');
})(jQuery);
// </nowiki>