MediaWiki:Gadget-WiktAccFormCreation.js

Ji Wîkîferheng
(Ji Wîkîferheng:BILEZ hat beralîkirin)
Jump to navigation Jump to search

Zanibe: Piştî tomarkirinê, tu gireke cache'a browser'î xwe dîsa wînê ji bo dîtina guherandinan.

  • Firefor / Safari: Kepsa Shift bigre û li Reload xe, ya Ctrl-Shift-R bikepsîne (Cmd-Shift-R li cem Apple Mac)
  • IE: Kepsa Ctrl bigre û li Reload xe, ya li Ctrl-F5
  • Konqueror: Bes li Reload xe ya li kepsa F5 xe
  • Opera: Girekin belkî cache'a xwe tevda di bin Tools → Preferences da valabikin

Ev amûr formên peyvan nîv otomatîkî çêdike.

ClickedCheckbox.jpg Formên peyvan bi nîv-otomatîkî çêke. (belgekirin)

bitikîne.


Acceleration tags

The following is a list of tags that you can add to links to enable accelerated forms to be created. Each tag is associated with either (a) a template parameter to provide it when used with link templates such as {{l}} or {{l-self}}; (b) a key in the table passed as the |accel= key in the |data= parameter to the |full_link()= function in Module:links, when called from a module.

|form= (in modules) or |accel-form= (in templates)

Which particular form the link is for. It should use the inflection codes used in {{inflection of}}, with pipe symbols (vertical bars) separating the inflection codes. When called from a module, use the pipe symbols directly, e.g. |{{{1}}}= for nominative masculine plural. When used in template code, you will need to encode the pipe symbols using {{!}}, e.g. |accel-form=nom{{!}}m{{!}}p. You can also specify arbitrary text such as comparative-of, but this must be interpreted by a language-specific module (such as Module:accel/ru for Russian) and converted into the appropriate definition. This is the only tag that is mandatory.

|gender= (in modules) or |accel-gender= (in templates)

The gender code that should be added to the headword line, using the same format that {{head}} uses, e.g. m-p for masculine plural. This should only be needed in rare occasions. If the definition already mentions the gender, like "feminine form of" for example, then this is redundant and you should not add it.

|translit= (in modules) or |accel-translit= (in templates)

The transliteration that should appear on the headword line. The value of this parameter goes into param |tr= of the call to {{head}} in the accelerated entry. You only need to specify this for languages that use a non-Latin script, and only when the auto-generated transliteration is insufficient, incorrect or nonexistent.

|lemma= (in modules) or |accel-lemma= (in templates)

The lemma to be included in the call to {{inflection of}} on the definition line. The value of this parameter goes into param |2= of the call to {{inflection of}}. It defaults to the name of the current page, and only needs to be specified when the lemma contains additional diacritics that are stripped in order to generate the pagename (as in Latin, Russian, Ancient Greek, Old English, etc.).

|lemma_translit= (in modules) or |accel-lemma-translit= (in templates)

The manual transliteration of the lemma to be included in the call to {{inflection of}} on the definition line. The value of this parameter goes into param |tr= of the call to {{inflection of}}. You only need to specify this for languages that use a non-Latin script, and only when the auto-generated transliteration is insufficient, incorrect or nonexistent.

|nostore=true (in modules) or |accel-nostore=true (in templates)

Normally, the forms on a page are processed in reading order (left to right, top to bottom), and the entries/definitions will be added to the page in that order. This tag is used when the same form appears multiple times in a page/inflection table, and causes a particular link to be ignored for the ordering. This is useful when an inflection table shows some forms in its collapsed state, like for example {{se-infl-adj-even}} or {{nl-conj-wk}}. By placing this tag on the links that appear in the collapsed state, their forms will not be shown before the forms in the rest of the table.

Adding the tags to a template

There are various ways to add accelerated creation to links, both in templates/wikicode and in modules/Lua. Acceleration tags should not be added directly to entries, there should always be a template that wraps around it.

Links that are created with the standard linking templates {{l}}, {{l-self}} and such have parameters for each of the tag types listed above, with the name prefixed with accel-. Thus, for the form tag, you use the accel-form= parameter, e.g.

{{l-self|en|foos|accel-form=p}}

If you are working in a module using the full_link function of Module:links, the accel key can be provided as one of the values in the table that's passed as the first parameter. The value should be a table containing the acceleration tags as keys, e.g.

full_link({lang = lang, term = "foos", accel = {form = "p"}})

When working on a headword template, you'll typically be using {{head}}, and providing it with one or more inflected forms to show on the headword line. To provide acceleration tags for the inflected forms, the tag name is prefixed with fNaccel- N stands for the number of the form, e.g.

{{head|en|noun|plural|foos|f1accel-form=p|diminutive|bar|f2accel-form=diminutive}}

In a module using the full_headword function of Module:headword, you provide the accel key in the list of inflections of a particular label. Its value is a table of tags, like as with Module:links, e.g.

full_headword({lang = lang, inflections = { {label = "plural", accel = {form = "p"}, "foos"}, {label = "diminutive", accel = {form = "diminutive"}, "bar"} }

When you're done, add {{bilezkirî}} to the documentation page of the template. This shows a notice that accelerated links are enabled for this template. It also adds the template to Kategorî:Şablonên ku hatine bilezkirin.

// <nowiki>

/*Ji [[ca:MediaWiki:Gadget-WiktAccFormCreation.js]] formeke kevn ya [[en:MediaWiki:Gadget-AcceleratedFormCreation.js]]

 * 
 * Automatically create form-of entries based on meta-data within entries.
 * See [[Gotûbêja MediaWiki:Gadget-WiktAccFormCreation.js]] for information.
 *
 * Entry creation rules per language are located at [[Bikarhêner:Balyozxane/creationrules.js]].
 */

/*
 * 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.
 */

mw.loader.using(["mediawiki.util"]).done(function() {
	// Don't do anything unless the current page is in the main namespace.
	if (mw.config.get("wgAction") === "view" && (mw.config.get("wgNamespaceNumber") === 0 || mw.config.get("wgPageName") == "Wîkîferheng:ceribandin"))
	{
		// 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 createAccelParam = function(link)
		{
			var classnames = $(link).closest(".form-of")[0].className.split(" ");
			
			for (var i = 0; i < classnames.length; ++i)
			{
				// Filter out anything that doesn't belong
				if (!(/^(gender|origin|origin_transliteration|pos|target|transliteration)-(.+)$/.test(classnames[i]) || /^(.+)-form-of$/.test(classnames[i])))
					classnames[i] = "";
			}
			
			var accelParam = classnames.join(" ").replace(/  +/g, " ").trim();
			
			var targetPagename = getTargetPagename(link);
			var targetHead = (link.innerText || link.textContent).replace(" ", "_");
			
			if (targetPagename != targetHead)
				accelParam = "target-" + targetHead + " " + accelParam;
			
			return "pos-" + get_part_of_speech(link).replace(" ", "_") + " " + 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 = $(link).closest("[lang]")[0].getAttribute("lang");
			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 process_link = function(link)
		{
			// Extract the targeted pagename from the URL,
			// and language code from the nearest element with a lang attribute
			var lang = $(link).closest("[lang]")[0].getAttribute("lang");
			var targetPagename = getTargetPagename(link);
			
			// Fetch the acceleration parameters from the store
			var accelParam = accelParamsByPagename[lang][targetPagename].slice(0);
			
			for (var i = 0; i < accelParam.length; ++i)
			{
				accelParam[i] = "accel" + (i + 1).toString() + "=" + encodeURIComponent(accelParam[i]);
			}
			
			accelParam = accelParam.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=User:Balyozxane/creation/intro" +
				"&accel_lang=" + encodeURIComponent($(link).closest("[lang]")[0].getAttribute("lang")) +
				//"&accel_langname=" + encodeURIComponent(get_language_name(link)) +
				"&accel_lemma=" + encodeURIComponent(mw.config.get("wgTitle")) +
				"&" + accelParam;
			link.style.color = "#22CC00";
		};
		
		// 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.style.color == "#22CC00")
					return;
				if (!$(link).hasClass("partlynew"))
					return;
				
				// Process
				process_link(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 table = $(this).closest("table");
			
			if (table.length > 0)
				table = table[0];
			else
				table = null;
			
			// Was a column number specified on the current table cell?
			var col = $(this).closest("td[data-accel-col]");
			
			if (col.length > 0)
				col = parseInt(col[0].getAttribute("data-accel-col"));
			else
				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")[0]).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() {
			if ($(this).hasClass("new") || $(this).hasClass("partlynew")) {
				process_link(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 = {
					lang: mw.util.getParamValue("accel_lang"),
					langheader: "{{ziman|" + mw.util.getParamValue("accel_lang") + "}}",
					pos: null,
					form: null,
					gender: null,
					transliteration: null,
					origin: mw.util.getParamValue("accel_lemma"),
					origin_pagename: mw.util.getParamValue("accel_lemma"),
					origin_transliteration: null,
					target: mw.config.get("wgTitle"),
					target_pagename: mw.config.get("wgTitle"),
					};
				
				// 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("_", " ");
					else if (part.match(/^(.+)-form-of$/))
						params.form = RegExp.$1.replace("_", " ");
				}
				
				accelParams.push(params);
				++i;
			}
			
			return accelParams;
		};
		
		jQuery.getScript("//ku.wiktionary.org/w/index.php?action=raw&title=Bikarhêner:Balyozxane/creationrules.js&ctype=text/javascript&smaxage=21600&maxage=86400", 
			function () {
				var textbox = document.getElementById("wpTextbox1");
				var summary = document.getElementById("wpSummary");
				var lang = mw.util.getParamValue("accel_lang");
				var langheader = "{{ziman|" + lang + "}}";
				var lemma = mw.util.getParamValue("accel_lemma");
				
				if (!textbox || !summary || !lang || !lemma)
					return;
				
				// Gather all the information that was given in the URL
				var accelParams = getAccelParams();
				
				if (!accelParams)
					return;
				
				// Generate entries from the information
				var entries = [];
				
				for (var i = 0; i < accelParams.length; ++i)
				{
					// Calls to creation rules
					var entry = generate_entry(accelParams[i]);
					
					if (entry)
						entries.push(entry);
				}
				
				if (!entries)
					return;
				
				// Put it all together into one language entry
				var newtext = entries_to_text(entries);
				
				if (!newtext)
					return;
				
				newtext = "== " + langheader + " ==\n\n" + newtext;
				
				// Does the page already exist?
				if (textbox.value)
				{
					var langsection_regex = /^==([^=\n]+)==$/mg;
					var match;
					
					// Go over language sections to find where to insert our new one
					while ((match = langsection_regex.exec(textbox.value)) !== null)
					{
						if (match[1] == langheader) {
							// There already exists a section for our language, abort.
							return;
						} else if (match[1] == "{{ziman|navz}}" || match[1] == "{{ziman|ku}}" || (langheader != "{{ziman|ku}}" && match[1] < langheader)) {
							// Skip past Kurdî and Navzimanî, or if the language sorts higher
							continue;
						} else {
							// We found the first match that sorts lower than our language, great.
							break;
						}
					}
					
					if (match === null) {
						// We found no language that our section should go before, so insert it at the end.
						textbox.value = textbox.value.trimEnd() + "\n\n" + newtext;
					} else {
						// We found a language to insert before, so do that.
						textbox.value = textbox.value.substring(0, match.index) +  newtext + "\n\n" + textbox.value.substring(match.index, textbox.value.length);
					}
					
					summary.value = "Lêzêdekirina formên [[" + lemma + "]] ([[WF:BILEZ|Bi lez]])";
				}
				else
				{
					textbox.value = newtext;
					summary.value = "Çêkirina formên [[" + lemma + "]] ([[WF:BILEZ|Bi lez]])";
				}
			}
		);
	}
});


// Exception class
function PreloadTextError(message)
{
	this.name = "PreloadTextError";
	this.message = message;
}

PreloadTextError.prototype = new Error();
PreloadTextError.prototype.constructor = PreloadTextError;


function get_part_of_speech(link)
{
	// Acceleration can be added to inflection tables too.
	// This tells the search script to skip headers with these names.
	var skipheaders = ["herwiha", "dijmane", "tewîn", "tewandin", "jê", 
		"têkildar", "hevmane", "werger", "bikaranîn", "formeke navdêrê", "formeke serenavê", "formeke hokerê", "formeke cînavê"];
	var node = link;
	
	while (node)
	{
		if (node.nodeType == 1 && node.nodeName.match(/^[hH][3]$/))
		{
			var header = $(node).find(".mw-headline").text().replace(/^[1-9.]* /, "").toLowerCase();
			
			if (skipheaders.indexOf(header) == -1)
				return header;
		}
		
		node = node.previousSibling || node.parentNode;
	}
	
	throw new Error("This entry seems to be formatted incorrectly. Does it have a language and part-of-speech header?");
}


function entries_to_text(entries)
{
	var entries_new = [];
	
	// Merge multiple entries into one if they differ only in the definition
	for (var i = 0; i < entries.length; ++i)
	{
		if (entries_new.length > 0 &&
			entries[i].pronunc == entries_new[entries_new.length-1].pronunc &&
			entries[i].pos_header == entries_new[entries_new.length-1].pos_header &&
			entries[i].head == entries_new[entries_new.length-1].head &&
			entries[i].inflection == entries_new[entries_new.length-1].inflection &&
			entries[i].declension == entries_new[entries_new.length-1].declension &&
			entries[i].conjugation == entries_new[entries_new.length-1].conjugation)
		{
			var params1 = entries_new[entries_new.length-1].def.match(/^{{formeke peyvê\|([^{}]+)}}$/);
			var params2 = entries[i].def.match(/^{{formeke peyvê\|([^{}]+)}}$/);
			
			entries_new[entries_new.length-1].def = entries_new[entries_new.length-1].def + "\n# " + entries[i].def;
			
			// Do some extra-special merging with "inflection of"
			if (params1 && params2)
			{
				// Find the last unnamed parameter of the first template
				params1 = params1[1].split("|");
				var lastNumberedIndex;
				
				for (var j = 0; j < params1.length; ++j)
				{
					if (params1[j].indexOf("=") === -1)
						lastNumberedIndex = j;
				}
				
				// Add grammar tags of the second template
				params2 = params2[1].split("|");
				var tags = [];
				var n = 0;
				
				for (var j = 0; j < params2.length; ++j)
				{
					if (params2[j].indexOf("=") === -1)
					{
						++n;
						
						// Skip the first two unnamed parameters,
						// which don't indicate grammar tags
						if (n < 3)
							continue;
						
						// Now append the tags
						tags.push(params2[j]);
						
					}
				}
				
				// Add the new parameters after the existing ones
				params1[lastNumberedIndex] += "|;|" + tags.join("|");
				entries_new[entries_new.length-1].def = "{{formeke peyvê|" + params1.join("|") + "}}";
			}
		}
		else
		{
			entries_new.push(entries[i]);
		}
	}
	
	for (var i = 0; i < entries_new.length; ++i)
	{
		var entry = entries_new[i];
		
		entry =
			(entry.pronunc ? "=== Bilêvkirin ===\n" + entry.pronunc + "\n\n" : "") +
			"=== " + entry.pos_header + " ===\n" +
			entry.head + "\n" +
			"# " + entry.def +
			(entry.inflection ? "\n\n==== Tewîn ====\n" + entry.inflection : "") +
			(entry.declension ? "\n\n==== Tewandin ====\n" + entry.declension : "") +
			(entry.conjugation ? "\n\n==== Conjugation ====\n" + entry.conjugation : "");
		
		entries_new[i] = entry;
	}
	
	return entries_new.join("\n\n");
}

// </nowiki>