注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
// <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') { 			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 = [ 	'A2093064-bot' ]; Twinkle.fluff.skipTalk = null; Twinkle.fluff.rollbackInPlace = null; // String to insert when a username is hidden Twinkle.fluff.hiddenName = wgULS('已隐藏的用户', '已隱藏的使用者');  // 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 normNode = document.createElement('strong'); 		var vandNode = document.createElement('strong');  		var normLink = Twinkle.fluff.linkBuilder.buildLink('SteelBlue', '回退'); 		var vandLink = Twinkle.fluff.linkBuilder.buildLink('Red', wgULS('破坏', '破壞'));  		$(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', '回退(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', wgULS('恢复此版本', '恢復此版本')); 		$(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 ( 			(mw.config.get('wgCanonicalSpecialPageName') === 'Recentchanges' && Twinkle.getPref('showRollbackLinks').indexOf('recentchanges') !== -1) 			|| (mw.config.get('wgCanonicalSpecialPageName') === 'Recentchangeslinked' && Twinkle.getPref('showRollbackLinks').indexOf('recentchangeslinked') !== -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; 			var revertToRevision = Twinkle.fluff.linkBuilder.restoreThisRevisionLink('wgDiffOldId'); 			oldTitle.insertBefore(revertToRevision, oldTitle.firstChild); 			if (Twinkle.getPref('customRevertSummary').length > 0) { 				revertToRevision.appendChild(document.createTextNode(' || ')); 				var revertsummary = new Morebits.quickForm.element({ type: 'select', name: 'revertsummary' }); 				revertsummary.append({ 					type: 'option', 					label: wgULS('选择回退理由', '選擇回退理由'), 					value: '' 				}); 				$(Twinkle.getPref('customRevertSummary')).each(function(_, e) { 					revertsummary.append({ 						type: 'option', 						label: e.label, 						value: e.value 					}); 				}); 				revertToRevision.appendChild(revertsummary.render().childNodes[0]); 			} 		}  		// 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].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 title = document.getElementById('mw-revision-info').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.sanitizeIPv6(vandal); 	}  	var pagename = page || mw.config.get('wgPageName'); 	var revid = rev || mw.config.get('wgCurRevisionId'); 	var summary = ''; 	if (document.getElementsByName('revertsummary')[0] !== undefined) { 		summary = document.getElementsByName('revertsummary')[0].value; 	}  	if (Twinkle.fluff.rollbackInPlace) { 		var notifyStatus = document.createElement('span'); 		mw.notify(notifyStatus, { 			autoHide: false, 			title: '回退' + 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, 		summary: summary 	};  	var query = { 		action: 'query', 		prop: ['info', 'revisions'], 		titles: pagename, 		intestactions: 'edit', 		rvlimit: Twinkle.getPref('revertMaxRevisions'), 		rvprop: [ 'ids', 'timestamp', 'user' ], 		curtimestamp: '', 		meta: 'tokens', 		type: 'csrf' 	}; 	var wikipedia_api = new Morebits.wiki.api(wgULS('抓取较早修订版本信息', '抓取較早修訂版本資訊'), query, Twinkle.fluff.callbacks.main); 	wikipedia_api.params = params; 	wikipedia_api.post(); };  Twinkle.fluff.revertToRevision = function revertToRevision(oldrev) {  	var summary = ''; 	if (document.getElementsByName('revertsummary')[0] !== undefined) { 		summary = document.getElementsByName('revertsummary')[0].value; 	}  	Morebits.status.init(document.getElementById('mw-content-text'));  	var query = { 		action: 'query', 		prop: ['info', 'revisions'], 		titles: mw.config.get('wgPageName'), 		rvlimit: 1, 		rvstartid: oldrev, 		rvprop: [ 'ids', 'user' ], 		format: 'xml', 		curtimestamp: '', 		meta: 'tokens', 		type: 'csrf' 	}; 	var wikipedia_api = new Morebits.wiki.api(wgULS('抓取较早修订版本信息', '抓取較早修訂版本資訊'), query, Twinkle.fluff.callbacks.toRevision); 	wikipedia_api.params = { rev: oldrev, summary: summary }; 	wikipedia_api.post(); };  Twinkle.fluff.callbacks = { 	toRevision: function(apiobj) { 		var xmlDoc = apiobj.responseXML;  		var lastrevid = parseInt($(xmlDoc).find('page').attr('lastrevid'), 10); 		var touched = $(xmlDoc).find('page').attr('touched'); 		var loadtimestamp = $(xmlDoc).find('api').attr('curtimestamp'); 		var csrftoken = $(xmlDoc).find('tokens').attr('csrftoken'); 		var revertToRevID = parseInt($(xmlDoc).find('rev').attr('revid'), 10);  		var revertToUser = $(xmlDoc).find('rev').attr('user'); 		var revertToUserHidden = typeof $(xmlDoc).find('rev').attr('userhidden') === 'string';  		if (revertToRevID !== apiobj.params.rev) { 			apiobj.statelem.error(wgULS('抓取到的修订版本与请求的修订版本不符,取消。', '抓取到的修訂版本與請求的修訂版本不符,取消。')); 			return; 		}  		var optional_summary = prompt(wgULS('请输入回退理由:', '請輸入回退理由:') + '                                ', apiobj.params.summary);  // padded out to widen prompt in Firefox 		if (optional_summary === null) { 			apiobj.statelem.error(wgULS('由用户取消。', '由使用者取消。')); 			return; 		}  		var summary = Twinkle.fluff.formatSummary(wgULS('回退到由$USER做出的修订版本', '回退到由$USER做出的修訂版本') + revertToRevID, 			revertToUserHidden ? null : revertToUser, optional_summary);  		var query = { 			action: 'edit', 			title: mw.config.get('wgPageName'), 			summary: summary, 			tags: Twinkle.changeTags, 			token: csrftoken, 			undo: lastrevid, 			undoafter: revertToRevID, 			basetimestamp: touched, 			starttimestamp: loadtimestamp, 			minor: Twinkle.getPref('markRevertedPagesAsMinor').indexOf('torev') !== -1 ? true : undefined, 			bot: true 		}; 		// 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 (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(wgULS('保存回退内容', '儲存回退內容'), query, Twinkle.fluff.callbacks.complete, apiobj.statelem); 		wikipedia_api.params = apiobj.params; 		wikipedia_api.post(); 	}, 	main: function(apiobj) { 		var xmlDoc = apiobj.responseXML;  		if (typeof $(xmlDoc).find('actions').attr('edit') === 'undefined') { 			apiobj.statelem.error("Unable to edit the page, it's probably protected."); 			return; 		}  		var lastrevid = parseInt($(xmlDoc).find('page').attr('lastrevid'), 10); 		var touched = $(xmlDoc).find('page').attr('touched'); 		var loadtimestamp = $(xmlDoc).find('api').attr('curtimestamp'); 		var csrftoken = $(xmlDoc).find('tokens').attr('csrftoken');  		var revs = $(xmlDoc).find('rev');  		var statelem = apiobj.statelem; 		var params = apiobj.params;  		if (revs.length < 1) { 			statelem.error(wgULS('没有其它修订版本,无法回退', '沒有其它修訂版本,無法回退')); 			return; 		} 		var top = revs[0]; 		var lastuser = top.getAttribute('user');  		if (lastrevid < params.revid) { 			Morebits.status.error(wgULS('错误', '錯誤'), wgULS([ '从服务器获取的最新修订版本ID ', Morebits.htmlNode('strong', lastrevid), ' 小于目前所显示的修订版本ID。这可能意味着当前修订版本已被删除、服务器延迟、或抓取到了坏掉的信息。取消。' ], [ '從伺服器取得的最新修訂版本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('警告', wgULS([ '最新修订版本 ', Morebits.htmlNode('strong', lastrevid), ' 与我们的修订版本 ', Morebits.htmlNode('strong', params.revid), '不同' ], [ '最新修訂版本 ', Morebits.htmlNode('strong', lastrevid), ' 與我們的修訂版本 ', Morebits.htmlNode('strong', params.revid), ' 不同' ])); 			if (lastuser === params.user) { 				switch (params.type) { 					case 'vand': 						Morebits.status.info(wgULS('信息', '資訊'), [ wgULS('最新修订版本由 ', '最新修訂版本由 '), Morebits.htmlNode('strong', userNorm), wgULS(' 做出,因我们假定破坏,继续回退操作。', ' 做出,因我們假定破壞,繼續回退操作。') ]); 						break; 					case 'agf': 						Morebits.status.warn('警告', [ wgULS('最新修订版本由 ', '最新修訂版本由 '), Morebits.htmlNode('strong', userNorm), wgULS(' 做出,因我们假定善意,取消回退操作,因为问题可能已被修复。', ' 做出,因我們假定善意,取消回退操作,因為問題可能已被修復。') ]); 						return; 					default: 						Morebits.status.warn('提示', [ wgULS('最新修订版本由 ', '最新修訂版本由 '), Morebits.htmlNode('strong', userNorm), wgULS(' 做出,但我们还是不回退了。', ' 做出,但我們還是不回退了。') ]); 						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.getAttribute('user')) !== -1 && revs.length > 1 && 					revs[1].getAttribute('revid') === params.revid) { 				Morebits.status.info(wgULS('信息', '資訊'), wgULS([ '最新修订版本由 ', Morebits.htmlNode('strong', lastuser), ',一个可信的机器人做出,但之前的版本被认为是破坏,继续回退操作。' ], [ '最新修訂版本由 ', Morebits.htmlNode('strong', lastuser), ',一個可信的機器人做出,但之前的版本被認為是破壞,繼續回退操作。' ])); 				index = 2; 			} else { 				Morebits.status.error(wgULS('错误', '錯誤'), wgULS([ '最新修订版本由 ', Morebits.htmlNode('strong', lastuser), ' 做出,所以这个修订版本可能已经被回退了,取消回退操作。'], [ '最新修訂版本由 ', 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(wgULS('信息', '資訊'), [ wgULS('将对 ', '將對 '), Morebits.htmlNode('strong', userNorm), wgULS(' 执行破坏回退,这是一个可信的机器人,我们假定您要回退前一个修订版本。', ' 執行破壞回退,這是一個可信的機器人,我們假定您要回退前一個修訂版本。') ]); 					index = 2; 					params.user = revs[1].getAttribute('user'); 					params.userHidden = revs[1].getAttribute('userhidden') === ''; 					break; 				case 'agf': 					Morebits.status.warn('提示', [ wgULS('将对 ', '將對 '), Morebits.htmlNode('strong', userNorm), wgULS(' 执行善意回退,但这是一个可信的机器人,取消回退操作。', ' 執行善意回退,但這是一個可信的機器人,取消回退操作。') ]); 					return; 				case 'norm': 				/* falls through */ 				default: 					var cont = confirm(wgULS('选择了常规回退,但最新修改是由一个可信的机器人(', '選擇了常規回退,但最新修改是由一個可信的機器人(') + userNorm + wgULS(')做出的。确定以回退前一个修订版本,取消以回退机器人的修改', ')做出的。確定以回退前一個修訂版本,取消以回退機器人的修改')); 					if (cont) { 						Morebits.status.info(wgULS('信息', '資訊'), [ wgULS('将对 ', '將對 '), Morebits.htmlNode('strong', userNorm), wgULS(' 执行常规回退,这是一个可信的机器人,基于确认,我们将回退前一个修订版本。', ' 執行常規回退,這是一個可信的機器人,基於確認,我們將回退前一個修訂版本。') ]); 						index = 2; 						params.user = revs[1].getAttribute('user'); 						params.userHidden = revs[1].getAttribute('userhidden') === ''; 						userNorm = params.user || Twinkle.fluff.hiddenName; 					} else { 						Morebits.status.warn('提示', [ wgULS('将对 ', '將對 '), Morebits.htmlNode('strong', userNorm), wgULS(' 执行常规回退,这是一个可信的机器人,基于确认,我们仍将回退这个修订版本。', ' 執行常規回退,這是一個可信的機器人,基於確認,我們仍將回退這個修訂版本。') ]); 					} 					break; 			} 		} 		var found = false; 		var count = 0;  		for (var i = index; i < revs.length; ++i) { 			++count; 			if (revs[i].getAttribute('user') !== params.user) { 				found = i; 				break; 			} 		}  		if (!found) { 			statelem.error([ wgULS('未找到之前的修订版本,可能 ', '未找到之前的修訂版本,可能 '), Morebits.htmlNode('strong', userNorm), wgULS(' 是唯一贡献者,或这个用户连续做出了超过 ', ' 是唯一貢獻者,或這個用戶連續做出了超過 ') + mw.language.convertNumber(Twinkle.getPref('revertMaxRevisions')) + wgULS(' 次编辑。', ' 次編輯。') ]); 			return; 		}  		if (!count) { 			Morebits.status.error(wgULS('错误', '錯誤'), wgULS('我们将要回退0个修订版本,这没有意义,所以取消回退操作。可能是因为这个修订版本已经被回退,但修订版本ID仍是一样的。', '我們將要回退0個修訂版本,這沒有意義,所以取消回退操作。可能是因為這個修訂版本已經被回退,但修訂版本ID仍是一樣的。')); 			return; 		}  		var good_revision = revs[found]; 		var userHasAlreadyConfirmedAction = false; 		if (params.type !== 'vand' && count > 1) { 			if (!confirm(userNorm + wgULS(' 连续做出了 ', ' 連續做出了 ') + mw.language.convertNumber(count) + wgULS(' 次编辑,是否要全部回退?', ' 次編輯,是否要全部回退?'))) { 				Morebits.status.info('提示', wgULS('用户取消操作', '使用者取消操作')); 				return; 			} 			userHasAlreadyConfirmedAction = true; 		}  		params.count = count;  		params.goodid = good_revision.getAttribute('revid'); 		params.gooduser = good_revision.getAttribute('user'); 		params.gooduserHidden = good_revision.getAttribute('userhidden') === '';  		statelem.status([ Morebits.htmlNode('strong', mw.language.convertNumber(count)), wgULS(' 个修订版本之前由 ', ' 個修訂版本之前由 '), Morebits.htmlNode('strong', params.gooduserHidden ? Twinkle.fluff.hiddenName : params.gooduser), wgULS(' 做出的修订版本 ', ' 做出的修訂版本 '), Morebits.htmlNode('strong', params.goodid) ]);  		var summary, extra_summary; 		switch (params.type) { 			case 'agf': 				extra_summary = prompt(wgULS('可选的编辑摘要:', '可選的編輯摘要:') + '                              ', params.summary);  // padded out to widen prompt in Firefox 				if (extra_summary === null) { 					statelem.error(wgULS('用户取消操作。', '使用者取消操作。')); 					return; 				} 				userHasAlreadyConfirmedAction = true;  				summary = Twinkle.fluff.formatSummary(wgULS('回退$USER做出的出于[[WP:AGF|善意]]的编辑', '回退$USER做出的出於[[WP:AGF|善意]]的編輯'), 					params.userHidden ? null : params.user, extra_summary); 				break;  			case 'vand': 				summary = Twinkle.fluff.formatSummary('回退$USER做出的' + params.count + wgULS('次编辑,到由', '次編輯,到由') + 					(params.gooduserHidden ? Twinkle.fluff.hiddenName : params.gooduser) + wgULS('做出的最后修订版本 ', '做出的最後修訂版本 '), params.userHidden ? null : params.user); 				break;  			case 'norm': 			/* falls through */ 			default: 				if (Twinkle.getPref('offerReasonOnNormalRevert')) { 					extra_summary = prompt(wgULS('可选的编辑摘要:', '可選的編輯摘要:') + '                              ', params.summary);  // padded out to widen prompt in Firefox 					if (extra_summary === null) { 						statelem.error(wgULS('用户取消操作。', '使用者取消操作。')); 						return; 					} 					userHasAlreadyConfirmedAction = true; 				}  				summary = Twinkle.fluff.formatSummary('回退$USER做出的' + params.count + wgULS('次编辑', '次編輯'), 					params.userHidden ? null : params.user, extra_summary); 				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(wgULS('回退页面:您确定吗?', '回退頁面:您確定嗎?'))) { 			statelem.error(wgULS('用户取消操作。', '使用者取消操作。')); 			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.getAttribute('timestamp'); 		}  		var query = { 			action: 'edit', 			title: params.pagename, 			summary: summary, 			tags: Twinkle.changeTags, 			token: csrftoken, 			undo: lastrevid, 			undoafter: params.goodid, 			basetimestamp: touched, 			starttimestamp: loadtimestamp, 			minor: Twinkle.getPref('markRevertedPagesAsMinor').indexOf(params.type) !== -1 ? true : undefined, 			bot: true 		}; 		// 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 (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(wgULS('保存回退内容', '儲存回退內容'), 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 xml = apiobj.getXML(); 		var $edit = $(xml).find('edit');  		if ($(xml).find('captcha').length > 0) { 			apiobj.statelem.error(wgULS('不能回退,因维基服务器要求您输入验证码。', '不能回退,因維基伺服器要求您輸入驗證碼。')); 		} else if ($edit.attr('nochange') === '') { 			apiobj.statelem.error(wgULS('要回退到的版本与当前版本相同,没什么要做的', '要回退到的版本與目前版本相同,沒什麼要做的')); 		} else { 			apiobj.statelem.info('完成'); 			var params = apiobj.params;  			if (params.notifyUser && !params.userHidden) { // notifyUser only from main, not from toRevision 				Morebits.status.info(wgULS('信息', '資訊'), [wgULS('开启用户 ', '開啟使用者 '), Morebits.htmlNode('strong', params.user), wgULS(' 的讨论页', ' 的討論頁')]);  				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; 				} 			} 		} 	} };  // 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 + wgULS('|讨论]])', '|討論]])'); 				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>