MediaWiki:Gadget-AcceleratedFormCreation.js

Revision as of 20:51, 16 September 2025 by Sware (talk | contribs)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// <nowiki>
// FIXME: This gadget relies to an excessive degree on OrangeLinks to function. These gadgets should be made independent.

/*
 * 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 Contionary namespace.
    // Set window.accelEverywhere = true to test this gadget elsewhere.
    if (window.accelEverywhere || (mw.config.get("wgAction") === "view" && (mw.config.get("wgNamespaceNumber") === 120))) {
        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 closest = $(element).closest("[lang]");
            var lang = closest.length ? closest[0].getAttribute("lang") : null;
            element.accelLang = lang;
            return lang;
        };

        var getPartOfSpeech = function(link) {
            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.length) {
                        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 classList = $(link).closest(".form-of")[0].classList;
            var classNames = [];
            for (var i = 0; i < classList.length; i++) {
                if (/^(gender|origin|origin_transliteration|pos|target|transliteration)-.+|.+-form-of$/.test(classList[i])) {
                    classNames.push(classList[i]);
                }
            }

            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) {
            var lang = getLang(link);
            var targetPagename = getTargetPagename(link);

            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) {
            var lang = getLang(link);
            var targetPagename = getTargetPagename(link);
            var accelParamArr = accelParamsByPagename[lang][targetPagename];

            var accelParam = "";
            for (var i = 0; i < accelParamArr.length; i++) {
                if (i > 0) accelParam += "&";
                accelParam += "accel" + (i+1) + "=" + encodeURIComponent(accelParamArr[i]);
            }

            if (link.href.indexOf("action=edit") < 0) {
                link.href = link.href.replace(/^(.*)\/wiki\/([^#]+)(?:#.+)?$/, "$1/w/index.php?title=$2&action=edit");
            }

            link.href += "&editintro=MediaWiki:Gadget-AcceleratedFormCreation.js/intro" +
                         "&accel_lang=" + encodeURIComponent(lang) +
                         "&accel_lemma=" + encodeURIComponent(pageName.replace(/_/g, " ")) +
                         "&" + accelParam +
                         "&veswitched=1";
            link.classList.add("accelerated");
            link.processedLink = true;
        };

        var mutobs = new MutationObserver(function(mutations) {
            for (var m = 0; m < mutations.length; m++) {
                var mutation = mutations[m];
                var link = mutation.target;
                if (!(mutation.attributeName == "class" && link.tagName === "A")) {
                    continue;
                }
                if (link.processedLink) continue;
                if (!$(link).hasClass("orange-link")) continue;
                processLink(link);
            }
        });

        var oldtable = null;
        var columns = [];

        $(".form-of a").each(function() {
            var $this = $(this);
            var table = $this.closest("table");
            table = table.length ? table[0] : null;

            var col = $this.closest("td[data-accel-col]").first().data("accel-col");
            if (typeof col !== "number") col = null;

            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;

            if ($this.closest(".form-of").first().hasClass("form-of-nostore")) return;

            if (col !== null) {
                col--;
                while (columns.length <= col) columns.push([]);
                columns[col].push(this);
            } else {
                storeAccelParam(this);
            }
        });

        for (var i = 0; i < columns.length; i++) {
            for (var j = 0; j < columns[i].length; j++) {
                storeAccelParam(columns[i][j]);
            }
        }

        $(".form-of a").each(function() {
            var $this = $(this);
            if ($this.hasClass("new") || $this.hasClass("orange-link")) {
                processLink(this);
            } else {
                mutobs.observe(this, {attributes: true});
            }
        });

        window.accelForceProcessAllLinks = function() {
            $(".form-of a").each(function() {
                var $this = $(this);
                if (!($this.hasClass("new") || $this.hasClass("orange-link"))) {
                    this.classList.add("accelerated-forced");
                }
                processLink(this);
            });
        };

    } else if (mw.config.get("wgAction") === "edit") {

        var getAccelParams = function() {
            var accelParams = [];
            var i = 1;

            while (true) {
                var acceldata = mw.util.getParamValue("accel" + i);
                if (!acceldata) break;

                var params = {
                    pos: null,
                    form: null,
                    gender: null,
                    transliteration: null,
                    origin: mw.util.getParamValue("accel_lemma"),
                    origin_transliteration: null,
                    target: pageName
                };

                var parts = acceldata.split(" ");
                for (var j = 0; j < parts.length; j++) {
                    var part = parts[j];
                    var paramMatch = part.match(/^(gender|origin|origin_transliteration|pos|target|transliteration)-(.+)$/);
                    if (paramMatch) {
                        params[paramMatch[1]] = paramMatch[2].replace(/_/g, " ").replace(/\uFFF0/g, "_");
                    } else {
                        var formMatch = part.match(/^(.+)-form-of$/);
                        if (formMatch) {
                            params.form = formMatch[1].replace(/_/g, " ").replace(/\uFFF0/g, "_");
                        }
                    }
                }

                accelParams.push(params);
                i++;
            }
            return accelParams;
        };

        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].hasOwnProperty(key) && accelParams[i][key] !== null) {
                        args.push(key + (i + 1) + "=" + accelParams[i][key].replace(/\|/g, "&#124;"));
                    }
                }
            }

            return args.join("|");
        };

        var showModuleError = function(errorText) {
            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 = "<div id=\"accel-error\">" +
                           "<p><big>An error occurred while generating the entry:</big></p>" +
                           "<p>" + errorText + "</p>" +
                           "</div>";

            wikipreview.insertAdjacentHTML("beforebegin", errorBox);
        };

        var receiveModuleResponse = function(response) {
            var newtext, result;
            try {
                result = JSON.parse(response.expandtemplates.wikitext);
            } catch (err) {
                mw.notify(err.msg);
                return;
            }

            if (result.error) {
                showModuleError(result.error);
            } else {
                newtext = result.entries;
            }

            for (var 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];

            if (textbox.value) {
                var resultInBox = false;
                langsection_regex.lastIndex = 0;

                while ((match = langsection_regex.exec(textbox.value)) !== null) {
                    if (match[1] == langname) {
                        resultInBox = true;
                        break;
                    /*} else if (match[1] == "Translingual" || match[1] == "English" || (langname != "English" && match[1] < langname)) {
                        continue;*/
                    } else {
                        break;
                    }
                }

                var scrollIndex;
                newValue = textbox.value;

                if (resultInBox) {
                    var insertTextBoxIn = document.getElementById("accel-form-conflict-textbox-here");
                    if (insertTextBoxIn) {
                        var newElement = document.createElement("div");
                        newElement.id = insertTextBoxIn.id;
                        var warning = document.createElement("p");
                        warning.textContent = "A section for this language already exists. Please combine the new text manually:";
                        var textBox = document.createElement("textarea");
                        textBox.setAttribute("readonly", true);
                        textBox.setAttribute("rows", 10);
                        textBox.textContent = newtext;
                        newElement.appendChild(warning);
                        newElement.appendChild(textBox);
                        insertTextBoxIn.parentNode.replaceChild(newElement, insertTextBoxIn);
                    }
                    scrollIndex = match !== null ? match.index : newValue.length;
                } else if (match === null) {
                    newValue = newValue.trimEnd() + "\n\n" + newtext;
                    scrollIndex = newValue.length;
                } else {
                    newValue = newValue.substring(0, match.index) + newtext + "\n\n" + newValue.substring(match.index);
                    scrollIndex = match.index;
                }

                textbox.scrollTop = textbox.scrollHeight;
                textbox.selectionStart = scrollIndex;
                textbox.selectionEnd = scrollIndex;
                summary.value = "Adding forms of " + langname + " [[Contionary:" + lemma + "|" + lemma + "]] ([[wikt:WT:ACCEL|Accelerated]])";
            } else {
                newValue = newtext;
                summary.value = "Creating forms of " + langname + " [[Contionary:" + lemma + "|" + lemma + "]] ([[wikt:WT:ACCEL|Accelerated]])";
            }

            $(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;

        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": "{{#invoke:" + module + "|" + funcName + "|" + printArgs(accelParams) + "}}",
                "prop": "wikitext"
            }).done(receiveModuleResponse);
        });
    }
});
// </nowiki>