MediaWiki:Gadget-AcceleratedFormCreation.js: difference between revisions
Jump to navigation
Jump to search
Content deleted Content added
No edit summary |
No edit summary |
||
Line 456: | Line 456: | ||
// Add the new parameters after the existing ones |
// Add the new parameters after the existing ones |
||
params1[lastNumberedIndex] += "|;|" + tags.join("|"); |
params1[lastNumberedIndex] += "|;|" + tags.join("|"); |
||
entries_new[entries_new.length-1].def = "{ |
entries_new[entries_new.length-1].def = "{{inflection of|" + params1.join("|") + "}}"; |
||
} |
} |
||
} |
} |
Revision as of 21:28, 23 November 2017
/*
* {{documentation}}
* Automatically create form-of entries based on meta-data within entries.
* See [[MediaWiki:Gadget-AcceleratedFormCreation.js/documentation]] for information.
*
* Entry creation rules per language are located at [[User:Conrad.Irwin/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") == "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 = 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:Conrad.Irwin/creation.js/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;
if (!$(link).hasClass("partlynew"))
return;
// Process
process_link(link);
observer.disconnect(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"),
langname: mw.util.getParamValue("accel_langname"),
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("//en.wiktionary.org/w/index.php?action=raw&title=User:Conrad.Irwin/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 langname = mw.util.getParamValue("accel_langname");
var lemma = mw.util.getParamValue("accel_lemma");
if (!textbox || !summary || !lang || !langname || !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 = "==" + langname + "==\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] == 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;
}
if (match === null)
// We found no language that our section should go before, so insert it at the end.
textbox.value += "\n\n----\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----\n\n" + textbox.value.substring(match.index, textbox.value.length);
summary.value = "Adding forms of [[" + lemma + "]] ([[WT:ACCEL|Accelerated]])";
}
else
{
textbox.value = newtext;
summary.value = "Creating forms of [[" + lemma + "]] ([[WT:ACCEL|Accelerated]])";
}
}
);
}
});
// Exception class
function PreloadTextError(message)
{
this.name = "PreloadTextError";
this.message = message;
}
PreloadTextError.prototype = new Error();
PreloadTextError.prototype.constructor = PreloadTextError;
function get_language_name(link)
{
var node = link;
while (node)
{
if (node.nodeType == 1)
{
if (node.nodeName.match(/^[hH]2$/))
return $(node).find(".mw-headline").text().replace(/^[1-9.]* /, "");
else if (node.className == "languageContainer") // TabbedLanguages support
return node.id.replace(/container$/, "");
}
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 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 = ["alternative forms", "antonyms", "conjugation", "declension", "derived terms",
"inflection", "mutation", "related terms", "synonyms", "translations", "usage notes"];
var node = link;
while (node)
{
if (node.nodeType == 1 && node.nodeName.match(/^[hH][3456]$/))
{
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(/^{{inflection of\|([^{}]+)}}$/);
var params2 = entries[i].def.match(/^{{inflection of\|([^{}]+)}}$/);
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 = "{{inflection of|" + 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 ? "===Pronunciation===\n" + entry.pronunc + "\n\n" : "") +
"===" + entry.pos_header + "===\n" +
entry.head + "\n\n" +
"# " + entry.def +
(entry.inflection ? "\n\n====Inflection====\n" + entry.inflection : "") +
(entry.declension ? "\n\n====Declension====\n" + entry.declension : "") +
(entry.conjugation ? "\n\n====Conjugation====\n" + entry.conjugation : "");
entries_new[i] = entry;
}
return entries_new.join("\n\n");
}