User:Automatik/editor.js

/** * Create a new DOM node for the current document. *   Basic usage:  var mySpan = newNode('span', "Hello World!") *   Supports attributes and event handlers*: var mySpan = newNode('span', {style:"color: red", focus: function{alert(this)}, id:"hello"}, "World, Hello!") *   Also allows nesting to create trees: var myPar = newNode('p', newNode('b',{style:"color: blue"},"Hello"), mySpan) * * *event handlers, there are some issues with IE6 not registering event handlers on some nodes that are not yet attached to the DOM, * it may be safer to add event handlers later manually. var newNode = window.newNode = function newNode(tagname) { var node = document.createElement(tagname);

for (var i = 1; i < arguments.length; ++i){ if (typeof arguments[i] === 'string') { // text node.appendChild(document.createTextNode(arguments[i])); } else if (typeof arguments[i] === 'object') { if (arguments[i].nodeName) { //If it is a DOM Node node.appendChild(arguments[i]); } else { // Attributes (hopefully) for (var j in arguments[i]){ if (j === 'class') { //Classname different because... node.className = arguments[i][j]; } else if (j === 'style') { //Style is special node.style.cssText = arguments[i][j]; } else if (typeof arguments[i][j] === 'function') { //Basic event handlers newNode.addEventHandler(node, j, arguments[i][j]); } else { node.setAttribute(j, arguments[i][j]); //Normal attributes }				}			}		}	}

node.addEventHandler = function(eventName, handler) { newNode.addEventHandler(this, eventName, handler); };

return node; };

newNode.addEventHandler = function(node, eventName, handler) { try{ node.addEventListener(eventName,handler,false); //W3C }catch(e){try{ node.attachEvent('on'+eventName,handler,"Language"); //MSIE }catch(e){ node['on'+eventName]=handler; }} //Legacy };

// This prevents the parser from processing the file and generating transclusions and categories for it. /** See talk page for details **/

//JsMwApi documentation is at http://en.wiktionary.org/wiki/User_talk:Conrad.Irwin/Api.js (function { function JsMwApi (api_url, request_type) {

if (!api_url) {

api_url = mw.config.get('wgScriptPath') + "/api.php"; }

if (!request_type) {		if (api_url.indexOf('http://') === 0 || api_url.indexOf('https://') === 0) request_type = "remote"; else request_type = "local"; }	function call_api (query, callback) {		if(!query || !callback) throw "Insufficient parameters for API call";

query = serialise_query(query);

if(request_type == "remote") request_remote(api_url, query, callback, call_api.on_error || default_on_error); else request_local(api_url, query, callback, call_api.on_error || default_on_error);

}

var default_on_error = JsMwApi.prototype.on_error || function (xhr, callback, res) {		if (typeof(console) != 'undefined') console.log([xhr, res]);

callback(null); };

function get_xhr {		try{ return new XMLHttpRequest; }catch(e){ try { return new ActiveXObject("Msxml2.XMLHTTP"); }catch(e){ try { return new ActiveXObject("Microsoft.XMLHTTP"); }catch(e){ throw "Could not create an XmlHttpRequest"; }}}	}

function request_local (url, query, callback, on_error) {		var xhr = get_xhr;

xhr.open('POST', url + '?format=json', true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(query); xhr.onreadystatechange = function {			if (xhr.readyState == 4) {				var res; if (xhr.status != 200) res = {error: { code: '_badresponse', info: xhr.status + " " + xhr.statusText }};				else {					try {						res = JSON.parse("("+xhr.responseText+")"); }					catch(e) {						res = {error: { code: '_badresult', info: "The server returned an incorrectly formatted response" }};					}				}				if (!res || res.error /* || res.warnings*/) on_error(xhr, callback, res); else callback(res); }		};	}

function request_remote (url, query, callback, on_error) {		if(! window.__JsMwApi__counter) window.__JsMwApi__counter = 0;

var cbname = '__JsMwApi__callback' + window.__JsMwApi__counter++;

window[cbname] = function (res) {			if (res.error /* || res.warnings*/) on_error(null, callback, res); else callback(res); };

var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.setAttribute('src', url + '?format=json&callback=window.' + cbname + '&' + query); document.getElementsByTagName('head')[0].appendChild(script); }

function serialise_query (obj) {		var amp = ""; var out = ""; if (String(obj) === obj) {			out = obj; }		else if (obj instanceof Array) {			for (var i=0; i < obj.length; i++) {				out += amp + serialise_query(obj[i]); amp = (out === '' || out.charAt(out.length-1) === '&') ? '' : '&';			}		}		else if (obj instanceof Object) {			for (var k in obj) {				if (obj[k] === true) out += amp + encodeURIComponent(k) + '=1'; else if (obj[k] === false) continue; else if (obj[k] instanceof Array) out += amp + encodeURIComponent(k) + '=' + encodeURIComponent(obj[k].join('|')); else if (obj[k] instanceof Object) throw "API parameters may not be objects"; else out += amp + encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]); amp = '&'; }		}		else if (typeof(obj) !== 'undefined' && obj !== null) {			throw "An API query can only be a string or an object"; }		return out; }

// Make JSON.parse work var JSON = (typeof JSON == 'undefined' ? {} : JSON);

if (typeof JSON.parse != 'function') JSON.parse = function (json) { return eval('(' + json + ')'); };

// Allow .prototype. extensions if (JsMwApi.prototype) {		for (var i in JsMwApi.prototype) {			call_api[i] = JsMwApi.prototype[i]; }	}	return call_api; }

JsMwApi.prototype.page = function (title) {

function call_with_page (params, callback) {		call_with_page.api([params, {title: title, titles: title}], callback); }

call_with_page.api = this;

call_with_page.edit = function (params, edit_function) {		if (typeof(params) == 'function') {			edit_function = params; params = null; }		params = [params, { action: "query", prop: ["info", "revisions"], intoken: "edit", rvprop: ["content", "timestamp"] }];

call_with_page(params, function (res)		{			if (!res || !res.query || !res.query.pages)				return edit_function(null);

// Get the first (and only) page from res.query.pages for (var pageid in res.query.pages) break; var page = res.query.pages[pageid];

var text = page.revisions ? page.revisions[0]['*'] : '';

function save_function (ntext, params, post_save) {				if (typeof(params) == 'function') {					post_save = params; params = null; }				params = [params, { action: "edit", text: ntext, token: page.edittoken, starttimestamp: page.starttimestamp, basetimestamp: (page.revisions ? page.revisions[0].timestamp : false) }];

call_with_page(params, post_save); }

edit_function(text, save_function, res);

});	};

call_with_page.parse = function (to_parse, callback) {		if (typeof to_parse == "function") {			callback = to_parse; to_parse = null; }		var params = (to_parse == null ? {page: title} : {title: title, text: to_parse});

call_with_page.api([{action: "parse", pst: true}, params], function (res)		{			if (!res || !res.parse || !res.parse.text)				callback(null, res);			else				callback(res.parse.text['*'], res);		}); };

call_with_page.parseFragment = function (to_parse, callback) {		call_with_page.parse(" \n" + to_parse + " ", function (parsed, res)		{			callback(parsed ? parsed.replace(/^ \n?/,).replace(/(\s*\n)?<\/div>\n*(\s*)?$/,) : parsed, res);		}); };

return call_with_page; };

mw.log.deprecate(window, 'JsMwApi', JsMwApi, "use mw.Api from the mediawiki.api module instead; see ResourceLoader/Default_modules"); });

/** * Storage of "string" preferences. */ function CookiePreferences (context) {	//Repeated calls with the same context should get the same preferences object. if (arguments.callee[context]) return arguments.callee[context]; else arguments.callee[context] = this;

/**	 * Change the value of a preference and store into a cookie. */	this.set = function (name, value) {		if (value === null || storage[name] === value) return; storage[name] = value; updateCookie; };

/**	 * Get the value of a preference from the cookie or default *	 * If the preference isn't set, return the second argument or undefined. */	this.get = function (name, def) {		if (storage[name]) return storage[name]; else if (defaults[name]) return defaults[name]; else return def; };

/**	 * let the default for get(name) be value for this session */	this.setDefault = function(name, value) {		defaults[name] = value; };

var storage = {}; var defaults = {};

// Save storage into the cookie. function updateCookie {		var value = ""; for (var name in storage) {			value += '&' + encodeURIComponent(name) + "=" + encodeURIComponent(storage[name]); }

jQuery.cookie('preferences' + context, value, { expires: 30 }); }	// Load storage from the cookie. // NOTE: If you wish to update the cookie format, both loading and storing // must continue to work for 30 days. function updateStorage {		var value = jQuery.cookie('preferences' + context, value, { expires: 30 }) || ''; var pairs = value.split('&');

for (var i=1; i < pairs.length; i++) {			var val = pairs[i].split('=');

if (storage[val[0]] === val[1]) continue;

storage[val[0]] = val[1]; }	}

//__init__ updateStorage; } /** * A generic page editor for the current page. * * This is a singleton and it displays a small interface in the top left after * the first edit has been registered. * * @public * this.page * this.addEdit * this.error * */ function Editor {	//Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this;

// @public - the JsMwApi object for the current page this.page = JsMwApi.page(mw.config.get('wgPageName'));

// get the current text of the article and call the callback with it // NOTE: This function also acts as a loose non-re-entrant lock to protect currentText. this.withCurrentText = function(callback) {		if (callbacks.length == 0) {			callbacks = [callback]; for (var i=0; i 0) {			return callbacks.push(callback); }

callbacks = [callback]; thiz.page.edit(function (text, _save)		{			if (text === null)				return thiz.error("Could not connect to server");

currentText = originalText = text; saveCallback = _save;

for (var i=0; i<callbacks.length; i++) {				callbacks[i](currentText); }			callbacks = [];

});	}	// A decorator for withCurrentText	function performSequentially(f)	{		return (function {			var the_arguments = arguments; thiz.withCurrentText(function 			{				f.apply(thiz, the_arguments);			}); });	}

// add an edit to the editstack function addEdit(edit, node, fromRedo) {		withPresenceShowing(false, function 		{			if (node)			{				nodestack.push(node);				node.style.cssText = "border: 2px #00FF00 dashed;"			}

if (! fromRedo) redostack = [];

var ntext = false; try {				ntext = edit.edit(currentText);

if (ntext && ntext != currentText) {					edit.redo; currentText = ntext; }				else return false; }			catch (e) {                               // TODO Uncaught TypeError: Object [object Window] has no method 'error' // I may have just fixed this by changing "this" below to "thiz" ... thiz.error("ERROR:" + e); }

editstack.push(edit); });	}	this.addEdit = performSequentially(addEdit);

// display an error to the user this.error = function (message) { 		if (!errorlog) {			errorlog = newNode('ul',{style: "background-color: #FFDDDD; margin: 0px -10px -10px -10px; padding: 10px;"}); withPresenceShowing(true, function (presence)			{				presence.appendChild(errorlog);			}); }		errorlog.appendChild(newNode('li', message)); }

var thiz = this; // this is set incorrectly when private functions are used as callbacks.

var editstack = []; // A list of the edits that have been applied to get currentText var redostack = []; // A list of the edits that have been recently undone. var nodestack = []; // A lst of nodes to which we have added highlighting var callbacks = {}; // A list of onload callbacks (initially .length == undefined)

var originalText = ""; // What was the contents of the page before we fiddled? var currentText = ""; // What is the contents now?

var saveCallback; // The callback returned by the api's edit function to save.

var errorlog; // The ul for sticking errors in. var savelog; // The ul for save messages.

//Move an edit from the editstack to the redostack function undo {		if (editstack.length == 0) return false; var edit = editstack.pop; redostack.push(edit); edit.undo;

var text = originalText; for (var i=0; i < editstack.length; i++) {			var ntext = false; try {				ntext = editstack[i].edit(text); }			catch (e) {				thiz.error("ERROR:" + e); }			if (ntext && ntext != text) {				text = ntext; }			else {				editstack[i].undo; editstack = editstack.splice(0, i); break; }		}		currentText = text; return true; }	this.undo = performSequentially(undo);

//Move an edit from the redostack to the editstack function redo {		if (redostack.length == 0) return; var edit = redostack.pop; addEdit(edit, null, true); }	this.redo = performSequentially(redo);

function withPresenceShowing(broken, callback) {		if (arguments.callee.presence) {			arguments.callee.presence.style.display = "block"; return callback(arguments.callee.presence); }

var presence = newNode('div',{'style':"position: fixed; top:0px; left: 0px; background-color: #00FF00; z-index: 10;padding: 30px;"}) //Fix fixed positioning for IE6/ /* @cc_on @if (@_jscript_version <= 5.6) presence.style.cssText = "position: absolute; top: expression((dummy = (document.documentElement.scrollTop || document.body.scrollTop || 0)) + 'px'); background-color: #00FF00; z-index: 10000; padding: 30px;" @end @ */		window.setTimeout(function {			presence.style.backgroundColor = "#CCCCFF";			presence.style.padding = "10px";		}, 400);

presence.appendChild(newNode('div',{'style':"position: relative; top:0px; left:0px; margin: -10px; color: #0000FF;cursor:pointer;", click:performSequentially(close)},"X")) document.body.insertBefore(presence, document.body.firstChild);

var contents = newNode('p', {style: 'text-align: center'},			newNode('b', "Page Editing"), newNode('br'));

if (!broken) {			contents.appendChild(newNode('button',"Save Changes", {'title':'Save your changes [s]','accesskey':'s','click': save})); contents.appendChild(newNode('br')); contents.appendChild(newNode('button',"Undo", {'title':'Undo last change [z]','accesskey':'z','click': thiz.undo})); contents.appendChild(newNode('button', "Redo", {'click':thiz.redo})); mw.loader.using('mediawiki.util').then(function {				$(contents.childNodes).updateTooltipAccessKeys;			}); }		presence.appendChild(contents);

arguments.callee.presence = presence; callback(presence); }

// Remove the button function close {		while (undo) ;

withPresenceShowing(true, function (presence)		{			presence.style.display = "none";			if (errorlog)			{				errorlog.parentNode.removeChild(errorlog);				errorlog = false;			}		}); }

//Send the currentText back to the server to save. function save {		thiz.withCurrentText(function 		{			if (editstack.length == 0)				return;

var cleanup_callbacks = callbacks; callbacks = []; var sum = {}; for (var i=0; i<editstack.length; i++) {				sum[editstack[i].summary] = true; if (editstack[i].after_save) cleanup_callbacks.push(editstack[i].after_save); }			var summary = ""; for (var name in sum) {				summary += name + " "; }			editstack = []; redostack = []; var saveLi = newNode('li', 'Saving:' + summary + "..."); withPresenceShowing(false, function (presence)			{				if (! savelog)				{					savelog = newNode('ul', {style: "background-color: #DDFFDD; margin: 0px -10px -10px -10px; padding: 10px;"});					presence.appendChild(savelog);				}				savelog.appendChild(saveLi);

if (originalText == currentText) return thiz.error("No changes were made to the page.");

else if (!currentText) return thiz.error("ERROR: page has become blank."); });

originalText = currentText; var nst = [] var node; while (node = nodestack.pop) {				nst.push(node); }			saveCallback(currentText, {summary: summary + "(Assisted)", notminor: true}, function (res)			{				if (res == null)					return thiz.error("An error occurred while saving.");

try { saveLi.appendChild(newNode('span', newNode('b', " Saved "), newNode('a', {'href': mw.config.get('wgScript') + 						'?title=' + encodeURIComponent(mw.config.get('wgPageName')) + 						'&diff=' + encodeURIComponent(res.edit.newrevid) +						'&oldid=' + encodeURIComponent(res.edit.oldrevid)}, "(Show changes)"))); }catch(e){ if (res.error) {						thiz.error("Not saved: " + String(res.error.info)); }					else {						thiz.error(newNode('p',String(e))); }				}

for (var i=0; i < nst.length; i++) nst[i].style.cssText = "background-color: #0F0;border: 2px #0F0 solid;";

window.setTimeout(function {					var node;					while (node = nst.pop)						node.style.cssText = "";				}, 400);

// restore any callbacks that were waiting for currentText before we started for (var i=0; i < cleanup_callbacks.length; i++) thiz.withCurrentText(cleanup_callbacks[i]); });		});	} }

var util = { getVanillaIndexOf: function (str, text, pos) {		if (!pos) pos = 0; var cpos = 0, tpos = 0, wpos = 0, spos = 0; do {			cpos = text.indexOf('', pos) + 3;

else if (pos == wpos) pos = text.indexOf('<\/nowiki>', pos) + 9;

else if (pos == tpos) //FIXME pos = text.indexOf('}}', pos) + 2;

} while (pos < Infinity) return -1; },

validateNoWikisyntax: function(field, nonempty) {		return function(txt, error) {			if(/[\{\}#\|]/.test(txt)) return error("Please don't use wiki markup ({}#|) in the " + field +"."); if(nonempty && !txt) return error("Please specify a " + field + "."); return txt; }	},

escapeRe: function(txt) {		return txt.replace(/([\\{}(\|)[\].?*+])/g, "\\$1"); },

// pos is a position in the line containing the gloss getWikitextGloss: function (txt, pos) {		var g_start = txt.lastIndexOf('\n{{trans-top', pos) + 1; var g_end = txt.indexOf('\n', pos); var g_line = txt.substr(g_start, g_end - g_start); g_line = g_line.replace("{{trans-top}}", "{{trans-top|Translations}}"); console.log(g_line.replace(/\{\{trans-top\|(.*)\}\}/, "$1")); return g_line.replace(/\{\{trans-top\|(.*)\}\}/, "$1"); },

// get [start_pos, end_pos] of position of wikitext for trans_table containing node in text getTransTable: function (text, node, recursive) {		var gloss = util.getTransGloss(node); var pos = 0; var transect = []; while(pos > -1) {			pos = util.getVanillaIndexOf('			if (pos > -1 && util.matchGloss(util.getWikitextGloss(text, pos), gloss))			{				console.log('getTransTable');				transect.push(pos);			}		}		if (transect.length > 1)		{			var poss = transect;			transect = [];			for (var i=0; i 1 && !recursive) transect = util.tieBreakTransTable(text, transect, node); }		if (transect.length == 1) {			pos = transect[0]; pos = util.getVanillaIndexOf("\n", text, pos) + 1; var endpos = text.indexOf('', pos); if (endpos > -1 && pos > 0) return [pos, endpos]; }		console.log('getTransTable: false'); return false; },

// try to narrow down the correct poss if multiple matching trans tables tieBreakTransTable: function (text, poss, node) {		if (node.nodeName.toLowerCase == 'div') {			while (node && !(node.className && node.className.indexOf('NavFrame') > -1)) node = node.parentNode;

var nodes = node.getElementsByTagName('table'); if (! nodes.length) return poss;

node = nodes[0]; }		else {			while (node && node.nodeName.toLowerCase != 'table') node = node.parentNode; }

var tables = document.getElementsByTagName('table'); var before_count = 0; var after_count = 0; var is_found = false; for (var i=0; i < tables.length; i++) {			if (tables[i].className.indexOf('translations') >= 0) {				var gloss = util.getTransGloss(tables[i]); if (gloss == "Translations to be checked") continue;

if (tables[i] == node) {					is_found = true; continue; }

var pos = util.getTransTable(text, tables[i], true);

if (pos) {					for (var j=0; j < poss.length; j++) {						if (poss[j] == pos) return util.tieBreakTransTable(poss.splice(j, 1), node); }				}				else {					var matched = 0; for (var j=0; j < poss.length; j++) {						if (util.matchGloss(util.getWikitextGloss(text, poss[j]), gloss) &&							 util.matchGloss(gloss, util.getWikitextGloss(text, poss[j]))) {							matched++; }					}					if (matched == poss.length) {						if (is_found) after_count++; else before_count++; }				}			}		}

if (before_count + 1 + after_count == poss.length) return [poss[before_count]]; else return poss; },

matchGloss: function (line, gloss) {		if (gloss.match(/^ *$/)) return !!(line.match(/\{\{trans-top\| *\}\}/) || line.match(/^ *$/)); var words = gloss.split(/\W+/); var pos = 0; for (var i=0; i < words.length; i++) {			pos = line.indexOf(words[i], pos); if (pos == -1) return false; }		return pos > -1; },

//User:Karelklic getTransGlossText: function (node) { var ret = ''; var children = node.childNodes; for (var i=0; i -1) ) ret += util.getTransGlossText(children[i]); else if (ret.match(/\w$/)) //Prevent new words from being created across node boundaries ret += " "; }		// all characters except a-zA-Z0-9 are changed to spaces return ret.replace(/\W/g, ' '); },

getTransGloss: function (ul) {		var node = ul; while (node && node.className.indexOf('NavFrame') == -1) node = node.parentNode;

if (!node) return '';

var children = node.childNodes; for (var i=0; i< children.length; i++) {			if(children[i].className && children[i].className.indexOf('NavHead') > -1) return util.getTransGlossText(children[i]); }		return ''; },

isTrreq: function (li) {		var spans = li.getElementsByTagName('span'); return (spans && spans.length > 0 && spans[0].className.indexOf("trreq") > -1) } };

/** * A small amount of common code that can be usefully applied to adder forms. * * An adder is assumed to be an object that has: * * .fields A object mapping field names to either validation functions used *         for text fields, or the word 'checkbox' * * .createForm  A function  that returns a newNode('form') to be added to the *             document (by appending to insertNode) * * .onsubmit  A function (values, register (wikitext, callback)) that accepts *           the validated set of values and processes them, the register *           function accepts wikitext and a continuation function to be  *            called with the result of rendering it. * * Before onsubmit or any validation functions are called, but after running * createForm, a new property .elements will be added to the adder which is a * dictionary mapping field names to HTML input elements. * * @param {editor} The current editor. * @param {adder} The relevant adder. * @param {insertNode} Where to insert this in the document. * @param {insertSibling} Where to insert this within insertNode. */ function AdderWrapper (editor, adder, insertNode, insertSibling) {	var form = adder.createForm var status = newNode('span');

form.appendChild(status); if (insertSibling) insertNode.insertBefore(form, insertSibling); else insertNode.appendChild(form);

adder.elements = {};

//This is all because IE doesn't reliably allow form.elements['name'] for (var i=0; i< form.elements.length; i++) {		adder.elements[form.elements[i].name] = form.elements[i]; }

form.onsubmit = function {		try {			var submit = true; var values = {}

status.innerHTML = ""; for (var name in adder.fields) {				if (adder.fields[name] == 'checkbox') {					values[name] = adder.elements[name].checked ? name : false; }				else {					adder.elements[name].style.border = ''; // clear error styles values[name] = adder.fields[name](adder.elements[name].value || '', function (msg) 					{						status.appendChild(newNode('span',{style:'color: red'}, newNode('img', {'src':'http://upload.wikimedia.org/wikipedia/commons/4/4e/MW-Icon-AlertMark.png'}), msg, newNode('br'))); 						adder.elements[name].style.border="solid #CC0000 2px";						return false					}); if (values[name] === false) submit = false; }			}			if (!submit) return false;

var loading = newNode('span', 'Loading...'); status.appendChild(loading); adder.onsubmit(values, function (text, callback)			{				editor.page.parseFragment(text, function (res) {					if (!res) return loading.appendChild(newNode('p', {style: 'color: red'}, "Could not connect to the server."));

callback(res); status.removeChild(loading); });			});  		}		catch(e) {			status.innerHTML = "ERROR:" + e.description; return false; }

return false; }

} // An adder for translations on en.wikt function TranslationAdders (editor) {	function TranslationLabeller (insertDiv) {		var original_span; var adder_form; var initial_value; var edit_button;

var editing = false;

var adderInterface = { 'fields': { 'gloss': function (txt, error) { return util.validateNoWikisyntax('gloss', true)(txt, error) } },			'createForm': function {				var thisId = "a" + String(Math.random).replace(".",""); return adder_form = newNode('form', {style:'display: inline', width: 'auto', click: kill_event}, 					newNode('label', {'for': thisId}, "Gloss: "),					newNode('input', {type: 'text', name:'gloss', value: initial_value, style: 'width: 50%', title: 'Insert a summary of the relevant definition', id: thisId}),					newNode('input', {type: 'submit', name: 'preview', value: 'Preview'}),					newNode('a', {href: '/wiki/Help:Glosses'}, 'Help?!')					); },			'onsubmit': function (values, render) {				render(values.gloss, function (new_html) {					if (editing)						toggle_editing(false);

var old_html = original_span.innerHTML; editor.addEdit({						'undo': function { original_span.innerHTML = old_html; if(!editing) toggle_editing;},						'redo': function  { original_span.innerHTML = new_html; if(editing) toggle_editing;},						'edit': function (text) { return perform_edit(text, values.gloss) },						'summary': 'tgloss:"' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '...' : values.gloss + '"')					}, original_span); });			}		};

// The actual modification to the wikitext function perform_edit(wikitext, gloss) {			var pos = util.getTransTable(wikitext, insertDiv)[0] - 4; var g_start = wikitext.lastIndexOf('\n\n', pos) + 2;

if (g_start == 0 || wikitext.substr(g_start, g_end - g_start).indexOf("\n") > - 1) {				editor.error("Could not find translation table."); return wikitext; }			else {				return wikitext.substr(0, g_start) + '' + wikitext.substr(g_end); }		}

// Don't open and close box when interacting with form. function kill_event(e) {			if (e && e.stopPropagation) e.stopPropagation; else window.event.cancelBubble = true; }

// What to do when the +/- button is clicked. function toggle_editing {			if (editing) {				adder_form.style.display = "none"; original_span.style.display = "inline"; editing = false; return; }			editing = true; edit_button.innerHTML = "Loading..."; editor.withCurrentText(function (currentText)			{				var pos = util.getTransTable(currentText, insertDiv);				edit_button.innerHTML = '±';

if (!pos) return editor.error("Could not find translation table");

var gloss_line = currentText.substr(currentText.lastIndexOf('\n', pos[0] - 2) + 1); gloss_line = gloss_line.substr(0, gloss_line.indexOf('\n')); initial_value = gloss_line.replace(/^\{\{trans-top(\|(.*)|)\}\}\s*$/,"$2");

if (initial_value.indexOf("\n") > 0) return editor.error("Internal error: guess spanning multiple lines");

if (!original_span) {					original_span = newNode('span', {'class':'wt-edit-recurse'}); for (var i=0; i<insertDiv.childNodes.length; i++) {						var child = insertDiv.childNodes[i]; if (child != edit_button && (!child.className || child.className != 'NavToggle')) {							original_span.appendChild(insertDiv.removeChild(child)); i--; }					}					insertDiv.appendChild(original_span);

new AdderWrapper(editor, adderInterface, insertDiv, original_span); }				original_span.style.display = "none"; adder_form.style.display = "inline"; adder_form.getElementsByTagName('input')[0].focus });		}		edit_button = newNode('a', '±', {href: '#', click: function (e) { 			if (e && e.preventDefault) e.preventDefault; kill_event(e); toggle_editing; return false; }, title:"Edit table heading", style:"padding:2px; margin-left: -5px;"});		insertDiv.insertBefore(edit_button, insertDiv.firstChild.nextSibling);	}

function TranslationAdder (insertUl) {		// Hippietrail var langmetadata = new LangMetadata ;

this.fields = { lang: function (txt, error) {				if (txt == 'en') return error("Please choose a foreign language. (fr, es, aaa)"); if (txt == 'nds') return error("Please use the code nds-de for German Low German or nds-nl for Dutch Low Saxon"); if (/^[a-z]{2,3}(-[a-z\-]{1,7})?$/.test(txt)) return txt; return error("Please use a language code. (fr, es, aaa)"); },			word: function (txt, error) {				if (txt == '') return ''; if (txt == '') return txt;

if (txt.indexOf(',') == -1 || forceComma) {					forceComma = false; if (langmetadata.expectedCase(thiz.elements.lang.value, mw.config.get('wgTitle'), txt) || forceCase) {						forceCase = false; return util.validateNoWikisyntax('translation', true)(txt, error); }

if (prefs.get('case-knowledge', 'none') == 'none') {						return error(newNode('span', "Translations normally don't have capital letters. If you're certain it does, you can ", newNode('span', {style: "color: blue; text-decoration: underline; cursor: pointer;", click: function 								{									forceCase = true;									inputForm.onsubmit;									prefs.set('case-knowledge', 'guru');								}}, "continue by clicking here."))); }					else {						var msg = newNode('span',							newNode('span', {style: "color: blue; text-decoration: underline; cursor: pointer;", click: function {									prefs.set('case-knowledge', 'none') try{ msg.parentNode.removeChild(msg); } catch(e) {} editor.undo }}, "Please click undo"), " unless you are certain that this translation has a capital letter.");

error(msg) return txt; }				}

if (prefs.get('comma-knowledge', 'none') == 'none') {					return error(newNode('span', "You can only add one translation at a time. If this is one translation that contains a comma, you can ", newNode('span', {style: "color: blue; text-decoration: underline; cursor: pointer;", click: function 							{								forceComma = true;								inputForm.onsubmit;								prefs.set('comma-knowledge', 'guru')							}}, "add it by clicking here."))) }				else {					var msg = newNode('span',						newNode('span', {style: "color: blue; text-decoration: underline; cursor: pointer;", click: function {								prefs.set('comma-knowledge', 'none') try{ msg.parentNode.removeChild(msg); } catch(e) {} editor.undo }}, "Please click undo"), " if you were trying to create a list of translations in one go, this is currently not supported.");

error(msg) return txt; }			},			qual: util.validateNoWikisyntax('qualifier'), tr: util.validateNoWikisyntax('transcription'), rawPageName: util.validateNoWikisyntax('raw page name'), sc: function (txt, error) {				if (txt && !/^((?:[a-z][a-z][a-z]?-)?[A-Z][a-z][a-z][a-z]|polytonic|unicode)$/.test(txt)) return error(newNode('span', "Please use a ", newNode('a',{href: '/wiki/Wiktionary:List_of_scripts'},"valid script code"), "(e.g. fa-Arab, Deva, polytonic)"))

if (!txt) txt = prefs.get('script-' + thiz.elements.lang.value, langmetadata.guessScript(thiz.elements.lang.value) || ''); if (txt == 'Latn') txt = ''; return txt; },			nested: util.validateNoWikisyntax('nested'), "s": 'checkbox',"d" : 'checkbox',"p"  : 'checkbox', "m": 'checkbox',"m-d": 'checkbox',"m-p": 'checkbox', "f": 'checkbox',"f-d": 'checkbox',"f-p": 'checkbox', "c": 'checkbox',"c-d": 'checkbox',"c-p": 'checkbox', "n": 'checkbox',"n-d": 'checkbox',"n-p": 'checkbox', "impf": 'checkbox',"pf": 'checkbox', nclass1: util.validateNoWikisyntax('noun class'), nclass2: util.validateNoWikisyntax('plural class') };

this.createForm = function {			var controls = { lang: newNode('input', {size:4, type:'text', name:'lang', value:prefs.get('curlang',''), title:'The two or three letter ISO 639 language code'}), transliteration: newNode('span', newNode('a', {href: '/wiki/Wiktionary:Transliteration'}, "Transliteration"), ": ", 									 newNode('input', {name: "tr", title: "The word transliterated into the Latin alphabet."}), " (e.g. ázbuka for азбука)"), qualifier: newNode('p', "Qualifier: ", newNode('input', {name: 'qual', title: "A qualifier for the word"}), " (e.g. literally, formally, slang)"), display: newNode('p',"Raw page name: ", newNode('input', {name: 'rawPageName', title: "The word without all of the dictionary-only diacritics."}), " (e.g. amo for amō)"), script: newNode('p', newNode('a', {href: '/wiki/Wiktionary:List_of_scripts'},"Script code"),": ", 					newNode('input', {name: 'sc', size: 6, title: "The script code to apply to this word."}), "(e.g. Cyrl for Cyrillic, Latn for Latin)", newNode('br')), nested: newNode('p', "Nesting: ", newNode('input', {name: 'nested', title: "The nesting of this language"}), " (e.g. Norwegian/Nynorsk)"), // Genders genders: { 's' : newNode('label', newNode('input', {type: 'checkbox', name: 's'  }), 'singular'), 'd' : newNode('label', newNode('input', {type: 'checkbox', name: 'd'  }), 'dual'), 'p' : newNode('label', newNode('input', {type: 'checkbox', name: 'p'  }), 'plural'), 'm' : newNode('label',newNode('input', {type: 'checkbox', name: 'm'  }), 'masc.'), 'm-d': newNode('label',newNode('input', {type: 'checkbox', name: 'm-d'}), 'masc. dual'), 'm-p': newNode('label',newNode('input', {type: 'checkbox', name: 'm-p'}), 'masc. pl.'), 'f' : newNode('label', newNode('input', {type: 'checkbox', name: 'f'  }), 'fem.'), 'f-d': newNode('label', newNode('input', {type: 'checkbox', name: 'f-d'}), 'fem. dual'), 'f-p': newNode('label', newNode('input', {type: 'checkbox', name: 'f-p'}), 'fem. pl.'),

'c' : newNode('label', newNode('input', {type: 'checkbox', name: 'c'  }), 'common'), 'c-d': newNode('label', newNode('input', {type: 'checkbox', name: 'c-d'}), 'common dual'), 'c-p': newNode('label', newNode('input', {type: 'checkbox', name: 'c-p'}), 'common pl.'), 'n' : newNode('label', newNode('input', {type: 'checkbox', name: 'n'  }), 'neuter'), 'n-d': newNode('label', newNode('input', {type: 'checkbox', name: 'n-d'}), 'neuter dual'), 'n-p': newNode('label', newNode('input', {type: 'checkbox', name: 'n-p'}), 'neuter pl.'), 'impf': newNode('label', newNode('input', {type: 'checkbox', name: 'impf'}), 'imperfective'), 'pf': newNode('label', newNode('input', {type: 'checkbox', name: 'pf'}), 'perfective'),	}, // Noun class nclass: newNode('p',					"Noun class: ", newNode('input', {type:'text', size:4, name:'nclass1', title:"The noun class of the word in the singular or citation form."}),					" Plural class: ", newNode('input', {type:'text', size:4, name:'nclass2', title:"The noun class of the word in the plural form, if any."})) };

controls.gender = newNode('p',				controls.genders['m'],controls.genders['m-d'],controls.genders['m-p'],newNode('br'),				controls.genders['f'],controls.genders['f-d'],controls.genders['f-p'],newNode('br'),				controls.genders['c'],controls.genders['c-d'],controls.genders['c-p'],newNode('br'),				controls.genders['n'],controls.genders['n-d'],controls.genders['n-p'],newNode('br'),				controls.genders['s'],controls.genders['d'],controls.genders['p'],newNode('br'),				controls.genders['impf'],controls.genders['pf'],newNode('br'));

langInput = controls.lang;

var showButton = newNode('span',{'click': function 			{				if (!advancedMode)				{					advancedMode = true;					showButton.innerHTML = " Less";				}				else				{					advancedMode = false;					showButton.innerHTML = " More";				}				updateScriptGuess.call(langInput, true);			}, 'style':"color: #0000FF;cursor: pointer;"}, advancedMode ? " Less" : " More");

function autoTransliterate { var rawPageName = langmetadata.computeRawPageName(thiz.elements.lang.value, thiz.elements.word.value); if (rawPageName && rawPageName !== thiz.elements.word.value) thiz.elements.rawPageName.value = rawPageName; }			function updateScriptGuess (preserve) { preserve = (preserve === true);

//show all arguments function show {					for (var i=0; i<arguments.length; i++) {						if (arguments[i].nodeName.toLowerCase == 'p') arguments[i].style.display = "block"; else arguments[i].style.display = "inline"; }

}				//hide all arguments function hide {					for (var i=0; i < arguments.length; i++) arguments[i].style.display = "none"; }				//if the first argument is false hide the remaining arguments, otherwise show them. function toggle (condition) {					if (condition) //eww... show.apply(this, [].splice.call(arguments, 1, arguments.length - 1)); else hide.apply(this, [].splice.call(arguments, 1, arguments.length - 1)); }

if (!preserve) langInput.value = langmetadata.cleanLangCode(langInput.value);

var guess = prefs.get('script-' + langInput.value, langmetadata.guessScript(langInput.value || '')); if (!preserve) {					if (guess) thiz.elements.sc.value = guess; else thiz.elements.sc.value = '';

thiz.elements.nested.value = langmetadata.getNested(langInput.value || '');

autoTransliterate; }

var lang = langInput.value;

if (!advancedMode) {					var g = langmetadata.getGenders(lang);

if (!lang) {						hide(controls.gender); }					else if (g == undefined) {						hide(controls.gender); }					else {						toggle(g.indexOf('s') > -1, controls.genders['s']); toggle(g.indexOf('d') > -1, controls.genders['d']); toggle(g.indexOf('p') > -1, controls.genders['p']); toggle(g.indexOf('m')  > -1, controls.genders['m'  ]); toggle(g.indexOf('m-d') > -1, controls.genders['m-d']); toggle(g.indexOf('m-p') > -1, controls.genders['m-p']); toggle(g.indexOf('f')  > -1, controls.genders['f'  ]); toggle(g.indexOf('f-d') > -1, controls.genders['f-d']); toggle(g.indexOf('f-p') > -1, controls.genders['f-p']);

toggle(g.indexOf('c')  > -1, controls.genders['c'  ]); toggle(g.indexOf('c-d') > -1, controls.genders['c-d']); toggle(g.indexOf('c-p') > -1, controls.genders['c-p']);

toggle(g.indexOf('n')  > -1, controls.genders['n'  ]); toggle(g.indexOf('n-d') > -1, controls.genders['n-d']); toggle(g.indexOf('n-p') > -1, controls.genders['n-p']); toggle(g.indexOf('impf') > -1, controls.genders['impf']); toggle(g.indexOf('pf') > -1, controls.genders['pf']); }

toggle(g, controls.gender);

toggle(langmetadata.hasNounClasses(lang), controls.nclass);

toggle(guess && guess != 'Latn', controls.transliteration);

toggle(langmetadata.needsRawPageName(lang), controls.display);

hide(controls.qualifier, controls.nested); //only in more //should be hidden if the language has only one script toggle(langmetadata.getScripts(lang).length != 1, controls.script); }				else {					show(						controls.gender,						controls.genders['s'],controls.genders['d'] ,controls.genders['p'],						controls.genders['m'],controls.genders['m-d'],controls.genders['m-p'],						controls.genders['f'],controls.genders['f-d'],controls.genders['f-p'],						controls.genders['c'],controls.genders['c-d'],controls.genders['c-p'],						controls.genders['n'],controls.genders['n-d'],controls.genders['n-p'],						controls.genders['impf'],controls.genders['pf'],						controls.nclass, controls.transliteration, controls.qualifier, controls.display,						controls.script, controls.nested); }			} 			//autocomplete language names var langNameToCode={}; function langAutoFill(e){ e = (e || event).keyCode; if((e >= 33 && e <= 40) || e == 8 || e == 46 || e == 27 || e == 16){ return; }				var t = this, v = t.value; if(v.substr(0,1) != v.substr(0,1).toUpperCase){ return; }				JsMwApi({action:'query',rawcontinue:,generator:'allpages',gapnamespace:10,gapprefix:'langrev/'+v,gaplimit:3,prop:'revisions',rvprop:'content'},function(r){					if(r.query && r.query.pages && t.value == v){						var l = {}, ll = {};						for(var i in r.query.pages){							var rqp = r.query.pages[i];							var content = rqp.revisions[0];							content['*'] = content['*'].replace(/\[\[Category:langrev subtemplates\]\]<\/noinclude\>$/, );							ll = rqp.title < ll.title ? ll : rqp;							l = rqp.title > l.title ? l : rqp;						}						if(!r['query-continue'] && ll.title.indexOf(l.title) == 0){							langNameToCode[l.title.substr(17)]=l.revisions[0]['*'];							if(l.title != "Template:langrev/"+v){								if (t.setSelectionRange){									t.setSelectionRange([t.value.length, t.value = l.title.substr(17)][0], t.value.length);								} else if (t.createTextRange) {									var z = t.createTextRange; z.moveEnd('character', 0 - z.move('character', [t.value.length, t.value = l.title.substr(17)][0]) + t.value.length); z.select }							}						}					}				})			}			langInput.onkeyup = langAutoFill;			langInput.onblur = function{				if(langNameToCode[this.value]){					this.value=langNameToCode[this.value]				}				updateScriptGuess.call(langInput)			}

window.setTimeout(function {updateScriptGuess.call(langInput)}, 0);

inputForm = newNode('form',						newNode('p', newNode('a',{href:"/wiki/User_talk:Conrad.Irwin/editor.js#Usage"},"Add translation"),' ', langInput, newNode('b',': '), newNode('input', {'name': 'word', size:20, keyup:autoTransliterate, change:autoTransliterate}), newNode('input',{'type': 'submit', 'value':'Preview translation'}), showButton ), 						controls.gender,						controls.nclass,						controls.transliteration,						controls.display,						controls.qualifier,						controls.script,						controls.nested					) return inputForm; }

this.onsubmit = function (values, render) {			var wikitext; function callRender { render(wikitext, function (html) { registerEdits(values, wikitext, html)}); }			if (values.word.indexOf('{{t-needed') == 0) { wikitext = '{{subst'+':#invoke:languages/templates|getByCode|' + values.lang + '|getCanonicalName}}: {{t-needed|' + values.lang + '}}'; callRender; } else { langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) {					if (needsRawPageName) {						values.rawPageName = rawPageName;						values.wordMinus = ;					} else {						values.rawPageName = ;						values.wordMinus = rawPageName;					}					wikitext = '{{subst'+':#invoke:languages/templates|getByCode|' + values.lang + '|getCanonicalName}}: ' + 						'{{t|' + values.lang + '|' + (values.rawPageName || values.word) +						(values.nclass1 ? '|c' + values.nclass1 : '') +						(values.nclass2 ? '|c' + values.nclass2 : '');					// Add gender codes					wikitext +=						(values['s'] ?   : ) + (values['d']   ? '|d'  : '') + (values['p']   ? '|p'  : '') +						(values['m'] ? '|m' : '') + (values['m-d'] ? '|m-d' : '') + (values['m-p'] ? '|m-p' : '') +						(values['f'] ? '|f' : '') + (values['f-d'] ? '|f-d' : '') + (values['f-p'] ? '|f-p' : '') +						(values['c'] ? '|c' : '') + (values['c-d'] ? '|c-d' : '') + (values['c-p'] ? '|c-p' : '') +						(values['n'] ? '|n' : '') + (values['n-d'] ? '|n-d' : '') + (values['n-p'] ? '|n-p' : '') +						(values['impf'] ? '|impf' : '') + (values['pf'] ? '|pf' : '');					wikitext +=						(values.tr ? '|tr=' + values.tr : '') + 						(values.rawPageName ? '|alt=' + values.word : '') +						/* (values.sc ? '|sc=' + values.sc : '') + */ '}}' + 						(values.qual? ' {{qualifier|' + values.qual + '}}' : '');					langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (result) { if (result) wikitext = wikitext.replace(/\{t\|/, '{t+|'); callRender; });				});			}			if (!this.balancer) this.balancer = new TranslationBalancer(editor, insertUl.parentNode.parentNode.parentNode); }

var thiz = this; var prefs = new CookiePreferences('EditorJs'); var langInput; var inputForm; var advancedMode = prefs.get('more-display', 'none') != 'none'; var forceComma = false; var forceCase = false;

//Reset elements to default values. function resetElements {			if (prefs.get('more-display', 'none') != advancedMode ? 'block' : 'none') prefs.set('more-display', advancedMode ? 'block' : 'none'); //named for compatibility thiz.elements.word.value = thiz.elements.nclass1.value = thiz.elements.nclass2.value = thiz.elements.tr.value = thiz.elements.rawPageName.value = thiz.elements.qual.value = ''; thiz.elements['s'].checked = false; thiz.elements['d'] .checked = false; thiz.elements['p']  .checked = false; thiz.elements['m'].checked = false; thiz.elements['m-d'].checked = false; thiz.elements['m-p'].checked = false; thiz.elements['f'].checked = false; thiz.elements['f-d'].checked = false; thiz.elements['f-p'].checked = false; thiz.elements['c'].checked = false; thiz.elements['c-d'].checked = false; thiz.elements['c-p'].checked = false; thiz.elements['n'].checked = false; thiz.elements['n-d'].checked = false; thiz.elements['n-p'].checked = false; thiz.elements['impf'].checked = false; thiz.elements['pf'].checked = false; prefs.set('curlang', thiz.elements.lang.value); if ((thiz.elements.sc.value || 'Latn') != (prefs.get('script-'+thiz.elements.lang.value, langmetadata.guessScript(thiz.elements.lang.value) || 'Latn'))) {				prefs.set('script-'+thiz.elements.lang.value, thiz.elements.sc.value); // Uncaught TypeError: Object # has no method 'update' thiz.elements.lang.update; }		}

// This is onsubmit after the wikitext has been rendered to give content function registerEdits (values, wikitext, content) {			var li = newNode('li',{'class': 'trans-'+values.lang}); li.innerHTML = content; var lang = getLangName(li); var summary = 't+' + values.lang + ':| values.wordMinus || values.word) + '';

var insertBefore = null; var nextLanguage = null;

var nestedHeading, nestedLang; var nestedLang; if (values.nested.indexOf('/') > -1) {				nestedLang = values.nested.replace(/.*\//, ''); nestedHeading = values.nested.replace(/\/.*/,''); if (nestedHeading == '') nestedHeading = lang; content = content.replace(/.*: /, nestedLang + ": "); wikitext = wikitext.replace("subst:", "").replace(/.*: /, nestedLang + ": "); }			else { 				nestedHeading = values.nested; nestedLang = lang; }			var nestedWikitext = "\n* " + nestedHeading + ":\n*: " + wikitext;

function addEdit (edit, span) {				editor.addEdit({					'undo': function 					{						edit.undo;						if (thiz.elements.word.value == "" && thiz.elements.nclass1.value == "" && thiz.elements.nclass2.value == "" && thiz.elements.tr.value == "" && thiz.elements.rawPageName.value == "" && thiz.elements.qual.value == "")						{							var fields = ["lang","word","nclass1","nclass2","rawPageName","qual","tr","sc"];							for (var i=0; i < fields.length; i++)							{								thiz.elements[fields[i]].value = values[fields[i]];							}							var cb = [								"s","d" ,"p",								"m","m-d","m-p",								"f","f-d","f-p",								"c","c-d","c-p",								"n","n-d","n-p",								"impf","pf"];							for (var i=0; i < cb.length; i++)							{								thiz.elements[cb[i]].checked = values[cb[i]];							}						}					},					'redo': function 					{ 						edit.redo;						var fields = ["lang","word","nclass1","nclass2","rawPageName","qual","tr","sc"];						for (var i=0; i < fields.length; i++)						{							if (thiz.elements[fields[i]].value != values[fields[i]])								return;						}						resetElements;					},					'edit': edit.edit,					'summary': summary				}, span); }

if (lang) {				//Get all li's in this table row. var lis = []; var ls = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var j=0; j < ls.length; j++) lis.push(ls[j]);

ls = insertUl.parentNode.parentNode.getElementsByTagName('dd'); for (var j=0; j < ls.length; j++) lis.push(ls[j]);

for (var j=0; j < lis.length; j++) {					if (lis[j].getElementsByTagName('form').length > 0) continue; var ln = getLangName(lis[j]); if (ln == lang) {						if (values.word == '') { addEdit({								'redo': function {},								'undo': function  {},								'edit': function  {									return editor.error("Can not add a translation request for a language with translations");								}							}); return resetElements; }						var span = newNode('span'); var parent = lis[j]; if (util.isTrreq(parent)) {							span.innerHTML = content.substr(content.indexOf(':') + 1); var trspan = parent.getElementsByTagName('span')[0]; addEdit({								'redo': function { parent.removeChild(trspan); parent.appendChild(span); },								'undo': function  { parent.removeChild(span); parent.appendChild(trspan); },								'edit': getEditFunction(values, wikitext, ln, values.lang, true, function (text, ipos) {									//Converting a Translation request into a translation var lineend = text.indexOf('\n', ipos); return text.substr(0, ipos) + wikitext + text.substr(lineend); })							}, span); return resetElements; }						else {							if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) {								span.innerHTML = ", " + content.substr(content.indexOf(':') + 1); addEdit({									'redo': function { parent.appendChild(span) },									'undo': function  { parent.removeChild(span) },									'edit': getEditFunction(values, wikitext, ln, values.lang, false, function (text, ipos) {												 //We are adding the wikitext to a list of translations that already exists. var lineend = text.indexOf('\n', ipos); var wt = wikitext.replace('subst:#invoke:',''); wt = wt.substr(wt.indexOf(':') + 1); return text.substr(0, lineend) + "," + wt + text.substr(lineend); })								}, span); return resetElements; }							else {								var node = parent.firstChild; var hastrans = false; while (node) {									if (node.nodeType == 1) {										var nn = node.nodeName.toUpperCase; if (nn == 'UL' || nn == 'DL') {											// If we want to use the dialectical nesting for orthographical nesting // then we need to skip this (otherwise perfect) match. if (!hastrans && nestedHeading == ln) {												node = node.nextSibling; continue; }											span.innerHTML = (hastrans ? ", ": " ") + content.substr(content.indexOf(':') + 1); addEdit({												'redo': function { parent.insertBefore(span, node) },												'undo': function  { parent.removeChild(span) },												'edit': getEditFunction(values, wikitext, ln, values.lang, false, function (text, ipos) {															//Adding the translation to a language that has nested translations under it															var lineend = text.indexOf('\n', ipos); var wt = wikitext.replace('subst:#invoke:',''); wt = wt.substr(wt.indexOf(':') + 1); return text.substr(0, lineend) + (hastrans ? "," : "") + wt + text.substr(lineend); })											}, span); return resetElements; }										else {											hastrans = true; }

}									node = node.nextSibling; }							}						}					}					else if (ln && ln > lang && (!nextLanguage || ln < nextLanguage) && lis[j].parentNode.parentNode.nodeName.toLowerCase != 'li') {						nextLanguage = ln; var parent = lis[j]; insertBefore = [ {								'redo': function {parent.parentNode.insertBefore(li, parent);}, 'undo': function {parent.parentNode.removeChild(li)}, 'edit': getEditFunction(values, wikitext, ln, getLangCode(parent), util.isTrreq(parent), function (text, ipos)										{											//Adding a new language's translation before another language's translation											var lineend = text.lastIndexOf('\n', ipos);											return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend);										}) },li]; }				}			}			if (values.nested) {				nextLanguage = null; insertBefore = null; li.innerHTML = nestedHeading + ":" + "" + content + "";

var lis = insertUl.parentNode.parentNode.getElementsByTagName('li'); for (var j = 0; j < lis.length; j++) {					//Ignore the editor form if (lis[j].getElementsByTagName('form').length > 0) continue;

//Don't look at nested translations if (lis[j].parentNode.parentNode.nodeName.toLowerCase != 'td') continue;

var ln = getLangName(lis[j]); if (ln == nestedHeading) {						var sublis = lis[j].getElementsByTagName('li');

if (! sublis.length) sublis = lis[j].getElementsByTagName('dd');

if (sublis.length == 0) {							var parent = lis[j]; var dd = newNode('dd', {'class': 'trans-'+values.lang}); var dl = newNode('dl', dd); dd.innerHTML = content;

addEdit({								'redo': function {parent.appendChild(dl);},								'undo': function  {parent.removeChild(dl);},								'edit': getEditFunction(values, wikitext, nestedHeading, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {											//Adding a new dl to an existing translation line var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend); })							}, dd); return resetElements; }						else {							var dd = newNode(sublis[0].nodeName, {'class': 'trans-'+values.lang}); var linestart = dd.nodeName.toLowerCase == 'dd' ? '\n*: ' : '\n** '; dd.innerHTML = content; for (var k = 0; k < sublis.length; k++) {								var subln = getLangName(sublis[k]); var parent = sublis[k]; if (subln == nestedLang) { var span = newNode('span'); span.innerHTML = ", " + content.substr(content.indexOf(':') + 1); addEdit({										'redo': function { parent.appendChild(span) },										'undo': function  { parent.removeChild(span) },										'edit': getEditFunction(values, wikitext, nestedLang, values.lang, false, function (text, ipos) {													 // Adding the wikitext to a list of translations that already exists. var lineend = text.indexOf('\n', ipos); var wt = wikitext.replace('subst:#invoke:',''); wt = wt.substr(wt.indexOf(':') + 1); return text.substr(0, lineend) + "," + wt + text.substr(lineend); })									}, span);

return resetElements; } else if (langmetadata.nestsBefore(nestedLang, subln)) {

addEdit({										'redo': function { parent.parentNode.insertBefore(dd, parent); },										'undo': function  { parent.parentNode.removeChild(dd); },										'edit': getEditFunction(values, wikitext, subln, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {													// Adding a nested translation in-order var lineend = text.lastIndexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); })									}, dd); return resetElements; }							}

addEdit({								'redo': function { parent.parentNode.appendChild(dd); },								'undo': function  { parent.parentNode.removeChild(dd); },								'edit': getEditFunction(values, wikitext, subln, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {											// Adding a nested translation at the end of its group var lineend = text.indexOf('\n', ipos); return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend); })							}, dd); return resetElements; }					}					else if (ln && ln > nestedHeading && (!nextLanguage || ln < nextLanguage)) {						nextLanguage = ln; var parent = lis[j]; insertBefore = [ {								'redo': function {parent.parentNode.insertBefore(li, parent);}, 'undo': function {parent.parentNode.removeChild(li)}, 'edit': getEditFunction(values, wikitext, ln, getLangCode(parent), util.isTrreq(parent), function (text, ipos)										{											//Adding a new nested translation section.											var lineend = text.lastIndexOf('\n', ipos);											return text.substr(0, lineend) + nestedWikitext + text.substr(lineend);										}) },li]; }				}				wikitext = nestedHeading + ":\n*: " + wikitext; }

li.className = "trans-" + values.lang; if (insertBefore) {				addEdit(insertBefore[0], insertBefore[1]); }			else {				//Append the translations to the end (no better way found) addEdit({					'redo': function {insertUl.appendChild(li);},					'undo': function  {insertUl.removeChild(li)},					'edit': getEditFunction(values, wikitext)				}, li); }			return resetElements; }

//Get the wikitext modification for the current form submission. function getEditFunction (values, wikitext, findLanguage, findLangCode, trreq, callback) {			return function(text) {				var p = util.getTransTable(text, insertUl);

if (!p) return editor.error("Could not find translation table for '" + values.lang + ":" + values.word + "'. Glosses should be unique");

var stapos = p[0]; var endpos = p[1]; var trreqLegacy = false;

if (findLanguage) {					var ipos = 0; if (trreq) {						ipos = text.substr(stapos).search(RegExp(util.escapeRe(findLanguage) + ":\\s*\\{\\{t-needed(?:\\|" + findLangCode + "}})?")) + stapos; if (ipos < stapos || ipos > endpos) ipos = text.indexOf('{{trreq|'+findLanguage+'}}', stapos); if (ipos < stapos || ipos > endpos) ipos = text.indexOf('{{trreq|'+findLangCode+'}}', stapos); }

// If we have a nested trreq, then we still need to look for the non-trreq form of the heading language if (!trreq || ipos < stapos || ipos > endpos ) {						ipos = text.substr(stapos).search(RegExp("\\*[:*]? ?\\[\\[" + util.escapeRe(findLanguage) + "\\]\\]:")) + stapos; if (ipos < stapos || ipos > endpos) ipos = text.substr(stapos).search(RegExp('\\*[:*]? ?' + util.escapeRe(findLanguage) + ':')) + stapos; if (ipos < stapos || ipos > endpos) ipos = text.substr(stapos).search(RegExp('\\*[:*]? ?\\{\\{ttbc\\|' + util.escapeRe(findLangCode) + '}}:')) + stapos; if (ipos < stapos || ipos > endpos) ipos = text.indexOf('{{subst'+':#invoke:languages/templates|getByCode|'+findLangCode+'|getCanonicalName}}:', stapos); }

if (ipos >= stapos && ipos < endpos) {						return callback(text, ipos, trreq); }					else {						return editor.error("Could not find translation entry for '" + values.lang + ":" +values.word + "'. Please reformat"); }				}

return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos); };		}

// For an  in well-formed translation sections, return the language name. function getLangName(li) {			var guess = li.textContent || li.innerText;

if (guess) guess = guess.substr(0, guess.indexOf(':'));

if (guess == 'Template') { return false; }

return guess.replace(/^[\s\n]*/,''); }

// Try to get the language code from an  containing { {t t+ or t-	// }} function getLangCode(li) {			if (li.className.indexOf('trans-') == 0) return li.className.substr(6); var spans = li.getElementsByTagName('span'); for (var i=0; i < spans.length; i++) {				if (spans[i].lang) { return spans[i].lang; }				if (spans[i].dataset && spans[i].dataset.lang) { return spans[i].dataset.lang; }			}			return false; }

}	var tables = document.getElementsByTagName('table'); for (var i=0; i -1 && util.getTransGloss(tables[i]) != 'Translations to be checked') {			var _lists = tables[i].getElementsByTagName('ul'); var lists = []; for (var j=0; j<_lists.length; j++) if (_lists[j].parentNode.nodeName.toLowerCase == 'td') lists.push(_lists[j]);

if (lists.length == 0) {				tables[i].getElementsByTagName('td')[0].appendChild(newNode('ul')); lists = tables[i].getElementsByTagName('ul'); }			if (lists.length == 1) {				var table = tables[i].getElementsByTagName('td')[2] if (table) {					table.appendChild(newNode('ul')); lists = tables[i].getElementsByTagName('ul'); }			}			if (lists) {				var li = newNode('li'); var ul = lists[lists.length - 1]; var table = tables[i]; if (table.getElementsByTagName('tbody').length > 0) table = table.getElementsByTagName('tbody')[0]; table.appendChild(newNode('tr', newNode('td'), newNode('td'), newNode('td', {'style':'text-align: left'},newNode('ul', li)))); new AdderWrapper(editor, new TranslationAdder(ul), li); if ((new CookiePreferences('EditorJs')).get('labeller') == 'true') {					var div = tables[i].parentNode.parentNode.getElementsByTagName('div')[0]; if (div.className.indexOf('NavHead') > -1) {						new TranslationLabeller(div) }				}			}		}	} }

function TranslationBalancer (editor, insertTable) {	var status;

//create the form function init {		var cns = insertTable.getElementsByTagName('tr')[0].childNodes; var tds = []; //Find all three table cells in the translation table. for (var i=0; i on the left side of the balancer. var left = tds[0].getElementsByTagName('ul'); if (left.length > 0) left = left[0]; else {			left = newNode('ul'); tds[0].appendChild(left); }

//Ensure that there is a  on the right side of the balancer. var right = tds[2].getElementsByTagName('ul'); if (right.length > 0) right = right[0]; else {			right = newNode('ul'); tds[2].appendChild(right); }

var moveLeft = newNode('input',{'type':'submit','name':'ml', 'value':'←', 'click': function{return prepareEdits('←', left, right)}}); var moveRight = newNode('input',{'type':'submit','name':'mr', 'value':'→', 'click': function{return prepareEdits('→', left, right)}}); status = newNode('span');

var form = newNode('form', moveLeft, newNode('br'), moveRight, newNode('br'), status); tds[1].appendChild(form); form.onsubmit = function { return false; } //Must be done after the appendChild for IE :(	}

function moveOneRight(left, right) {		var li = left.lastChild; while (li && li.nodeName.toLowerCase != 'li') li = li.previousSibling;

if (li) right.insertBefore(left.removeChild(li), right.firstChild); }

function moveOneLeft(left, right) {		var li = right.firstChild; while (li && li.nodeName.toLowerCase != 'li') li = li.nextSibling;

if (li) left.appendChild(right.removeChild(li)); }

//store the edit object with the editor function prepareEdits(direction, left, right) {		status.innerHTML = "Loading...";

editor.addEdit({			'redo': function { (direction == '→' ? moveOneRight : moveOneLeft)(left, right) },			'undo': function { (direction == '→' ? moveOneLeft : moveOneRight)(left, right) },			'edit': function (text) {return editWikitext(right, direction, text);},			'summary': 't-balance'		}); }

//get the wikitext modification function editWikitext(insertUl, direction, text) {		status.innerHTML = ""; //Find the position of the translation table var p = util.getTransTable(text, insertUl);

if (!p) return editor.error("Could not find translation table, please improve glosses.");

var stapos = p[0]; var endpos = p[1];

//Find the start and end of the { {trans-mid}} in the table var midpos = text.indexOf('', stapos); var midstart = text.lastIndexOf("\n", midpos); var midend = text.indexOf("\n", midpos);

if (midstart < stapos - 1 || midend > endpos) return editor.error("Could not find, please correct page.");

if (direction == '→') {			// Select the last list item of the left list (may be more than one line if nested translations are present) var linestart = text.lastIndexOf("\n", midstart - 3); while (/^[:*#;]$/.test(text.substr(linestart+2,1))) linestart = text.lastIndexOf("\n", linestart - 1);

if (linestart < stapos - 1 || linestart >= endpos) return editor.error("No translations to move");

return text.substr(0, linestart) //Everything before the item we are moving + text.substr(midstart, midend - midstart) //Then { {trans-mid}} + text.substr(linestart, midstart - linestart) //Then the item we are moving + text.substr(midend); //Then everything after { {trans-mid}} }		else if (direction == '←') {			// Select the first list item of the right list (may be more than one line if nested translations are present) var lineend = text.indexOf("\n", midend + 3); while (/^[:*#;]$/.test(text.substr(lineend+2,1))) lineend = text.indexOf("\n", lineend + 1);

if (lineend < stapos - 1 || lineend >= endpos) return editor.error("No translations to move");

return text.substr(0, midstart) //Everything before { {trans-mid}} + text.substr(midend, lineend - midend) //Then the item we are moving + text.substr(midstart, midend - midstart) //Then { {trans-mid}} + text.substr(lineend); //Then everything after the item we are moving }		return text; }	init; }

function LangMetadata {	//Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this;

//

// FIXME: merge into above var c = 'Chinese';var a = 'Arabic'; var nesting = { aae:'Albanian/Arbëresh',aat:'Albanian/Arvanitika',als:'Albanian/Tosk',aln:'Albanian/Gheg', apj:'Apache',apm:'Apache',apw:'Apache', syr:'Aramaic',syc:'Aramaic', xcl:'Armenian',axm:'Armenian', // ang:'English',enm:'English', don't nest English (Encyclopetey) fro:'French',frm:'French', oge:'Georgian', gsw:'German',ksh:'German',pfl:'German',sxu:'German', //gmh:'German',goh:'German', don't nest OHG/MHG (-sche) grc:'Greek/Ancient',gmy:'Greek/Mycenaean', //el:'Greek/Modern', don't nest Modern Greek (Atelaes) sga:'Irish',mga:'Irish', "nds-de":'Low German',"nds-nl":'Low German', nb:'Norwegian/Bokmål',nn:'Norwegian/Nynorsk', mhr:'Mari',mrj:'Mari', cmg:'Mongolian', rmn:'Romani',rml:'Romani',rmc:'Romani',rmf:'Romani',rmo:'Romani',rmy:'Romani',rmw:'Romani', dsb:'Sorbian',hsb:'Sorbian', osp:'Spanish', owl:'Welsh',wlm:'Welsh', "nai-ply":'Yokuts',"nai-bvy":'Yokuts',"nai-tky":'Yokuts',"nai-kry":'Yokuts',"nai-svy":'Yokuts',"nai-nvy":'Yokuts',"nai-dly":'Yokuts', kmr:'Kurdish/Northern Kurdish',ckb:'Kurdish/Central Kurdish', zh:c,yue:c,dng:c,gan:c,hak:c,czh:c,cjy:c,cmn:c,mnp:c,cdo:c,nan:c,czo:c,cpx:c,wuu:c,hsn:c,lzh:c, arq:a,aao:a,bbz:a,abv:a,shu:a,acy:a,adf:a,avl:a,arz:a,afb:a,ayh:a,acw:a,ayl:a,acm:a,ary:a, ars:a,apc:a,ayp:a,acx:a,aec:a,ayn:a,ssh:a,ajp:a,arb:a,apd:a,pga:a,acq:a,abh:a,aeb:a,auz:a }

// These should reflect the replacements made in Module:languages, but should not necessarily be equal. var diacriticStrippers = { ab: {from: "ҔҕҦҧ", to: "ӶӷԤԥ"}, /** obsolete to new **/ abq: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ ady: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ av: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ ce: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ dar: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ inh: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ kbd: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ lbe: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ lez: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ tab: {from: "Il1", to: "ӏӏӏ"}, /** letters and number 1 are used instead of palochka **/ cv: {from: "ĂăĔĕ", to: "ӐӑӖӗ"}, /** Roman to Cyrillic**/ ang: {from:"ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ", to:"AaÆæCcEeGgIiOoUuYy", strip:"\u0304\u0307"}, //macron and above dot ar: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, aao: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, acm: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, acx: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, adf: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, aeb: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, afb: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, ajp: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, apc: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, apd: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, arq: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, ary: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, arz: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, fa: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, ps: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, sd: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, ur: {strip:"\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652"}, chl: {from:"ÁáÉéÍíÓóÚú", to:"AaEeIiOoUu",strip:"\u0304"}, //acute accent he: {strip:"\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2", from: "-'\"", to: "־׳״"},		la: {from:"ĀāĒēĪīŌōŪūȲȳ", to:"AaEeIiOoUuYy",strip:"\u0304"}, //macron		lt: {from:"áãàéẽèìýỹñóõòúù", to:"aaaeeeiyynooouu", strip:"\u0340\u0301\u0303"},               nci: {from:"ĀāĒēĪīŌōŪūȲȳ", to:"AaEeIiOoUu",strip:"\u0304"}, //macron		ro: {from:"ŞŢşţ", to:"ȘȚșț"},		//strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list		ru: {strip:"\u0300\u0301"},		uk: {strip:"\u0300\u0301"},		be: {strip:"\u0300\u0301"},		bg: {strip:"\u0300\u0301"},		orv: {strip:"\u0300\u0301"},		cu: {strip:"\u0300\u0301"},		rue: {strip:"\u0300\u0301"},		mk: {strip:"\u0300\u0301"},		kv: {from: "ÖöIi", to: "ӦӧІі"},  /** Roman to Cyrillic**/		koi: {from: "ÖöIi", to: "ӦӧІі"},  /** Roman to Cyrillic**/		kpv: {from: "ÖöIi", to: "ӦӧІі"},  /** Roman to Cyrillic**/ sh: { from:"ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ", to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии", strip:"\u030F\u0300\u0311\u0301\u0304" },		sl: {from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł", to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel", strip: "\u0301\u0300\u0302\u0311\u030f\u0323"}, kk: {strip:"\u0300\u0301"}, ky: {strip:"\u0300\u0301"}, mn: {from:"ЄєѲѳЇїVv", to:"ӨөӨөҮүҮү",strip:"\u0300\u0301"}, tg: {strip:"\u0300\u0301"}, sa: {strip:"ः"}, /** visarga **/ bo: {strip:"།"}, /** shad **/ tr: {from:"ÂâÛû", to:"AaUu",strip:"\u0302"}, ja: {from: "０１２３４５６７８９", to: "0123456789"}, cmn: {from: "０１２３４５６７８９", to: "0123456789"}, yue: {from: "０１２３４５６７８９", to: "0123456789"}, nan: {from: "０１２３４５６７８９", to: "0123456789"}, ko: {from: "０１２３４５６７８９", to: "0123456789"}, os: {from: "Ӕӕ", to: "Ӕӕ"}, /** Roman to Cyrillic**/ uz: {from: "'`", to: "ʻʻ"}, /** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/ zu: {strip_init_hyphen: 1} }; 	//Returns true if a Wiktionary exists for the specified language this.hasWiktionary = function(lang) {		if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix)) return true; }	//Returns the domain-name prefix of the Wiktionary for the specified language this.getWiktionaryPrefix = function(lang) {		if (metadata[lang]) return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang); }	// Calls the callback with a boolean indicating whether the specified language // has a Wiktionary with the specified entry. The callback might be called // synchronously, or it might be called asynchronously. this.hasWiktionaryWithEntry = function (lang, title, callback) { if (this.hasWiktionary(lang)) $.getJSON(				'//' + this.getWiktionaryPrefix(lang) + '.wiktionary.org/w/api.php' +				'?format=json&action=query&titles=' + encodeURIComponent(title) +				'&converttitles=1&callback=?',				function (data) {					data = data && data.query && data.query.pages;					if (data) {						callback(!data[-1]);						return;					}					callback(false);				}			); else callback(false); }

//Given a language code return a default script code. this.guessScript = function(lang) {		if (metadata[lang]) { // enwikt language template? (ur-Arab, polytonic) if (metadata[lang].wsc) { return metadata[lang].wsc; }			// ISO script code? (Arab, Grek) if (metadata[lang].sc) { if (typeof metadata[lang].sc == 'object') return metadata[lang].sc[0]; else return metadata[lang].sc; }		}

return false; }

// In a given language, would we expect a translation of the title to have the capitalisation // of word? this.expectedCase = function (lang, title, word) { if (lang == 'de' || lang == 'lb') return true;

if (title.substr(0, 1).toLowerCase != title.substr(0, 1)) return true;

return word.substr(0, 1).toLowerCase == word.substr(0, 1) }

//Returns a string of standard gender letters (mfnc) or an empty string this.getGenders = function(lang) {		if (metadata[lang]) return metadata[lang].g;	}

//Returns a string of standard noun class numbers or an empty string this.hasNounClasses = function(lang) {		if (metadata[lang]) return metadata[lang].nclass; }

//Returns true if the specified lang uses optional vowels or diacritics this.needsRawPageName = function(lang) {		if (metadata[lang]) return metadata[lang].alt && !diacriticStrippers[lang]; }

// Computes the raw page name by removing diacritics (for Latin, etc.) this.computeRawPageName = function (lang, word) {		if (diacriticStrippers[lang]) {			var stripper = diacriticStrippers[lang];

var map = {};

if (stripper.from && stripper.to) {				for (var i = 0; i < stripper.from.length; i++) {					map[stripper.from.charAt(i)] = stripper.to.charAt(i); }			}			if (stripper.strip) {				for (var i = 0; i < stripper.strip.length; i++) {					map[stripper.strip.charAt(i)] = ""; }			}

var input = word.split(""); var output = "";

for (var i = 0; i < input.length; i++) {				var repl = map[input[i]]; output += (repl == null) ? input[i] : repl; }

if (stripper.strip_init_hyphen && output.length > 0 && output[0] == '-') output = output.substr(1);

return output; }	}	// Calls Module:links's 'remove_diacritics' method on word and rawPageName, // and invokes callback with two arguments: (1) the *real* raw page name // (superseding even rawPageName), and (2) whether this real raw page name // needs to be explicitly specified in the wikitext (i.e., whether the raw page	// name computed from rawPageName is different from the one that would have been	// computed from just word). this.retrieveRawPageName = function (lang, word, rawPageName, callback) { var ents = { '<': 'lt', '&': 'amp', '>': 'gt' }; var temps = { '|': '!', '=': '=', '{': '(', '}': ')' }; function encode(s) { s = s || ''; s = s.replace(/[|={}]/g, function (c) {				return '{' + '{' + temps[c] + '}}';			}); s = s.replace(/[<&>]/g, function (c) {				return '&' + ents[c] + ';';			}); s = s.replace(/\t/g, ' '); s = encodeURIComponent(s); return s;		} function removeDiacritics(s) { return '{' + '{%23invoke:languages/templates|getByCode|' + encode(lang) + '|makeEntryName|' + encode(s) + '}}'; }		var text = removeDiacritics(word) + '%09' + removeDiacritics(rawPageName || word); $.getJSON(			'/w/api.php?format=json&action=expandtemplates&text=' + text,			function (data) {				data = data && data.expandtemplates && data.expandtemplates['*'];				if (/<!--/.test(data)) {					data = false;				}				if (data) {					data = data && data.split('\t');					var wordMinus = data[0];					var rawPageNameMinus = data[1];					callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus);				} else {					callback(rawPageName || word, rawPageName && rawPageName != word);				}			}		); };

//Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3. this.cleanLangCode = function(lang) {		var key = lang.toLowerCase.replace(' ',''); if (clean[key]) return clean[key]; else return lang; }

// Get the nesting for a given sub-language this.getNested = function (lang) {		if (nesting[lang]) return nesting[lang]; else return ""; }

function temporalSortKey(langname) {		return langname.replace(/^(Ancient|Classical|Old|Middle|Early Modern|Modern) (.*)/, function(m, modifier, name)			{				return ({Ancient: 0, Old: 1, Middle: 2, "Early Modern": 3, Modern: 4})[modifier] + name;			}); }	// For enforcing an ordering on nested languages. this.nestsBefore = function (a, b)	{ return temporalSortKey(a) < temporalSortKey(b); }

this.getScripts = function(lang) {		var script = metadata[lang]?metadata[lang].wsc || metadata[lang].sc:[]; return (typeof script === 'string' ? [script] : script) || []; } }

jQuery(document).ready(function {

// Check if we are on a sensible page var actions = window.location.toString.replace(/.*\?/, '&'); if (mw.config.get('wgAction') != 'view' || actions.indexOf('&printable=yes') > -1 || actions.indexOf('&diff=') > -1 || actions.indexOf('&oldid=') > -1) return;

// Check that we have not been disabled var prefs = new CookiePreferences('EditorJs'); if (prefs.get('enabled', 'true') == 'true') {		if (! window.loadedEditor) {			prefs.setDefault('labeller', mw.config.get('wgUserName') ? 'true' : 'false' ); window.loadedEditor = true; var editor = new Editor; TranslationAdders(editor); }	}

// The enable-disable button on WT:EDIT var node = document.getElementById('editor-js-disable-button');

if (node) {		node.innerHTML = ""; var toggle = newNode('span', {click: function 		{			if (prefs.get('enabled', 'true') == 'true')			{				toggle.innerHTML = "Enable";				prefs.set('enabled', 'false');			}			else			{				toggle.innerHTML = "Disable";				prefs.set('enabled', 'true');			}

} }, (prefs.get('enabled', 'true') == 'true' ? 'Disable' : 'Enable'))

node.appendChild(toggle); }

var node = document.getElementById("editor-js-labeller-button"); if (node) {		node.innerHTML = ""; var toggle2 = newNode('span', {click: function 		{			if (prefs.get('labeller') == 'true')			{				toggle2.innerHTML = "Enable";				prefs.set('labeller', 'false');			}			else			{				toggle2.innerHTML = "Disable";				prefs.set('labeller', 'true');			}

} }, (prefs.get('labeller') == 'true' ? 'Disable' : 'Enable'))

node.appendChild(toggle2);

} })