/*
     $Revision$
     $Id$

     File:          iface.js
     Description:   clientside functions for shared/iface.inc
*/
var secfilters;
// Toggle-t de zichtbaarheid van secundaire zoekvelden
// Gebruikt globale boolean secfilters om de huidige status te onthouden
// iface.inc definieert de strings
function iface_toggleSecondarySearchblock(aanuit)
{
  if (typeof aanuit == 'boolean')
    window.secfilters = !aanuit;
  if (!window.secfilters) {
    $(".secsearch").show();
    window.secfilters = true; // en vervang label door L("lcl_less")
    $("#btnMoreLess").text(" " + L("lcl_shared_less"));
    $("#btnMoreLess").prepend(I("fa-chevron-left"));
  } else {
    $(".secsearch").hide();
    window.secfilters = false; // en vervang label door L("lcl_more")
    $("#btnMoreLess").text(" " + L("lcl_shared_more"));
    $("#btnMoreLess").prepend(I("fa-chevron-right"));
  }
}

function headertofooter()
{
    $('#editbody div.fcltframeheader, .editmode div.fcltframeheader').each(function () {
        var $footer = $("<div>").addClass("fcltframefooter block-wrapper col-12");
        $footer.append($("div#buttons", this).clone()).css('clear','both');
        $(this).closest("#editbody, .editmode").append($footer);
        FcltMgr.resized();
    });
}

var iface =
{
    button:
    {
        click: async function(evt, btn)
        {
            if ($(btn).is("#printhamburger"))
            {
                $(btn).children(".fa-bars, .fa-times").toggleClass("fa-times fa-bars");
            }

            FcltMgr.stopPropagation(evt);

            if (btn.getAttribute("singlepress") && btn.getAttribute("isactive")=="true")
            {   // FcltMgr.alert("Heb geduld");
                return;
            }
            if (btn.getAttribute("singlepress"))
            {
                iface.button.disable(btn);
            }
            var elem = btn.getAttribute("fcltclick");

            window.fcltevent = evt;
            var result = eval(elem);
            if (typeof result === "object" && result instanceof Promise) {
                result = await result;
            }
            // Dit lijkt erg onzinning (we zitten binnen iface) maar soms als
            // een scherm/dialoog net gesloten is door de action van de button
            // blijkt de code toch nog hier te komen terwijl iface weg is
            if (typeof iface == "undefined")
                return;

            if (result === false) {
                iface.button.enable(btn);
            }

        },
        disable: function(btn)
        {
            if (typeof btn == "object")
                btn = btn.id;
            if (!btn)
                FcltMgr.alert("For button.disable it should have an id");
            // Kan ondertussen dubbel aanwezig zijn doordat we knoppen onderin frame herhalen
            // Een selectie via $("." + btn") werkt niet omdat jQuery dan alleen de eerste id ophaalt
            var $btn = $("li[id=" + btn + "]");
            $btn.attr("isactive", "true");
            $btn.addClass("btn_disabled");
            $btn.children().addClass("btn_disabled");
        },
        enable: function(btn)
        {
            if (typeof btn == "object")
                btn = btn.id;
            var $btn = $("li[id='" + btn + "']");
            $btn.removeClass("btn_disabled");
            $btn.children().removeClass("btn_disabled");
            $btn.attr("isactive", "false");
        }
    },
    translate: function (kolomnaam, key, lbl, deftxt, typ)
    {
        var url = rooturl + "/appl/fac/fac_locale_data.asp?kolomnaam="+kolomnaam+"&kolomkeyval=" + key
                +  "&kolomkeydata=" +escape(deftxt)
                +  "&lbl=" +escape(lbl)
                +  "&typ=" +typ;
        FcltMgr.openModalDetail(url, L("lcl_lcl_vertaling"), {xheight: 200, width: 600 } );

    },
    overrule: function (lcl_name)
    {
        var url = rooturl + "/appl/fac/fac_locale_edit_std.asp?module=ASP&lcl_label=" + escape(lcl_name);
        FcltMgr.openModalDetail(url, L("lcl_lcl_vertaling"), {xheight: 200, width: 600 } );

    },
    bb_code: function _bb_code(e, uid, code) {
        uid = uid   || this.parentElement.getAttribute("aria-controls");
        code = code || this.getAttribute("data-style");
        if (code === "toggle") {
            this.parentElement.classList.remove("closed");
            FcltMgr.resized();
            return;
        } else if (code === "file" || code === "color") {
            if (code === "color") {
                this.previousElementSibling.value = "#000000"; // Vergeet vorige waarde
            }
            this.previousElementSibling.click(); // Click de <input type=file/color>
            e.preventDefault();
            return false;
        }
        iface._insert_bb_code(uid, code);
    },
    onKeydown_bb_code: function (e) {
        var hotkeys = {
          66: "b", // Bold
          73: "i", // Italic
          83: "s", // Strikethrough
          85: "u"  // Underlined
        };

        if (Object.keys(hotkeys).indexOf(String(e.keyCode)) > -1 &&
            (e.metaKey || e.ctrlKey) &&
            !e.shiftKey && !e.altKey)
        {
            e.preventDefault();
            iface.bb_code(e, this.id, hotkeys[e.keyCode]);
        }
    },
    _insert_bb_code: function(uid, code, insert_this_text) {
        uid  =  uid || this.parentElement.getAttribute("aria-controls");
        code = code || this.getAttribute("data-style");
        var text_elem = document.getElementById(uid);
        var text  = text_elem.value;
        var start = text_elem.selectionStart;
        var end   = text_elem.selectionEnd;
        var wrap_text = text.slice(start, end);
        var bb_text = "";
        switch (code)
        {
            case "table":
                bb_text = "[table]\n[tr]\n[td]" + wrap_text + "[/td]\n[/tr]\n[/table]";
                break;
            case "color":
                bb_text = "[color=" + this.value + "]" + wrap_text + "[/color]";
                break;
            default:
                bb_text = "[" + code + "]" + (insert_this_text || wrap_text) + "[/" + code + "]";
                break;
        }
        text_elem.value = text.slice(0, (insert_this_text ? end : start))
                        + bb_text
                        + text.slice(end);

        if (wrap_text === "") {
            var bb_open_len = code.length;
            if (code === "table") {
                bb_open_len += 17;
            } else if (code === "color") {
                bb_open_len += 3 + this.value.length;
            } else {
                bb_open_len += 2;
            }
        }

        text_elem.selectionStart = start + (insert_this_text ? bb_text.length : (wrap_text === "" ? bb_open_len : 0));
        text_elem.selectionEnd   = insert_this_text ? text_elem.selectionStart : (start + (wrap_text === "" ? bb_open_len : bb_text.length));
        $(text_elem).trigger("keyup");
        if (typeof validateTextarea === "function") {
            validateTextarea(true);
        }
        text_elem.focus();
    },
    encodeToRelativeDate: function (fld) { // Zet een datum om naar de rapportage-filter notatie
        const endOfDay = fld.dataset.volgnr == 2;
        const truncedToday = new Date().setHours(0, 0, 0, 0);
        const truncedVal = new Date(parseInt(fld.value)).setHours(0, 0, 0, 0);
        const dayMS = 1000 * 3600 * 24;

        const pn = (truncedVal < truncedToday ? "p" : (truncedVal > truncedToday ? "n" : ""));
        const offsetDays = Math.floor(Math.abs(truncedToday - truncedVal) / dayMS) || "";

        return ":" + (endOfDay ? "D" : "d") + pn + (offsetDays ? offsetDays : "");
    },
    formToString: function (stringForm, explicit, relativeDates)
    {
        // explicit:  expliciet =   bewust opgeslagen pagina's (tot nu toe alleen pinnen van tabs)
        // !explicit: impliciet = onbewust opgeslagen pagina's (tot nu toe alleen het impliciet onthouden van rapport filters bij S(fac_usrrap_keepfilter) == 1)
        var notFilter = "";
        if (explicit)
            notFilter = "[name^='fclt_f_colname']";
        else // Bij impliciet opslaan, deze filters negeren;
            notFilter = "[name='mode'],  [name='columns'],    [name='groupby'], "
                      + "[name^='scf_'], [name='usrrap_key'], [name^='fclt_f_colname']";

        var formObject = {};
        stringForm.find("input, select").not(notFilter).each(function()
        {
            var nm = $(this).attr("colname") || this.id || this.name || null;
            if (!nm)
                return;

            // Sla adhoc filters op als "veldnaam.adhoc"
            var objName = $(this).hasClass("adhoc") ? nm + ".adhoc" : nm;

            if ($(this).attr("type") == "checkbox" || $(this).attr("type") == "radio")
            {
                formObject[objName] = $(this).prop("checked");
            }
            else
            {
                var val = $(this).val();

                // Type specifieke modificaties
                // Sla bij opengeklapte multiSuggests met 1 geselecteerde waarde, de text op ipv de key(s)
                if ($(this).prop("tagName") == "SELECT" && $(this).hasClass("multiSuggest") && $(this).find(":selected").length == 1) {
                    val = $(this).find(":selected").text();
                } else if (!$(this).hasClass("flddate") && relativeDates) { // !flddate, want we willen het hidden veld met de echte waarde
                    if ($("input.flddate#show_" + this.id).length && val && // Testen of ik een "show_" tegenhanger heb met de flddate class
                        val > 0 && val <= 8640000000000000) {               // Bevat ik een valide datum?
                        val = iface.encodeToRelativeDate(this);
                    }
                } else if ($(this).hasClass("flddate") && relativeDates && val.match(/^:[dwmqy][pn]?\d{0,3}$/i)) { // Pro optie; je kunt hem ook zelf invullen
                    if (this.id.indexOf("show_") === 0 && $("input#" + this.id.slice(5)).length && val) {
                        formObject[this.id.slice(5)] = val; // Vul het rechtstreeks ingevulde filter in bij het hidden waarde veld
                    }
                }

                if (!$(this).hasClass("adhoc") && val == "" || val == null) // Save empty adhoc filters too: DJIN#41716
                    return;
                // Ik ben al opgeslagen als multiSuggestHasOne, pak mijn key vanuit de reeds opgeslagen suggest
                if (val == "-1" && "suggestattr" in formObject && Object.keys(formObject.suggestattr).indexOf(objName + "_show") > -1)
                    val = formObject.suggestattr[objName + "_show"].sgKey;
                else if (val == "-1" && nm.match(/key$/))
                    return;

                if (objName in formObject) { // Bestaat al -> volgende
                    return;
                }

                formObject[objName] = val;
                // Ben ik een opengeklapte multiSuggest met slechts 1 geselecteerde waarde? (Sla me dan als reguliere suggest op)
                var multiSuggestHasOne = $(this).prop("tagName") == "SELECT" && $(this).hasClass("multiSuggest") && $(this).find(":selected").length == 1;
                if (multiSuggestHasOne || $(this).hasClass("suggest"))
                {
                    if (!("suggestattr" in formObject))
                        formObject.suggestattr = {};
                    formObject.suggestattr[objName] =
                    {	// Ook deze 'hidden' suggest attributen meenemen
                        sgKey:          multiSuggestHasOne ? $(this).val()[0]                 : $(this).attr("sgKey"),
                        sgCurrentValue: multiSuggestHasOne ? $(this).find(":selected").text() : $(this).attr("sgCurrentValue"),
                        sgExtraParam:   multiSuggestHasOne ? undefined                        : $(this).attr("sgExtraParam")
                    }
                }
            }
        })
        return JSON.stringify(formObject);
    },

    stringToForm: async function (formString, stringForm)
    {
        var formObject = JSON.parse(formString);
        var promises = []; // In deze lijst zitten Promises die pas resolven wanneer de waardes uit stringForm correct in de Suggests zijn overgenomen

        const applyFilter = async function() {
            var nm = $(this).attr("colname") || this.id || this.name || null;

            // Adhoc filters zijn in het formObject opgeslagen als "veldnaam.adhoc"
            var isAdhoc = $(this).hasClass("adhoc");
            var objName = isAdhoc ? nm + ".adhoc" : nm;

            if (!nm || !(objName in formObject))
                return;

            if ($(this).attr("colname"))
                var $input = stringForm.find("[colname=" + nm + "]")
            else if (this.id)
                var $input = stringForm.find("#" + nm)
            else
                var $input = stringForm.find("[name=" + nm + "]");
            if (!$input.length)
                return;

            var multiple = formObject[objName] instanceof Array && formObject[objName].length > 1;

            if ($input.attr("type") == "checkbox" || $input.attr("type") == "radio")
            {
                $input.prop("checked", !!formObject[objName])
            }
            else if ($input.prop("tagName") == "SELECT")
            {
                // This fills out the filters as saved from a (multi)select into the input field (so we dont have to perform any FillListbox()'s)
                if ($input.is(":hidden") && nm.length > 2 && stringForm.find("input#" + nm.slice(0, -2)).length > 0)
                {
                    // Not supported; Could take too long to open the list, so just take the first value
                    if (multiple)
                        formObject[objName] = formObject[objName][0];

                    stringForm.find("input#" + nm.slice(0, -2)).val(formObject[objName])
                                                            .prop('defaultValue', formObject[objName]);
                }
                else
                {
                    if (multiple) // Maak hem multiselect
                    {
                        $input.attr('multiple', 1);
                        $input.attr('size', Math.min($input.find("option").length, 8));
                        $input.val(formObject[objName]); // gewoon
                        $input.prev().html(I("fa-angle-up"));
                    }
                    else {
                        if ($input.data("depends-on")) {
                            await $("[name=" + $input.data("depends-on") + "]").triggerHandler("change");
                        }
                        $input.val(formObject[objName]); // gewoon
                    }
                }
            }
            else if (!$input.hasClass("final") &&                // Final's niet opslaan als filter
                    ((isAdhoc && $input.hasClass("adhoc")) ||    //   Adhoc filters alleen invullen bij adhoc filters
                    (!isAdhoc && !$input.hasClass("adhoc"))))   // Normale filters alleen invullen bij normale filters
            {
                // Meerdere waarden alleen supported bij multiSuggest's
                if (multiple && !$input.hasClass("multiSuggest"))
                    formObject[objName] = formObject[objName][0];

                if ($input.hasClass("suggest") && !multiple)
                {
                    if (!("suggestattr" in formObject) || !(nm in formObject.suggestattr) || !("sgKey" in formObject.suggestattr[nm]) || formObject.suggestattr[nm].sgKey == -1)
                        return; // Als er bij de suggest geen key staat opgeslagen, vul dan ook de weergegeven waarde niet in (return; == continue; maar dan in een each()-loop)
                }

                $input.val(formObject[objName]);
                $input.prop('defaultValue', formObject[objName]);

                // multiSuggests met meerdere waarden vast openklappen
                if ($input.hasClass("multiSuggest") && multiple) {
                    promises.push(new Promise(resolve => $input.prev(".multi_suggest_toggle").trigger("click", [resolve])));
                }

                if ("suggestattr" in formObject && nm in formObject.suggestattr)
                {
                    for (var sg in formObject.suggestattr[nm])
                        $input.attr(sg, formObject.suggestattr[nm][sg])
                }
            }
        }

        var input_arr = stringForm.find("input, select").not("reportmulti").toArray(); // negeer reportmulti, die kunnen we niet aan
        for (var id in input_arr) {
            await applyFilter.call(input_arr[id]);
        }

        return Promise.all(promises);
    },
    // Persoonselector functies
    persoonDetails: function (fieldName, info, key)
    {
        var pkey=(typeof key == "number" ? key : $('#'+fieldName)[0].value);
        if (info == 'PRS')
        {
            FcltMgr.openDetail("appl/prs/prs_perslid.asp?prs_key=" + pkey, { reuse: true });
        }
        else // Standaard fac_user info aanroepen.
        {
          if (pkey != -1)
            FcltMgr.openDetail("appl/fac/fac_user.asp?prs_key=" + pkey, { reuse: true });
          else
            FcltMgr.openDetail("appl/fac/fac_user.asp", { reuse: true }); // self
        }
    },
    toggleColorPicker: function (zelf)
    {
        if (!Modernizr.inputtypes.color)
            return;
        var $colorPicker = $(zelf).prev("input");
        var isTypeColor = $colorPicker[0].type == "color";
        if (isTypeColor)
        {
            $colorPicker[0].type = "text";
            $colorPicker.val("");
        }
        else
        {
            $colorPicker.val("#ffffff");
            $colorPicker[0].type = "color";
        }
        $(zelf).find("i").toggleClass("fa-trash-alt fa-paint-brush");
    },
    showCaller: function (zelf)
    {
        var callerinfo=FcltMgr.getCaller();
        if (callerinfo)
        {
            zelf.title=callerinfo.prs_naam;
        }
        else
            zelf.title="beller beschikbaar";
    },
    setCaller: function (suggestfield)
    {
        var callerinfo=FcltMgr.getCaller();
        if (callerinfo)
        {
            suggestfield.setValue(callerinfo.prs_key, callerinfo.prs_naam, true, true); // wel checkExist doen!
        }
    },
    tabFrames: function () // Maak van alle subframes automatisch interne tabjes
    {
        var $pageHeader = $("h2.page-header");
        if ($pageHeader.length > 0)
            $("body").prepend($pageHeader);
        var $frames = $(".fcltframe").not(".sidebar");
        if ($frames.length < 2 || $(".subframe").length)
            return;

        var $ul = $("<ul class='subtabs'></ul>");
        $frames.each(function ()
        {
            var $li = $("<li></li>");
            var $icon = "<span class='subtab-icon'>" + I(this.getAttribute("data-subtab-icon")) + "</span>";
            var spinner = "";
            if (this.getAttribute("data-subtab-icon") == "fa-list") {
                spinner = "&nbsp;" + I("fa-fclt-spinner fal fa-xs");
            }
            $li.append("<a href='#{0}'>{2}{1}<span class='subtabextra'>{3}</span></a>".format(this.id, (this.getAttribute("data-subtab-title") || L(this.id)), $icon, spinner));
            $ul.append($li);
        });
        $('#frametabs').prepend($ul);
        $('#frametabs').tabs({
            beforeActivate: function(event, ui) {
                var old_div = ui.oldPanel;
                if (old_div.length) {
                    var old_frm = $(old_div).find("iframe").get(0).contentWindow;
                    if (FcltMgr._frmaccessible(old_frm))
                        old_frm.wasScroll = $(old_frm).scrollTop();
                }
            },
            activate: function(event, ui) {
                var new_div = ui.newPanel;
                if (new_div.length) {
                    var $new_frm = $(new_div).find("iframe");
                    var new_wnd = $new_frm.get(0).contentWindow;
                    if (FcltMgr._frmaccessible(new_wnd))
                    {
                        $(new_wnd).scrollTop(new_wnd.wasScroll);
                        if ($new_frm[0].id === "widget_page_frame" && typeof new_wnd.resizeOnActivate == "function")
                            new_wnd.resizeOnActivate();
                    }
                    $("iframe", new_div).each(function () {
                        FcltMgr.iframeLoaded(this);
                    });
                }
            }
        });
    },
    // Activeer een specifieke subtab, op basis van index of iFrame-naam
    activateTab: function(index)
    {
        if (!FcltMgr._frmaccessible(parent))
            return false;
        if (typeof index === "number")
            parent.$("#frametabs").tabs({ "active": index });
        else if (typeof index === "string") // iFrame naam ?
        {
            for (var i = 0; i < parent.frames.length; i++)
                if (parent.frames[i].name === index)
                {
                    parent.$("#frametabs").tabs({ "active": i });
                    break;
                }
        }
    },
    // Pas clientside de layout aan
    overrulelayout: function(layoutfields, params)
    {
        params = params || {};
        for (var i = 0; i < layoutfields.length; i++)
        {
            var layoutfield = layoutfields[i];
            if (layoutfield.urole && layoutfield.urole != params.urole)
                continue;

            if (layoutfield.newonly && !params.isnew)
                continue;

            var $from = $(layoutfield.from);
            if (layoutfield.closest)
                $from = $from.closest(layoutfield.closest);
            //console.log($from);
            switch (layoutfield.action)
            {
                case "move":
                    var $to = $(layoutfield.to);
                    if (layoutfield.closest)
                        $to = $to.closest(layoutfield.closest);
                    //console.log($to);
                    switch (layoutfield.where)
                    {
                        case "insertAfter": $from.insertAfter($to);
                                            break;
                        case "insertBefore": $from.insertBefore($to);
                                            break;
                    }
                    break;
                case "hide":
                    $from.hide();
                    break;
            }
        }
    },
    uploads: [],
    bb_upload: function _bb_upload(file_input, files) {
        files = files || file_input.files;
        if (!files.length) {
            return false;
        }
        var formData = new FormData();
        for (var i = 0; i < files.length; i++) {
            formData.append(file_input.name, files[i]);
        }
        formData.append("RVT_token", window.RVT_token);
        // Hier wordt $.ajax gebruikt ipv $.post omdat we contentType & processData = false willen verzenden, dat kan niet met $.post
        iface.uploads.push(
            $.ajax({
                "url": $(file_input).data("upload-url"),
                "type": "POST",
                "data": formData,
                "dataType": "json",
                "processData": false,
                "contentType": false
            }).then(
                FcltCallbackAndThenAlways(function (json) { return iface.bb_upload_callback(json, file_input); }),
                new $.Deferred().reject()
            )
        );
    },
    bb_upload_callback: function _bb_upload_callback(json, file_input) {
        if (json?.success) {
            for (var i in json.safefilename) {
                FcltMgr.topmanager().window.$.toast({ loaderBg: "#fff", text: L("lcl_appendix_added").format(safe.html(json.safefilename[i])), icon: "success", position: "top-center"});
                let ext = json.safefilename[i].split(".").pop().toLowerCase();
                let isImage = ["bmp", "gif", "png", "jpg", "jpeg"].indexOf(ext) != -1;
                /* Voor notities een indicator-blokje toevoegen */
                $(".edit-note .note-attachments").append('<div class="note-attachment">'
                                                        + '  <span class="note-attachment-name' + (isImage ? ' note-attachment-image' : '') + '">' + safe.html(json.safefilename[i]) + '</span>'
                                                        + '  <span class="note-attachment-delete default-clickable-icon" data-delete-url="' + safe.htmlattr(json.deleteUrl[i]) + '">'
                                                        +      I("fa-lg fa-xmark")
                                                        + '  </span>'
                                                        + '</div>');

                /* bij nieuwe records ook een lijst met bestandsnamen toevoegen die we on-save verplaatsen moeten */
                if ($(file_input).data("new")) {
                    var formid = $(file_input).attr("form");
                    var bb_files_nr = $(file_input).attr("bb_files_nr");
                    $(file_input.form).append($('<input type="hidden" name="bb_files' + (bb_files_nr? bb_files_nr : '') + '" value="' + safe.htmlattr(json.safefilename[i]) + '"' + (formid ? ' form="' + formid + '"' : '') + '>'));
                }
                if (isImage) {
                    iface._insert_bb_code.call(file_input, null, "img", json.safefilename[i]);
                }
            }
            FcltMgr.resized();
            new $.Deferred().resolve();
        } else {
            new $.Deferred().reject()
        }
    },
    // Opent een upload modal en callbacked naar update_symbol_fld
    upload_symbol: function _upload_symbol() {
        let fld     = $(this).attr("aria-controls");
        let url     = $(this).data("upload-url");
        let isNew   = $(this).data("new");
        let $val_fld = $("#" + fld);
        const isPlaceholder = $val_fld.val().match(/^## .* ##$/) !== null;
        if (isNew && $val_fld.val() && !isPlaceholder) {
            url += "&newfilename=" + $val_fld.val();
        }
        parent.FcltMgr.openModalDetail( url
                                      , L("lcl_mld_symbol_upload")
                                      , { callback: FcltCallbackAndThen(function (json) { iface.update_symbol_fld(json, fld); }) }
                                      );
    },
    // Verwijdert een bestand of icoon uit een symbool-veld en callbacked naar update_symbol_fld
    remove_symbol: function _remove_symbol() {
        let fld = $(this).attr("aria-controls");
        let $val_fld = $("#" + fld);
        let $filebtn = $("[aria-controls=" + fld + "].symbol-upload");
        let valType = $(this).closest("td[data-type]").attr("data-type"); // Gecombineerd symbool-veld
        if (typeof valType === "undefined") {
            if ($(this).closest(".iconpicker").length) { // Alleen een iconpicker
                valType = 1;
            } else if ($(this).closest(".filepicker").length) { // Alleen een filepicker
                valType = 2;
            }
        }
        let fileName = $val_fld.val();
        const isPlaceholder = fileName.match(/^## .* ##$/) !== null;
        if (valType == 1 || isPlaceholder) {
            iface.update_symbol_fld({ "delete": true }, fld);
        } else if (valType == 2) {
            let url = $("[aria-controls=" + fld + "].symbol-delete").attr("data-delete-url");
            if (!url) { // Hier unlinken we alleen en hoeven we het bestand niet echt te deleten
                iface.update_symbol_fld({ "delete": true }, fld);
            } else {
                FcltMgr.confirm(L("lcl_delete") + " " + fileName + "?", function() {
                    let data = {};
                    protectRequest.dataToken(data);
                    let isNew = $filebtn.data("new");
                    if (isNew && $val_fld.val()) {
                        url += "&newfilename=" + $val_fld.val();
                    }
                    $.post(
                        url,
                        data,
                        FcltCallbackAndThen(function (json) { iface.update_symbol_fld({ "delete": true }, fld); })
                        );
                    });
                }
            }
    },
    // Opent de iconpicker in een modal en callbacked naar update_symbol_fld
    iconpicker: function _iconpicker() {
        let fld = $(this).attr("aria-controls");
        parent.FcltMgr.openModalDetail( rooturl + "/appl/FontAwesome/iconpicker.asp"
                                      , L("lcl_fac_choose_fa_icon")
                                      , { callback: FcltCallbackAndThen(function (json) { iface.update_symbol_fld(json, fld); }),
                                          width: 1000 }
                                      );
    },

    update_symbol_fld: function _update_symbol_fld(json, fld) {
        let $iconbtn = $("[aria-controls=" + fld + "].symbol-iconpicker");
        let $filebtn = $("[aria-controls=" + fld + "].symbol-upload");
        let $del_btn = $("[aria-controls=" + fld + "].symbol-delete");
        let $val_fld = $("[type=hidden]#" + fld);  // Het hidden value veld (de echte waarde)
        if (json.success) {
            if (json.icon) {
                $iconbtn = $(".symbol-iconpicker[aria-controls=" + fld + "]");
                $iconbtn.val(json.icon).attr("title", json.icon); // Knop
                $iconbtn.closest(".button-icon-wrapper").find("i").replaceWith(I(json.icon));
                $val_fld.val(json.icon).closest("td[data-type]").attr("data-type", "1");
            } else if ("fileName" in json) {
                if (json.fileName) { // Er is een bestand
                    if ($filebtn.data("reload-on-callback")) {
                        FcltMgr.reload();
                    } else if (!json.multi) {
                        if ($filebtn.data("module").indexOf("FGII") === 0) {
                            json.fileName = json.fileName.replace(/\.dwf$/, ""); // DWF's worden zonder extensie in de DB opgeslagen
                        }
                        $filebtn = $(".symbol-upload[aria-controls=" + fld + "]");
                        $filebtn.val(json.fileName).attr("title", json.fileName); // Knop
                        $val_fld.val(json.fileName).closest("td[data-type]").attr("data-type", "2");
                        if (json.deleteUrl) {
                            $del_btn.attr("data-delete-url", json.deleteUrl); // Om de delete knop te doen werken
                        }
                    }
                } else {
                    iface.update_symbol_fld({ delete: true }, fld);
                }
            }
        } else if (json.delete) {
            $iconbtn.val(L("mgt_kenmerk_icon")).attr("title", $iconbtn.data("default-title"));
            $filebtn.val($filebtn.data("default-value") || L("lcl_image")).attr("title", $filebtn.data("default-title"));
            $val_fld.val("").closest("td[data-type]").attr("data-type", "0"); // Leeg
            $iconbtn.closest(".button-icon-wrapper").find("i").replaceWith(I("fa-flag")); // Default FA-icoon
            $del_btn.attr("data-delete-url", "");
        }
        let fnCallback = $val_fld.data("callback");
        if (fnCallback && typeof window[fnCallback] === "function") {
            window[fnCallback](json);
        }
    },

    preview_image: function _preview_image() {
        FcltMgr.openModalDetail($(this).data("preview-url"), L("lcl_flex_preview"));
    },

    fullscreen_image: function _fullscreen(src) {
        if (!src) {
            return;
        }
        const _destructFullscreen = (orgContext) => {
            $(orgContext).off("keyup", _escapeClose); // orgContext is het document van waaruit de fullscreen is aangeroepen
            $(FcltMgr.topmanager().window.document).off("keyup", _escapeClose); // Het document die de section.fullscreen bevat
            $("section.fullscreen, section.fullscreen img.preview", FcltMgr.topmanager().window.document).off().remove();
        }
        const _escapeClose = (function (e) {
            if (e.key === "Escape") {
                if ($("section.fullscreen img.preview", e.currentTarget).hasClass("zoomed")) { // Escape terwijl je het plaatje inzoomt
                    $("section.fullscreen img.preview", e.currentTarget).trigger("click"); // Exit zoom
                } else {
                    _destructFullscreen(this);
                }
            }
        }).bind(document); // Geef het originele context-document mee als 'this' (middels bind)
        if (FcltMgr !== FcltMgr.topmanager()) {
            $(document).on("keyup", _escapeClose);
            $(FcltMgr.topmanager().window.document).on("keyup", _escapeClose);
            FcltMgr.topmanager().window.iface.fullscreen_image(src);
        } else {
            if ($("body").length > 0 &&
                new URL(src).origin === window.location.origin) { // We laten hier alleen plaatjes zien van ons zelf
                $("body").prepend('<section class="fullscreen no-select">'
                                + '  <span class="default-clickable-icon top-right">'
                                +      I("fa-times")
                                + '  </span>'
                                + '  <img class="preview" src="' + src + '">'
                                + '</section>');
                $("section.fullscreen").on("click", (e) => {
                    if (!$(e.target).is($("img.preview"))) { // 'Ik heb op de achtergrond geklikt'
                        $("section.fullscreen, section.fullscreen img.preview").off();
                        $(e.currentTarget).remove();
                    }
                });
                $("section.fullscreen img.preview").on("click", (e) => {
                    $(e.target).toggleClass("zoomed");
                }).on("mousemove", (e) => { // Zoom in op de plek waar je muis-cursor is, alleen de assen die nodig zijn
                    let x = "50%";
                    let y = "50%";
                    if (2 * e.target.naturalWidth > window.screen.availWidth) {
                        x = e.clientX + "px";
                    }
                    if (2 * e.target.naturalHeight > window.screen.availHeight) {
                        y = e.clientY + "px";
                    }
                    $(e.target).attr("style", "transform-origin: " + x + " " + y + ";");
                });
            }
        }
    }
}
// params: checkOnly    : [] beperkt array met velden die gecontroleerd moeten worden
//                        (default alles met class 'required')
//         checkNot     : [] deze velden niet controleren (default leeg)
//         relaxed      : alleen controle, geen class aanpassingen of alerts
async function validateForm(fName, params)
{   // TODO: Alleen binnen fName kijken?
    if (typeof fvalid != "undefined" && !fvalid) {
        return fvalid;
    }
    params = params || {};
    if (!params.checkOnly && typeof calculateFlexExpressions === "function") { // Is er geen check-scope en zijn er uberhaupt kenmerken?
      await calculateFlexExpressions(); // Kijk dan eerst of we flex expressies moeten evalueren
    }

    function _isGoodCurrency(str, checkInteger, relativeValuesAllowed)
    {
      if (relativeValuesAllowed && str.match("%"))
        var anum = /^[-\+](\d+[.,]?\d*)[%]$/; // RegExpr for currency field with % sign and with mandatory + or -
      else if (relativeValuesAllowed && str.match(/^-{2}|\+{2}/))
        var anum = /^(\+{2}|-{2})\d+[.,]?\d*$/;   // RegExpr for currency field with ++ (add) or -- (sub) without % sign
      else
        var anum = /^([-\+]?\d+[.,]?\d*)$/;   // RegExpr for currency field without % sign
  // original: var anum = /^([-\+]?\d+[.,]?|[-\+]?\d+[.,]\d+$)$/ [results would have been the same with /^([-\+]?\d+[.,]?\d*)$/]
      return anum.test(str);
    }

    function _isGoodTextFormat(aObject)
    {
      var regexptekst = aObject.getAttribute("regexp");
      var re = new RegExp(regexptekst);
      var hint = '';
      var vIsGood = true;

      if (!re.test($(aObject).val()))
      {
        var spantitle = $("label[for="+ aObject.name +"] span[title]");
        if (spantitle.length)
        {
          hint = spantitle[0].getAttribute("title");
          if (hint == '')
            hint = L("lcl_shared_validator_format");

          hint = (spantitle[0].textContent || spantitle[0].innerText) + ' ' + hint

          // FSN#41130, voorkomen van dubbele hint's
          // door datum-validatie wordt _isGoodTextFormat() 2x doorlopen
          if ($.inArray(hint, validatorHint) == -1)
      {
              validatorHint.push(hint);
          }
        }
        vIsGood = false;
      }
      return vIsGood;
    }

    try
    {
      if (document.activeElement && document.activeElement.tagName != 'BODY' && !params.noBlur)
          document.activeElement.blur(); // trigger laatste onChanges
    }
    catch (e)
    {
      // Faalt in onthouden tabje van frontend meldingenoverzicht die autosearch doet
    }

    var clsName = "missing";
    var anyMissing = false;
    var anyBad = false;
    var validatorHint = [];

    var jqcheckonly="*";
    if (params.checkOnly) // slechts enkele velden controleren
      jqcheckonly = "#" + params.checkOnly.join(",#");
    var jqchecknot="";
    if (params.checkNot && params.checkNot.length) // Deze velden niet meer controleren
      jqchecknot = "#" + params.checkNot.join(",#");

    if (params.relaxed)
      clsName = "";

    // Klap blokken open waaronder verplichte kenmerken hangen
    $("[data-toggle='collapse'][aria-expanded='false']").each(function () { // Elke card header die een card-body open klapt
        var hasRequired = $(this.dataset.target).find(".required").filter(jqcheckonly).not(jqchecknot).length;
        if (hasRequired)
            $(this).trigger("click"); // Klapt blok open
    });

    // Onderstaand stukje klapt mobile collapsible blocks uit indien er zich een input veld met waarde in bevind.
    $(".ui-body-c").each(function ()
    {
      var $collapsable = $(this);
      $(this).find("input.required").filter(':hidden').each(function() {

        if ($(this).val() && $collapsable.prev(".ui-collapsible-heading-collapsed"))
          $collapsable.prev(".ui-collapsible-heading-collapsed").trigger("click");
      });
    });

    $(".required" + (params.checkExtraClass? ", ." + params.checkExtraClass : "")).filter(jqcheckonly).not(jqchecknot).filter(':visible').each(function (i)
        {
            switch (this.tagName)
            {
                case "SELECT":
                {
                  $(this).removeClass(clsName);
                  $("#req_"+this.name + ",#" + this.name).removeClass(clsName);
                  var opt = $(this).find("option:selected");
                  if (this.options.length == 0 || opt.length == 0|| opt.val() == "-1" || opt.val() == "") // flex 'R' heeft leeg
                  {
                    $(this).addClass(clsName);
                    if (this.options.length)
                      opt.addClass(clsName);
                    else
                      $("#" + this.name).addClass(clsName);
                    anyMissing = true;
                  }
                  break;
                }
                case "A":
                  if (this.getAttribute("data-role") != "button") {
                    break;
                  }
                case "INPUT":
                {
                  if (this.type == 'button' || this.getAttribute("data-role") == "button")
                  { // specifiek voor de bijlage knop, om te kijken of er bijlagen zijn indien dit vereist wordt.
                    $(this).removeClass(clsName);
                    if (this.getAttribute("isBijlage")==1)
                    {
                      if (this.getAttribute("nBijlagen") == 0) // Als het aantal op de button 0 is, dan isMissing
                      {
                        $(this).addClass(clsName);
                        anyMissing = true;
                      }
                    }
                    if (this.getAttribute("isObject")==1)
                    {
                      if (this.getAttribute("disabled") == "disabled")
                      {
                        $(this).addClass(clsName);
                        anyMissing = true;
                      }
                    }
                    break;
                  }
                  if (this.type == 'text' && this.getAttribute("hasvalue"))
                  { // de class missing moet op de div boven de input staan.
                    $("#req_"+this.name).removeClass(clsName);
                    if (this.getAttribute("hasvalue") == 0)
                    {
                      $("#req_"+this.name).addClass(clsName);
                      anyMissing = true;
                    }
                    break;
                  }
                  if (this.type == 'checkbox')
                  { // Required checkbox is always checked
                    var check = $(this).is(":checked");
                    // Deze krijg een tekst eronder ipv een rode achtergrond, vandaar een eigen validateField
                    validateField($(this), check, L("lcl_mld_verplicht"));
                    if (!check) {
                        $(this).addClass(clsName);
                        anyMissing = true;
                    }
                    break;
                  }
                  ;
                  if (this.type == 'radio')
                  { // Required radio requires 1 checked radiobutton
                    if ($(this).is("[name='" + this.name + "']:last")) { // Voor alle radio's met dezelfde naam 1 check uitvoeren

                        $("[name='" + this.name + "']").removeClass(clsName);

                        var check = $("[name='" + this.name + "']:checked").length === 1;
                        // Deze krijg een tekst eronder ipv een rode achtergrond, vandaar een eigen validateField
                        validateField($(this), check, L("lcl_mld_verplicht"));
                        if (!check) {
                            $("[name='" + this.name + "']").addClass(clsName);
                            anyMissing = true;
                        }
                    }
                    break;
                  }
                  // voor andere type=text doorvallen naar textarea
                }
                case "TEXTAREA":
                {
                  // bij currency zijn we extra streng: 0.0 wordt niet geaccepteerd.
                  // tenzij je oorspronkelijk requiredor0:true (ipv required) hebt meegegeven
                  $(this).removeClass(clsName);
                  if ($(this).val().trim() == "" ||
                      ($(this).hasClass("currency") && !$(this).hasClass("required0isoke") && parseFloat($(this).val().replace(',', '.')) == 0) ||
                      $(this).hasClass("suggestBad") ||
                      ($(this).hasClass("suggest") && this.getAttribute("sgKey") < 0))
                  {
                    $(this).addClass(clsName);
                    anyMissing = true;
                  }
                  // Ten behoeve van foute invoer/formattering datum.
                  // Indien geen reguliere expressie (regexp) dan bad zetten anders eerst controlen of format nu goed is.
                  else if ($(this).hasClass("bad") && (this.getAttribute("regexp") == null || !_isGoodTextFormat(this)))
                  {
                    // this won't work in some cases. Since a value can be changed to a correct value, with the bad class still on it.
                    //$(this).addClass(clsName);
                    //anyBad = true;
                  }
                  break;
                }
            }
        }
    )

    $(".suggestBad").each(function (i)
        {
          if($(this).val() != "" && !$(this).hasClass("missing"))
          {
              anyBad = true;
          }
        }
    )

    $(".number").filter(jqcheckonly).not(jqchecknot).filter(':visible').each(function (i)
        {
            var check = ($(this).val() != "" && !isNaN(parseInt($(this).val()), 10));
            if($(this).val() != "" || $(this).hasClass("required") || (params.checkExtraClass && $(this).hasClass(params.checkExtraClass)))
            {
              validateField($(this), check, L("lcl_shared_validator_format_number"));
            }
            if ($(this).val() != "" && isNaN(parseInt($(this).val()), 10))
            {
                anyBad = true;
            }
            else
            {
                $(this).removeClass("bad"); // Een eventuele attention blijft er op staan!
            }
        }
    )
    $(".float").filter(jqcheckonly).not(jqchecknot).filter(':visible').each(function (i)
        {
            var check = _isGoodCurrency($(this).val().replace(',', '.'), undefined, true);
            if ($(this).val() != "")
            {
              if ($(this).hasClass('relativeValuesAllowed'))
              {
                if (!check)
                {
                    $(this).addClass("bad");
                    anyBad = true;
                }
              }
              else if (!isFinite(parseFloat($(this).val())))
              {
                  $(this).addClass("bad");
                  anyBad = true;
              }
            }
            else
            {
                $(this).removeClass("bad"); // Een eventuele attention blijft er op staan!
            }
        }
    )
    $(".currency").filter(jqcheckonly).not(jqchecknot).filter(':visible').each(function (i)
        {
            var check = _isGoodCurrency($(this).val().replace(',', '.'), undefined, $(this).hasClass('relativeValuesAllowed'));
            if($(this).val() != "" || $(this).hasClass("required"))
            {
              validateField($(this), check, L("lcl_shared_validator_format_currency"));
            }
            if ($(this).val() != "" && !check)
            {
                anyBad = true;
            }
            else
            {
                $(this).removeClass("bad"); // Een eventuele attention blijft er op staan!
            }
        }
    )
    $("input[regexp]").filter(jqcheckonly).not(jqchecknot).filter(':visible').each(function (i)
        {
          if (($(this).hasClass("required") || $(this).val()) && (!_isGoodTextFormat(this)))
          {
            $(this).addClass("bad");
            anyBad = true;
          }
          else
          {
            $(this).removeClass("bad"); // Een eventuele attention blijft er op staan!
          }
        }
    )
    $("textarea[regexp]").filter(jqcheckonly).not(jqchecknot).filter(':visible').each(function (i)
        {
          if (($(this).hasClass("required") || $(this).val()) && (!_isGoodTextFormat(this)))
          {
            $(this).addClass("bad");
            anyBad = true;
          }
          else
          {
            $(this).removeClass("bad"); // Een eventuele attention blijft er op staan!
          }
        }
    )

    $(".flddate").filter(jqcheckonly).not(jqchecknot).filter(':visible').each(function (i)
    {
      if (($(this).hasClass("required") || $(this).val()))
      {
          id = $(this).attr("id").substring(5); // = id minus 'show_'
          allow_old_dates = $(this).hasClass("allow_old_dates");
          if(document.getElementById(id))
          {
              val = document.getElementById(id).value;
              if(val != undefined)
              {
                  if (!(val > 0 && val<=8640000000000000) && !allow_old_dates)
                  {
                      if(!params.relaxed)
                      {
                          $(this).addClass("bad");
                      }
                      anyBad = true;
                      validatorHint.push(L("mgt_srtkenmerk_err_datum"));
                  }
              }
          }
          else
          {
              //flexkenmerk has a different ID, could be null
              id = $(this).attr("id").split("val")[0];
              val = $("input#" + id + "key").attr("value");
              if(val != undefined)
              {
                  if (!(val > 0 && val<=8640000000000000) && !allow_old_dates)
                  {
                      $(this).addClass("bad");
                      anyBad = true;
                      validatorHint.push(L("mgt_srtkenmerk_err_datum"));
                  }
              }
          }
      }
    }
    )

    if (hasBadFields(jqcheckonly, jqchecknot))
    {
      validatorHint.push(L("lcl_shared_validator_invalid"));
      anyBad = true;
    }

    /* Experimenteel. Nieuwe browsers staan bij een maxlength ook in een textarea niet al
      te veel karakters toe dus is dan dubbelop */
    // Echter bij importeren van tekst in een input of texarea veld kunnen er wel meer tekens in komen te staan dan maxlength.
    // In dat geval moeten we deze check bijvoorbeeld laten uitvoeren.
    if (params.checklength)
    {
      // Lengte controle
      $("input:enabled,textarea:enabled").filter(jqcheckonly).not(jqchecknot).filter(':visible').each(function (i)
        {
          var txt = $(this).val()||"";
          var maxlen = parseInt($(this).attr("maxlength"), 10)||4000;
          if (txt.length > maxlen)
          {
            var lbl = $("label[for="+ this.name +"]").text() ||                                        // Label van flexkenmerken op eigen regel. Label staat voor het veld.
                      $(this).closest('table').find('th').eq($(this).closest('td').index()).text() ||  // Label van flexkenmerken op eerste regel achter parkeerplaats. Label staat boven in de kolom boven het veld.
                      "";
            if ($("label[for="+ this.name +"]").text() != "")
              lbl = lbl.replace(/\:$/, ""); // Dubbele punt aan einde weg indien aanwezig indien flex op eigen regel
            if (lbl != "")
              lbl = "'" + lbl + "'";
            validatorHint.push(L("lcl_shared_validator_length").format(lbl, maxlen, txt.length));
            $(this).addClass("bad");

            anyBad = true;
          }
        }
      )
    }

    // Klap blokken open waaronder 'missing' of 'bad' kenmerken hangen
    $("[data-toggle='collapse'][aria-expanded='false']").each(function () { // Elke card header die een card-body open klapt
        var hasMissingBad = $(this.dataset.target).find(".missing, .bad").length;
        if (hasMissingBad)
            $(this).trigger("click"); // Klapt blok open
    });

    if (params.relaxed && (anyMissing || anyBad))
      return false;

    if (anyMissing)
    {
        FcltMgr.alert(L("lcl_shared_validator_missing"));
        return false;
    }
    else if (anyBad)
    {
        if (!validatorHint.length)
        {
            validatorHint = [L("lcl_shared_validator_format")];
        }
        FcltMgr.alert(validatorHint.join("\n"));
        return false;
    }

    return true;
}


// Functies t.b.v. tijd velden
// werk veld om naar h:mm syntax
// 1.5 ==> 1:30, 1:5==>1:05, 1:50==>1:50,  1.50==>1:30,
// 2==>2:00, 10==>0:10, 40==>0:40, 10:00==>10:00 etc.
function sanitizeHours(veld, minvalue)
{
  var valid = true;
  if (!minvalue)
  {
    var TIME_RE = /^(\d*)([\/\.\,\-\:])?(\d\d?)?$/;
  }
  else
  {
  	var TIME_RE = /^(\-)?(\d*)([\/\.\,\-\:])?(\d\d?)?$/;
  }

  var tmtxt = $(veld).val();
  var posstime = tmtxt.match(TIME_RE);

  if (posstime && posstime.length
               && !isNaN(parseInt(posstime, 10))
     )  // er moet minstens iets goed zijn
  { // De indexen van posstime verschuiven 1 positie als minvalue = true
  	// omdat op plaats 0 nu eventueel het -teken komt te staan.
  	var pt_sep = (minvalue?3:2); // index van uur/minuut seperator
  	var pt_mm = pt_sep + 1;      // index van minuten

    var hh = Math.abs(parseInt(posstime, 10) || 0);
    var mm = 0;
    if (posstime.length > pt_sep && posstime[pt_mm] != "")
    {
      var mm = parseInt(posstime[pt_mm], 10) || 0;
      if (posstime[pt_sep] == "." || posstime[pt_sep] == ",")
      {
          mm = Math.floor(0.5 + 60 * parseFloat("0." + posstime[pt_mm]));
          // 2.5 moet 2:30 worden en niet 2.05, 2.25 moet 2:15 worden
      }
      // else // 2:5 wordt 2:05
    }
    else // 40 voor 0:40
      if (hh >= 10)
      {
          mm = hh % 60;
          hh = Math.floor(hh / 60);
      }
    hh = hh % 24;
    mm = mm % 60;
    var hm = hh + mm/60;
    // Opgeschoonde waarde opslaan
    if (minvalue && posstime[1] == '-')
    {
    	hm = -1 * hm;
    }
    $(veld).val(decimalToHour(hm));
  }
  else
      if (tmtxt != "")
      {
        valid = false;
      }
  validateField(veld, valid, L("lcl_shared_validator_format"));
  return valid;
}

// Usage: "Wilt U melding {0} afmelden?".format(12345)
String.prototype.format = function()
{
    var formatted = this;
    for (var i = 0; i < arguments.length; i++)
    {
        if (typeof arguments[i] == "string")
          arguments[i] = arguments[i].replace(/\$/g, '$$$$');
        var regexp = new RegExp('\\{'+i+'\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[i]);
    }
    return formatted;
};

// altijd :-syntax
function hourToDecimal(val)
{
  if (!val || val == "")
      return 0;
  var hhmm = String(val).split(":");
  var mintime = hhmm[0].indexOf('-') >= 0 ; // is tijd negatief of positief
  var dtime = Math.abs(parseInt(hhmm[0], 10));
  if (hhmm.length != 1)
  {
  	dtime = dtime + parseInt(hhmm[1], 10) / 60;
  }
  return (mintime ? -1 * dtime : dtime);
}

// altijd :-syntax
function decimalToHour(val)
{
  if (!val || val == 0)
      return "";
  var mintime = Math.abs(val) != val; // is tijd negatief of positief
  var hh = Math.floor(Math.abs(val) + 0.5 / 60); // reken met positief tijd
  var mm = Math.floor((Math.abs(val) - hh) * 60 + 0.5);

  var timestr = String(hh) + ":" + String((mm < 10? "0" : "") + mm);
  return (mintime ? "-" + timestr : timestr); // voeg eventueel weer min-teken toe
}

// Clientside variant van de shared.inc versie
var protectRequest =
{
    dataToken: function (dataName) // Voeg aan een data hash een input token toe
    {
        dataName["RVT_token"] = window.RVT_token;
    }
}

function _initialize_sidebar_drag()
{
    /* Toggle-knop om het notitieframe in- en uit te klappen */
    var $frameheader_overlay = $("<div class='sidebar-toggler hidden'>" + I("fa-arrow-from-left") + "</div>");
    $(".sidebar").prepend($frameheader_overlay);
    $frameheader_overlay.on("click", _toggleSidebar);

    /* Div/icon om het frame te resizen */
    var $resizeHandle = $("<div class='resizeHandle hidden'>" + I("fa-ellipsis-v", { fa_fw: false, fastyle: "fal" }) + "</div>");
    $(".sidebar").prepend($resizeHandle);

    $resizeHandle.on("pointerdown", function (e) {
        var last_ts = $(this).data("last-click");
        if (last_ts > 0 && e.timeStamp - last_ts <= 500) // 500ms = default double click speed
            _reset_sidebar_width();
        else
            _startDrag(e);
        $(this).data("last-click", e.timeStamp);
    });
}

function _reset_sidebar_width()
{
    localStorage.removeItem("sidebar-width");
    $(".sidebar").css({ "flex-basis": "" });
}

function _load_stored_sidebar_state()
{
    /* Moet ik ingeklapt starten? */
    var stored_collapsed_state = localStorage.getItem("sidebar-collapsed");
    if (stored_collapsed_state && stored_collapsed_state !== "0")
        _toggleSidebar(null, true);
    else /* Uitgeklapt starten */
    {
        var stored_sidebar_width = localStorage.getItem("sidebar-width");
        /* Hoe breed dan? */
        if (stored_sidebar_width)
        {
            stored_sidebar_width = _correct_sidebar_width(stored_sidebar_width);
            $(".sidebar").css({ "flex-basis": stored_sidebar_width, "max-width": "100%" });
            _refreshSidebarToggleState();
        }
    }
}

function _toggle_resize_handlers(hide) {
    /* Laat handlers met opzet wat later zien, anders zie je nu al handlers voordat je inhoud ziet */
    $(".sidebar-toggler, .resizeHandle").toggleClass("hidden", hide);
}

function _startDrag(e)
{
    if (e.pointerType !== "mouse")
        return; /* Not supported */
    $("body").addClass("no-select").prepend($("<div class='resize-area'></div>"));
    window.startX = e.clientX;
    window.startWidth = parseInt(getComputedStyle($(".sidebar").get(0)).width, 10);
    window.$sidebar = $(".sidebar").eq(0);
    window.$sidebar.css({ "max-width": "100%" });
    $(window).on("mouseup", _stopDrag)
             .on("mousemove", _doDrag)
             .on("keyup", _escDrag);
    $(".fcltframe[aria-hidden=false], .fcltframe.sidebar").find("iframe").addClass("inactive"); /* Zorgt ervoor dat de mouse-events niet door de iframe ge-captured worden (tijdelijke class) */
}

function _stopDrag(e, reset)
{
    $(".resize-area").remove();
    $("body").removeClass("no-select");
    $("iframe.inactive").removeClass("inactive");
    $(window).off("keyup", _escDrag)
             .off("mousemove", _doDrag)
             .off("mouseup", _stopDrag);
    var $sidebar = window.$sidebar;
    var startWidth = window.startWidth;
    delete window.startX;
    delete window.startWidth;
    delete window.$sidebar;
    var w = reset ? startWidth : parseInt(getComputedStyle($sidebar.get(0)).width, 10);   /* Pixels */
    w = Math.round(100 * w / parseInt(getComputedStyle(document.body).width, 10)) + "%";  /* Percentage */
    $sidebar.css({ "flex-basis": w });
    localStorage.setItem("sidebar-width", w);
    _refreshSidebarToggleState();
}

function _doDrag(e)
{
    e.preventDefault(); /* Voorkomt dat je tekst selecteert tijdens het draggen */
    var change = startX - Math.max(40 /* Min-width linker frame */, e.clientX);
    var dragWidth = startWidth + change;
    var newWidth = Math.max(40 /* Min-width sidebar */, dragWidth);
    $sidebar.css({ "flex-basis": newWidth + "px" });
}

function _escDrag(e)
{
    if (e.keyCode === 27) { /* Escape */
        _stopDrag(e, true);
    }
}

function _toggleSidebar(e, init)
{
    if (!init)
        $(".sidebar").addClass("sidebar-transition");

    var isCollapsed = $(".sidebar").hasClass("collapsed");
    if (isCollapsed)
    {
        var stored_width = localStorage.getItem("sidebar-width");
        var new_width = (stored_width ? _correct_sidebar_width(stored_width) : "");
        $(".sidebar").css({ "flex-basis": new_width })
                     .removeClass("collapsed");
    }
    else
    {
        $(".sidebar").addClass("collapsed")
                     .css({ "flex-basis": "", "max-width": "100%" });
    }

    $(".sidebar-toggler").find(".fa-arrow-from-left, .fa-arrow-from-right").toggleClass("fa-arrow-from-left fa-arrow-from-right");

    if (!init) {
        localStorage.setItem("sidebar-collapsed", isCollapsed ? 0 : 1);
        setTimeout(function () { $(".sidebar").removeClass("sidebar-transition"); }, 150);
    }
}

function _refreshSidebarToggleState()
{
    var isCollapsed = parseInt(getComputedStyle($(".sidebar").get(0)).width, 10) < 55;

    /* collapsed class */
    $(".sidebar").toggleClass("collapsed", isCollapsed);

    /* Icon */
    $(".sidebar-toggler").find(".fa-arrow-from-left, .fa-arrow-from-right")
                         .toggleClass("fa-arrow-from-left", !isCollapsed)
                         .toggleClass("fa-arrow-from-right", isCollapsed);
}

/* Deze functie corrigeert de opgeslagen breedte voor de afmetingen van het huidige scherm */
function _correct_sidebar_width(w)
{
    var win_width = parseInt(getComputedStyle(document.body).width, 10);
    w = parseInt(w, 10) / 100 * win_width;          // van percentage naar pixels
    w = Math.max(Math.min(win_width - 40, w), 40);  // corrigeren (hou minimaal 40px over voor beide frames)
    return parseInt(w / win_width * 100, 10) + "%"; // van pixels naar percentage
}

// delay execution
function delayed(delay, fn)
{
    if (window.timerID)
        clearTimeout(window.timerID);
    window.timerID = setTimeout(fn, delay);
}

function buttons_hit_test() {
    $("div.buttoncontainer").each(function ()
    {
        var w = Math.max($(window).width(), $(this).width()); /* this.width voor de onLoad */
        var p = $(this).position();
        var $frameheader = $(this).closest("div.fcltframeheader");
        var headertop    = $frameheader.length ? $frameheader.position().top : 0;
        var smallsize = $(this).data("smallsize");

        if (p.left < 0 ||           // Ik schuif uit beeld
            p.top > headertop + 20) // Ik schuif naar de volgende regel (bij frametitel/filter)
            $(this).addClass("smallbuttons")
                   .data("smallsize", Math.max(w, smallsize || 0));
        else if (w > smallsize) // Weer groot genoeg geworden
            $(this).removeClass("smallbuttons");
    })
}

function addHinterEventlistener(modal) {
    let context = modal && FcltMgr._frmaccessible(parent) ? parent.document : document; // Het hint-icon staat buiten het iframe
    $(":input").on("focusin", function () {
        if (!$("#hinter", context).is(':visible')) {
            $("span.hints", context).addClass("active");
        }
    }).on("focusout", function () {
        if (!$("#hinter", context).is(':visible')) {
            $("span.hints", context).removeClass("active");
        }
    });
}

$(function ()
{
    /* De lege blokken hiden; */
    $(".block-wrapper").each(function()
    { /* Als ik zelf zichtbaar ben en ik heb alleen maar onzichtbare of lege tr's, of onzichtbare (niet-tr) children */
      if ((($(this).is(":visible") && $(this).find(".card-body.collapse").length == 0 && $(this).find(".card-body").find("tr:not(:empty):visible, *:not(table, tr):visible").length === 0) ||
          /* Of als ik zelf onzichtbaar ben en ik heb alleen maar lege tr's of andere children */
          (($(this).is(":not(:visible)") || $(this).find(".card-body.collapse").length != 0) && $(this).find(".card-body").find("tr:not(:empty), :input:not([hidden]), *:not(table, tr, :input)").length === 0) ||
          /* Of (om uitzonderingen af te vangen) ik ben niet hoger dan 8px */
          ($(this).is(":visible") && $(this).find(".card-body").height() < 8)) &&
          /* En ik heb ook geen knoppen in de card-header */
          $(this).find("div.blockbuttoncontainer:not(:empty)").length === 0 &&
          /* En ik heb ook geen behandelteam en behandelaar in de card-header */
          $(this).find("span.span_treatable:not(:empty)").length === 0 &&
          /* En ik heb geen resultsetTable */
          $(this).find(".card-body .divrstable").length == 0)
      {   /* Hide dan de card */
          $(this).children(".card").hide();
      }
    })

    if ($(".fcltframe.sidebar").length)
    {
        _initialize_sidebar_drag();
        _load_stored_sidebar_state();

        /* Het is beter om dit pas te tonen als het frame geladen is */
        if (document.readyState === "complete")
            _toggle_resize_handlers();
        else /* document.readyState === "interactive" */
            $(document).one("readystatechange", _toggle_resize_handlers);
    }

    // Kijk of bij de buttons de teksten niet meer passen en we dus terugvallen op alleen de icons
    $(window).on("resize", buttons_hit_test);
    buttons_hit_test(); /* Ook tijdens het laden */

    if (!Modernizr.inputtypes.color)
        $("input[type=color]").addClass("unsupported");

    $("input:not([type=hidden], [type=file]), textarea, select").on("focus active", function()
    {
        var thisid = $(this).attr("id") || $(this).attr("name");
        if (thisid)
        {
            $("label[for='"+thisid+"']").addClass("active");
            thisid = thisid.replace(/_show$/, "");
            $("label[for='"+thisid+"']").addClass("active");
        }
    });

    $("input:not([type=hidden], [type=file]), textarea, select").on("blur", function()
    {
        var thisid = $(this).attr("id") || $(this).attr("name");
        if (thisid)
        {
            $("label[for='"+thisid+"']").removeClass("active");
            thisid = thisid.replace(/_show$/, "");
            $("label[for='"+thisid+"']").removeClass("active");
        }
    });

    if (window.hints) {
        let isModal = window.frameElement && $(window.frameElement).hasClass("fcltmodal");
        addHinterEventlistener(isModal);
    }

    // Zorg dat mailto-type links niet de spinners triggeren.
    $("a[href^='mailto:'], a[href^='tel:'], a[href^='callto:']").each(function()
    {
        // Dit is een ongedocumenteerde interne (!) jQuery eventhandler collection
        var e = $._data($(this).get(0), "events");

        // Alleen er op zetten als deze er nog niet op zit
        if (!e || !(e.mouseover && e.mouseout))
          $(this).hover(function() { FcltMgr.setData("preventBeforeUnload", true); },   // mouseOver
                        function() { FcltMgr.setData("preventBeforeUnload", false); }); // mouseOut
    });

    // Zorg dat li knoppen geactiveerd kunnen worden (na focus) met enter (67938)
    $("form .emphasishigh, form .emphasismedium, form .emphasislow, .buttoncontainer .emphasishigh, .buttoncontainer .emphasismedium, .buttoncontainer .emphasislow").on("keyup", function(event) {
        if (event.keyCode === 13) {
            $(this).trigger("click");
        }
    });

    $("input.float, input.number, input.currency").blur(function()
    {
        allowInputExpression(this);
    });

    $(".text-styles > .default-clickable-icon").on("click", iface.bb_code);
    $("textarea.bb_enabled:not([readonly])").on("keydown", iface.onKeydown_bb_code);

    $(".symbol-upload").on("click", iface.upload_symbol);
    $(".symbol-delete").on("click", iface.remove_symbol);
    $(".symbol-iconpicker").on("click", iface.iconpicker);
    $("[data-preview-url]").on("click", iface.preview_image);

    // Het uploaden van bestanden bij een textarea (bb_upload_enabled)
    $(".bb_upload_enabled").each((i, elem) => {
        var $dropZone = $(elem);
        if ($dropZone.is($("#note")))
        {
          $dropZone = $("section.edit-note"); // Bij notities een iets groter gebied
        }
        $dropZone.on("drag dragend dragover dragenter dragleave drop",
                     (e) => {
                              if (e.originalEvent.dataTransfer.types.indexOf("Files") != -1)
                              {
                                e.originalEvent.dataTransfer.dropEffect = "copy";
                                e.preventDefault();
                                e.stopPropagation();
                              }
                            })
                 .on("drop",
                     (e) => {
                              let $textarea = $(e.currentTarget).parent().find(".bb_upload_enabled");
                              var bb_file_input = $("#" + $textarea.attr("aria-controls")).get(0);
                              iface.bb_upload(bb_file_input, e.originalEvent.dataTransfer.files);
                            })
                 .on("dragover dragenter",
                     function() { // Styling purposes
                                  $(this).addClass("dragon");
                                })
                 .on("dragleave dragend drop",
                     function() { // Styling purposes
                                  $(this).removeClass("dragon");
                                })
                 .on("paste",
                     (e) => {
                              var data = (e.originalEvent || e).clipboardData;
                              if (data.items.length && data.items[0].type.indexOf("image") !== -1)
                              {
                                let $textarea = $(e.currentTarget).parent().find(".bb_upload_enabled");
                                var bb_file_input = $("#" + $textarea.attr("aria-controls")).get(0);
                                iface.bb_upload(bb_file_input, [data.items[0].getAsFile()]);
                              }
                            });
    });

    // set trigger on mouseenter event to resize labels
    function setResizeOnMouseenter() {
        $('td.label label, td.label label > span').filter(':not([title]), [title=""]').off('mouseenter').on('mouseenter', function(event) {
            var $this = $(event.target);

            if(event.target.offsetWidth < event.target.scrollWidth){
                $this.attr('title', $this.text());
            }
        });
    }
    setResizeOnMouseenter();
    $( document ).on('ajaxComplete', function _ajaxComplete(event, request, settings) {
        if (settings.url.indexOf('load_kenmerk.asp?') > -1) {
            setResizeOnMouseenter();
        }
    });

    if ($(".scrollTopTab").length) // Ik heb een scrollTop 'lipje' (zoekscherm)
    {                              // Voeg dan een scroll-handler toe aan mij
        $(window.document).on("scroll", function() {
            $(".scrollTopTab").toggleClass("scrolled", $(this).scrollTop() > 10);
        });
    }

    // Maak subtabs
    iface.tabFrames();

    // Activeer de juiste, indien ge-set (scf js' zijn eerder)
    if (typeof activeSubTab !== "undefined")
        iface.activateTab(activeSubTab);

    masonryInit();

});

function masonryInit()
{
    delete window.$masonry;

    // Gebeurd ook in FcltMgr.resized()
    if (window.Masonry && $(".content-wrapper").length)
    {
        /* Popups hoeven niet want die zijn altijd max 1 kolom breed */
        if (!$(".content-wrapper > .block-sizer").hasClass("col-lg-6")) { // .block-sizer van pop-ups hebben alleen de col-12 class
            return;
        }

        /* Pak alvast de wrapper van de verzameling die we willen masonry-en */
        window.$masonry = $(".content-wrapper");

        /* Masonry options */
        var masonryOptions = {
            itemSelector: ".block-wrapper, .search-block-wrapper, .simple-block-wrapper",
            stamp: "*:not(.card-body) > .block-buttons.order-first, *:not(.card-body) > .fcltfiller.order-first", /* not-floating, floating */
            percentPosition: true,
            columnWidth: ".block-sizer",
            horizontalOrder: true,
            transitionDuration: 0
        }

        /* Eventhandler als masonry klaar is */
        function _onLayoutComplete(this_window) {
            this_window = this_window || window;
            // Als er gescrolled wordt tijdens masonryInit(), dan wordt de masonry_float_test niet uitgevoerd
            // Dat moet dan achteraf alsnog zodat we de layout niet achter de floatende frameheader plakken, maar achter de stilstaande frameheader
            if (this_window.elems?.isFloating && this_window.$masonry.data("masonry")?._isLayoutInited) {
                this_window.masonry_float_test(true);
                this_window.$masonry.masonry("layout");
            }
            var frm = this_window.parent.document.getElementById(this_window.name);
            this_window.FcltMgr.resized(frm, { noMasonry: true });
        }

        /* Plak eventhandler er aan voor als masonry klaar is en voer masonry uit */
        function _masonryThenResize(this_window) {
            this_window = this_window || window;
            if (!this_window.$masonry) // Dat kan, als de parent eerder klaar is dan de window
                this_window.$masonry = this_window.$(".content-wrapper");
            if ($(this_window.document.documentElement).is(":visible"))
            { /* Niet onzichtbaar doen, dan komt het later wel (als je het tab activeert bijvoorbeeld) */
                this_window.$masonry.off().one("layoutComplete", function () { _onLayoutComplete(this_window); });
                this_window.$masonry.masonry(masonryOptions);
            }
        }

        /* Ben ik geladen? -> voer masonry uit, anders wacht eerst tot ik (helemaal) geladen ben */
        function _masonryWhenReady(this_window) {
            this_window = this_window || window;
            if (this_window.document.readyState !== "complete")
            { // Nog maar een keertje als alles ingeladen is (in-page lijsten bijvoorbeeld)
                this_window.document.onreadystatechange = function () {
                    if (this_window.document.readyState === "complete")
                        _masonryThenResize(this_window);
                }
            }
            else
                _masonryThenResize(this_window);
        }

        _masonryWhenReady(window);

        /* Is mijn parent nog niet geladen? Doe het dan daarna nogmaals (ivm evt resize van parent) */
        if (parent.window != window &&                        // Ik heb een parent
            !window.name.match(/^ID.*/) &&                    // Ik ben zelf geen frametab
            parent.FcltMgr &&                                 // Mijn parent heeft een FcltMgr
            parent.window.document.readyState !== "complete") // Mijn parent is nog niet klaar met laden
        {
            parent.window.document.onreadystatechange = function () {
                $(this).find(".fcltframe > iframe").each(function () {
                    if (this.contentDocument.readyState === "complete" && this.contentWindow.Masonry && $(".content-wrapper", this.contentDocument).length)
                        _masonryWhenReady(this.contentWindow);
                });
            }
        }
    }
}

/* Afbreken en opnieuw maken zorgt voor een juiste volgordelijkheid van dynamische onderdelen/blokken */
function masonryRebuild()
{
    if (window.$masonry?.data("masonry")?._isLayoutInited === true)
    {
        var itemsChanged = window.$masonry.data("masonry").items.length !== window.$masonry.find(window.$masonry.data("masonry").options.itemSelector).length;
        var currentItems = window.$masonry.data("masonry").items;
        var $newItems = window.$masonry.find(window.$masonry.data("masonry").options.itemSelector);
        for (var i = 0; i < currentItems.length && !itemsChanged; i++) {
            itemsChanged = !currentItems[i].element.isEqualNode($newItems.get(i));
        }
        if (itemsChanged) {
            window.$masonry.off("layoutComplete");
            window.$masonry.masonry("destroy");
            masonryInit();
        } else {
            window.masonry_float_test(window.elems?.isFloating);
            window.$masonry.masonry("layout"); // Deel alleen de layout opnieuw in
        }
    } else if (typeof window.$masonry !== "undefined") { // We zijn nog aan het initialiseren, probeer straks nog een keer
        window.$masonry.off("layoutComplete")
                       .one("layoutComplete", masonryRebuild);
    } else {
        masonryInit();
    }
}

// Als de input met een '=' begint doen we een 'eval' op de rest
function allowInputExpression(thisObj)
{
    var val = $(thisObj).val();
    if (val.substr(0,1) == '=')
    {
        try {
            val = val.replace(/,/g,"."); // we staan ook komma's toe
            var newval = eval(val.substr(1));
            if (isFinite(newval))
            {
                if (val.indexOf(".") > -1 || $(thisObj).hasClass("currency"))
                    newval = num2currEditable(newval);
                $(thisObj).val(newval);
            }
            // else negeren
        }
        catch (e) { /* negeren */ }
    }
}

// Afronden van geldbedragen.
// roundcurr(1.275, 2);        // Returns 1.28
// roundcurr(-1.275, 2);       // Returns -1.28
// roundcurr(1.27499, 2);      // Returns 1.27
// roundcurr(-1.27499, 2);     // Returns -1.27
// roundcurr(1.2345678e+2, 2); // Returns 123.46
// roundcurr(1234.5678, -2);   // Returns 1200
// roundcurr(123.45);          // Returns 123
// Basis code: https://stackoverflow.com/questions/11832914/round-to-at-most-2-decimal-places-only-if-necessary?page=1&tab=votes#tab-top punt 22.
//             Basis code iets aangepast zodat waarde aan het eind met nullen wordt aangevult indien nodig.
function roundcurr(value, exp) {
  if (typeof exp === 'undefined' || +exp === 0)
    return Math.round(value);

  value = +value;
  exp  = +exp;

  if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
    return NaN;

  // Negatieve getallen voor het afronden even positief maken.
  var negatief = false;
  if (value < 0)
  {
    negatief = true;
    value = value * -1;
  }

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1]? (+value[1] + exp) : exp)));

  // Negatieve getallen weer negatief maken.
  if (negatief)
    value = value * -1;

  // Shift back
  value = value.toString().split('e');
  value = +(value[0] + 'e' + (value[1]? (+value[1] - exp) : -exp))
  return (value).toFixed(exp); // value is al afgerond. ik kan nu gerust toFixed() gebruiken om eind nullen terug te krijgen (1.5toFixed(2) => 1.50).
}

// Voorheen num2curr.js
function num2curr(s) {
  if( !isNaN(s) ) {
    // MARX#58496: Be careful using  ".toFixed()" as it might return different rounding results for different browsers.
    //
    // Afronding waarde.toFixed(2) voor verschillende browsers:
    // IE: 251,075 => 251,08,
    // FF,Crome: 251,075 => 251,07)
    //
    // Therefore we use roundcurr instead.
    //s = s.toFixed(2);
    s = roundcurr(s, 2);
  }
  return s;
}

// Met eventueel gewenste decimaalscheider (punt of komma) maar
// nooit 1000-scheiders
// voorbeelden als decimaalscheider een komma is:
//  num2currEditable(2.45);     -> 2,45     (default 2 decimalen)
//  num2currEditable(2.45 , 1); -> 2,5
//  num2currEditable(2.45 , 0); -> 2
function num2currEditable(fnum, decimals)
{
    if (fnum == null)
        return "";

    if (typeof decimals == "undefined")
        decimals = 2;
    // MARX#58496: Be careful using  ".toFixed()" as it might return different rounding results for different browsers.
    //
    // Afronding waarde.toFixed(2) voor verschillende browsers:
    // IE: 251,075 => 251,08,
    // FF,Crome: 251,075 => 251,07)
    //
    // Therefore we use roundcurr instead.
    //fnum = fnum.toFixed(decimals);
    fnum = roundcurr(fnum, decimals);

    var waarde2 = (1.5).toLocaleString(); // Eventueel gewenste komma.
    fnum = fnum.substring(0, fnum.length - decimals - 1) + waarde2.substring(2, 1) + fnum.substring(fnum.length - decimals);

    return fnum;
}

function isGoodNumber(str, intOnly, posOnly, numLen, numDec)
{
    var posOnlyStr = (posOnly ? "" : "\\-?");
    var numLenStr  = (numLen == -1 ? "" : (numDec == -1 ? numLen : (intOnly ? numLen : numLen - numDec)));
    var numDecStr  = (numDec == -1 ? "" : numDec);

    var intOnlyStr = "\\d{1," + numLenStr + "}[.,]?";
    var dblOnlyStr = "\\d{0," + numLenStr + "}[.,]\\d{1," + numDecStr + "}" + "|"
                   + "\\d{1," + numLenStr + "}[.,]\\d{0," + numDecStr + "}" ;

    var anum = "^"
             + posOnlyStr + "(" + intOnlyStr
                                + (intOnly ? "" : "[.,]?" + "|" + dblOnlyStr)
                          + ")"
             + "$";

    var patt = new RegExp(anum);
    return patt.test(str);
}

// Test: wat is de browser language.
function get_browser_language()
{
    var browser_lang = "";
    if (navigator.languages && navigator.languages.length)
    {
        browser_lang = navigator.languages[0];
    }
    else
    {
        browser_lang = (navigator.userLanguage || navigator.language || navigator.browserLanguage || "en (default)");
    }
    return browser_lang;
}

// float2floatEditable lijkt op num2currEditable maar gaat anders om met het aantal decimalen
// voorbeelden als decimaalscheider een komma is:
//  float2floatEditable(2.45);              -> 2,45     (default het aantal decimalen dat het getal heeft)
//  float2floatEditable(2.457);             -> 2,457
//  float2floatEditable(2);                 -> 2
//  float2floatEditable(2.457 , 2);         -> 2,45     (geen afronding)
//  float2floatEditable(2.50, null, true);  -> 2,5      (nullen aan einde verwijderen)
//  float2floatEditable(2.50);              -> 2,50     (alle significante decimalen meenemen)
function float2floatEditable(fnum, decimals, trimZeros)
{
    if (fnum == null)
        return "";

    var snum = fnum.toString();
    if (typeof decimals == "undefined")
    {
        var dp = snum.indexOf(".");
        decimals = (dp > -1 ? snum.substring(dp+1).length : 0);
    }

    if (decimals > 0)
    {
        snum = fnum.toFixed(decimals);
        var waarde2 = (1.5).toLocaleString(); // Eventueel gewenste komma, dank zij LCID gezet in appl\FAC\facilitor.inc
        snum = snum.substring(0, snum.length - decimals - 1) + waarde2.substring(2, 1) + snum.substring(snum.length - decimals);
    }

    if (trimZeros)
    {
        while (snum.substring(snum.length-1) == "0")
            snum = snum.substring(0, snum.length-1);
        if (snum.substring(snum.length-1) == waarde2.substring(2, 1))
            snum = snum.substring(0, snum.length-1);
    }

    return snum;
}

function getScrollHeight(elem)
{
    if ($(elem).is(":hidden"))
    {   // Insert a visible clone of the element as to gather its scrollHeight
        var cloneParams = { "position": "absolute", "left": "-10000px" };
        if ($(elem).parent().hasClass("bubble"))
            cloneParams.width = "90%";
        var $elemClone = $(elem).clone().css(cloneParams);
        $elemClone.appendTo($(elem).closest("body")).show();
        var scrollHeight = $elemClone.prop("scrollHeight");
        // And immediately remove it again
        $elemClone.remove();
        return scrollHeight;
    }
    else
        return $(elem).prop("scrollHeight");
}

function validateField(thisObj, valid, errorMsg)
{
    var hasIcon = $(thisObj).parent(".input-icon-wrapper").length;
    var $afterThisObj = hasIcon ? $(thisObj).parent(".input-icon-wrapper") : $(thisObj);

    var hasIconFollower = $afterThisObj.next("i.fa-fw").length;
    $afterThisObj = hasIconFollower ? $afterThisObj.next("i.fa-fw") : $afterThisObj;

    if ($(thisObj).get(0).type === "radio") {
        $afterThisObj = $afterThisObj.next("label");
    }

    var stateChanged = $(thisObj).hasClass('bad') === !!valid;
    $(thisObj).removeClass('bad');
    $afterThisObj.next('div.bad').remove();
    if (!valid)
    {
        $(thisObj).addClass('bad');
        $afterThisObj.after('<div class="bad">' + errorMsg + '</div>')
    }
    if (stateChanged)
        FcltMgr.resized();
}

function hasBadFields(jqcheckonly, jqchecknot)
{
    var hasBadFields = false;
    $(document).find("div.bad[for]").each(function () {
        var thisObj = $(this).attr("for");
        if ($("#" + thisObj).filter(jqcheckonly).not(jqchecknot).length) {
            hasBadFields = true;
            return false; // = break uit each()-loop
        }
    });
    return hasBadFields;
}

/* Via icons.inc naar header.inc */
function getIconPlaceholder(icon)
{
    for (var str in placeholder_icons)
        if (icon.indexOf(str) !== -1)
            icon = icon.replace(str, placeholder_icons[str]);
    return icon;
}

// Returns an icon to be displayed. icon can be an image (e.g. "notfound.png") or a fontawesome icon-name (e.g. "fa-user")
// Trailing fa-qualifiers (e.g. "fa-2x fa-rotate-180") are preserved
// The optional params provide some options
//    params.fastyle:   overrule global fastyle
//    params.fa_fw:     allows you to disable the fa-fw class
function I(icon, params)
{
    if (!icon)
        return "";
    var params = params || {};

    if (icon.match(/^fa-/))
    {
        var lstyle = params.fastyle == null ? S_fontawesomestyle : params.fastyle;
        var fa_fw = !("fa_fw" in params) || params.fa_fw ? " fa-fw" : ""; /* Default met fa-fw maar je kunt het er afhalen met { fa-fw: false } */
        icon = getIconPlaceholder(icon);

        /* Geef ik een style mee in de icoon naam, bijv: I("fa-image fas"), dan is die leidend, nuttig voor placeholder-overrules */
        var styles = ["fal", "far", "fas"];
        for (var style in styles)
            if (icon.split(/\s/).indexOf(styles[style]) !== -1)
            {
                lstyle = styles[style];
                icon = icon.replace(new RegExp("\\s?" + lstyle), "");
            }
        return "<i class='{0}{1} {2}'></i>".format(lstyle, fa_fw, safe.htmlattr(icon));
    }
    return "";
}

function notificatie_remove(noti_key)
{
    FcltMgr.confirm(L("lcl_noti_delete"), function() {
        var data = {
            noti_key: noti_key
        };
        protectRequest.dataToken(data);
        $.post("../shared/noti_remove.asp",
            data,
            FcltCallbackRefresh,
            "json");
    });
}
