Note – after saving, you may have to bypass your browser’s cache to see the changes.

  • Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
  • Konqueror and Chrome: click Reload or press F5;
  • Opera: clear the cache in Tools → Preferences;
  • Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.

// {{documentation}} <nowiki>
window.Tbot = window.Tbot || {};
window.Tbot.LanguagesToGreenify = window.Tbot.LanguagesToGreenify || [];

/* ==Change redlinks to greenlinks== */

Tbot.greenifyTranslinks = function(langcode) {
	if (mw.config.get('wgAction') !== 'view')
		return;
	if (!document.querySelector)
		return;
	if (!document.querySelector('table.translations span:lang(' + langcode + ') > a.new'))
		return;
	var headers = document.querySelectorAll('h3, h4, h5');
	for (var i = 0; i < headers.length; ++i) {
		if ($(headers[i].querySelector('.mw-headline')).text() !== 'Translations')
			continue;
		var posHeader = getPosForTransSect(i);
		if (!posHeader)
			continue;
		for (var elem = headers[i].nextElementSibling; elem && elem.tagName.search(/^[hH]\d$/) === -1; elem = elem.nextElementSibling) {
			var linksToGreenify =
				elem.querySelectorAll('span:lang(' + langcode + ') > a.new');
			if (linksToGreenify.length === 0)
				continue;
			var gloss = getGlossForTransTable(elem);
			for (var j = 0; j < linksToGreenify.length; ++j) {
				var link = linksToGreenify[j];

				var tbotData = {
					lang: langcode,
					pos: posHeader.toLowerCase(),
					head: $(link).text().trim().replace(/ +/g, ' '),
					xlit: getXlitForLink(link),
					g: getGenderForLink(link),
					trans: mw.config.get('wgTitle'),
					gloss: gloss
				};
				if (mw.util.wikiUrlencode(tbotData.head) ===
					link.getAttribute('href').match(/\?(?:.*?&)?title=([^&]+)/)[1])
					delete tbotData.head;
				removeEmptyProps(tbotData);

				var href = link.getAttribute('href');
				href = href.split('#')[0]; // shouldn't be needed
				if (href.indexOf('?') === -1) // shouldn't happen
					href += '?';
				href += '&tbotData=' + encodeURIComponent(JSON.stringify(tbotData));

				link.setAttribute('href', href);
				link.style.color = 'rgb(34, 204, 0)';
			}
		}
	}

	function getPosForTransSect(i) // headers[i] is the trans-sect-header
	{
		// We start at headers[i-1], and scroll back until we find a header at
		// a higher level than headers[i]. For example, if ====Translations====
		// is at L4, then the closest-previous L3 header is the POS header.

		for (var j = i - 1; j >= 0; --j)
			if (headers[j].tagName < headers[i].tagName) {
				var ret = $(headers[j].querySelector('.mw-headline')).text();
				ret = ret.replace(/\s+\d+$/, ''); // e.g. 'Noun 1' -> 'Noun'
				return ret;
			}
		return null; // shouldn't happen
	}

	function getGlossForTransTable(navFrame) {
		var ret = $(navFrame.querySelector('.NavHead')).text();
		ret = ret.trim().replace(/^\[.*?\]\s*/, '').replace(/^\u00B1\s*/, '');
		return ret;
	}

	function getXlitForLink(link) {
		for (var node = link.parentNode.nextSibling; node; node = node.nextSibling) {
			if (node.nodeType !== Node.ELEMENT_NODE)
				continue;
			if (node.lang === langcode) // hit {t} or {t+} or {l}
				break;
			if (node.tagName.toUpperCase() === 'A') // hit [[ ]]
				break;
			if (node.tagName.toUpperCase() === 'SPAN' &&
				node.hasAttribute('lang') &&
				node.lang === '')
				return node.innerHTML;
		}
		return '';
	}

	function getGenderForLink(link) // e.g. '' or 'm' or 'c1' or 'm|f'
	{
		var ret = '';
		for (var elem = link.parentNode.nextElementSibling; elem; elem = elem.nextElementSibling)
			if (elem.tagName.toUpperCase() === 'A') // hit {{t}} or [[ ]]
				return '';
			else if (elem.lang === langcode) // hit {{t-SOP}} or {{l}}
			return '';
		else if (elem.className === 'gender')
			return decodeGenderSpan(elem);
		return '';
	}

	function decodeGenderSpan(span) {
		var child = span.firstChild;
		if (child.nodeValue && child.nodeValue.trim() === 'class') {
			child = child.nextSibling;
			var ret = '';
			while (child) {
				if (child.nodeType === Node.ELEMENT_NODE) {
					ret += '|c' + child.innerHTML.trim();
				}
				child = child.nextSibling;
			}
			return ret.replace(/^[|]/, '');
		} else {
			var abbrToCode = {
				pl: 'p',
				anim: 'an',
				du: 'd',
				inan: 'in',
				sg: 's',
				pers: 'pr'
			};
			var ret = '';
			while (child) {
				if (child.nodeType === Node.ELEMENT_NODE) {
					var abbr = child.innerHTML.trim();
					ret += '-' + (abbrToCode[abbr] || abbr);
				} else if (child.nodeType === Node.TEXT_NODE) {
					if (child.nodeValue.trim() === ',')
						ret += '|';
				}
				child = child.nextSibling;
			}
			return ret.replace(/(^|[|])-/g, '$1');
		}
	}

	function removeEmptyProps(obj) {
		var propsToRemove = [];
		for (var key in obj)
			if (obj[key] === undefined || obj[key] === null || obj[key] === '')
				propsToRemove.push(key);
		for (var i = 0; i < propsToRemove.length; ++i)
			delete obj[propsToRemove[i]];
	}
};

/*</pre>
==Populate edit-window at target of a greenlink==
<pre>*/

Tbot.getHeadLine = Tbot.getHeadLine || {};

if (!Tbot.getHeadLine.run)
	Tbot.getHeadLine.run = function(tbotData) {
		var f;

		// Has a special case has been set up for this lang & POS?
		f = f || Tbot.getHeadLine[tbotData.lang + '~' + tbotData.pos];

		// . . . how about a special case for this lang, regardless of POS?
		f = f || Tbot.getHeadLine[tbotData.lang + '~'];

		// . . . or for this POS, regardless of lang?
		f = f || Tbot.getHeadLine['~' + tbotData.pos];

		// No? O.K., then just use the default:
		f = f || Tbot.getHeadLine.default;

		return f(tbotData);
	};

// a suitable default, using the {{head}} template
if (!Tbot.getHeadLine.default)
	Tbot.getHeadLine.default = function(tbotData) {
		var ret = '{{head|' + tbotData.lang;
		if (tbotData.pos.indexOf('=') > -1)
			ret += '||1=' + tbotData.pos;
		else
			ret += '|' + tbotData.pos;
		if (tbotData.head)
			ret += '|head=' + tbotData.head;
		if (tbotData.xlit)
			ret += '|tr=' + tbotData.xlit;
		if (tbotData.g)
			ret += '|g=' + tbotData.g.split('|')[0];
		if (tbotData.g && tbotData.g.indexOf('|') > -1)
			ret += '|g2=' + tbotData.g.split('|')[1];
		ret += '}}';
		return ret;
	};

// An augmented default: use {{head}} initially, but also launch an
// AJAX request to see if there exists a more-specific template and,
// if so, to replace {{head}} with a stab in the dark at how to use
// that template.
Tbot.getHeadLine['attempt-{{xx-pos}}'] = function(tbotData) {
	var lang = tbotData.lang;
	var pos = tbotData.pos;

	jQuery.getJSON(
		'/w/api.php?format=jsonfm&action=query&titles=' +
		'Template:' + lang + '-' + pos,
		function(data) {
			data = data && data.query && data.query.pages;
			if (!data)
				return;
			var regex = new RegExp('[{][{]head[|]' + lang + '[|]' + pos + '(?=[|}]');
			var wpTextbox1 = document.getElementById('wpTextbox1');
			if (!wpTextbox1 || !regex.test(wpTextbox1.value))
				return;
			for (var pageid in data)
				if (pageid.charAt(0) !== '-') {
					wpTextbox1.value =
						wpTextbox1.value.replace(regex, '{{' + data[pageid].title.substr(9));
					return;
				}
		}
	);

	return Tbot.getHeadLine['{{head}}'](tbotData);
};

Tbot.getHeadLine['ru~adjective'] =
	Tbot.getHeadLine['ru~adverb'] =
	Tbot.getHeadLine['ru~noun'] =
	Tbot.getHeadLine['ru~proper noun'] =
	Tbot.getHeadLine['ru~verb'] = function(tbotData) {
		var ret = '{{ru-';
		if (tbotData.pos === 'adjective' || tbotData.pos === 'adverb')
			ret += tbotData.pos.substr(0, 3); // 'adj' or 'adv'
		else
			ret += tbotData.pos; // 'noun', 'proper noun', 'verb'
		ret += '|' + (tbotData.head || '{' + '{subst:PAGENAME}}');
		if (tbotData.g)
			if (tbotData.pos === 'verb')
				// not actually a gender: it's 'impf' or 'pf':
				ret += '|' + tbotData.g.split('|')[0];
			else if (tbotData.pos === 'noun' || tbotData.pos === 'proper noun') {
			var genders = tbotData.g.split(/[|]/);
			ret += '|' + genders[0];
			for (var i = 1; i < genders.length; ++i)
				ret += '|g' + (i + 1) + '=' + genders[i];
		}
		if (tbotData.xlit)
			ret += '|tr=' + tbotData.xlit;
		ret += '}}';
		return ret;
	};

Tbot.getHeadLine['tpi~verb'] = function(tbotData) {
	if (mw.config.get('wgPageName').search(/im$/) > -1)
		return '{{tpi-verb|t}}';
	else
		return '{{tpi-verb|i}}';
};

Tbot.getHeadLine['yi~adjective'] =
	Tbot.getHeadLine['yi~adverb'] =
	Tbot.getHeadLine['yi~noun'] =
	Tbot.getHeadLine['yi~phrase'] =
	Tbot.getHeadLine['yi~proper noun'] =
	Tbot.getHeadLine['yi~verb'] = function(tbotData) {
		var ret = '{{yi-';
		if (tbotData.pos === 'adjective' || tbotData.pos === 'adverb')
			ret += tbotData.pos.substr(0, 3); // 'adj' or 'adv'
		else
			ret += tbotData.pos; // 'noun', 'phrase', etc.
		if (tbotData.head)
			ret += '|head=' + tbotData.head;
		if (tbotData.xlit)
			ret += '|tr=' + tbotData.xlit;
		if (tbotData.pos === 'noun' && tbotData.g)
			ret += '|g=' + tbotData.g.replace(/[|]/, '');
		if (tbotData.pos === 'noun' && tbotData.g === 'f' &&
			mw.config.get('wgPageName').search(/\u05E2$/) > -1)
			ret += '|pl=s';
		ret += '}}';
		return ret;
	};

Tbot.getInflSect = function(tbotData) {
	// allow special-casing of specific languages and POSes:
	if (Tbot.getInflSect[tbotData.lang + '~' + tbotData.pos])
		return Tbot.getInflSect[tbotData.lang + '~' + tbotData.pos](tbotData);

	return '';
};

Tbot.getInflSect['yi~adjective'] = function(tbotData) {
	var template = '{{yi-decl';
	if (tbotData.xlit)
		template += '|tr=' + tbotData.xlit;
	template += '}}';
	return '\n====Declension====\n' + template + '\n';
};

$(function() {
	if (mw.config.get('wgNamespaceNumber') !== 0)
		return;
	if (mw.config.get('wgAction') !== 'edit')
		return;
	if (!document.getElementById('ca-nstab-main'))
		return;
	if (document.getElementById('ca-nstab-main').className !== 'selected new')
		return;
	var tbotData = document.location.href.match(/&tbotData=([^&]+)/);
	if (!tbotData)
		return;
	tbotData = JSON.parse(decodeURIComponent(tbotData[1]));
	if (tbotData.pos)
		tbotData.posHeader =
		tbotData.pos.charAt(0).toUpperCase() + tbotData.pos.substr(1);
	var wikitext = '';
	if (!tbotData.lang || tbotData.lang.search(/^[a-z-]+$/) === -1)
		return;
	wikitext += '=={{subst:#invoke:languages/templates|getByCode|' + tbotData.lang + '|getCanonicalName}}==\n\n';
	if (!tbotData.posHeader)
		return;
	if (tbotData.posHeader.search(/^=|=$/) > -1)
		wikitext += '=== ' + tbotData.posHeader + ' ===\n';
	else
		wikitext += '===' + tbotData.posHeader + '===\n';
	wikitext += Tbot.getHeadLine.run(tbotData) + '\n\n';
	if (!tbotData.trans)
		return;
	wikitext += '# [[' + tbotData.trans + ']]';
	if (tbotData.gloss)
		wikitext += ' {{gloss|' + tbotData.gloss + '}}';
	wikitext += '\n';
	wikitext += Tbot.getInflSect(tbotData);
	document.getElementById('wpTextbox1').value = wikitext;
});

$(function(){
	Tbot.LanguagesToGreenify.forEach(function(langcode){ Tbot.greenifyTranslinks(langcode);});
});

// </nowiki>