User:Dkpiatt/monobook.js

/*	SUBJECT'S AGE FROM YEAR Description: In an article about a person or a company, when the mouse hovers over a year in the article, the age of the article's subject by that year appears in a tooltip. Link: User:Gary King/subjects age from year.js TODO Show age of item for establishments on death year. TODO For biographies with no birth year but with a death year (Cyrus the Great, Leonidas I), make dates relative to death year instead so that they stil work. TODO For articles with more than one category beginning with a number, like Babe Ruth, use the year closest to the present year. if (typeof(unsafeWindow) != 'undefined') {	var console = unsafeWindow.console; mw = unsafeWindow.mw; } var fromYearType; function fromYearPhrases(phrase) {	// fromYearType var phrases = { 'biography': { 'birth': 'birth', 'death': 'death' }, 'establishment': { 'birth': 'established', 'death': 'disestablished' } }; return phrases[fromYearType][phrase]; } function subjectsAgeFromYear {	function findYearsInText(node) {		if (node[0].nodeType != 3) return true; var nodeText = node[0].nodeValue; var years = nodeText.match(patterns[l]); if (!years) return true; var newNode = $(' '); // loop through each year in the same text node for (var k = 0; k < years.length; k++) {			var abbrText = currentYear = years[k]; var birthYearIndex = nodeText.indexOf(currentYear); var workThisYear = true; // don't work on this year—for AD years if (l == 0 && ( // 'year' is followed by a ' BC'; wait for next pattern to work on this nodeText.substr(birthYearIndex + currentYear.length, 3).indexOf('BC') != -1 // 'year' is preceded by a ','; this is probably a unit such as 1,000 km				|| nodeText.substr(birthYearIndex - 1, 1).indexOf(',') != -1 // 'year' is preceded by a month; this is probably part of a day, like "January 1" || (currentYear.length <= 2 && (nearAMonth(nodeText, birthYearIndex, -1, years, k) && currentYear.indexOf('AD') == -1)				// 'year' is followed by a month; this is probably part of a day, like "January 1"				|| nearAMonth(nodeText, birthYearIndex + currentYear.length, 1)) // 'year' is followed by a hyphen; this is probably part of a longer phrase // || nodeText.substr(birthYearIndex + currentYear.length, 1).indexOf('-') != -1 // 'year' is followed by "years", indicating a length, or relative date, as opposed to an absolute date // || nodeText.substr(birthYearIndex + currentYear.length, 6).indexOf('years') != -1 // 'year' is followed by "?year", such as "-year", " years" || nodeText.substr(birthYearIndex + currentYear.length, 5).indexOf('year') != -1 ))				workThisYear = false; if (currentYear.indexOf('BC') != -1 || ((birthYear < 0 || deathYear < 0) && nodeText.substr(birthYearIndex + currentYear.length + ' BC'.length, 10).indexOf('BC') != -1)) var currentYear = -1 * parseInt(currentYear); else if (currentYear.indexOf('AD') != -1 || currentYear.indexOf('CE') != -1) var currentYear = parseInt(currentYear.replace(/AD/, ).replace(/CE/, )); var firstPart = nodeText.substring(0, birthYearIndex); var difference = currentYear - birthYear; // find a year to act on; work on AD years first, then BC years if (workThisYear && (currentYear >= birthYear || currentYear >= (birthYear - birthYearBuffer)) && (currentYear <= deathYear || currentYear <= (deathYear + birthYearBuffer))) {				var abbr = $(' '); // after death year but before the buffer if (currentYear > deathYear && currentYear <= (deathYear + birthYearBuffer)) {					var yearsLater = currentYear - deathYear; abbr.attr('title', yearsLater + ' ' + pluralize('year', yearsLater) + ' after ' + fromYearPhrases('death')); }				// was alive at currentYear else if (difference >= 0) {					// age at currentYear abbr.attr('title', difference + ' ' + pluralize('year', difference) + ' old'); // birth year if (difference == 0) abbr.attr('title', abbr.attr('title') + ' (' + fromYearPhrases('birth') + (fromYearType == 'biography' && isAlive ? '; now ' + (today.getFullYear - birthYear) + ' years old' : '') + ')'); // death year else if (currentYear == deathYear && allDeathYears.length == 1) abbr.attr('title', abbr.attr('title') + ' (' + fromYearPhrases('death') + ')'); }				// currentYear is before birth year else abbr.attr('title', Math.abs(difference) + ' ' + pluralize('year', Math.abs(difference)) + ' before ' + fromYearPhrases('birth')); abbr.append(abbrText); }			else var abbr = ''; // after the year, only for the last occurrence of a year if ((k + 1) == years.length) var secondPart = nodeText.substring(birthYearIndex + abbrText.length); else var secondPart = ''; newNode.append(firstPart); if (abbr.length) newNode.append(abbr); else newNode.append(abbrText); if (secondPart) newNode.append(secondPart); var nodeText = nodeText.substring(birthYearIndex + abbrText.length); }		if (newNode.contents.length > 0) {			node.replaceWith(newNode); spansToRemove.push(newNode); }	}	if ((mw.config.get('wgCanonicalNamespace') != '' || mw.util.getParamValue('disable') == 'age' || mw.config.get('wgAction') != 'view') && !(mw.config.get('wgPageName') == 'User:Gary_King/Sandbox' && (mw.config.get('wgAction') == 'view' || mw.config.get('wgAction') == 'submit'))) return false; var minBirthYearBuffer = 100; var maxPossibleAge = 1000; var catsOld = mw.config.get('wgCategories'); var cats = []; // Categories now have underscores (since late December 2011) for (var i = 0; i < catsOld.length; i++) cats.push(catsOld[i].replace(/_/g, ' ')); var birthYear = deathYear = ''; var allBirthYears = [], allDeathYears = [], allMatches = []; var otherMatchesPattern = new RegExp('^([0-9]{1,4})( BC)? ([A-Za-z ]+)|(.*) (in|for) ([0-9]+)( BC)?$|^([0-9]{1,4})( BC)?$|^([0-9]{4,4}) ([A-Z][a-z]+)$'); for (var i = 0; i < cats.length; i++) {		var matches = cats[i].match(otherMatchesPattern); // There is a match if (matches) {			allMatches.push(cats[i]); // If true then we are dealing with an AD year if (!matches[2]) matches[2] = ''; if (!matches[7]) matches[7] = ''; // births if (!((matches[3] && (matches[3].indexOf('disestablishments') == 0 || matches[3].indexOf('deaths') == 0)) || (matches[4] && matches[4].indexOf('disestablished') == 0))) {				if (matches[3]) birthYear = matches[1] + matches[2]; else if (matches[4]) birthYear = matches[6] + matches[7]; else if (matches[11]) birthYear = matches[10]; else birthYear = matches[8] + matches[9]; birthYear = parseBirthYear(birthYear); allBirthYears.push(birthYear); }			// deaths else {				if (matches[3] && (matches[3].indexOf('disestablishments') == 0 || matches[3].indexOf('deaths') == 0)) deathYear = matches[1] + matches[2]; else if (matches[4]) deathYear = matches[6] + matches[7]; else deathYear = matches[8] + matches[9]; deathYear = parseDeathYear(deathYear); allDeathYears.push(deathYear); }			if (matches[3] && (matches[3].indexOf('births') == 0 || matches[3].indexOf('deaths') == 0)) fromYearType = 'biography'; else fromYearType = 'establishment'; continue; }		if (birthYear && deathYear) break; }	if (!birthYear) return; else {		var today = new Date; allBirthYears.sort(sortYears); allDeathYears.sort(sortYears); if (allBirthYears.length > 1) birthYear = allBirthYears[0]; if (allDeathYears.length > 1) deathYear = allDeathYears[0]; }	// Show which category was matched for birth/death dates var catLinks = $('#catlinks'); if (catLinks.length) catLinks.attr('title', 'Matched categories: ' + allMatches.join('; ')); var isAlive, birthYearBuffer; if (!deathYear) {		// deathYear = today.getFullYear; var newDeathYear = birthYear + maxPossibleAge; if (newDeathYear < today.getFullYear) deathYear = today.getFullYear; else deathYear = newDeathYear; isAlive = true; }	else isAlive = false; var age = deathYear - birthYear; if (age >= minBirthYearBuffer && fromYearType == 'biography') birthYearBuffer = age; else birthYearBuffer = minBirthYearBuffer; var spansToRemove = []; var patterns = []; var birthYearLength = Math.abs(birthYear).toString.length; var deathYearLength = Math.abs(deathYear).toString.length; var todayLength = today.getFullYear.toString.length; var yearLength; if (birthYear < 0 && deathYear > 0) yearLength = 1; else if (birthYearLength < deathYearLength) yearLength = birthYearLength; else yearLength = deathYearLength; patterns.push(new RegExp('(AD |AD\u00A0)?\\b[0-9]{' + yearLength + ',' + todayLength + '}\\b( AD|\u00A0AD| CE|\u00A0CE)?', 'g')); if (birthYear < 0) patterns.push(new RegExp('\\b[0-9]{' + yearLength + ',' + todayLength + '}( |\u00A0)?BC[E]?\\b', 'g')); if (mw.config.get('wgAction') == 'submit') var allP = $('#wikiPreview > div > p'); else var allP = $('#bodyContent > div > p'); // loop through each pattern to find for (var l = 0; l < patterns.length; l++) {		// loop through each paragraph allP.each(function		{			// loop through each text node in each paragraph			$(this).contents.each(function {				findYearsInText($(this)); });		});		// remove SPANs from spansToRemove, and merge children with parent for (var i = 0; i < spansToRemove.length; i++) {			var span = spansToRemove[i]; var children = span.contents; var parent = span.parent; if (!parent.length) continue; children.each(function			{				span.before($(this).clone);			}); span.remove; parent[0].normalize; }	} } function monthsArray {	return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; } function nearAMonth(text, startIndex, beforeOrAfter, years, k) { if (!beforeOrAfter) beforeOrAfter = 1; var pattern = new RegExp(monthsArray.join('|')); if (beforeOrAfter == 1) {		// find the word immediately following the startIndex text = text.substring(startIndex, text.length); var match = text.match(pattern); // is this match only a few characters ahead of startIndex? if (match && text.indexOf(match[0]) == ' '.length) return true; else return false; }	else if (beforeOrAfter == -1) {		// first check if after the current year, there is NO ", nextYearIteration" if (years[k + 1] && (startIndex + years[k].length + ', '.length) != text.indexOf(years[k + 1])) return false; text = text.substring(0, startIndex); var match = text.match(pattern); if (match && text.indexOf(match[0]) == (startIndex - ' '.length - match[0].length)) return true; else return false; } } function parseBirthYear(birthYear) {	if (birthYear.indexOf('BC') != -1) return -1 * parseInt(birthYear); else return parseInt(birthYear); } function parseDeathYear(deathYear) {	if (typeof(deathYear) == 'string' && deathYear.indexOf('BC') != -1) return -1 * parseInt(deathYear); else return parseInt(deathYear); } function sortYears(a, b) { return a - b; } function pluralize(phrase, count, plural) {	if (plural == null) plural = phrase + 's'; return (count == 1 ? phrase : plural) } $(subjectsAgeFromYear);