User:R'n'B/missingredirapp.js

// /* missingredirs.js : Wikipedia app to fix broken templates * Copyright (c) 2011 en:User:R'n'B * Creative Commons Attribution-ShareAlike License applies * Requires wrappi.js, MediaWiki 1.17, and * jQuery 1.4.2 (included with MediaWiki) */ // Version 0.1; alpha release /*global console, mw, jQuery, importScript */ (function ($) {	// pseudo-global variables	var api, 		app,		startup,		originalcontent,		onready,		re_escape,		firstinsensitive,		putresponse,		missinglist = [],		prefix = mw.config.get("wgFormattedNamespaces")[10]	+ ":",		prefixlen = prefix.length,		redirtemplates = ["Redirect", "Redirect-acronym", "Redirect2",			"Redirect3", "Redirect4", "Redirect6", "Redirect10"],		redircount = redirtemplates.length,		stylesheet;	//	importScript("User:R'n'B/wrappi.js");	re_escape = function re_escape(pattern) {		//	Escape all non-alphanumeric characters in pattern.		var i, s = [], c, len = pattern.length;		for (i = 0; i < len; i += 1) {			c = pattern.charAt(i);			if (( c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c === ' ') {				s[i] = c;			} else {				s[i] = "\\" + c;			}		}		return s.join("");	};	firstinsensitive = function firstinsensitive(title) {		var t = re_escape(title), f = t.charAt(0);		if (f.toUpperCase === f.toLowerCase) {			return t;		}       return ("[" + f.toUpperCase + f.toLowerCase + "]" + t.slice(1));	};	putresponse = function putresponse(response) {		if (response.parse && response.parse.text && response.parse.text["*"]) {			$('#mr-render').html(response.parse.text["*"]);		} else {			$('#mr-render').html( "" + "Error: could not parse. ");		}	};	// the application object	app = mw.RnB.missingredirapp = {		abort: false,		ready: 0, // setup is finished when this reaches redircount + 1		// populate global vars; this can run before document.ready		setup: function {			// get list of titles of articles in 			api = new mw.RnB.Wiki;			api.request({ action: 'query', list: 'categorymembers', cmtitle: 'Category:Missing redirects', cmnamespace: '0', cmsort: 'timestamp', cmlimit: 'max' }, function (response) { $.each(response.query.categorymembers, function(i, cmitem) {						missinglist.push(cmitem.title);					}); app.ready += 1; }			);			// get list of all synonyms for Template:Redirect etc.			$.each(redirtemplates, function (i, name) { api.request({					action: 'query',					list: 'backlinks',					bltitle: prefix + name,					blnamespace: '10',					blfilterredir: 'redirects',					bllimit: 'max'					}, function (response) {						if (response.query && response.query.backlinks) {							$.each(response.query.backlinks, function(j, blitem) { var t = blitem.title; if (t.slice(0, prefixlen) === prefix) { redirtemplates.push(t.slice(prefixlen)); } else { mw.log("Error: " + t + 										" received as redirect to " + prefix										+ name + ""); }							});						}						app.ready += 1;					}				); });		},		nextpage: function {			if (! missinglist) {				app.exit;				return;			}			app.title = missinglist.shift;			// load the wikitext			if (! app.title) {				app.exit;				return;			}			api.request({ action: 'query', titles: app.title, prop: 'info|revisions', inprop: 'watched', intoken: 'edit', rvprop: 'content|timestamp' }, app.pageloaded );			$('#mr-pagetitle').text(app.title);			$('#mr-source').text('Loading ...');			$('#mr-render').text('Loading ...');			$('#mr-options').html('');		},		pageloaded: function (response, query) {			// after page loads, retrieve its text and look for redir template			app.templateRx = /\{\{\s*(.+?)\s*(\|.*?)?\}\}/g;			//	need to create a new RegExp object for each page in order			//	to track position of last match			$.each(response.query.pages, function (id, pageobj) { if (pageobj.title === app.title) { app.edittoken = pageobj.edittoken; app.wikitext = pageobj.revisions[0]["*"]; app.timestamp = pageobj.revisions[0].timestamp; app.watched = !(pageobj.watched === undefined); app.nexttemplate; return false; }			});		},		nexttemplate: function {			// find next matching template on loaded page			var found = false,				match_t,				tname,				tparams,				parampattern = /\|\s*([^\|])*\s*/g;			while ((match_t = app.templateRx.exec(app.wikitext)) !== null) {				tname = match_t[1].replace(/_+/g, ' ');				if (tname.slice(0, prefixlen) === prefix) {					// in case user entered 					tname = tname.slice(prefixlen);				}				tname = tname.charAt(0).toUpperCase + tname.slice(1);				if (redirtemplates.indexOf(tname) !== -1) {					$('#mr-source').text(match_t[0]);					app.hatnote = match_t[0];					found = true;					api.request({ action: 'parse', text: match_t[0] }, putresponse);					// find and display valid redirects					api.request({ action: 'query', list: 'backlinks', bltitle: app.title, blnamespace: '0', blfilterredir: 'redirects', bllimit: 'max' }, app.showchoices);					break;				}			}			if (! found) {				app.nextpage;			}		},		showchoices: function (response) {			var h,				optionindex = 0;			if (response.query && response.query.backlinks && response.query.backlinks.length > 0) {				$('#mr-options').html( ' '					+ ' Change to: ' + ' ' + ' ').resizable;				$.each(response.query.backlinks, function(i, blitem) { var t = blitem.title; $('#mr-changeto').append(						$(' ').attr("type", 'radio')							.attr("name", "mr-radio")							.attr("value", t)							.attr("id", "mr-radio" + optionindex.toString)						).append($(' ')							.attr("for", "mr-radio" + optionindex.toString)							.text(t)						).append($(' ')); optionindex += 1; });				h = 42 * optionindex + 2; // tentative height				h = (h > window.innerHeight - 430) ? window.innerHeight - 430 : h;				h = (h < 42) ? 42 : h;				$('#mr-changeto').width($('#content').width - 128)					.height(h);			}			// more options			$('#mr-options').append( $(' ').attr("type", "radio").attr("name", 'mr-radio') .attr("value", "#Remove") .attr("id", "mr-radio" + optionindex.toString) ).append($(' ') .attr("for", "mr-radio"	+ optionindex.toString) .text("Remove hatnote") ).append($(' '));			optionindex += 1;			$('#mr-options').append( $(' ').attr("type", "radio").attr("name", 'mr-radio') .attr("value", "#Create") .attr("id", "mr-radio" + optionindex.toString) ).append($(' ') .attr("for", "mr-radio" + optionindex.toString) .text("Create missing redirect") ).append($(' '));			optionindex += 1;			$('#mr-options').append( $(' ').attr("type", "radio").attr("name", 'mr-radio') .attr("value", "#Edit") .attr("id", "mr-radio" + optionindex.toString) ).append($(' ') .attr("for", "mr-radio" + optionindex.toString) .text("Manually edit hatnote") ).append($(' '));			optionindex += 1;			$('#mr-options').append( $(' ').attr("type", "radio").attr("name", "mr-radio") .attr("value", "#Skip") .attr("id", "mr-radio" + optionindex.toString) ).append($(' ') .attr("for", "mr-radio" + optionindex.toString) .text("Skip this hatnote") ).append($(' '));			optionindex += 1;			$('#mr-options').append( $(' ').attr("type", "radio").attr("name", "mr-radio") .attr("value", "#Quit") .attr("id", "mr-radio" + optionindex.toString) ).append($(' ') .attr("for", "mr-radio" + optionindex.toString) .text("Exit the app") ).append($(' '));			$('#mr-options').buttonset;			$('#mr-options input').change(app.onchoice);		},		onchoice: function (evt) {			// user has selected one of the buttons			switch (evt.target.value) {				case "#Remove":					app.removehatnote;					break;				case "#Create":					app.createredirect;					break;				case "#Edit":					app.edithatnote; // TODO					break;				case "#Skip":					app.nexttemplate;					break;				case "#Quit":					app.exit;					break;				default:					app.changehatnote(evt.target.value);			}		},		exit: function {			// user clicked "Exit" button			$('#content').html(originalcontent);			stylesheet.disabled = true;			app.abort = true;		},		insertsummary: function  {			// add form elements for edit summary			$('#content').append($(' ').attr("id", "bodyContent"));			$('#bodyContent').html(' \n' + ' ' + ' ' + 'Edit summary' + ' (Briefly describe the changes you have made) ' + ' ' + ' ' + '  ' + '' +  'This is a minor edit (what\'s this?) ' + ' ' +  'Watch this page ' + ' ' + '' + ' Cancel ' + ' '			);			if (app.watched || mw.user.options.get("watchdefault")) {				$('#wpWatchthis').attr("checked", "checked");			}			$('#wpMinoredit').attr("checked", "checked");			$('input[name="wpSave"]').click(app.savepage);			$('#mw-editform-cancel').click(function  { $('#bodyContent').remove; $('input[name="mr-radio"]:checked').prop("checked", false); });		},		createredirect: function (replacement) {			// don't change the hatnote, create the missing redirect instead			var newhatnote,				paramRx = /\|(.*?)(\}\}|\|)/,				match = paramRx.exec(app.hatnote);			if (match) {				app.redirect = match[1].trim;				app.insertsummary;				$('#bodyContent').prepend( $(' ').attr("rows", "3").attr("cols", "80") .attr("id", "mr-redirect").attr("name", "mr-redirect") .text("#REDIRECT " + app.title + "") ).prepend( $(' ').text(app.redirect) );				$('input[name="wpSummary"]').val('Create missing redirect');				$('#wpMinoredit').prop("checked", false);				if (mw.user.options.get("watchcreations")) {					$('#wpWatchthis').attr("checked", "checked");				}				$('input[name="wpSave"]').unbind.click(app.createpage);			} else {				mw.log("No parameter found!");				app.nexttemplate;			}		},		createpage: function {			// save the new redirect			var t = app.redirect,				params = {					action: 'edit',					title: app.redirect,					text: $('#mr-redirect').text,					token: app.edittoken,					createonly: '',					summary: $('input[name="wpSummary"]').val				};			if ($('#wpMinoredit:checked').length) {				params.minor = "";			}			params.watchlist = $('#wpWatchthis:checked').length ?				"watch" : "unwatch";			api.request(params, function {mw.log("Page " + t + " created");});			$('#bodyContent').remove;			app.nexttemplate;		},		changehatnote: function (replacement) {			// replace the missing parameter with replacement, and save			var newhatnote,				paramRx = /\|(.*?)(\}\}|\|)/,				match = paramRx.exec(app.hatnote);			if (match) {				newhatnote = app.hatnote.replace(match[1], replacement);				app.newtext = app.wikitext.replace(app.hatnote, newhatnote);				$('#mr-source').append($(' ')).append( $(' ').css("color", "#27AA7A") .text(newhatnote) );				app.insertsummary;				$('input[name="wpSummary"]').val( 'Change hatnote to use valid redirect');			} else {				mw.log("No parameter found!");				app.nexttemplate;			}		},		edithatnote: function {			// allow user to rewrite the hatnote in a text box			var oldtext = $('#mr-source').text;			$('#mr-source').html("").append( $(' ').attr("type", "text") .attr("name", "mr-hatnote") .attr("id", "mr-hatnote") .attr("size", "75") .val(oldtext) .css("margin", "0.25em") );			app.insertsummary;		},		removehatnote: function {			// remove the hatnote from the page text, and save			app.newtext = app.wikitext.replace(app.hatnote, "<<@@>>\n");			app.newtext = app.newtext.replace(/\s*<<@@>>\s*/, "\n");			app.newtext = app.newtext.replace(/^\n+/, '');			app.insertsummary;			$('input[name="wpSummary"]').val( 'Remove hatnote; no such redirect exists');		},		savepage: function {			var t = app.title,				params = {					action: 'edit',					title: t,					text: app.newtext,					token: app.edittoken,					basetimestamp: app.timestamp,					summary: $('input[name="wpSummary"]').val				};			if ($('#mr-source input').length) {				params.text = app.wikitext.replace(app.hatnote, $('#mr-source input').val);			}			if ($('#wpMinoredit:checked').length) {				params.minor = "";			}			if ($('#wpWatchthis:checked').length) {				params.watchlist = "watch";			}			api.request(params, function {mw.log("Page " + t + " saved");} );			$('#bodyContent').remove;			app.nexttemplate;		}	};	onready = function {		// wait until app.ready reaches redircount + 1		if (app.ready < redircount + 1) {			window.setTimeout(onready, 100);			return;		}		originalcontent = $('#content').html;	// existing 		// set up the page for the application		stylesheet = mw.util.addCSS( '#content { border: 3px solid }\n' + '#content h1 { background-color: #27AA7A; color: white; ' + 'font-size: 125%; text-align: center; margin-bottom: 0 }\n' + '#content h2 { font-size: 150%; }' + '#mr-source { font-family: monospace; font-size: 110% } ' + '#mr-render { border: 1px solid #27AA7A; font-size: 90%; ' + 'padding: 0.25em; }' );		$('#content').html(" Missing redirect fixer " + "<h2 id=\"mr-pagetitle\"> "	+ "<div id=\"mr-source\"> " + "<div id=\"mr-render\"> " + "<div id=\"mr-options\" style=\"font-size: 83%\"> ");		if (missinglist) {			app.nextpage;		} else {			app.exit;		}	};	$(document).ready(onready);	startup = function {		if (mw.RnB && mw.RnB.Wiki) {			app.setup;		} else {			setTimeout(startup, 100);		}	};	startup; } (jQuery)); //