Järjestelmäviesti:Gadget-NyttUppslag2.js

Huomautus: Selaimen välimuisti pitää tyhjentää asetusten tallentamisen jälkeen, jotta muutokset tulisivat voimaan.

  • Firefox ja Safari: Napsauta Shift-näppäin pohjassa Päivitä, tai paina Ctrl-F5 tai Ctrl-R (⌘-R Macilla)
  • Google Chrome: Paina Ctrl-Shift-R (⌘-Shift-R Macilla)
  • Internet Explorer ja Edge: Napsauta Ctrl-näppäin pohjassa Päivitä tai paina Ctrl-F5
  • Opera: Paina Ctrl-F5.
/**
 * NyttUppslagin ja käännös-gadgetin yhteiset apufunktiot.
 **/
(function () {
    window.fiwikt = window.fiwikt || {};

    /**
     * Yleiskäyttöinen funktio, joka hakee sivun html-sisällön.
     *
     * @param title: sivun nimi
     * @return {Promise}, jossa sivun teksti.
     * Käyttö:
     *     haeData("Järjestelmäviesti:Testi2").then(function (data) {
     *         alert(JSON.stringify(data));
     *     });
     **/
    window.fiwikt.haeHtml = function(title) {
        var q = {
            action: "parse",
            page: title,
            prop: "text",
        };

        return (new mw.Api()).get(q).then(function (data) {
            var pageid,
                page,
                revision,
                content;

            if ( ! data.parse
              || ! data.parse.text
              || ! data.parse.text['*'] ) {
                throw "Ongelma vastauksen tulkitsemisessa";
            }

            text   = data.parse.text['*'];

            return text;
        });
    };

    /**
     * Yleiskäyttöinen funktio, joka hakee JSON-objektin annetun sivun ensimmäisestä pre-elementistä.
     *
     * @param title: sivun nimi
     * @return {Promise}, jossa sivun data objektina.
     * Käyttö:
     *     haeData("Järjestelmäviesti:Testi2").then(function (data) {
     *         alert(JSON.stringify(data));
     *     });
     **/
    window.fiwikt.haeHtmlJson = function(title) {
        return fiwikt.haeHtml(title)
                     .then(function (html) {
                         var json;

                         json = html.substring(html.search('<pre>') + 5, html.search("</pre>"));

                         return JSON.parse(json);
                     });
    };


    /**
     * Tekee tekstistä suomen kielen aakkostuksen mukaisen aakkostusavaimen.
     * TODO paremmin
     **/
    window.fiwikt.aakkostus = function (teksti) {
        return teksti
            .toLowerCase()
            .replace("ä", "A")
            .replace("ö", "O")
            .normalize("NFD")
            .replace(/[^a-zAO]/, "")
            .replace("A", "ä")
            .replace("O", "ö");
    };


    /**
     * Muotoilee mallinekutsun annetuista parametreista.
     *
     * @param params: {Object} mallineen parametrit. Nimettömät parametrit numeroilla, joissa
     *                params[0] on mallineen nimi.
     **/
    fiwikt.muotoileMalline = function (params) {
        var nimet = Object.keys(params)
                          .sort(),
            nimi,
            i,
            out = [];

        for ( i = 0; i < nimet.length; i++ ) {
            nimi = nimet[i];
            if ( parseInt(nimi) === i ) {
                out.push(params[i]);
            } else {
                out.push(nimi + "=" + params[nimi]);
            }
        }

        return "{{" + out.join("|") + "}}";
    };

    /**
     * Muotoilee kielikohtaisen sanarivin annetuista parametreista.
     *
     * @param params: {Object} mallineen parametrit. Nimettömät parametrit numeroilla, joissa
     *                params[0] on mallineen nimi.
     * @param templ:  {String} sanrivin kaava, esim. "{{verbi|ru«|paino=#paino#»«|lat=#lat#»}}« {{ru-#aspekti#}}»"
     **/
    fiwikt.muotoileSanarivi = function (params, templ) {
        templ = templ.replace(/&#([^\s;]*);/g,
                              function(_, m) {
                                  return String.fromCharCode(m);
                              }
        );
        var found = {};
        for ( var key in params ) {
            found[key] = false;
        }

        var res = templ.replace(
            /«([^#]*)#([^#]+)#([^»]*)»/g,
            function korvaa(x, pre, name, suf) {
                if ( params[name] ) {
                    var val = params[name];
                    found[name] = true;
                    return pre + val + suf;
                }
                return "";
            }
        );

        console.assert(
            Object.values(found).every(function (val) { return val; }),
            "Kaikkia annettuja parametreja ei löytynyt mallista:", templ, params
        );

        return res;
    };

}());

//fiwikt.haeHtmlJson("Järjestelmäviesti:Testi2").then(function (data) {
//alert(JSON.stringify(data));
//});
/**
 * Vain nyttUppslagin käyttämät apufunktiot.
 **/
(function () {
    /**
     * Poistaa linkkimerkit annetusta tekstistä.
     *
     * Esim. "[[katti|katin]]" -> "katin", "[[koira]]" -> "koira"
     **/
    window.fiwikt.poistaLinkit = function(text) {
        return text.replace(/\[\[[^\]|]+\|([^\]]+)\]\]/g, "$1")
            .replace(/[\]\[]{2}/g, "");
    };

    window.fiwikt.isolla = function(sana) {
        return sana.replace(/^(['!]?)(.)(.*)$/, function(m, m1, m2, m3) { return m1 + m2.toUpperCase() + m3; });
    };

}());
(function () {
    window.fiwikt = window.fiwikt || {};

    // TODO valmistelu lua-päähän
    function valmisteleKieliluettelo (data) {
        var i,
            refs = [], // viittaukset fiwikt.kielet -taulukkoon
            item,
            kielet = data.kielet,
                   nimiohjaukset = data.rinnakkaisnimet || [],
                   koodiohjaukset = data.rinnakkaiskoodit || [];

        fiwikt.kielet = {};

        // Aakkostus
        for ( i = 0; i < kielet.length; i++ ) {
            item = kielet[i];
            fiwikt.kielet[item.koodi] = item;

            refs.push( {
                aak:  fiwikt.aakkostus(item.nimi),
                nimi: item.nimi,
                koodi: item.koodi
            });
            refs.push( {
                aak:  fiwikt.aakkostus(item.koodi),
                nimi: item.nimi,
                koodi: item.koodi
            });

            // Yhdyssana tai sanaliitto, esim. muinaisenglanti aakkostetaan myös
            // englantimuinais, että löytyy englanti-sanalla. TODO
            if ( item.jako ) {
                refs.push( {
                    aak:  fiwikt.aakkostus(item.jako),
                    nimi: item.nimi,
                    koodi: item.koodi || item.ohjaus,
                    tyyppi: "osa"
                } );
            }
        }

        for ( i = 0; i < nimiohjaukset.length; i++ ) {
            item = nimiohjaukset[i];
            refs.push( {
                aak:  fiwikt.aakkostus(item.nimi),
                nimi: fiwikt.kielet[item.ohjaus].nimi,
                koodi: item.ohjaus,
                tyyppi: "nimiohjaus"
            } );
        }

        for ( i = 0; i < koodiohjaukset.length; i++ ) {
            item = koodiohjaukset[i];
            refs.push( {
                aak:  item.koodi,
                nimi: fiwikt.kielet[item.ohjaus].nimi,
                koodi: item.ohjaus,
                tyyppi: "koodiohjaus"
            } );
        }

        // Lajitellaan aak-kentän mukaan.
        return refs.sort(function (a, b) { return a.aak > b.aak; });
    }

    /**
     * Palauttaa kohdan, jossa ensimmäinen arvo alkaa aak tai seuraavan, jos mikään ei ala.
     * Listan `array` tulee olla järjestetty `aak`in mukaan.
     * @private
     */
    function haePaikka(array, aak) {
        var lo = -1,
            hi = array.length,
            mi;

        while (1 + lo < hi) {
            mi = lo + ((hi - lo) >> 1);
            if ( array[mi].aak >= aak ) {
                hi = mi;
            } else {
                lo = mi;
            }
        }

        return hi;
    }


    /**
     * Kielen syöttölaatikko lomakkeeseen.
     *
     * @class
     * @extends OO.ui.TextInputWidget
     * @mixins  OO.ui.mixin.LookupElement
     *
     * @constructor
     * @param {Object} config
     */
    fiwikt.LanguageLookupTextInputWidget = function fwiktLanguageLookupTextInputWidget( config ) {
        var cookie = $.cookie(config.cookieNimi);
        this.cookieNimi = config.cookieNimi;
        this.defaults = valmisteleKieliluettelo( { "kielet" : JSON.parse($.cookie(this.cookieNimi) || "[]") } );
        this.omienKieltenMaara = config.omienKieltenMaara || 10;
        this.kielikoodisivu = config.kielikoodisivu;
        this.cache_no = 1;
        this.cache = null;
        this.kenttienTiedot = JSON.parse($.cookie(config.cookieNimi + ".tiedot") || "[]");

        config.allowSuggestionsWhenEmpty = true;

        OO.ui.TextInputWidget.call( this, $.extend( { validate: function (a) { return a.match(/. \[[-a-z]+\]$/); } }, config ) );
        OO.ui.mixin.LookupElement.call( this, config );
    };

    OO.inheritClass( fiwikt.LanguageLookupTextInputWidget, OO.ui.TextInputWidget );
    OO.mixinClass( fiwikt.LanguageLookupTextInputWidget, OO.ui.mixin.LookupElement );




    /**
     *
     **/
    fiwikt.LanguageLookupTextInputWidget.prototype.getLookupRequest = function () {
        var value = this.getValue().match(/^[^\[]*/)[0].trim(),
            aak = fiwikt.aakkostus(value),
            deferred = $.Deferred(),
            that = this,
            arr,
            index;

        // Jos kentän tieto oikean muotoinen (kieli + kielikoodi hakasuluissa), ei
        // palauteta mitään. Muuten joko ladataan tiedot tai palautetaan tallennetut tiedot.
        this.getValidity().then(
            function () {
                deferred.resolve( [], [] );
            },
            function () {
                if ( (that.cache_no === 1 || value === "") && that.kenttienTiedot.length > 0 ) {
                    // Näytetään omat kielet niin kauan kuin ainakin yksi omien kielten
                    // kielen nimi alkaa kentän arvolla. Heti kun prefiksiä ei enää löydy
                    // ladataan kieliluettelo.
                    arr = that.defaults;
                    index = haePaikka(arr, aak);

                    if ( index < arr.length && arr[index].aak.startsWith(aak) ) {
                        return deferred.resolve( arr, that.kenttienTiedot );
                    }

                    that.cache_no = 2;
                    that.requestCache = {};

                }

                // Ladataan kielitiedot, jos niitä ei ole vielä ladattu. Muuten
                // palautetaan tallennettu data.
                if ( that.cache === null || that.kenttienTiedot.length === 0 ) {
                        fiwikt.haeHtmlJson(that.kielikoodisivu)
                              .then(function (data) {
                                  that.cache = valmisteleKieliluettelo(data);
                                  that.tallennaKenttienTiedot(data["kenttien tiedot"]);
                                  deferred.resolve(that.cache, data["kenttien tiedot"]);
                              });
                } else {
                    deferred.resolve( that.cache, that.kenttienTiedot );
                }
            });

        return deferred.promise( { abort: function () {} } );
    };

    /**
     * Valitsee näytettävät kielet kieliluettelosta.
     */
    fiwikt.LanguageLookupTextInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
        var items = [],
            i,
            index,
            item,
            val = this.getValue().match(/^[^\[]*/)[0].trim(),
            aak = fiwikt.aakkostus(val),
            index = haePaikka(response, aak),
            item = response[index];

        for ( i = index; i < response.length; i++ ) {
            item = response[i];
            if ( ! item.aak.startsWith(aak) ) {
                break;
            }

            items.push( item );
            prev = item.aak;
        }

        // Jos pelkät omat kielet, lajitellaan ne nimen mukaan ja poistetaan tuplat. Jos koko kieliluettelo on ladattu
        // jätetään lajittelematta.
        if ( response === this.defaults ) {
            items = items
                .sort(function (a, b) { return (a.nimi > b.nimi); })
                .filter(function (item, index) { return ( index === 0 || (items[index - 1].nimi !== item.nimi) ); });
        }

        return items;
    };


    /**
     * Tekee luetteloelementit näytettävästä datasta.
     */
    fiwikt.LanguageLookupTextInputWidget.prototype.getLookupMenuOptionsFromData = function ( data ) {
        var items = [],
            i,
            prev,
            item,
            item_data,
            item_label;

        // Näytetään viimeiset kielet, jos kieliluetteloa ei ole ladattu tai laatikko on tyhjä.
        if ( (this.cache_no === 1 || this.getValue() === "") && data.length > 0 ) {
            items.push( new OO.ui.MenuSectionOptionWidget( {
                label: 'Omat ja viimeksi käytetyt kielet'
            } ) );
        }

        for ( i = 0; i < data.length; i++ ) {
            item = data[i];

            item_data = fiwikt.kielet[item.koodi];

            if ( item.tyyppi == "koodiohjaus" && item.aak !== item_data.koodi ) {       // Ohjaus käytettävään kielikoodiin.
                item_label = item_data.nimi + " [" + item.aak + " → " + item.koodi + "]";
            } else if ( item.tyyppi == "nimiohjaus" && item.nimi !== item_data.nimi ) { // Ohjaus käytettävään nimeen.
                item_label = item.nimi + " → " + item_data.nimi + " [" + item.koodi + "]";
            } else {
                item_label = item_data.nimi + " [" + item.koodi + "]";
            }

            if ( item_label !== prev ) {
                items.push( new OO.ui.MenuOptionWidget( { data: item_data, label: item_label } ) );
            }

            prev = item_label;
        }



        return items;
    };

    fiwikt.LanguageLookupTextInputWidget.prototype.onLookupMenuChoose = function ( item ) {
        // Asetetaan alkion data koko härvelin dataksi, koska alkioon ei enää pääse käsiksi
        // valinnan jälkeen.
        this.setData( item.data );
        this.setValue( item.data.nimi + " [" + item.data.koodi + "]" );
    };



    /**
     * Lisää kielen tiedot evästeeseen viimeisimmän kymmenen kielen listaan.
     **/
    fiwikt.LanguageLookupTextInputWidget.prototype.tallennaViimeaikaisiin = function ( kielidata ) {
        var omatKielet = JSON.parse($.cookie(this.cookieNimi) || "[]"),
                       uusi = [ kielidata ],
                       i;

        for ( i = 0; i < omatKielet.length && uusi.length <= this.omienKieltenMaara; i++ ) {
            // Jos kieli oli jo listassa, jätetään vain ohitetaan se, koska vasta lisätty on ensimmäisenä.
            if ( omatKielet[i].nimi !== kielidata.nimi ) {
                uusi.push(omatKielet[i]);
            }
        }

        $.cookie(this.cookieNimi, JSON.stringify(uusi), { expires: 30, path: "/;SameSite=Strict" } );
    };

    /**
     * Palauttaa kenttien tiedot.
     **/
    fiwikt.LanguageLookupTextInputWidget.prototype.haeKenttienTiedot = function ( ) {
        var deferred = $.Deferred();

        if ( this.kenttienTiedot.length > 0 ) {
            deferred.resolve( this.kenttienTiedot );
        } else {
            console.info("Gadget nytt-uppslag2: päivitetään kenttien tiedot ja kieliluettelo");
            this.getLookupRequest().then(function (_, tiedot) {
                deferred.resolve( tiedot );
            });
        }

        return deferred;
    };

    /**
     * Tallentaa kenttien tiedot jäsenmuuttujaan ja evästeeseen.
     **/
    fiwikt.LanguageLookupTextInputWidget.prototype.tallennaKenttienTiedot = function ( tiedot ) {
        this.kenttienTiedot = tiedot;
        $.cookie(this.cookieNimi + ".tiedot", JSON.stringify(tiedot), { expires: 7, path: "/;SameSite=Strict" } );
    };

}());
(function () {

    fiwikt.ParamFieldsetLayout = function FiWiktParamFieldsetLayout( config ) {
        config = config || {};

        this.kentatMap = {};
        this.nakyvatKentat = [];

        this.switchWidget = new OO.ui.ToggleSwitchWidget( {
            value: false,
            title: 'Näytä kaikki parametrit'
        } );

        config.label = $("<div>" + config.label + "</div>")
            .append($('<label class="param-lomake-toggle">Näytä kaikki </label>')
                    .append(this.switchWidget.$element));


        this.switchWidget.connect( this, { 'change': this.onSwitchChange } );

        // Parent constructor
        fiwikt.ParamFieldsetLayout.parent.call( this, config );

        this.$element.addClass( 'fiwikt-paramFieldsetLayout' );

        // Events
        this.aggregate( {
            "ctrlEnter": 'submit', // Emitoidaan, kun painetaan ctrl-enter missä tahansa kentässä.
        } );

    };
    OO.inheritClass( fiwikt.ParamFieldsetLayout, OO.ui.FieldsetLayout );

    /**
     * Piilottaa kaikki kentät.
     **/
    fiwikt.ParamFieldsetLayout.prototype.piilotaKentat = function () {
        var i,
            kentatMap = this.kentatMap,
            kaikkiKentat = Object.keys(kentatMap)
                                 .map(function (key) { return kentatMap[key]; });

        for (i in kaikkiKentat ) {
            kaikkiKentat[i].toggle(false);
        }
    };


    /**
     * Näyttää kaikki kentät.
     **/
    fiwikt.ParamFieldsetLayout.prototype.naytaKaikki = function () {
        var i,
            kentatMap = this.kentatMap,
            kaikkiKentat = Object.keys(kentatMap)
            .map(function (key) { return kentatMap[key]; });

        for (i in kaikkiKentat ) {
            kaikkiKentat[i].toggle(true);
        }
    };

    /**
     * Näyttää kentät joiden parametriarvot on annettu taulukossa `fields`.
     *
     * Jos kenttiä ei ole, lisätään ne.
     **/
    fiwikt.ParamFieldsetLayout.prototype.naytaKentat = function () {
        var i,
            fields = this.nakyvatKentat,
            field,
            superfield,
            suvut = {};

        this.piilotaKentat();


        for ( i in fields ) {
            field = fields[i];
            if ( field.indexOf(":") !== -1 ) {
                superfield = field.split(":")[0];
                if ( this.kentatMap[superfield] ) {
                    this.kentatMap[superfield].toggle(true);
                }
            }
            if ( this.kentatMap[field] ) {
                this.kentatMap[field].toggle(true);
            } else {
                this.lisaaKentta(field, "<" + field + ">", "Kentän määritys puuttuu sivulta Järjestelmäviesti:KielitietoJSON.");
            }
        }
    };

    /**
     * Asettaa taulukossa `fields` annetut kentät näkyvksi ja piilottaa muut.
     *
     * Jos kenttiä ei ole, lisätään ne.
     **/
    fiwikt.ParamFieldsetLayout.prototype.asetaNakyvaksi = function (fields) {
        this.nakyvatKentat = fields;
        this.naytaKentat();
        this.switchWidget.setValue(false);
    };

    /**
     * Palauttaa multiselectwidgetin ´multiselect` checkboxien arvot merkkijonona. Arvot yhdistetään
     * pötköön.
     **/
    fiwikt.ParamFieldsetLayout.prototype.haeValinta = function (multiselect) {
        var selected = multiselect.findSelectedItems();
        if ( !selected ) {
            return "";
        }
        if ( selected.length === undefined ) {
            // Jos radionappi, findSelectedItems ei palauta listaa.
            selected = [ selected ];
        }

        // Valitaan laatikot, jotka on valittu ja näkyvissä ja yhdistetään niiden nimet pötköksi.
        // Esim. jos maskuliini "m" ja feminiini "f" on valittu -> "mf"
        return selected
            .filter(function (cb) { return cb.isVisible(); })
            .map(function(cb) { return cb.getData(); })
            .join("");
    };

    /**
     * Asettaa multiselectwidgetin ´multiselect` checkboxien arvot merkkijonon `val` mukaisesti.
     **/
    fiwikt.ParamFieldsetLayout.prototype.asetaCheckboxienArvo = function (field, val) {
        var i,
            items = field.getItems(),
            item;

        for ( i = 0; i < items.length; i++ ) {
            item = items[i];
            if ( val.search(item.getData()) !== -1 ) {
                item.setSelected(true);
            } else {
                item.setSelected(false);
            }
        }
    };

    /**
     * Asettaa multiselectwidgetin ´multiselect` checkboxien arvot merkkijonon `val` mukaisesti.
     **/
    fiwikt.ParamFieldsetLayout.prototype.asetaRadionappienArvo = function (field, val) {
        var i,
            items = field.getItems(),
            item;

        for ( i = 0; i < items.length; i++ ) {
            item = items[i];
            if ( val === item.getData() ) {
                item.setSelected(true);
            } else {
                item.setSelected(false);
            }
        }
    };

    /**
     * Palauttaa fieldsetin `fieldset` tekstikenttien arvot taulukkona.
     **/
    fiwikt.ParamFieldsetLayout.prototype.haeParametrit = function () {
        var out = {},
            that = this;

        this.getItems().forEach(function (item) {
            var val, key;

            // Tarkistetaan onko näkyvä, niin kenttiä ei tarvitse tyhjentää sanojen välissä.
            if ( item.fieldWidget && item.isVisible() ) {
                if ( item.fieldWidget.getValue ) {
                    key = item.fieldWidget.getData()['param'];
                    val = item.fieldWidget.getValue();
                } else {
                    key = item.fieldWidget.getData()['param'];
                    val = that.haeValinta(item.fieldWidget);
                }

                if ( val !== "" ) {
                    out[key] = val;
                }
            }
        });

        return out;
    };

    /**
     * Asettaa kenttien tiedot parametrien `parametrit` mukaan.
     **/
    fiwikt.ParamFieldsetLayout.prototype.asetaParametrit = function (parametrit) {
        var that = this;

        this.getItems().forEach(function (item) {
            var val, key;

            if ( !item.fieldWidget ) {
                return;
            }

            if ( item.fieldWidget.setValue ) {
                key = item.fieldWidget.getData()['param'];
                item.fieldWidget.setValue(parametrit[key]);
            } else if ( item.fieldWidget instanceof OO.ui.CheckboxMultiselectWidget ) {
                key = item.fieldWidget.getData()['param'];
                val = parametrit[key] || "";
                that.asetaCheckboxienArvo(item.fieldWidget, val);
            } else if ( item.fieldWidget instanceof OO.ui.RadioSelectWidget ) {
                key = item.fieldWidget.getData()['param'];
                val = parametrit[key] || "";
                that.asetaRadionappienArvo(item.fieldWidget, val);
            }
        });

    };

    /**
     * Lisää parametria `param` vastaavan kentän fieldsetiin `fieldset`. Jos label on
     * annettu käytetään sitä kentän tekstinä. Muuten käytetään `param`-argumentin vastaavaa
     * arvoa.
     **/
    fiwikt.ParamFieldsetLayout.prototype.lisaaKentta = function (param, label, huom) {
        var that = this,
            textInput = new OO.ui.TextInputWidget( {
                validate: function (val) {
                    return ( val !== mw.config.get("wgPageName") );
                } }),
            field = new OO.ui.FieldLayout( textInput, {
                label: (label || param) + ": ",
                title: huom,
            } );

        textInput.setData({ "param" : param });
        this.addItems( [ field ]);
        this.kentatMap[param] = field;

        // Propagoidaan tekstilaatikon enter-eventti layoutelementille, joka sisältää sen.
        textInput.on('enter', function(event) {
            if ( event.originalEvent.ctrlKey ) {
                field.emit("ctrlEnter");
            }
        });

        return  field;
    };

    /**
     * Lisää taulukon `params` parametreille kentät.
     * Esim. params = [ { parametri: "lat", teksti: "Latinisointi" }, ... ]
     **/
    fiwikt.ParamFieldsetLayout.prototype.lisaaKentat = function (params, info) {
        var i;

        for ( i = 0; i < params.length; i++ ) {
            if ( params[i].tyyppi === "cb" ) {
                this.lisaaCBKentta(params[i].parametri, params[i].teksti, params[i].arvot);
            } else if ( params[i].tyyppi === "rb" ) {
                this.lisaaRBKentta(params[i].parametri, params[i].teksti, params[i].arvot);
            } else {
                this.lisaaKentta(params[i].parametri, params[i].teksti);
            }
        }
    };


    /**
     * Lisää parametria `param` vastaavan checkbox-alueen, joka on otsikoitu `text` fieldsetiin
     * Checkboxien arvot annetaan taulukossa `values`.
     **/
    fiwikt.ParamFieldsetLayout.prototype.lisaaCBKentta = function (param, label, values) {
        var i,
            multiselect = new OO.ui.CheckboxMultiselectWidget( { } ),
            items = [],
            checkbox,
            field = new OO.ui.FieldLayout( multiselect, {
                label: (label || param) + ": "
            } );


        for ( i = 0; i < values.length; i++ ) {
            var fullName = param + ":" + values[i].arvo;
            checkbox = new OO.ui.CheckboxMultioptionWidget( {
                data: values[i].arvo,
                label: values[i].teksti
            } )
            items.push(checkbox);
            this.kentatMap[fullName] = checkbox;

        }
        multiselect.setData({ "param" : param });
        multiselect.addItems( items );
        this.addItems( [ field ] );
        this.kentatMap[param] = field;
        // Propagoidaan checkboxien enter-eventti ulommalle layoutelementille.
        multiselect.$element.find("input").on('keypress', function (event) {
            if ( event.key === "Enter" && event.ctrlKey ) {
                field.emit("ctrlEnter");
            }
        });

    };

    /**
     * Lisää parametria `param` vastaavan radiobutton-alueen, joka on otsikoitu `text` fieldsetiin
     * Radiobuttonien arvot annetaan taulukossa `values`.
     **/
    fiwikt.ParamFieldsetLayout.prototype.lisaaRBKentta = function (param, label, values) {
        var i,
            multiselect = new OO.ui.RadioSelectWidget( { } ),
            items = [],
            radiobutton,
            field = new OO.ui.FieldLayout( multiselect, {
                label: (label || param) + ": "
            } );


        for ( i = 0; i < values.length; i++ ) {
            var fullName = param + ":" + values[i].arvo;
            radiobutton = new OO.ui.RadioOptionWidget( {
                data: values[i].arvo,
                label: values[i].teksti
            } )
            items.push(radiobutton);
            this.kentatMap[fullName] = radiobutton;

        }
        multiselect.setData({ "param" : param });
        multiselect.addItems( items );
        this.addItems( [ field ] );
        this.kentatMap[param] = field;
        // Propagoidaan radiobuttonien enter-eventti ulommalle layoutelementille.
        multiselect.$element.find("input").on('keypress', function (event) {
            if ( event.key === "Enter" && event.ctrlKey ) {
                field.emit("ctrlEnter");
            }
        });

    };


    fiwikt.ParamFieldsetLayout.prototype.onSwitchChange = function (state) {
        if ( state === true ) {
            this.naytaKaikki();
        } else {
            this.naytaKentat();
        }
    };

}());
/**
 * Pääohjelma.
 **/
$( function () {
    window.nyttUppslag2 = window.nyttUppslag2 || {};

    var $container,
        langLookup = new fiwikt.LanguageLookupTextInputWidget({
            cookieNimi: 'nytt-uppslag2.kielet',
            kielikoodisivu : "Järjestelmäviesti:KielitietoJSON",
            icon: 'language',

        } ), fsLomake = new OO.ui.FieldsetLayout( {
            classes: [ 'gnu-lomake', 'gnu-paalomake' ],
            label: 'Uusi artikkeli'
        } ), fsParams = new fiwikt.ParamFieldsetLayout( {
            classes: [ 'gnu-lomake', 'gnu-parametrilomake' ],
            label: "Parametrit"
        } ), fsSuku = new OO.ui.FieldsetLayout( {
            classes: [ 'gnu-lomake', 'gnu-sukulomake' ],
            label: 'Suku'
        } ), checkbox = new OO.ui.CheckboxInputWidget( {
            value: 'käännösosio',
        } ), flKaannososio = new OO.ui.FieldLayout( checkbox, {
            label: 'Lisää käännösosio'
        } ), cbSanaluokka = new OO.ui.ComboBoxInputWidget( {
            allowSuggestionsWhenEmpty : true,
            options: [
                //{ data: "aakkonen" },
                { data: "adjektiivi" },
                { data: "adverbi" },
                //{ data: "artikkeli" },
                { data: "erisnimi" },
                { data: "fraasi" },
                { data: "interjektio" },
                { data: "kirjoitusmerkki" },
                { data: "konjunktio" },
                { data: "lyhenne" },
                { data: "numeraali" },
                { data: "prefiksi" },
                { data: "prepositio" },
                { data: "pronomini" },
                { data: "substantiivi" },
                { data: "suffiksi" },
                { data: "supistuma" },
                { data: "symboli" },
                { data: "verbi" },
            ],
            menu: {
                highlightOnFilter : true,
                filterFromInput: true
            },
            validate: function (val) { return val.match(/\S/); }
        } ), txtMaaritelma = new OO.ui.TextInputWidget( {
            validate : function (val) { return val.match(/\S/); }
        } ), btnGeneroi = new OO.ui.ButtonInputWidget( {
            label: 'Generoi wikiteksti',
            icon:  'downTriangle',
            flags: [ 'primary', 'progressive' ]
        } ), swNayta = new OO.ui.ButtonWidget( {
            value: true,
            icon: "collapse",
            title: 'Näytä/piilota artikkelinluontilomake',
            id: 'nyttUppslag2-toggle'
        } ),
        omatKielet,
        /**
         * Kenttien suodatus sanaluokan perusteella.
         **/
        suodataKentat = function (kentat, sanaluokka) {
            // Jos sanaluokka on jokin seuraavista, poistetaan sukukentät.
            if ( ["adverbi", "verbi", "fraasi"].indexOf(sanaluokka) !== -1 ) {
                return kentat.filter(function (kentta) {
                    return !kentta.startsWith("suku");
                });
            }
            return kentat;
        },
        /**
         * Kielilomakkeen rakennus kieleen liitettyjen kenttien perusteella.
         **/
        teeKielilomake = function (lang_data) {
            var fieldsIn,
                fieldsOut = [],
                i, j;

            if ( lang_data && lang_data.kentat ) {
                fieldsIn = lang_data.kentat.split(/\s*,\s*/);
                for ( i in fieldsIn ) {
                    var field = fieldsIn[i];
                    var m = field.match("^([^:]+):(.*)$");
                    if ( m ) {
                        var superfield = m[1];
                        var subfields = m[2].split("|");
                        for ( j in subfields ) {
                            fieldsOut.push(superfield + ":" + subfields[j]);
                        }
                    } else {
                        fieldsOut.push(field);
                    }
                }
            } else {
                fieldsOut = [ ];
            }

            flKaannososio.toggle(lang_data.koodi === "fi");
            fsParams.asetaNakyvaksi(suodataKentat(fieldsOut, cbSanaluokka.getValue()));
        },
        uusiMaaritelmaKentta = function (numero) {
            var x = new OO.ui.TextInputWidget( {
                validate : function (val) { return val.match(/\S/); }
            } );

            txtMaaritelma.$input.attr("id", "nyttUppslag2-maaritelma" + numero);
            x.$input[0].ooParent = x;
            x.$input.on("keyup", txtMaaritelma_onKeyPress);
            x.$element.css('max-width', 'none');
            return x;
        },
        txtMaaritelma_onKeyPress = function (event) {
            if ( event.key === "Enter" && event.ctrlKey ) {
                btnGeneroi.emit('click');
            } else if ( event.key === "Enter" && !event.ctrlKey ) {
                // Lisätään perään uusi määritelmälaatikko.
                var x = uusiMaaritelmaKentta(fsMaaritelmat.items.length + 1);
                fsMaaritelmat.addItems([ x ]);
                x.focus();
            } else if ( event.key === "Delete"  // Poistetaan laatikko, jos se on tyhjä eikä viimeinen.
                     && this.value === "" && fsMaaritelmat.items.length > 1 ) {
                fsMaaritelmat.removeItems([ this.ooParent ]);
                fsMaaritelmat.items[fsMaaritelmat.items.length - 1].focus();
            }
        };

    flKaannososio.toggle(false);

    if ( (mw.config.get("wgAction") !== "edit" &&
          mw.config.get("wgAction") !== "submit") ||
         mw.config.get("wgNamespaceNumber") !== 0 ) {
        return;
    }

    $container = $('<div id="nyttUppslag2-sailio"></div>');


    // Enterin painallus missä tahansa fsParams-laatikon kentässä vastaa generoi-napin painallusta.
    fsParams.on('submit', function () { btnGeneroi.emit('click'); });


    var fsMaaritelmat = new OO.ui.FieldsetLayout( { label: 'Määritelmä: ', align: 'top', id: 'gnu-maaritelmat' } );
    fsMaaritelmat.addItems([ txtMaaritelma ]);

    fsLomake.addItems( [
        new OO.ui.FieldLayout( langLookup, { label: 'Kieli: ' } ),
        new OO.ui.FieldLayout( cbSanaluokka, { label: 'Sanaluokka: ' } ),
        fsMaaritelmat,
        fsParams,
        flKaannososio,
        new OO.ui.FieldLayout( btnGeneroi, { } ),
    ] );

    $container.append(
        swNayta.$element,
        fsLomake.$element
    );

    if ( $("#firstHeading").text().startsWith("Muokataan sivua") ) {
        if ( window.articleMerger ) {
            // Piilotetaan lomake oletuksena.
            swNayta.setIcon("expand");
            fsLomake.toggle(false);
        } else {
            return;
        }
    }

    $( '#mw-content-text' ).prepend( $container );

    cbSanaluokka.setValue($.cookie("nytt-uppslag2.sanaluokka"));
    cbSanaluokka.getMenu().toggle(false);
    flKaannososio.fieldWidget.setSelected(true);

    txtMaaritelma.$input.attr("id", "nyttUppslag2-maaritelma");
    txtMaaritelma.$input[0].ooParent = txtMaaritelma;
    txtMaaritelma.$input.on('keyup', txtMaaritelma_onKeyPress);
    txtMaaritelma.$element.css('max-width', 'none');

    function kokoaData() {
        return {
            maaritelmat : fsMaaritelmat.getItems().map(function (item) {
                return item.getValue();
            }),
            lang_data : langLookup.getData(),
            sanaluokka : cbSanaluokka.getValue(),
            params: fsParams.haeParametrit()
        };
    }

    function palautaLomakkeenArvot(sessionData) {
        langLookup.setValue(sessionData.lomakedata.lang_data.nimi + " [" + sessionData.lomakedata.lang_data.koodi + "]");
        langLookup.setData(sessionData.lomakedata.lang_data);

        txtMaaritelma.setValue(sessionData.lomakedata.maaritelmat[0]);
        for ( var i = 1; i < sessionData.lomakedata.maaritelmat.length; i++ ) {
            // Lisätään perään uusi määritelmälaatikko.
            var x = uusiMaaritelmaKentta(i + 1);
            x.setValue(sessionData.lomakedata.maaritelmat[i])
            fsMaaritelmat.addItems([ x ]);
        }

        fsParams.asetaParametrit(sessionData.lomakedata.params);
    }


    swNayta.on( 'click', function () {
        fsLomake.toggle(swNayta.getIcon() === "expand");
        swNayta.setIcon(fsLomake.isVisible() ? "collapse" : "expand");
        if ( fsLomake.isVisible() ) {
            langLookup.select();
        }
    });

    langLookup.lookupMenu.on( 'choose', function (item) {
        teeKielilomake(item.getData());
    } );

    cbSanaluokka.on( 'change', function (item) {
        teeKielilomake(langLookup.getData());
    } );


    btnGeneroi.on ( 'click', function () {
        var lang_data = langLookup.getData();
        if ( lang_data) {
            var lang = lang_data.nimi;
            var code = lang_data.koodi;
            var sanalk = cbSanaluokka.getValue();
            var out = [];
            var maaritelmat = fsMaaritelmat.getItems().map(function (item) {
                return item.getValue();
            });
            var args = fsParams.haeParametrit();
            var index = 0;
            args[0] = sanalk;
            args[1] = code;

            if ( args["teksti"] ) {
                args[2] = args["teksti"];
                delete args["teksti"];
            }

            out.push("==" + fiwikt.isolla(lang) + "==\n")
            out.push("===" + fiwikt.isolla(sanalk) + "===\n")

            if ( lang_data.sanarivit && lang_data.sanarivit[sanalk] ) {
                out.push(fiwikt.muotoileSanarivi(args, lang_data.sanarivit[sanalk]) + "\n");
            } else {
                out.push(fiwikt.muotoileMalline(args) + "\n");
            }
            out.push("\n");
            out.push(maaritelmat.map(function (maaritelma) { return "# " + maaritelma + "\n"; }).join(""));

            if ( flKaannososio.isVisible() && flKaannososio.fieldWidget.isSelected() ) {
                out.push("\n====Käännökset====\n");
                out.push(maaritelmat.map(
                    function (maaritelma) {
                        return fiwikt.muotoileMalline([ "subst:kohta", ++index, fiwikt.poistaLinkit(maaritelma), "\n\n", "loppu" ]);
                    }).join("\n\n"));
                out.push("\n");
            }

            // Jos artikkeliyhdistäjä on käytössä, käytetään sitä. Muuten lisätään tekstilaatikon loppuun.
            if ( window.articleMerger ) {
                articleMerger.mergeToArticle(out.join(""));
            } else {
                $("#wpTextbox1").val($("#wpTextbox1").val() + out.join("") + "\n");
            }

            langLookup.tallennaViimeaikaisiin(lang_data);
            $.cookie("nytt-uppslag2.sanaluokka", cbSanaluokka.getValue(), { expires: 30, path: "/;SameSite=Strict" } );


            var kokodata = {
                "revId:"     : mw.config.get('wgRevisionId'),
                "lomakedata" : kokoaData()
            };

            sessionStorage.setItem('gadget-nytt-uppslag2-formdata', JSON.stringify(kokodata));

            langLookup.focus();
        }
    } );


    if ( window.articleMerger ) {
        btnGeneroi.setLabel("Generoi ja yhdistä");
    }


    langLookup.haeKenttienTiedot().then(function (kentat) {
        fsParams.lisaaKentat(kentat);
        fsParams.piilotaKentat();
        // Laitetaan viimeisin kieli laatikkoon valmiiksi, jos sellainen on.
        omatKielet = JSON.parse($.cookie("nytt-uppslag2.kielet") || "[]");
        if ( omatKielet.length > 0 ) {

            langLookup.setData(omatKielet[0]);
            langLookup.setValue( omatKielet[0].nimi + " [" + omatKielet[0].koodi + "]");
            teeKielilomake(omatKielet[0]);
        } else {
            langLookup.setValue("");
            langLookup.setData("");
        }

        // Palautetaan lomakkeen tiedot, jos palattiin samalle sivulle selaimen takaisin-napin avulla.
        // Huom. ohittaa cookieen tallennetun kielen ja sanaluokan.
        var sessionData = JSON.parse(sessionStorage.getItem('gadget-nytt-uppslag2-formdata'));

        if ( sessionData && sessionData.revId === mw.config.get('wgRevisionId') ) {
            palautaLomakkeenArvot(sessionData);
        }
    });



} );