Järjestelmäviesti:Gadget-Kaannokset2.js

Wikisanakirjasta

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 käännös-gadgetin käyttämät apufunktiot.
 **/
(function () {
    window.fiwikt = window.fiwikt || {};

    /**
     * Yleiskäyttöinen funktio, joka hakee sivun wikitekstin.
     * @param title: sivun nimi
     * @return {Promise}, jossa sivun data objektina.
     **/
    window.fiwikt.haeWikiteksti = function(title) {
        var q = {
            action: "query",
            titles: title,
            prop: "revisions",
            rvprop: "content",
            rvlimit: "1",
            rvslots: "main",
        };

        return (new mw.Api()).get(q).then(function (data) {
            var pageid,
                page,
                revision,
                editToken,
                startTime,
                pageTime,
                content;
            
            if ( ! data.query 
                 || ! data.query.pages ) {
                throw "Ongelma vastauksen tulkitsemisessa";
            }
            
            pageid = Object.keys(data.query.pages)[0];
            page   = data.query.pages[pageid];
            editToken = page.edittoken;
            startTime = page.starttimestamp;
            if ( ! page
                 || ! page.revisions
                 || ! page.revisions[0] ) {
                throw "Ongelma vastauksen tulkitsemisessa";
            }

            revision = page.revisions[0];  // Uusin versio
            pageTime = revision.timestamp;
            if ( ! revision
                 || ! revision.slots 
                 || ! revision.slots.main
                 || ! revision.slots.main['*'] ) {
                throw "Ongelma vastauksen tulkitsemisessa";
            }
            
            content = revision.slots.main['*'];
            
            return content;
        });
        
    };
}());
(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();
        }
    };

}());
(function () {

    window.fiwikt = window.fiwikt || {};

    fiwikt.WordTagMultiMultiselectWidget = function FiWiktWordTagMultiMultiselectWidget( config ) {
        config = config || {};

        this.map = {}

        fiwikt.WordTagMultiMultiselectWidget.parent.call( this, config );
        OO.ui.mixin.GroupElement.call( this, config );

        this.$group.addClass( 'oo-ui-fieldsetLayout-group' );
        this.$element
            .addClass( 'fiwikt-wordTagMultiMultiselectWidget' )
            .prepend( this.$header, this.$group );
        if ( Array.isArray( config.items ) ) {
            this.addItems( config.items );
        }

        // Events
        this.aggregate( {
            "empty": 'childEmpty',
            "itemSelect" : 'itemSelect'
        } );
        this.connect( this, {
            childEmpty: 'onChildEmpty',
            //itemSelect: 'onItemSelect',
        } );

    };
    OO.inheritClass( fiwikt.WordTagMultiMultiselectWidget, OO.ui.Layout );
    OO.mixinClass( fiwikt.WordTagMultiMultiselectWidget, OO.ui.mixin.GroupElement );

    fiwikt.WordTagMultiMultiselectWidget.prototype.setGroupData = function ( group, data ) {
        this.map[group].setData(data);
    };

    fiwikt.WordTagMultiMultiselectWidget.prototype.getGroupData = function ( group ) {
        return this.map[group].getData();
    };

    fiwikt.WordTagMultiMultiselectWidget.prototype.addTag = function ( group, data, label ) {
        // Lisätään ryhmä jos sitä ei vielä ole.
        if ( ! this.map[group] ) {
            this.map[group] = this.createTagMultiselectWidget(group + ": ");
            this.addItems ( [ this.map[group] ] );
            // Lajitellaan alielementit kielen nimen mukaan aakkosjärjestekyseen.
            var $children = this.$element.find('.oo-ui-fieldsetLayout-group').children();
            $children.sort(function (a, b) {
                // kielen nimi on innerTextin alussa.
                return a.innerText.localeCompare(b.innerText);
            });
            this.$element.find('.oo-ui-fieldsetLayout-group').append($children);
        }

        // Lisätään tagi ryhmään.
        this.map[group].addTag ( data, label );
    };


    fiwikt.WordTagMultiMultiselectWidget.prototype.createTagMultiselectWidget = function ( label, data ) {
        return new fiwikt.WordTagMultiselectWidget( { text: label, data: data } );
    };

    fiwikt.WordTagMultiMultiselectWidget.prototype.setData = function ( data ) {
        var key,
            lang_data,
            kaannos,
            list;

        for ( key in data ) {
            meta = data[key].meta;
            list = data[key].list;
            for ( i in list ) {
                this.addTag(key, list[i], list[i][2]);
            }
            this.setGroupData(key, meta);
        }
    };

    fiwikt.WordTagMultiMultiselectWidget.prototype.onChildEmpty = function ( item ) {
        for ( key in this.map ) {
            if ( this.map[key] === item ) {
                delete this.map[item.getData().nimi];
                this.removeItems ( item );
                item.$element.remove();
                return;
            }
        }
    };

    /**
     * Palauttaa kenttien arvot JSON-formaatissa.
     **/
    fiwikt.WordTagMultiMultiselectWidget.prototype.getData = function ( ) {
        var out = {},
            i,
            items,
            item;

        for ( key in this.map ) {
            out[key] = {
                meta: this.map[key].getData(),
                list: []
            };

            items = this.map[key].getItems();
            for ( i = 0; i < items.length; i++ ) {
                out[key].list.push(items[i].getData());
            }
        }

        return out;
    }

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

    fiwikt.WordTagMultiselectWidget = function FiWiktWordTagMultiselectWidget( config ) {
        config = config || {};

        fiwikt.WordTagMultiselectWidget.parent.call( this, $.extend( {
            inputPosition: 'none',
            allowArbitrary: true
        }, config ) );

        this.text = config.text;

        // Poistetaan fokusointikenttä, koska syöttökenttääkään ei ole.
        // TODO tähän ehkä siistimpi tapa??
        this.$tabIndexed.remove();


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

    };

    /* Initialization */

    OO.inheritClass( fiwikt.WordTagMultiselectWidget, OO.ui.TagMultiselectWidget );

    fiwikt.WordTagMultiselectWidget.prototype.createTagItemWidget = function ( data, label ) {
        label = label || data;

        var vals = fiwikt.muotoileMalline(data);

        var htmlLabel = $( '<span>' + label + ' <a target="_BLANK" title="Avaa sivu ' + label + '" href="/wiki/' + label + '">↗</a></span>' );

        return new fiwikt.WordTagItemWidget( { data: data, label: htmlLabel, title: vals } );
    };

    fiwikt.WordTagMultiselectWidget.prototype.onTagRemove = function ( item ) {
        fiwikt.WordTagMultiselectWidget.parent.prototype.onTagRemove.call( this, item );

        if ( this.isEmpty() ) {
            this.emit ( "empty" );
        }
    };

}());

fiwikt.WordTagItemWidget = function FiWiktWordTagItemWidget( config ) {
        config = config || {};

        // Parent constructor
        fiwikt.WordTagItemWidget.parent.call( this, config );
        OO.ui.mixin.TitledElement.call( this, config );

    this.$element.addClass( 'fiwikt-wordTagItemWidget' );
};

OO.inheritClass( fiwikt.WordTagItemWidget, OO.ui.TagItemWidget );
OO.mixinClass( fiwikt.WordTagItemWidget, OO.ui.mixin.TitledElement );



var PAGENAME = "testi";

/*
 * Muotoilee aikaleiman tämänhetkisestä ajasta. Peräisin HotCatin koodista.
 */
function currentTimestamp () {
    var now = new Date();
    var ts  = "" + now.getUTCFullYear();
    function two (s) { return s.substr (s.length - 2); }
    ts = ts
       + two ('0' + (now.getUTCMonth() + 1))
       + two ('0' + now.getUTCDate())
       + two ('00' + now.getUTCHours())
       + two ('00' + now.getUTCMinutes())
       + two ('00' + now.getUTCSeconds());
    return ts;
}

/**
 * Tallentaa sivun tekstin `teksti` muutoskommentilla `kommentti`.
 **/
function tallenna(teksti, kommentti) {
    var $form = $('<form id="commitForm" method="post" enctype="multipart/form-data">'),
        commitForm = $form.get(0);

    var pageTime = null;

    $form.attr("action", mw.config.get('wgScript') + '?title=' + encodeURIComponent(mw.config.get('wgPageName')) + '&action=edit');
    //$form.attr("action", '/script/' + '?title=' + encodeURIComponent('pagename') + '&action=edit');
    //$form.attr("method", 'GET');

    $form.append( [
        $('<input type="hidden" name="wpTextbox1" />').val(teksti),
        $('<input type="hidden" name="wpSummary" value="" />').val(kommentti),
        //$('<input type="hidden" name="wpMinoredit" />').val(),
        $('<input type="hidden" name="wpWatchthis" />').val(1),
        $('<input type="hidden" name="wpAutoSummary" value="" />').val('d41d8cd98f00b204e9800998ecf8427e'),
        $('<input type="hidden" name="wpStarttime" />').val(currentTimestamp()),
        $('<input type="hidden" name="wpEdittime" />').val(currentTimestamp()),
        $('<input type="hidden" name="wpEditToken" />').val(),
        $('<input type="hidden" name="wpDiff" value="wpDiff" />'),
        $('<input type="hidden" name="oldid" value="0" />'),
        $('<input type="hidden" name="model" value="wikitext" />'),
        $('<input type="hidden" name="format" value="text/x-wiki" />'),
        $('<input type="hidden" name="wpChangeTags" value="" />'),
        $('<input type="hidden" name="wpUnicodeCheck" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" />'),
        $('<input type="hidden" name="wpUltimateParam" value="1" />'),
    ] );

    $('#mw-content-text').append($form);

    $form.submit();
}


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

    } ), fsUloin = new OO.ui.FieldsetLayout( {
        classes: [ 'gk-lomake', 'gk-kaannostenlisayslomake' ],
        label: 'Lisää käännöksiä',
    } ), fsLomake = new OO.ui.FieldsetLayout( {
        classes: [ 'gk-lomake', 'gk-sanalisayslomake' ],
        label: 'Uusi'
    } ), fsParams = new fiwikt.ParamFieldsetLayout( {
        classes: [ 'gk-lomake', 'gk-parametrilomake' ],
        label: 'Parametrit',
    } ), btnLisaa = new OO.ui.ButtonInputWidget( {
        label: 'Lisää',
        icon: 'add',
    } ), btnTallenna = new OO.ui.ButtonInputWidget( {
        label: 'Siirry tallentamaan',
        icon: 'next',
        flags: [ 'progressive', 'primary' ]
    } ), sanalisays = new fiwikt.WordTagMultiMultiselectWidget( {
    } ), txtSana = new OO.ui.TextInputWidget( {
        value: '',
        validate : function (val) { return val.match(/\S/); }
    } ), txtLat = new OO.ui.TextInputWidget( {
        value: ''
    } ), txtVok = new OO.ui.TextInputWidget( {
        value: ''
    } ),
        sanaluokka,
        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 = [ ];
            }

            fsParams.asetaNakyvaksi(suodataKentat(fieldsOut, sanaluokka));
        },
        palautaLomakkeenArvot = function(sessionData) {
            $kaannoslaatikko = $('.käännöskohta[data-gk-numero=' + sessionData['käännöskohta'] + ']');
            $kaannoslaatikko.find('button').click();
            langLookup.setValue(sessionData.lomakedata.lang_data.nimi + " [" + sessionData.lomakedata.lang_data.koodi + "]");
            langLookup.setData(sessionData.lomakedata.lang_data);
            txtSana.setValue(sessionData.lomakedata.kaannos);
            fsParams.asetaParametrit(sessionData.lomakedata.params);
            sanalisays.setData(sessionData['sanalisäys']);
        };

    // Numeroidaan käännöstaulukot / kohta-mallineet.
    $( '.käännöskohta' ).each(function (index) {
        if ( !$(this).hasClass('käännöskohta_ohjaus') ) {
            $(this).find(".käännöstaulukko > tbody").append( $('<tr class="gk-kaannoslisays"></tr>') );
            $(this).attr('data-gk-numero', index);
            sanaluokka = $(this).prevAll("h3:first").children(".mw-headline").text().toLowerCase();
        }
    });

    $('.gk-kaannoslisays').append('<button>Lisää käännöksiä...</button>').on('click', function () {
        // Kun Lisää käännöksiä -nappia painetaan, poistetaan kaikki napit ja laitetaan painetun tilalle lomake.
        $('.gk-kaannoslisays > button').remove();
        $(this).closest("table").after(
            fsUloin.$element
        );

        // 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]);
        }


        langLookup.select();
    });


    fsLomake.addItems( [
        new OO.ui.FieldLayout( txtSana, { label: 'Käännös: ' } ),
        fsParams,
        new OO.ui.FieldLayout( btnLisaa, { } ),
    ] );


    fsUloin.addItems( [
        sanalisays,
        new OO.ui.FieldLayout( langLookup, { label: 'Kieli: ' } ),
        fsLomake,
        btnTallenna,
    ] );


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

        txtSana.select();
    } );

    function kokoaData() {
        return {
            kaannos : txtSana.getValue(),
            lang_data : langLookup.getData(),
            params: fsParams.haeParametrit()
        };
    }

    var lisays = function () {
        var kaannos = txtSana.getValue();
        var lang_data = langLookup.getData();

        if ( lang_data && kaannos.trim() !== "" ) {
            var lang = lang_data.nimi;
            var code = lang_data.koodi;
            var out = [];
            var params = fsParams.haeParametrit();
            params[0] = "käännös";
            params[1] = code;
            params[2] = kaannos;

            // Paino- tai vok-parametrista tulee linkin teksti. Molempia ei pitäisi olla missään kielessä.
            if ( params["paino"] ) {
                params[3] = params["paino"];
                delete params["paino"];
            } else if ( params["vok"] ) {
                params[3] = params["vok"];
                delete params["vok"];
            } else if ( params["teksti"] ) {
                params[3] = params["teksti"];
                delete params["teksti"];
            }


            $("#wpTextbox1").val(out.join(""))
            sanalisays.addTag(lang, params, kaannos);
            sanalisays.setGroupData(lang, lang_data);
            txtSana.select();

            langLookup.tallennaViimeaikaisiin(lang_data);
        }
    };

    btnLisaa.on('click', lisays);
    // Ctrl-enterin painallus missä tahansa fsParams-laatikon kentässä tai sanakentässä vastaa tallennusnapin painallusta.
    fsParams.on('submit', lisays);
    txtSana.on('enter', function (event) { if ( event.originalEvent.ctrlKey ) { lisays(); } });


    /**
     * Sanan valinta sanaluettelosta.
     **/
    sanalisays.on ( 'itemSelect', function ( item, subitem ) {
        var data = item.getData(),
            params = subitem.getData(),
            fields;

        langLookup.setValue(data.nimi + " [" + data.koodi + "]");
        langLookup.setData(data);
        txtSana.setValue(params[2]);
        fsParams.asetaParametrit(params);

        if ( data && data.kentat ) {
            fields = data.kentat.split(/\s*,\s*/);
        } else {
            fields = [ ];
        }

        fsParams.asetaNakyvaksi(suodataKentat(fields, sanaluokka));

    } );

    btnTallenna.on ( 'click', function (event) {
        var data,
            key,
            out = [],
            items,
            numero = btnTallenna.$element.closest('.käännöskohta').attr('data-gk-numero');

        console.assert(numero >= 0);

        // Lisätään mahdollinen kesken jäänyt sana.
        lisays();

        data = sanalisays.getData();
        for ( key in data ) {
            out.push("*", key, ": ");
            items = data[key].list;
            out.push(items.map(function (item) { return fiwikt.muotoileMalline(item); }).join(", "), "\n");
        }

        kokodata = {
            "käännöskohta" : numero,
            "revId:"       : mw.config.get('wgRevisionId'),
            "sanalisäys" : data,
            "lomakedata" : kokoaData()
        };

        // Tallennetaan kaikki lomakkeen tiedot sesson storageen siltä varalta, että käyttäjä
        // haluaa tulla takaisin selaushistoriassa.
        sessionStorage.setItem('gadget-kaannokset2-formdata', JSON.stringify(kokodata));

        fiwikt.haeWikiteksti(mw.config.get("wgPageName"))
              .then(function (wtxt) {
                  var e = '{{kohta|',
                      a, l, i = 0,
                      outtxt,
                      lisays = out.join("");

                  //alert("KOHTA: " + numero + "\n" + wtxt);

                  // Etsitään numeroa vastaava kohta-malline
                  for ( i = 0; i <= numero; i++ ) {
                      a = wtxt.indexOf(e, l);
                      if ( a === -1 ) {
                          throw new Error("Virheellinen numero");
                      }
                      l = a + 8;
                  }

                  // Otsikkorivin loppu
                  l = wtxt.indexOf('\n', l) + 1;

                  if ( l === -1 ) {
                      throw new Error("Malline loppuu kesken");
                  }

                  // korvataan kohta subst:kohtalla ja lisätään uusi käännös seuraavalle riville
                  outtxt = wtxt.substring(0, a) + '{{subst:' +  wtxt.substring(a + 2, l) + lisays + wtxt.substring(l);

                  tallenna(outtxt, "+" + Object.keys(data).join(", "));
              });


    } );

    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.
        var sessionData = JSON.parse(sessionStorage.getItem('gadget-kaannokset2-formdata'));

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

    });




} );