MediaWiki:Gadget-AcceleratedFormCreation.js

// /* globals mw, $ */ // jshint maxerr:500

/* * The starting point of the whole script. * * This adds a hook to the page load event so that the script runs * adds the generated text to the edit window once the page is done loading. */

"use strict";

mw.loader.using(["mediawiki.util"]).done(function {	var pageName = mw.config.get("wgPageName");	// Don't do anything unless the current page is in the main namespace.	// Set window.accelEverywhere = true to test this gadget elsewhere.	if (window.accelEverywhere || (mw.config.get("wgAction") === "view" && (mw.config.get("wgNamespaceNumber") === 0 || pageName == "Wiktionary:Sandbox"))) {		// Stores all accelerated data, by language, by target pagename.		// Sub-arrays are in HTML order.		var accelParamsByPagename = {};		var getTargetPagename = function(link) {			var targetPagename = mw.util.getParamValue("title", link.href);			if (targetPagename === null) {				var match = link.href.match(/^(.*)\/wiki\/([^#]+)(?:#.+)?$/);				if (match) {					targetPagename = decodeURIComponent(match[2]);				}			}			return targetPagename;		};		var getLang = function (element) {			if (element.accelLang !== undefined) { return element.accelLang; }			var hasLang = $(element).closest("[lang]")[0]; var lang = hasLang ? hasLang.getAttribute("lang") : null; element.accelLang = lang; return lang; };		var getPartOfSpeech = function(link) { // Acceleration can be added to inflection tables too. // This tells the search script to skip headers with these names. var skipheaders = [ "alternative forms", "antonyms", "conjugation", "declension", "derived terms", "inflection", "mutation", "related terms", "synonyms", "translations", "usage notes" ];			for (var node = link; node !== null; node = node.previousSibling || node.parentNode) { if (node.nodeType == 1 && (node.nodeName.match(/^H[3-6]$/) || (node.nodeName === 'DIV' && node.className.indexOf('mw-heading') !== -1))) { var header = $(node).find(".mw-headline, h3, h4, h5, h6"); if (!header) { continue; }					header = header.text.replace(/^[1-9.]* /, "").toLowerCase; if (skipheaders.indexOf(header) == -1) { return header; }				}			}			throw new Error("This entry seems to be formatted incorrectly. Does it have a language and part-of-speech header?"); };		var createAccelParam = function(link) { var classNames = Array.prototype.filter.call($(link).closest(".form-of")[0].classList,				function (className) {					return (/^(gender|origin|origin_transliteration|pos|target|transliteration)-.+|.+-form-of$/.test(className));				}); var accelParam = classNames.join(" "); var targetPagename = getTargetPagename(link); var targetHead = (link.innerText || link.textContent).replace(/ /g, "_"); if (targetPagename != targetHead) { accelParam = "target-" + targetHead + " " + accelParam; }			return "pos-" + getPartOfSpeech(link).replace(/ /g, "_") + " " + accelParam; };		var storeAccelParam = function(link) { // Extract the targeted pagename from the URL, // and language code from the nearest element with a lang attribute var lang = getLang(link); var targetPagename = getTargetPagename(link); // Add page name to the list if (accelParamsByPagename[lang] === undefined) { accelParamsByPagename[lang] = {}; }			if (accelParamsByPagename[lang][targetPagename] === undefined) { accelParamsByPagename[lang][targetPagename] = []; }			var accelParam = createAccelParam(link); if (accelParamsByPagename[lang][targetPagename].indexOf(accelParam) === -1) { accelParamsByPagename[lang][targetPagename].push(accelParam); }		};		var processLink = function(link) { // Extract the targeted pagename from the URL, // and language code from the nearest element with a lang attribute var lang = getLang(link); var targetPagename = getTargetPagename(link); // Fetch the acceleration parameters from the store var accelParam = accelParamsByPagename[lang][targetPagename].slice(0) .map(function (accel, i) {					return "accel" + (i + 1).toString + "=" + encodeURIComponent(accel);				}) .join("&"); // Convert an orange link into an edit link if ($(link).hasClass("partlynew")) { link.href = link.href.replace(/^(.*)\/wiki\/([^#]+)(?:#.+)?$/, "$1/w/index.php?title=$2&action=edit"); }			// Now build a new "green link" URL to replace the original red link with link.href += "&editintro=MediaWiki:Gadget-AcceleratedFormCreation.js/intro" + "&accel_lang=" + encodeURIComponent(lang) + "&accel_lemma=" + encodeURIComponent(pageName.replace(/_/g, " ")) + "&" + accelParam; link.classList.add("accelerated"); link.processedLink = true; };		// Mutation observer to respond when OrangeLinks modifies links var mutobs = new MutationObserver(function(mutations, observer) {			mutations.forEach(function(mutation) { if (mutation.attributeName != "class") { return; }				var link = mutation.target; // Don't process a link we've already been to				if (link.processedLink) { return; }				if (!$(link).hasClass("partlynew")) { return; }				// Process processLink(link); });		});		// First generate and store all the parameters var oldtable = null; // Were we previously inside a table? var columns = []; $(".form-of a").each(function {			// Are we currently inside a table?			var $this = $(this);			var table = $this.closest("table");			if (table.length > 0) {				table = table[0];			} else {				table = null;			}			// Was a column number specified on the current table cell?			// jQuery.fn.data automatically converts an integer-like string			// to a number.			var col = $this.closest("td[data-accel-col]").first.data("accel-col");			if (typeof col !== "number") {				col = null;			}			// If we were in a table, and we changed to another table or are no longer in one,			// or if there is no column number attribute, flush the column lists.			if (oldtable && (oldtable !== table || col === null)) {				for (var i = 0; i < columns.length; ++i) {					for (var j = 0; j < columns[i].length; ++j) {						storeAccelParam(columns[i][j]);					}				}				columns = [];			}			oldtable = table;			// The nostore parameter causes the link to not be stored, // but it is processed later. The effect is that this link has no // effect on the ordering of forms. if ($(this).closest(".form-of").first.hasClass("form-of-nostore")) { return; }			// If there is a column number attribute, defer storing the link, // put it in the columns array instead. if (col !== null) { --col; // Column attributes are 1-based, JS arrays are 0-based // Expand the columns list to fit the number of columns while (columns.length <= col) { columns.push([]); }				// Save the link in the columns list columns[col].push(this); } else { // Store the link directly storeAccelParam(this); }		});		// Flush column lists		for (var i = 0; i < columns.length; ++i) {			for (var j = 0; j < columns[i].length; ++j) {				storeAccelParam(columns[i][j]);			}		}		// Then add them onto the links, or add a mutation observer		$(".form-of a").each(function { var $this = $(this); if ($this.hasClass("new") || $this.hasClass("partlynew")) { processLink(this); } else { // FIXME: There's a small window for a race condition here. // If the "partlynew" class is added by OrangeLinks after the above if-statement is evaluated, // but before the observer is added, then the link won't be processed. mutobs.observe(this, {attributes : true}); }		});	} else if (mw.config.get("wgAction") === "edit") {		// Get the parameters from the URL		var getAccelParams = function {			var accelParams = [];			var i = 1;			while (true) {				var acceldata = mw.util.getParamValue("accel" + i.toString);				if (!acceldata) {					break;				}				// Default values				var params = {					pos: null,					form: null,					gender: null,					transliteration: null,					origin: mw.util.getParamValue("accel_lemma"),					origin_transliteration: null,					target: pageName,				};				// Go over each part and add it				var parts = acceldata.split(" ");				for (var j = 0; j < parts.length; ++j) {					var part = parts[j];					if (part.match(/^(gender|origin|origin_transliteration|pos|target|transliteration)-(.+)$/)) {						params[RegExp.$1] = RegExp.$2.replace(/_/g, " ").replace(/\uFFF0/g, "_");					} else if (part.match(/^(.+)-form-of$/)) {						params.form = RegExp.$1.replace(/_/g, " ").replace(/\uFFF0/g, "_"); }				}				accelParams.push(params); ++i; }			return accelParams; };		// Generates entries from the information var printArgs = function(accelParams) { var args = [ "lang=" + mw.util.getParamValue("accel_lang"), "origin_pagename=" + mw.util.getParamValue("accel_lemma"), "target_pagename=" + pageName, "num=" + accelParams.length, ];			for (var i = 0; i < accelParams.length; ++i) { for (var key in accelParams[i]) { if (accelParams[i][key] !== null) { args.push(key + (i + 1) + "=" + accelParams[i][key].replace(/\|/g, "&#124;")); }				}			}			return args.join("|"); };		var showModuleError = function(errorText) { // Attempt to link to the line of the module in which the error occurred. errorText = errorText.replace(				/(Module:[^#<>\[\]|{}_]+)(?: at line |:)(\d+)/,				function (wholeMatch, moduleName, lineNumber) {					var link = document.createElement('a');					link.href = mw.util.getUrl(moduleName, {action: "edit"}) + "#mw-ce-l" + lineNumber;					link.innerHTML = moduleName + " at line " + lineNumber;					return "Lua error in " + link.outerHTML;				}); var errorBox = "" + " An error occurred while generating the entry: " + " " + errorText + " " + " ";			wikipreview.insertAdjacentHTML("beforebegin", errorBox); };		var receiveModuleResponse = function(response) { var newtext, result; try { result = JSON.parse(response.expandtemplates.wikitext); } catch (err) { // JSON parse error should not happen. mw.notify(err.msg); return; }			if (result.error) { // module error showModuleError(result.error); } else { // successfully generated entries newtext = result.entries; }			for (i = 0; i < result.messages.length; ++i) { mw.notify(result.messages[i]); }			if (!newtext) { return; }			var newValue;

var langsection_regex = /^==([^=\n]+)==$/mg; var match = langsection_regex.exec(newtext); if (!match) { showModuleError("No language section was found in the returned text."); }			var langname = match[1]; // Does the page already exist? if (textbox.value) { // Reset position at which regex starts its search. // Otherwise, regex starts matching after the index where it // found the language header in newtext. langsection_regex.lastIndex = 0; // Go over language sections to find where to insert our new one while ((match = langsection_regex.exec(textbox.value)) !== null) { if (match[1] == langname) { // There already exists a section for our language, abort. return; } else if (match[1] == "Translingual" || match[1] == "English" || (langname != "English" && match[1] < langname)) { // Skip past English and Translingual, or if the language sorts higher continue; } else { // We found the first match that sorts lower than our language, great. break; }				}				var scrollIndex; newValue = textbox.value; if (match === null) { // We found no language that our section should go before, so insert it at the end. newValue = newValue.trimEnd + "\n\n"; scrollIndex = newValue.length; newValue = newValue + newtext; } else { // We found a language to insert before, so do that. newValue = newValue.substring(0, match.index) + newtext + "\n\n" + newValue.substring(match.index); scrollIndex = match.index; }				// Scroll the textbox to the newly added section. First scroll all the way down, // then set the cursor to the start of the new section, which scrolls back up // to the new section's language header. textbox.scrollTop = textbox.scrollHeight; textbox.selectionStart = scrollIndex; textbox.selectionEnd = scrollIndex; summary.value = "Adding forms of " + langname + " " + lemma + " (Accelerated)"; } else { newValue = newtext; summary.value = "Creating forms of " + langname + " " + lemma + " (Accelerated)"; }			// Set textbox text. Setting textbox.value is unreliable. $(textbox).val(newValue); };		var wikipreview = document.getElementById("wikiPreview"); var textbox = document.getElementById("wpTextbox1"); var summary = document.getElementById("wpSummary"); var lang = mw.util.getParamValue("accel_lang"); var lemma = mw.util.getParamValue("accel_lemma"); if (!(wikipreview && textbox && summary && lang && lemma)) { return; }		// Gather all the information that was given in the URL var accelParams = getAccelParams; if (!accelParams) { return; }		var module = "accel", funcName = "generate_JSON"; mw.loader.using("mediawiki.api", function {			new mw.Api.get({ "action": "expandtemplates", "format": "json", "text": "", "prop": "wikitext" }).done(receiveModuleResponse);		}); } });

//