User:Splarka/ajaxbatchmove.js

/* Ajax batch move module, version [0.0.3c] Originally from: http://en.wikipedia.org/wiki/User:Splarka/ajaxbatchmove.js

Notes:
 * It is a bit verbose, after debugging perhaps some output should be removed.
 * It waits 1 second after every move before starting the next.
 * Can be aborted by simply deleting the contents of the textarea, or leaving the page.
 * Stops when it hits a blank line, or line with no pipe.
 * Nonfatal errors (skip to next line):
 * Bad character or malformed line
 * Bad token
 * Unexpected response
 * Pauses in execution can be added with a blank line.
 * Not been postively tested! As of this writing API moves are broken on test.wikipedia [5:23 PM 3/13/09]
 * Suppressing redirects should work, maybe. I didn't bother with move subpage/talkpage switches, too scary!

To do:
 * Cache the token if two the same?
 * Please note the move token is not guaranteed to be static, but currently it always is.

$(function { mw.util.addPortletLink('p-tb','/wiki/Special:BlankPage?blankspecial=ajaxbm','Batch Move'); });

if(wgCanonicalSpecialPageName && wgCanonicalSpecialPageName.toLowerCase == 'blankpage' && queryString('blankspecial') == 'ajaxbm') { document.title = 'Ajax Batch Move'; addOnloadHook(abmForm); }

function abmForm { mw.util.addPortletLink('p-tb','/wiki/Special:Log/move?user=' + encodeURIComponent(wgUserName),'My move log');

//subvert this Special: page to our own needs. var con = document.getElementById('content') || document.getElementById('mw_content'); var bcon = document.getElementById('bodyContent') || document.getElementById('mw_contentholder'); var fh = getElementsByClassName(con,'h1','firstHeading')[0]; while(fh.firstChild) fh.removeChild(fh.firstChild) fh.appendChild(document.createTextNode('Ajax Batch Move')); for(var i=0;i<bcon.childNodes.length;i++) { bcur = bcon.childNodes[i]; if(bcur.id != 'siteSub' && bcur.id != 'contentSub' && bcur.className != 'visualClear') { while(bcur.firstChild) bcur.removeChild(bcur.firstChild) if(bcur.nodeType == 3) bcur.nodeValue = ''; } }

//generate content (this is ugly yes, but better than innerHTML and very portable; only needs wikibits.js) var form = document.createElement('form'); form.appendChild(document.createTextNode('Enter the pages to be moved here, one per line, with the format: Old title|New title')); form.setAttribute('action','javascript:void(0)'); form.appendChild(document.createElement('p')); form.setAttribute('action','javascript:void(0);'); var txt = document.createElement('textarea'); txt.style.height = '20em'; txt.style.width = '50%'; txt.setAttribute('id','abm-textarea'); form.appendChild(txt); form.appendChild(document.createElement('p')); var lab1 = document.createElement('label'); lab1.setAttribute('for','abm-reason') lab1.appendChild(document.createTextNode('Move reason: ')); form.appendChild(lab1); var inp1 = document.createElement('input'); inp1.style.width = '20em'; inp1.setAttribute('type','text'); inp1.setAttribute('id','abm-reason'); form.appendChild(inp1); var inp2 = document.createElement('input'); inp2.setAttribute('type','checkbox'); inp2.setAttribute('id','abm-redirect'); form.appendChild(inp2); var lab2 = document.createElement('label'); lab2.setAttribute('for','abm-redirect') lab2.appendChild(document.createTextNode('Suppress redirects')); form.appendChild(lab2); form.appendChild(document.createElement('p')); var sub1 = document.createElement('input'); sub1.setAttribute('type','button'); sub1.setAttribute('id','abm-startbutton'); sub1.setAttribute('value','start'); addHandler(sub1,'click',abmStart); form.appendChild(sub1); bcon.appendChild(form); var pre = document.createElement('pre'); pre.setAttribute('id','abm-output'); bcon.appendChild(pre); }

function abmStart { document.getElementById('abm-startbutton').setAttribute('disabled','disabled'); var out = document.getElementById('abm-output'); var txt = document.getElementById('abm-textarea'); var moves = txt.value.split('\n'); var move = moves[0]; if(move.indexOf('|') == -1 || move == '') { out.appendChild(document.createTextNode('* Done! Nothing left to do, or next line is blank.\n')); document.getElementById('abm-startbutton').removeAttribute('disabled'); } else { var badchars = /(\#|\<|\>|\[|\]|\{|\}|\|.*\|)/; if(badchars.test(move)) { out.appendChild(document.createTextNode('! Illegal characters detected, skipping:' + move + '\n')); setTimeout('abmStart',1000); } else { var oldtitle = move.substr(0,move.indexOf('|')); var newtitle = move.substr(move.indexOf('|')+1); out.appendChild(document.createTextNode('> Attempting to move ' + oldtitle + ' to ' + newtitle + '\n')); abdGetToken(oldtitle,newtitle); } }  moves = moves.slice(1,moves.length); txt.value = moves.join('\n'); }

function abdGetToken(oldtitle,newtitle) { var out = document.getElementById('abm-output'); out.appendChild(document.createTextNode(' > Fetching move token for ' + oldtitle + '\n')); var url = wgScriptPath + '/api.php?action=query&prop=info&indexpageids=1&intoken=move&format=json&titles=' + encodeURIComponent(oldtitle); var req = sajax_init_object; req.open('GET', url, true); req.onreadystatechange = function { if(req.readyState == 4 && req.status == 200) { eval("abmMove(" + req.responseText + ",'" + req.responseText.replace(/\'/g,"`") + "','" + oldtitle + "','" + newtitle + "')"); } }  req.send(null); }

function abmMove(obj,txt,oldtitle,newtitle) { var out = document.getElementById('abm-output'); if(obj['error']) { out.appendChild(document.createTextNode(' ! Api error: ' + obj['error']['code'] + ' - ' + obj['error']['info'] + '\n')); return; } if(!obj['query'] || !obj['query']['pageids'] || !obj['query']['pages'][obj['query']['pageids'][0]] || !obj['query']['pages'][obj['query']['pageids'][0]]['movetoken']) { out.appendChild(document.createTextNode(' ? Unexpected response: ' + txt + '\n')); return; } var token = obj['query']['pages'][obj['query']['pageids'][0]]['movetoken']; out.appendChild(document.createTextNode(' > Token found, attempting move\n')); var reason = document.getElementById('abm-reason').value;

var params = 'action=move&format=json&token=' + encodeURIComponent(token) + '&from=' + encodeURIComponent(oldtitle) + '&to=' + encodeURIComponent(newtitle) + '&reason=' + encodeURIComponent(reason); if(document.getElementById('abm-redirect').checked) params += '&noredirect=1' var url = wgScriptPath + '/api.php';

var req = sajax_init_object; req.open('POST', url, true); req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); req.setRequestHeader('Content-length', params.length); req.setRequestHeader('Connection', 'close'); req.onreadystatechange = function { if(req.readyState == 4 && req.status == 200) { eval("abmMoveAftermath(" + req.responseText + ",'" + req.responseText.replace(/\'/g,"`") + "')"); } }  req.send(params); }

function abmMoveAftermath(obj,txt) { var out = document.getElementById('abm-output');

//{"error":{"code":"Invalid token","info":"badtoken"}} //{"move": {"from": "User:Splarka\/cat","to": "User:Splarka\/cattest","reason": "better name","redirectcreated": ""}} if(obj['error']) { out.appendChild(document.createTextNode('  ! Api error: ' + obj['error']['code'] + ' - ' + obj['error']['info'] + '\n')); } else if(obj['move'] && obj['move']['from'] && obj['move']['to']) { out.appendChild(document.createTextNode('  > Page ' + obj['move']['from'] + ' moved to ' + obj['move']['to'] + '\n')); } else { out.appendChild(document.createTextNode('  ? Unexpected response: ' + txt + '\n')); } setTimeout('abmStart',1000); }

function queryString(p) { var re = RegExp('[&?]' + p + '=([^&]*)'); var matches; if (matches = re.exec(document.location)) { try { return decodeURI(matches[1]); } catch (e) { } }  return null; }