User:R'n'B/birthdeath.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/birthdeath and an accompanying .css page at User:R'n'B/birthdeath.css. |
/* birthdeath.js : Wikipedia app to add birth and/or death dates to
* a human name disambiguation page
* Copyright (c) 2011 [[en:User:R'n'B]]
* Creative Commons Attribution-ShareAlike License applies
* Requires wrappi.js, MediaWiki 1.18, and jQuery 1.6 (included with MediaWiki)
* <nowiki>
*/
// Version 0.1; alpha release
/*global console, mw, jQuery, importScript, importStylesheet */
(function ($) {
// pseudo-global variables
var api,
app,
onready,
original,
startup,
linkRx = /\[\[([^\]\|\[#<>\{\}]*)(\|.*?)?\]\]/, // groups: title, anchor
bornRx = / *\( *[Bb](?:orn|\.) +(\d+(?: *BC)?) *\) */,
diedRx = / *\( *[Dd](?:ied|\.) +(\d+(?: *BC)?) *\) */,
datesRx = / *\( *(\d+(?: *BC)?) *(?:[\-\u2013\u2014]|–|\{\{ *ndash *\}\}) *(\d+(?: *BC)?) *\) */,
birthcats = /Category:(\d{1,4}(?: BC)?) births/,
deathcats = /Category:(\d{1,4}(?: BC)?) deaths/;
importScript('User:Cacycle/diff.js');
importScript("User:R'n'B/wrappi.js");
importStylesheet("User:R'n'B/birthdeath.css");
onready = function () { // page setup that doesn't require API access
var cpointer = function () { $(this).css("cursor", "pointer"); },
cdefault = function () { $(this).css("cursor", "default"); };
original = {
'#content': $('#content').html()
};
$('#content').empty().addClass("bd-content")
.append(
$('<div class="bd-banner">Human name disambiguation page fixer</div>')
.append($('<div style="float: right; margin: 0px 0.25em"></div>')
.append($('<img alt="Close" id="bd-close"' +
'src="http://bits.wikimedia.org/skins-1.17/common/images/closewindow.png">'))
)
).append(
$('<h1 id="firstHeading" class="firstHeading"></h1>')
.text('(Loading...)')
).append(
$('<div id="bodyContent"></div>').html(
'<div id="wikEdDiffWrapper" class="wikEdDiffWrapper">'+
'<div id="wikEdDiffDiv" class="wikEdDiffDiv"></div>'+
'<div id="wikEdDiffButtonWrapper" class="wikEdDiffButtonWrapper">'+
'<button id="wikEdDiffButton" title="Refresh diff view" class="wikEdDiffButton">'+
'<img id="wikEdDiffButtonImg" src="http://upload.wikimedia.org/wikipedia/commons/c/c6/WikEdDiff.png" title="Refresh diff view" alt="wikEdDiff">'+
'</button></div>'+
'</div><!-- wikEdDiffWrapper -->' +
'<form id="editform">' +
'<div class="wikiEditor-ui-text">' +
'<textarea style="" rows="25" cols="80" id="wpTextbox1" accesskey="," tabindex="1">' +
'</textarea>' +
'</div>' +
'<div style="clear: both;"></div>' +
'<div id="editpage-copywarn">' +
'<p>Content that violates any copyrights will be deleted. Encyclopedic content ' +
'must be <b><a title="Wikipedia:Verifiability"' +
' href="/wiki/Wikipedia:Verifiability">verifiable</a></b>.</p>' +
'<p>By clicking the "Save Page" button, you agree to the ' +
'<a title="wmf:Terms of Use" class="extiw" href="http://wikimediafoundation.org/wiki/Terms_of_Use">' +
'Terms of Use</a>, and you irrevocably agree to release your contribution under ' +
'the <a title="Wikipedia:Text of Creative Commons Attribution-ShareAlike 3.0 Unported License"' +
' href="/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License">' +
'CC-BY-SA 3.0 License</a> and the ' +
'<a title="Wikipedia:Text of the GNU Free Documentation License"' +
' href="/wiki/Wikipedia:Text_of_the_GNU_Free_Documentation_License">GFDL</a>. ' +
'You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.</p>' +
'</div><!-- editpage-copywarn -->' +
'<div class="editOptions">' +
'<span id="wpSummaryLabel" class="mw-summary">' +
'<label for="wpSummary"><span style="text-align: left;">' +
'<a title="Help:Edit summary" 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" tabindex="1" maxlength="250" id="wpSummary" class="mw-summary">'+
'<div class="editCheckboxes">' +
'<input type="checkbox" id="wpMinoredit" accesskey="i" tabindex="3" value="1" 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" tabindex="4" checked="checked" value="1" name="wpWatchthis"> ' +
'<label title="Add this page to your watchlist [alt-shift-w]" id="mw-editpage-watch" for="wpWatchthis">Watch this page</label>' +
'</div><!-- editCheckboxes -->' +
'<div class="editButtons">' +
'<input type="button" title="Save your changes [alt-shift-s]" accesskey="s" value="Save page" tabindex="5" name="wpSave" id="wpSave">' +
'<input type="button" title="Preview your changes; please use this before saving. [alt-shift-p]" accesskey="p" value="Show preview" tabindex="6" name="wpPreview" id="wpPreview">' +
'<input type="button" title="Show which changes you made to the text [alt-shift-v]" accesskey="v" value="Show changes" tabindex="7" name="wpDiff" id="wpDiff">' +
'<span class="editHelp"><a id="mw-editform-exit" title="Exit this application" >Cancel</a> | ' +
'<a id="mw-editform-skip" title="Go to next unchecked page">Skip</a> | ' +
'<a href="/wiki/Wikipedia:Cheatsheet" target="helpwindow">Editing help</a> (opens in new window)</span>' +
'</div><!-- editButtons -->' +
'</div><!-- editOptions -->' +
'<div class="mw-tos-summary">' +
'<p><small id="mw-wikimedia-editpage-tos-summary">If you do not want your ' +
'writing to be edited, used, and redistributed at will, then do not submit it ' +
'here. All text that you did not write yourself, except brief excerpts, must ' +
'be available under terms consistent with Wikipedia\'s ' +
'<b><a title="foundation:Terms of Use" class="extiw" href="http://wikimediafoundation.org/wiki/Terms_of_Use">Terms of Use</a></b> '+
'before you submit it.</small>' +
'</p></div><!-- mw-tos-summary --></form><!-- editform -->' +
'<div id="dialog-form" title="Human name disambiguation page fixer">' +
'<form>' +
'<fieldset>' +
'<label for="startfrom">Start from</label>' +
'<input type="text" name="startfrom" id="startfrom" class="text ui-widget-content ui-corner-all" value="' + app.startfrom + '" /><br />' +
'<label for="skipunchanged">Skip pages without suggested changes</label>' +
'<input type="checkbox" name="skipunchanged" id="skipunchanged" value="" class="ui-widget-content ui-corner-all" />' +
'</fieldset>' +
'</form>' +
'</div><!-- dialog-form -->'
)
);
// assign actions to form elements
$('#bd-close').click(app.exit).hover(cpointer, cdefault);
$('#wikEdDiffButton').click(app.showdiff);
$('#wpSave').click(app.savepage);
$('#wpPreview').attr("disabled", true);
$('#wpDiff').click(app.showdiff);
$('#mw-editform-exit').click(app.exit).hover(cpointer, cdefault);
$('#mw-editform-skip').click(app.loadnext).hover(cpointer, cdefault);
};
app = mw.RnB.birthdeath = {
startfrom: mw.cookie.get('birthdeathstart') || '!',
setup: function () { // initialization that does require API access
api = new mw.RnB.Wiki();
$('#dialog-form').dialog({
autoOpen: true,
modal: true,
buttons: {
'Go': function () {
app.startfrom = $('#startfrom').val();
app.skipunchanged = $('#skipunchanged:checked').length;
// will be 1 if checked, 0 if not
$(this).dialog("close");
app.go();
},
'Cancel': function() {
$(this).dialog("close");
app.exit();
}
}
});
},
go: function() {
var copy = {};
$('#dialog-form').dialog("close");
// retrieve list of hndis pages
app.cmquery = {
action: 'query',
rawcontinue: '',
generator: 'categorymembers',
gcmtitle: 'Category:Human name disambiguation pages',
gcmnamespace: '0',
gcmstartsortkeyprefix: app.startfrom,
gcmlimit: '20',
indexpageids: true,
prop: 'categories|info|revisions',
clcategories: "Category:Human name disambiguation pages",
cllimit: 'max',
clprop: 'sortkey',
inprop: 'watched',
intoken: 'edit',
rvprop: 'content|timestamp'
};
app.catmemlist = [];
$('body').css("cursor", "wait");
$.extend(copy, app.cmquery);
api.request(copy, app.listrecvd);
},
listrecvd: function (response, query) {
// received list of category members from server
var pagelist = [],
rq = response.query,
rqc = response["query-continue"];
app.cmquery = $.extend({}, query);
if (rqc && rqc.categorymembers) {
app.cmquery.gcmcontinue = rqc.categorymembers.gcmcontinue;
} else {
delete app.cmquery.gcmcontinue;
}
if (rq && rq.pages) {
if (rq.pageids) {
// server provides index for sorting the results
$.each(rq.pageids, function () {
var item = rq.pages[this];
if (item.categories) {
pagelist.push({
sortkey: item.categories[0].sortkeyprefix,
text: item.revisions[0]['*'],
timestamp: item.revisions[0].timestamp,
title: item.title,
token: item.edittoken,
watched: (item.watched !== undefined)
});
}
});
} else {
// we have to sort ourselves, using the sortkey
$.each(rq.pages, function () {
if (this.categories) {
pagelist.push({
sortkey: this.categories[0].sortkeyprefix,
text: this.revisions[0]['*'],
timestamp: this.revisions[0].timestamp,
title: this.title,
token: this.edittoken,
watched: (this.watched !== undefined)
});
}
});
pagelist.sort(function (a, b) {
var as = a.sortkey,
bs = b.sortkey;
return as < bs ? -1 : as > bs ? 1 : 0;
});
}
}
$.merge(app.catmemlist, pagelist);
if (! query.gcmcontinue) {
// this is the first chunk received, not a continuation
app.loadnext();
}
},
loadnext: function () {
// load next disambig page and start processing
var q,
page = app.currentpage = app.catmemlist.shift();
app.currentlinks = {};
app.currentredirs = {};
if (! page) {
app.exit();
return;
}
mw.cookie.set("birthdeathstart", page.sortkey, {expires: 3652});
$('#firstHeading').text(page.title);
// load the pages linked from this disambiguation page
api.request(
{ action: 'query',
generator: 'links',
rawcontinue: '',
titles: page.title,
redirects: true,
gplnamespace: '0',
gpllimit: 'max',
prop: 'info|revisions|categories',
rvprop: 'content',
cllimit: 'max'
}, app.processlinks
);
$('#wpTextbox1').hide().val(app.currentpage.text);
$('#wikEdDiffDiv').hide();
$('#wpMinoredit').attr("checked", "checked");
if (page.watched || mw.user.options.get("watchdefault")) {
$('#wpWatchthis').attr("checked", "checked");
} else {
$('#wpWatchthis').prop("checked", false);
}
$('input[name="wpSummary"]').val(
"Add birth/death dates to hndis entries, from linked article(s)");
$('body').css("cursor", "default");
// continue query in background if list is almost empty
if (app.catmemlist.length < 5 && app.cmquery.gcmcontinue) {
q = $.extend({}, app.cmquery);
api.request(q, app.listrecvd);
delete app.cmquery.gcmcontinue;
}
},
processlinks: function (response, query) {
// go through blue links on page and propose changes to text
var rq = response.query,
rqc = response["query-continue"],
lines = app.currentpage.text.split("\n"),
links = {},
newtext,
redirs = {},
len = lines.length,
i;
if (rqc && rqc.links) {
// unlikely any hndis page would contain more than 500 links,
// but just in case...
query.gplcontinue = rqc.links.gplcontinue;
api.request(query, app.processlinks);
}
if (rq && rq.redirects) {
// create a map from redirect sources to target pages
$.each(rq.redirects, function () {
redirs[this.from] = this.to;
});
$.extend(app.currentredirs, redirs);
}
if (rq && rq.pages) {
// index links by linked page title
$.each(rq.pages, function () {
links[this.title] = this;
});
$.extend(app.currentlinks, links);
// go through each line in disambig page, see if it starts with
// a blue link
$.each(lines, function (index) {
var thislink,
born = null,
born_already = null,
died = null,
died_already = null,
m = this.match(linkRx),
n,
revised = this.slice(0),
bold,
newtext; // copy of line
if (m !== null && m.length > 1) {
// link found; find corresponding object in links
thislink = redirs[m[1]]
? links[redirs[m[1]]]
: links[m[1]];
if (thislink === undefined) {
// invalid link, probably to a category or iw
return;
}
if (thislink.missing !== undefined) {
// this is a red link, skip it
return;
}
// blue link found; try to find birth/death year
// in categories
$.each(thislink.categories, function () {
var m1 = birthcats.exec(this.title),
m2 = deathcats.exec(this.title);
if (m1 !== null) {
born = m1[1];
}
if (m2 !== null) {
died = m2[1];
}
});
// here we could, if desired, look for birth/death
// years in other places, like {{Persondata}}
// ...
if (born === null && died === null) {
return;
}
// birth and/or death year found;
// make sure the current line doesn't already
// contain them
n = bornRx.exec(this);
if (n !== null) {
born_already = n[1];
}
n = diedRx.exec(this);
if (n !== null) {
died_already = n[1];
}
n = datesRx.exec(this);
if (n !== null) {
born_already = n[1];
died_already = n[2];
}
if (born && !born_already) {
if (died && !died_already) {
// supply both birth and death years
newtext = " (" + born + "–" + died + ")";
} else {
// supply birth year only
if (died_already) {
newtext = " (" + born + "–" + died_already + ")";
} else {
newtext = " (born " + born + ")";
}
}
} else if (died && !died_already) {
if (born_already) {
newtext = " (" + born_already + "–" + died + ")";
} else {
newtext = " (died " + died + ")";
}
}
if (newtext) {
bold = "'''" + m[0] + "'''";
if (revised.indexOf(bold) !== -1) {
revised = revised.replace(bold, bold + newtext);
} else {
revised = revised.replace(m[0], m[0] + newtext);
}
}
}
lines[index] = revised;
});
}
newtext = lines.join('\n');
if (app.skipunchanged && newtext == app.currentpage.text) {
return app.loadnext();
}
//cosmetic changes
newtext = newtext
.replace(/–/g, '–')
.replace(/\{\{ *ndash *\}\}/g, '–')
.replace(/\( *b\. */i, '(born ')
.replace(/\( *d\. */i, '(died ')
.replace(/\{\{ *[Rr]efer *(\|.*?)?\}\}/, '{{subst:refer$1}}')
.replace(/\n*(\{\{[Hh]ndis.*?\}\})\n*/, '\n\n$1\n\n')
.replace(/\n*$/, '\n');
$('#firstHeading').append(
$('<span></span>').text("Skip").attr("id", "topskip")
.css("font-size", "50%")
);
$('#topskip').button().click(app.loadnext);
$('#wpTextbox1').val(newtext).show();
app.showdiff();
},
showdiff: function () {
// refresh the WikEdDiff display
var m,
link,
linkedpage,
text = app.currentpage.text,
diff = window.WDiffString(text, $('#wpTextbox1').val()),
allLinkRx = /\[\[([^\]\|\[#<>\{\}]*)(\|.*?)?\]\]([a-z]*)/g;
// same as linkRx, but with /g flag
// find each link in the text, and if it matches an article link
// from the database, wrap it in an <a> or <span> element
while ((m = allLinkRx.exec(text)) !== null) {
// linked title = m[1]
// anchor = m[2]
// trail = m[3]
linkedpage = app.currentredirs[m[1]]
? app.currentlinks[app.currentredirs[m[1]]]
: app.currentlinks[m[1]];
if (linkedpage !== undefined && linkedpage.missing !== undefined) {
link = $('<span></span>')
.addClass("new")
.attr("title", (m[2] ? m[2].replace(/^\|/, '') : m[1])
+ (m[3] || '') + ' (page does not exist)')
// FIXME above is wrong, doesn't account for pipe tricks!
.append($('<span></span>').css("color", "black").text(m[0]));
} else {
link = $('<a></a>')
.addClass("link")
.attr("href",
mw.config.get('wgArticlePath')
.replace('$1',
mw.util.wikiUrlencode(m[1]).replace(/ /g, '_')
)
).attr("target", "_blank")
.attr("title", (m[2] ? m[2].replace(/^\|/, '') : m[1])
+ (m[3] || ''))
// FIXME above is wrong, doesn't account for pipe tricks!
.append($('<span></span>').css("color", "black").text(m[0]));
if (app.currentredirs[m[1]]) {
link.addClass("mw-redirect")
.attr("title",
link.attr("title") + ' (redirect to [['
+ app.currentredirs[m[1]] + ']])');
}
}
// FIXME doesn't work if there are 2 identical links
diff = diff.replace(
m[0].replace(/"/g, '"'),
link.wrap("<span>").parent().html()
);
}
$('#wikEdDiffDiv').html(diff).show();
},
savepage: function () {
var t = app.currentpage.title,
params = {
action: 'edit',
title: t,
text: $('#wpTextbox1').val(),
token: app.currentpage.token,
basetimestamp: app.currentpage.timestamp,
summary: $('input[name="wpSummary"]').val()
};
if ($('#wpMinoredit:checked').length) {
params.minor = "";
}
if ($('#wpWatchthis:checked').length) {
params.watchlist = "watch";
}
api.request(params,
function () {mw.log("Page [[" + t + "]] saved");}
);
app.loadnext();
},
exit: function () {
// Restore original contents of page
$.each(original, function(elem, content) {
$(elem).html(content);
});
$('#content').removeClass("bd-content");
}
};
mw.loader.using(
['jquery.ui', 'mediawiki.cookie',
'jquery.ui',
'jquery.ui'],
function () {
var startup = function () {
if (mw.RnB && mw.RnB.Wiki) {
$(document).ready(app.setup);
} else {
setTimeout(startup, 100);
}
};
$(document).ready(onready);
startup();
}
);
} (jQuery));
// </nowiki>