User:R'n'B/missingredirapp.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | This user script seems to have a documentation page at User:R'n'B/missingredirapp. |
// <nowiki>
/* missingredirs.js : Wikipedia app to fix broken {{redirect}} 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(
"<strong class=\"error\">"
+ "Error: could not parse.</strong>");
}
};
// 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 [[Category:Missing redirects]]
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 {{Template:Redirect|Foo}}
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(
'<div style="float: left; padding-top: 0.5em">'
+ '<strong>Change to:</strong></div>'
+ '<div id="mr-changeto" style="float:left;'
+ ' padding-left:20px; padding-right:20px;'
+ ' overflow:auto"></div>'
+ '<br clear="all" />').resizable();
$.each(response.query.backlinks,
function(i, blitem) {
var t = blitem.title;
$('#mr-changeto').append(
$('<input>').attr("type", 'radio')
.attr("name", "mr-radio")
.attr("value", t)
.attr("id", "mr-radio"
+ optionindex.toString())
).append($('<label>')
.attr("for", "mr-radio"
+ optionindex.toString())
.text(t)
).append($('<br>'));
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(
$('<input>').attr("type", "radio").attr("name", 'mr-radio')
.attr("value", "#Remove")
.attr("id", "mr-radio" + optionindex.toString())
).append($('<label>')
.attr("for", "mr-radio" + optionindex.toString())
.text("Remove hatnote")
).append($('<br>'));
optionindex += 1;
$('#mr-options').append(
$('<input>').attr("type", "radio").attr("name", 'mr-radio')
.attr("value", "#Create")
.attr("id", "mr-radio" + optionindex.toString())
).append($('<label>')
.attr("for", "mr-radio" + optionindex.toString())
.text("Create missing redirect")
).append($('<br>'));
optionindex += 1;
$('#mr-options').append(
$('<input>').attr("type", "radio").attr("name", 'mr-radio')
.attr("value", "#Edit")
.attr("id", "mr-radio" + optionindex.toString())
).append($('<label>')
.attr("for", "mr-radio" + optionindex.toString())
.text("Manually edit hatnote")
).append($('<br>'));
optionindex += 1;
$('#mr-options').append(
$('<input>').attr("type", "radio").attr("name", "mr-radio")
.attr("value", "#Skip")
.attr("id", "mr-radio" + optionindex.toString())
).append($('<label>')
.attr("for", "mr-radio" + optionindex.toString())
.text("Skip this hatnote")
).append($('<br>'));
optionindex += 1;
$('#mr-options').append(
$('<input>').attr("type", "radio").attr("name", "mr-radio")
.attr("value", "#Quit")
.attr("id", "mr-radio" + optionindex.toString())
).append($('<label>')
.attr("for", "mr-radio" + optionindex.toString())
.text("Exit the app")
).append($('<br>'));
$('#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($('<div>').attr("id", "bodyContent"));
$('#bodyContent').html('<div class="editOptions">\n'
+ '<span id="wpSummaryLabel" class="mw-summary">'
+ '<label for="wpSummary"><span style="text-align: left;">'
+ '<a title="" href="/wiki/Help:Edit_summary">Edit summary</a>'
+ ' <small>(Briefly describe the changes you have made)</small>'
+ '</span></label></span>'
+ ' <input type="text" name="wpSummary" value="" accesskey="b" '
+ 'title="Enter a short summary [alt-shift-b]" size="60" maxlength="250" id="wpSummary" class="mw-summary">'
+ '<div class="editCheckboxes"><input type="checkbox" id="wpMinoredit" accesskey="i" name="wpMinoredit"> '
+ '<label title="Mark this as a minor edit [alt-shift-i]" id="mw-editpage-minoredit" for="wpMinoredit">'
+ 'This is a minor edit <span id="minoredit_helplink">(<a title="Help:Minor edit" href="/wiki/Help:Minor_edit">what\'s this?</a>)</span></label>'
+ '<input type="checkbox" id="wpWatchthis" accesskey="w" name="wpWatchthis"> '
+ '<label title="Add this page to your watchlist [alt-shift-w]" id="mw-editpage-watch" for="wpWatchthis">Watch this page</label></div>'
+ '<div class="editButtons">'
+ '<input type="button" title="Save your changes [alt-shift-s]" accesskey="s" value="Save page" name="wpSave" id="wpSave">'
+ '<span class="editHelp"><a id="mw-editform-cancel" title="">Cancel</a></span>'
+ '</div></div>'
);
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(
$('<textarea>').attr("rows", "3").attr("cols", "80")
.attr("id", "mr-redirect").attr("name", "mr-redirect")
.text("#REDIRECT [[" + app.title + "]]")
).prepend(
$('<h1>').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($('<br>')).append(
$('<span>').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(
$('<input>').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 <div id="content">
// 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("<h1>Missing redirect fixer</h1>" +
"<h2 id=\"mr-pagetitle\"></h2>" +
"<div id=\"mr-source\"></div>" +
"<div id=\"mr-render\"></div>" +
"<div id=\"mr-options\" style=\"font-size: 83%\"></div>");
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));
// </nowiki>