User:NguoiDungKhongDinhDanh/Scripts/pageswap.js

// For attribution: User:Ahecht/pageswap.js

if (typeof pagemoveDoPostMoveCleanup === 'undefined') { pagemoveDoPostMoveCleanup = true; } var pagemoveLink = "pageswap";

$(document).ready(function { mw.loader.using( [ 'mediawiki.api', 'mediawiki.util', ] ).then( function { "use strict";

/** * If user is able to perform swaps */ function checkUserPermissions { var ret = {}; ret.canSwap = true; var reslt = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async:false, error: function (jsondata) { mw.notify("Swapping pages unavailable.", { title: 'Page Swap Error', type: 'error' }); return ret; }, data: { action:'query', format:'json', meta:'userinfo', uiprop:'rights' } }).responseText).query.userinfo;

// check userrights for suppressredirect and move-subpages var rightslist = reslt.rights; ret.canSwap = $.inArray('suppressredirect', rightslist) > -1 && $.inArray('move-subpages', rightslist) > -1; ret.allowSwapTemplates = $.inArray('templateeditor', rightslist) > -1;

return ret; }

/** * Given namespace data, title, title namespace, returns expected title of page * Along with title without prefix * Precondition, title, titleNs is a subject page! */ function getTalkPageName(nsData, title, titleNs) { var ret = {}; var prefixLength = nsData['' + titleNs]['*'].length === 0 ? 0 : nsData['' + titleNs]['*'].length + 1; ret.titleWithoutPrefix = title.substring(prefixLength, title.length); ret.talkTitle = nsData['' + (titleNs + 1)]['*'] + ':' + ret.titleWithoutPrefix; return ret; }

/** * Given two (normalized) titles, find their namespaces, if they are redirects, * if have a talk page, whether the current user can move the pages, suggests * whether movesubpages should be allowed, whether talk pages need to be checked */ function swapValidate(titleOne, titleTwo, pagesData, nsData, uPerms) { var ret = {}; ret.valid = true; if (titleOne === null || titleTwo === null || pagesData === null) { ret.valid = false; ret.invalidReason = "Unable to validate swap."; return ret; }

ret.allowMoveSubpages = true; ret.checkTalk = true; var count = 0; for (var k in pagesData) { ++count; if (k == "-1" || pagesData[k].ns < 0) { ret.valid = false; ret.invalidReason = ("Page " + pagesData[k].title + " does not exist."); return ret; }		// enable in ns 0 to 5, 10 to 15, 118 and 119 (Main, Talk, U, UT, WP, WT, T, TT, H, HT, C, CT, D, DT) if ((pagesData[k].ns >= 6 && pagesData[k].ns <= 9)		 || (pagesData[k].ns >= 16 && pagesData[k].ns <= 117)		 || (pagesData[k].ns >= 120)) { ret.valid = false; ret.invalidReason = ("Namespace of " + pagesData[k].title + " (" + pagesData[k].ns + ") not supported.\n\nLikely reasons:\n"				+ "- Names of pages in this namespace relies on other pages\n"				+ "- Namespace features heavily-transcluded pages\n"				+ "- Namespace involves subpages: swaps produce many redlinks\n"				+ "\n\nIf the move is legitimate, consider a careful manual swap."); return ret; }		if (titleOne == pagesData[k].title) { ret.currTitle  = pagesData[k].title; ret.currNs     = pagesData[k].ns; ret.currTalkId = pagesData[k].talkid; // could be undefined ret.currCanMove = pagesData[k].actions.move === ''; ret.currIsRedir = pagesData[k].redirect === ''; }		if (titleTwo == pagesData[k].title) { ret.destTitle  = pagesData[k].title; ret.destNs     = pagesData[k].ns; ret.destTalkId = pagesData[k].talkid; // could be undefined ret.destCanMove = pagesData[k].actions.move === ''; ret.destIsRedir = pagesData[k].redirect === ''; }	}

if (!ret.valid) return ret; if (!ret.currCanMove) { ret.valid = false; ret.invalidReason = ('' + ret.currTitle + " is immovable. Aborting"); return ret; }	if (!ret.destCanMove) { ret.valid = false; ret.invalidReason = ('' + ret.destTitle + " is immovable. Aborting"); return ret; }	if (ret.currNs % 2 !== ret.destNs % 2) { ret.valid = false; ret.invalidReason = "Namespaces don't match: one is a talk page."; return ret; }	if (count !== 2) { ret.valid = false; ret.invalidReason = "Pages have the same title. Aborting."; return ret; }	ret.currNsAllowSubpages = nsData[ + ret.currNs].subpages !== ; ret.destNsAllowSubpages = nsData[ + ret.destNs].subpages !== ;

// if same namespace (subpages allowed), if one is subpage of another, // disallow movesubpages if (ret.currTitle.startsWith(ret.destTitle + '/')			|| ret.destTitle.startsWith(ret.currTitle + '/')) { if (ret.currNs !== ret.destNs) { ret.valid = false; ret.invalidReason = "Strange.\n" + ret.currTitle + " in ns " + ret.currNs + "\n" + ret.destTitle + " in ns " + ret.destNs + ". Disallowing."; return ret; }

ret.allowMoveSubpages = ret.currNsAllowSubpages; if (!ret.allowMoveSubpages) ret.addlInfo = "One page is a subpage. Disallowing move-subpages"; }

if (ret.currNs % 2 === 1) { ret.checkTalk = false; // no need to check talks, already talk pages } else { // ret.checkTalk = true; var currTPData = getTalkPageName(nsData, ret.currTitle, ret.currNs); ret.currTitleWithoutPrefix = currTPData.titleWithoutPrefix; ret.currTalkName = currTPData.talkTitle; var destTPData = getTalkPageName(nsData, ret.destTitle, ret.destNs); ret.destTitleWithoutPrefix = destTPData.titleWithoutPrefix; ret.destTalkName = destTPData.talkTitle; // possible: ret.currTalkId undefined, but subject page has talk subpages }

return ret; }

/** * Given two talk page titles (may be undefined), retrieves their pages for comparison * Assumes that talk pages always have subpages enabled. * Assumes that pages are not identical (subject pages were already verified) * Assumes namespaces are okay (subject pages already checked) * (Currently) assumes that the malicious case of subject pages *  not detected as subpages and the talk pages ARE subpages *  (i.e. A and A/B vs. Talk:A and Talk:A/B) does not happen / does not handle * Returns structure indicating whether move talk should be allowed */ function talkValidate(checkTalk, talk1, talk2) { var ret = {}; ret.allowMoveTalk = true; if (!checkTalk) { return ret; } // currTitle destTitle already talk pages if (talk1 === undefined || talk2 === undefined) { mw.notify("Unable to validate talk. Disallowing movetalk to be safe", { title: 'Page Swap Error', type: 'warn' }); ret.allowMoveTalk = false; return ret; }	ret.currTDNE = true; ret.destTDNE = true; ret.currTCanCreate = true; ret.destTCanCreate = true; var talkTitleArr = [talk1, talk2]; if (talkTitleArr.length !== 0) { var talkData = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async:false, error: function (jsondata) { mw.notify("Unable to get info on talk pages.", { title: 'Page Swap Error', type: 'error' }); return ret; }, data: { action:'query', format:'json', prop:'info', intestactions:'move|create', titles:talkTitleArr.join('|') } }).responseText).query.pages; for (var id in talkData) { if (talkData[id].title === talk1) { ret.currTDNE = talkData[id].invalid ===  || talkData[id].missing === ; ret.currTTitle = talkData[id].title; ret.currTCanMove = talkData[id].actions.move === ''; ret.currTCanCreate = talkData[id].actions.create === ''; ret.currTalkIsRedir = talkData[id].redirect === ''; } else if (talkData[id].title === talk2) { ret.destTDNE = talkData[id].invalid ===  || talkData[id].missing === ; ret.destTTitle = talkData[id].title; ret.destTCanMove = talkData[id].actions.move === ''; ret.destTCanCreate = talkData[id].actions.create === ''; ret.destTalkIsRedir = talkData[id].redirect === ''; } else { mw.notify("Found pageid ("+talkData[id].title+") not matching given ids ("+talk1+" and "+talk2+").", { title: 'Page Swap Error', type: 'error' }); return {}; }		}	}

ret.allowMoveTalk = (ret.currTCanCreate && ret.currTCanMove) && (ret.destTCanCreate && ret.destTCanMove); return ret; }

/** * Given existing title (not prefixed with "/"), optionally searching for talk, *  finds subpages (incl. those that are redirs) and whether limits are exceeded * As of 2016-08, uses 2 api get calls to get needed details: *  whether the page can be moved, whether the page is a redirect */ function getSubpages(nsData, title, titleNs, isTalk) { if ((!isTalk) && nsData[ + titleNs].subpages !== ) { return { data:[] }; } var titlePageData = getTalkPageName(nsData, title, titleNs); var subpages = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async:false, error: function (jsondata) { return { error:"Unable to search for subpages. They may exist" }; }, data: { action:'query', format:'json', list:'allpages', apnamespace:(isTalk ? (titleNs + 1) : titleNs), apfrom:(titlePageData.titleWithoutPrefix + '/'), apto:(titlePageData.titleWithoutPrefix + '0'), aplimit:101 } }).responseText).query.allpages;

// put first 50 in first arr (need 2 queries due to api limits) var subpageids = ],[; for (var idx in subpages) { subpageids[idx < 50 ? 0 : 1].push( subpages[idx].pageid ); }

if (subpageids[0].length === 0) { return { data:[] }; } if (subpageids[1].length === 51) { return { error:"100+ subpages. Aborting" }; } var dataret = []; var subpageData0 = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async:false, error: function (jsondata) { return { error:"Unable to fetch subpage data." }; },		data: { action:'query', format:'json', prop:'info', intestactions:'move|create', pageids:subpageids[0].join('|') } }).responseText).query.pages; for (var k0 in subpageData0) { dataret.push({			title:subpageData0[k0].title,			isRedir:subpageData0[k0].redirect === ,			canMove:subpageData0[k0].actions.move === 		}); }

if (subpageids[1].length === 0) { return { data:dataret }; } var subpageData1 = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async: false, error: function (jsondata) { return { error:"Unable to fetch subpage data." }; },		data: { action:'query', format:'json', prop:'info', intestactions:'move|create', pageids:subpageids[1].join('|') } }).responseText).query.pages; for (var k1 in subpageData1) { dataret.push({			title:subpageData1[k1].title,			isRedir:subpageData1[k1].redirect === ,			canMove:subpageData1[k1].actions.move === 		}); }	return { data:dataret }; }

/** * Prints subpage data given retrieved subpage information returned by getSubpages * Returns a suggestion whether movesubpages should be allowed */ function printSubpageInfo(basepage, currSp) { var ret = {}; var currSpArr = []; var currSpCannotMove = []; var redirCount = 0; for (var kcs in currSp.data) { if (!currSp.data[kcs].canMove) { currSpCannotMove.push(currSp.data[kcs].title); }		currSpArr.push((currSp.data[kcs].isRedir ? "(R) " : " ")			+ currSp.data[kcs].title); if (currSp.data[kcs].isRedir) redirCount++; }

if (currSpArr.length > 0) { alert((currSpCannotMove.length > 0 ? "Disabling move-subpages.\n" + "The following " + currSpCannotMove.length + " (of "				+ currSpArr.length + ") total subpages of " + basepage + " CANNOT be moved:\n\n " + currSpCannotMove.join("\n ") + '\n\n' : (currSpArr.length + " total subpages of " + basepage + ".\n"			+ (redirCount !== 0 ? ( + redirCount + " redirects, labeled (R)\n") : )			+ '\n' + currSpArr.join('\n')))); }

ret.allowMoveSubpages = currSpCannotMove.length === 0; ret.noNeed = currSpArr.length === 0; return ret; }

function doDoneMsg(doneMsg) { if (/failed/ig.test(doneMsg)) { mw.notify(doneMsg, { tag: 'status', title: 'Page Swap Status', type: 'warn' }); } else { mw.notify(doneMsg, { tag: 'status', title: 'Page Swap Status', type: 'success' }); }	setTimeout( => {		if(confirm(doneMsg			+ "\nPlease create new red-linked talk pages/subpages if there are incoming links"			+ "\n (check your contribs for \"Talk:\" redlinks),"			+ "\n  correct any moved redirects, and do post-move cleanup if necessary.\n"			+ "\nOpen contribs page?"))		{			window.open(mw.util.getUrl("Special:Contributions")+'/'+mw.util.wikiUrlencode(mw.user.getName));		}	}, 250); }

function createMissingTalk(movedTalk, movedSubpages, vData, vTData, doneMsg) { if (movedTalk) { var fromTalk, toTalk; if (vTData.currTDNE && !vTData.destTDNE) { fromTalk = vData.destTalkName; toTalk = vData.currTalkName; } else if (vTData.destTDNE && !vTData.currTDNE) { fromTalk = vData.currTalkName; toTalk = vData.destTalkName; }		if (fromTalk && toTalk) { mw.notify("Talk page moved...", { tag: 'status', title: 'Page Swap Status' }); setTimeout( => {				if (confirm(doneMsg + "\nCreate redirect " + fromTalk						+ "\n→ " + toTalk + " if possible?")) {					var talkRedirect = {						action:'edit',						title:fromTalk,						createonly: true,						text: "#REDIRECT " + toTalk + "\n",						summary: "Create redirect to " + toTalk + " using " + pagemoveLink,						watchlist:"unwatch"					};					mw.notify("Creating talk page redirect...", { tag: 'status', title: 'Page Swap Status' });					new mw.Api.postWithToken("csrf", talkRedirect).done(function (resltc) { doDoneMsg("Redirect " + fromTalk							+ "\n→ " +toTalk + " created.\n"); }).fail(function (resltc) { doDoneMsg("Failed to create redirect: " + resltc + ".\n"); });				} else { doDoneMsg(""); }			}, 250); } else { doDoneMsg(doneMsg); } } else { doDoneMsg(doneMsg); } }

/** * After successful page swap, post-move cleanup: * Make talk page redirect * TODO more reasonable cleanup/reporting as necessary * vData.(curr|dest)IsRedir */ /** TO DO: *Check if talk is self redirect */ function doPostMoveCleanup(movedTalk, movedSubpages, vData, vTData, doneMsg, current = "currTitle", destination = "destTitle") { if (typeof doneMsg === 'undefined') { doneMsg = "Moves completed successfully.\n"; mw.notify(doneMsg, { tag: 'status', title: 'Page Swap Status', type: 'success' }); }	// Check for self redirect var rData = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async:false, error: function (jsondata) { mw.notify("Unable to get info about " + vData[current] + ".\n", { title: 'Page Swap Error', type: 'error' }); }, data: { action:'query', format:'json', redirects:'true', titles: vData[current] } }).responseText).query; if (rData && rData.redirects && rData.redirects[0].from == rData.redirects[0].to){ var parseData = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async:false, error: function (jsondata) { mw.notify("Unable to fetch contents of " + vData[current] + ".\n", { title: 'Page Swap Error', type: 'error' });	}, data: {action:'parse', format:'json', prop:'wikitext', page: vData[current] } }).responseText).parse; if (parseData) { var newWikitext = parseData.wikitext['*'].replace(/^#((?:REDIRECT|ĐỔI) *)\[\[ *.* *\]\]/i, ('#$1' + vData[destination] + '')); if (newWikitext != parseData.wikitext['*']) { mw.notify("Retargeting redirect at " + vData[current] + " to "	+ vData[destination] + "...", { tag: 'status', title: 'Page Swap Status' }); new mw.Api.postWithToken("csrf", {					action:'edit',					title: vData[current],					text: newWikitext,					summary : "Retarget redirect to " 						+ vData[destination] + " using "						+ pagemoveLink,					watchlist: "unwatch"				} ).done(function (resltc) {					doneMsg = doneMsg + "Redirect at " 						+ vData[current] + " retargeted to "						+ vData[destination] + ".\n";					if (current == "currTitle") {						doPostMoveCleanup(movedTalk, movedSubpages, vData, vTData, doneMsg, "currTalkName", "destTalkName");					} else {						createMissingTalk(movedTalk, movedSubpages, vData, vTData, doneMsg);					}				} ).fail(function (resltc) {					doneMsg = doneMsg + "Failed to retarget redirect at "						+ vData[current] + " to "						+ vData[destination] + ": " + resltc + ".\n";					if (current == "currTitle") {						doPostMoveCleanup(movedTalk, movedSubpages, vData, vTData, doneMsg, "currTalkName", "destTalkName"); } else { createMissingTalk(movedTalk, movedSubpages, vData, vTData, doneMsg); }				} );				return;			} else {				doneMsg = doneMsg + "Failed to retarget redirect at "					+ vData[current] + " to " + vData[destination]					+ ": String not found.\n";			}		} else {			doneMsg = doneMsg + "Failed to check contents of"				+ vData[current] + ": " + err + ".\n";		}	}	if (current == "currTitle") {		doPostMoveCleanup(movedTalk, movedSubpages, vData, vTData, doneMsg, "currTalkName", "destTalkName");	} else {		createMissingTalk(movedTalk, movedSubpages, vData, vTData, doneMsg);	} }

/** * Swaps the two pages (given all prerequisite checks) * Optionally moves talk pages and subpages */ function swapPages(titleOne, titleTwo, moveReason, intermediateTitlePrefix,		moveTalk, moveSubpages, vData, vTData) { if (titleOne === null || titleTwo === null			|| moveReason === null || moveReason === '') { mw.notify("Titles are null, or move reason given was empty. Swap not done", { title: 'Page Swap Error', type: 'error' }); return false; }

var intermediateTitle = intermediateTitlePrefix + titleOne; var pOne = { action:'move', from:titleTwo, to:intermediateTitle, reason:"Round-robin history swap step 1 using " + pagemoveLink, watchlist:"unwatch", noredirect:1 }; var pTwo = { action:'move', from:titleOne, to:titleTwo, reason:moveReason, watchlist:"unwatch", noredirect:1 }; var pTre = { action:'move', from:intermediateTitle, to:titleOne, reason:"Round-robin history swap step 3 using " + pagemoveLink, watchlist:"unwatch", noredirect:1 }; if (moveTalk) { pOne.movetalk = 1; pTwo.movetalk = 1; pTre.movetalk = 1; }	if (moveSubpages) { pOne.movesubpages = 1; pTwo.movesubpages = 1; pTre.movesubpages = 1; }	mw.notify("Doing round-robin history swap step 1...", { tag: 'status', title: 'Page Swap Status' }); new mw.Api.postWithToken("csrf", pOne).done(function (reslt1) {		mw.notify("Doing round-robin history swap step 2...", { tag: 'status', title: 'Page Swap Status' });		new mw.Api.postWithToken("csrf", pTwo).done(function (reslt2) { mw.notify("Doing round-robin history swap step 3...", { tag: 'status', title: 'Page Swap Status' }); new mw.Api.postWithToken("csrf", pTre).done(function (reslt3) {				if (pagemoveDoPostMoveCleanup) {					doPostMoveCleanup(moveTalk, moveSubpages, vData, vTData);				} else {					doDoneMsg("Moves completed successfully.\n");				}			}).fail(function (reslt3) {				doDoneMsg("Fail on third move " + intermediateTitle + " → " + titleOne + "\n");			}); }).fail(function (reslt2) { doDoneMsg("Fail on second move " + titleOne + " → " + titleTwo + "\n"); });	}).fail(function (reslt1) {		doDoneMsg("Fail on first move " + titleTwo + " → " + intermediateTitle + "\n");	}); }

/** * Given two titles, normalizes, does prerequisite checks for talk/subpages, * prompts user for config before swapping the titles */ function roundrobin(uPerms, currNs, currTitle, destTitle, intermediateTitlePrefix) { // get ns info (nsData.query.namespaces) var nsData = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async:false, error: function (jsondata) { mw.notify("Unable to get info about namespaces", { title: 'Page Swap Error', type: 'error' }); }, data: { action:'query', format:'json', meta:'siteinfo', siprop:'namespaces' } }).responseText).query.namespaces;

// get page data, normalize titles var relevantTitles = currTitle + "|" + destTitle; var pagesData = JSON.parse($.ajax({ url: mw.util.wikiScript('api'), async:false, error: function (jsondata) { mw.notify("Unable to get info about " + currTitle + " or " + destTitle, { title: 'Page Swap Error', type: 'error' }); },		data: { action:'query', format:'json', prop:'info', inprop:'talkid', intestactions:'move|create', titles:relevantTitles } }).responseText).query;

for (var kp in pagesData.normalized) { if (currTitle == pagesData.normalized[kp].from) { currTitle = pagesData.normalized[kp].to; } if (destTitle == pagesData.normalized[kp].from) { destTitle = pagesData.normalized[kp].to; } }	// validate namespaces, not identical, can move var vData = swapValidate(currTitle, destTitle, pagesData.pages, nsData, uPerms); if (!vData.valid) { mw.notify(vData.invalidReason, { title: 'Page Swap Error', type: 'error' }); return; } if (vData.addlInfo !== undefined) { mw.notify(vData.addlInfo, { title: 'Page Swap Error', type: 'error' }); }

// subj subpages var currSp = getSubpages(nsData, vData.currTitle, vData.currNs, false); if (currSp.error !== undefined) { mw.notify(currSp.error, { title: 'Page Swap Error', type: 'error' }); return; } var currSpFlags = printSubpageInfo(vData.currTitle, currSp); var destSp = getSubpages(nsData, vData.destTitle, vData.destNs, false); if (destSp.error !== undefined) { mw.notify(destSp.error, { title: 'Page Swap Error', type: 'error' }); return; } var destSpFlags = printSubpageInfo(vData.destTitle, destSp);

var vTData = talkValidate(vData.checkTalk, vData.currTalkName, vData.destTalkName);

// future goal: check empty subpage DESTINATIONS on both sides (subj, talk) //  for create protection. disallow move-subpages if any destination is salted var currTSp = getSubpages(nsData, vData.currTitle, vData.currNs, true); if (currTSp.error !== undefined) { mw.notify(currTSp.error, { title: 'Page Swap Error', type: 'error' }); return; } var currTSpFlags = printSubpageInfo(vData.currTalkName, currTSp); var destTSp = getSubpages(nsData, vData.destTitle, vData.destNs, true); if (destTSp.error !== undefined) { mw.notify(destTSp.error, { title: 'Page Swap Error', type: 'error' }); return; } var destTSpFlags = printSubpageInfo(vData.destTalkName, destTSp);

var noSubpages = currSpFlags.noNeed && destSpFlags.noNeed && currTSpFlags.noNeed && destTSpFlags.noNeed; // If one ns disables subpages, other enables subpages, AND HAS subpages, //  consider abort. Assume talk pages always safe (TODO fix) var subpageCollision = (vData.currNsAllowSubpages && !destSpFlags.noNeed) || (vData.destNsAllowSubpages && !currSpFlags.noNeed);

var moveSubpages = false; // TODO future: currTSpFlags.allowMoveSubpages && destTSpFlags.allowMoveSubpages // needs to be separate check. If talk subpages immovable, should not affect subjspace if (!subpageCollision && !noSubpages && vData.allowMoveSubpages			&& (currSpFlags.allowMoveSubpages && destSpFlags.allowMoveSubpages)			&& (currTSpFlags.allowMoveSubpages && destTSpFlags.allowMoveSubpages)) { moveSubpages = confirm("Move subpages? (OK for yes, Cancel for no)"); } else if (subpageCollision) { mw.notify("One namespace does not have subpages enabled. Disallowing move subpages", { title: 'Page Swap Error', type: 'error' }); }

var moveTalk = false; // TODO: count subpages and make restrictions? if (vData.checkTalk && (!vTData.currTDNE || !vTData.destTDNE || moveSubpages)) { if (vTData.allowMoveTalk) { moveTalk = confirm("Move talk page(s)? (OK for yes, Cancel for no)"); } else { alert("Disallowing moving talk. "				+ (!vTData.currTCanCreate ? (vData.currTalkName + " is create-protected") : (!vTData.destTCanCreate ? (vData.destTalkName + " is create-protected")				: "Talk page is immovable"))); }	}	var moveReason = ''; var moveReasonPrompt = ''; if (typeof mw.util.getParamValue("wpReason") === 'string') { moveReasonPrompt = mw.util.getParamValue("wpReason"); } else if (document.getElementsByName("wpReason")[0] && document.getElementsByName("wpReason")[0].value != '') { moveReasonPrompt = document.getElementsByName("wpReason")[0].value; } else if (typeof moveReasonDefault === 'string') { moveReasonPrompt = moveReasonDefault; }	moveReason = prompt("Move reason:", moveReasonPrompt); var confirmString = "Round-robin configuration:\n " + currTitle + " → " + destTitle + "\n   : " + moveReason + "\n     with movetalk:" + moveTalk + ", movesubpages:" + moveSubpages + "\n\nProceed? (Cancel to abort)";

if (confirm(confirmString)) { swapPages(currTitle, destTitle, moveReason, intermediateTitlePrefix,			moveTalk, moveSubpages, vData, vTData); } }

var currNs = mw.config.get("wgNamespaceNumber"); var wpOldTitle = mw.util.getParamValue("wpOldTitle"); if (!wpOldTitle && document.getElementsByName("wpOldTitle")[0] && document.getElementsByName("wpOldTitle")[0].value != ''){ wpOldTitle = document.getElementsByName("wpOldTitle")[0].value; }	var wpNewTitle = mw.util.getParamValue("wpNewTitle"); if (!wpNewTitle && document.getElementsByName("wpNewTitleMain")[0] && document.getElementsByName("wpNewTitleMain")[0].value != '' && document.getElementsByName("wpNewTitleNs")[0]){ wpNewTitle = document.getElementsByName("wpNewTitleMain")[0].value; var nsid = document.getElementsByName("wpNewTitleNs")[0].value; if (nsid != 0) { wpNewTitle = mw.config.get("wgFormattedNamespaces")[nsid] + ":" + wpNewTitle; }	}	if (currNs < -1 || currNs >= 120			|| (currNs >= 6 && currNs <= 9)			|| (currNs >= 16 && currNs <= 99)			|| (currNs == -1 && mw.config.get("wgCanonicalSpecialPageName") != "Movepage")			|| (mw.config.get("wgCanonicalSpecialPageName") == "Movepage" && !wpOldTitle)			) return; // special/other page

var portletLink = mw.util.addPortletLink("p-cactions", "#", "Swap",		"ca-swappages", "Perform a revision history swap / round-robin move"); $( portletLink ).click(function(e) {		e.preventDefault;		var userPermissions = checkUserPermissions;		if (!userPermissions.canSwap) {			mw.notify("User rights insufficient for action.", { title: 'Page Swap Error', type: 'error' }); return;		}		var currTitle = wpOldTitle || mw.config.get("wgPageName");		var destTitle = wpNewTitle || prompt("Swap \"" + (currTitle.replace(/_/g, ' ')) + "\" with:", (currTitle.replace(/_/g, ' ')));		return roundrobin(userPermissions, currNs, currTitle, destTitle, "Draft:Move/");	}); if (mw.config.get("wgCanonicalSpecialPageName") == "Movepage" 			&& $( "div.mw-message-box-error" ).find( "p" ).eq(1).is( ":contains('name already exists')" )			&& wpOldTitle) {		$( "div.mw-message-box-error" ).find( "p" ).eq(2).html( 'Please choose another name, or perform a swap.' ); $( "#pageswapLink" ).click(function(e) {			e.preventDefault;			$( portletLink ).click;		}); }

}); });