User:Erutuon/scripts/accel.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.pathname.match(/^\/wiki\/(.+)$/);				if (match) {					return 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]$/)) { var header = $(node).find(".mw-headline").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?"); };		// Decode JSON in data-accel attribute. // Add "target" field if it is different from the pagename of the link target. var createAccelParam = function(link) { var acceleratedElement = $(link).closest(".form-of")[0]; var accelParam = null; var rawAccel = acceleratedElement.dataset.accel; if (typeof rawAccel !== "string") { console.debug("data-accel attribute of", link, "not present"); }			try { accelParam = JSON.parse(acceleratedElement.dataset.accel); } catch (e) { console.debug("data-accel parameter of", link, "could not be decoded as JSON", e); return null; // ?? }			var targetPagename = getTargetPagename(link); // Why replace space with underscore? var targetHead = (link.innerText || link.textContent).replace(/ /g, "_"); if (targetPagename != targetHead) { accelParam.target = targetHead; }			accelParam.target = getPartOfSpeech(link).replace(/ /g, "_"); return 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); // How to use Array.prototype.indexOf with Objects rather than Strings? 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); // 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 // Fetch the acceleration parameters from the store link.href += "&editintro=User:Conrad.Irwin/creation.js/intro" + "&accel=" + encodeURIComponent(JSON.stringify(accelParamsByPagename[lang][targetPagename].slice(0))); link.style.color = "#22CC00"; 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 = null;			try {				accelParams = JSON.parse(decodeURIComponent(mw.util.getParamValue("accel")));			} catch (e) {				console.debug("Error while percent-decoding and JSON-decoding ", mw.util.getParamValue("accel"));				console.debug(e);				console.debug("This is the fault of the acceleration script for encoding data in the URL wrong");			}			if (accelParams === null) {				// for compatibility; perhaps this should be changed				return [];			}			accelParams = accelParams.map(function(param) { return $.extend({					pos: null,					form: null,					gender: null,					transliteration: null,					origin: mw.util.getParamValue("accel_lemma"),					origin_transliteration: null,					target: pageName,				}, param); });			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;			// Does the page already exist?			if (textbox.value) {				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]; // 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\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\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 " + lemma + " (Accelerated)"; } else { newValue = newtext; summary.value = "Creating forms of " + 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/sandbox", funcName = "generate_JSON"; mw.loader.using("mediawiki.api", function {			new mw.Api.get({ "action": "expandtemplates", "format": "json", "text": "", "prop": "wikitext" }).done(receiveModuleResponse);		}); } });

//