/*
    $Revision$
    $Id$

    File:           FcltMgr.js

    Description:    FcltMgr is de manager van Facilitor die voornamelijk twee containers biedt
                    A) Een menu container
                    B) Een functie container (de tabjes rechts)
*/

;function L(lcl, params) {
    return FcltMgr.L(lcl, params);
}

// Aanvulling om makkelijker objecten op te slaan in de localStorage
Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    var value = this.getItem(key);
    return value && JSON.parse(value);
}

var FcltMgr = {
    _panelModalHot: false, // in case the tab was made hot by the modal window
    _pageManager: null, // defaultManager of tabbedManager
    _dialogModalHint: null,
    globalData: [],
    reload: function(params) {
        if (typeof fnReloadByPost === "function") { // Een POST heeft altijd een unieke manier van herladen die expliciet gedefinieerd moet worden
            fnReloadByPost();
        } else {
            params = params || "";
            // met (door jquery tabs toegevoegd) fragment identifier werkt onderstaande anders niet
            var hrefBase = window.location.href.split("#")[0];
            // nooit collapsed herladen? maar zet hem wel op 0!
            hrefBase = hrefBase.replace(/&collapsed=1/gi, "&collapsed=0");
            if (typeof mobile === "undefined" && hrefBase.indexOf("no_autoscroll") == -1)
                hrefBase = hrefBase + (hrefBase.indexOf("?") > 0 ? "&" : "?") + "no_autoscroll=1";
            if (params.appendurl) {
                var nm = params.appendurl.split("=")[0];
                if (hrefBase.indexOf("&" + nm + "=") > 0)
                    hrefBase = hrefBase.substr(0, hrefBase.indexOf("&" + nm + "="));
                hrefBase += "&" + params.appendurl;
            }
            // Vervang een eventuele hamburger door een spinner
            $("li#printhamburger i.fa-fw.fa-bars").toggleClass(getIconPlaceholder("fa-bars fa-fclt-refresh fa-spin"));
            window.location.href = hrefBase;
        }
    },
    defaultManager: {
        name: "defaultManager",
        openDetail: function(url, titel, params) {
            params = params || {};
            titel = titel || "";
            if (url.indexOf("hmac=") < 0) {
                url += (url.indexOf("?") < 0) ? "?" : "&";
                url += "_titel_=" + escape(titel);
            }
            if (params.forcedeep)
                FcltMgr.windowopen("../../?JumpTo=" + escape(params.orgurl));
            else
                FcltMgr.windowopen(url);
            //                FcltMgr.resized(window, params);
        },

        closeDetail: function(thiswindow, params) {
            window.close();
        },

        setPageHeader: function(text, params) {
            $("div.page-header h2", "body").text(text);
        },

        setTitle: function(title, params) {
            if (title)
                window.document.title = window.ttl_prefix + title;
        }
    },

    tabbedManager: {
        name: "tabbedManager",
        _counter: 0,

        openDetail: function(url, titel, params) {
            FcltMgr._pageManager._openTab(url, titel, params);
        },
        closeDetail: function(thiswindow, params) {
            var active = this._$tabshost.tabs('option', 'active');
            var tab = $(this._$tabshost.find(".ui-tabs-nav li")[active]);
            FcltMgr._pageManager._closeTab(tab, params);

            return;
        },

        setPageHeader: function(text, params) {
            $("#maintabs > #" + params.windowname).contents().find(".page-header > h2").text(text);
        },

        setTitle: function(title, params) {
            var orgtitle = null;
            var tab = this._$tabshost.find(".ui-tabs-nav li[aria-controls='" + params.windowname + "']");

            if (title && title.length > 25) {
                orgtitle = title;
                title = title.substr(0, 20) + "...";
            }
            // FcltMgr heeft voor ons params al met windowname gevuld.
            params = params || {};
            var waspinned = $(tab).find("span.tabpinned").length > 0;
            if (!waspinned)
            {
                var ttl_span = tab.find("span:first");
                ttl_span.attr("ttl", (orgtitle || title));

                if (title)
                    tab.find("span:first").text(title);
                if (orgtitle)
                    tab.find("span:first").attr('title', orgtitle);
            }
            if (params.hot) {
                tab.find(".tabclose").addClass('hot');
            } else {
                tab.find(".tabclose").removeClass('hot');
            }
            FcltMgr._pageManager._resize_forMaxTabs();
        },

        getTitle: function(params) {
            var tab = this._$tabshost.find(".ui-tabs-nav li[aria-controls='" + params.windowname + "']");
            return tab.find("span:first").text();
        },

        showTab: function(doshow, params) {
            params = params || {};
            var tab = this._$tabshost.find(".ui-tabs-nav li[aria-controls='" + params.windowname + "']");
            if (doshow) {
                tab.removeClass("hidden");
                this._$tabshost.find("#" + params.windowname).removeClass("hidden");
            } else {
                tab.addClass("hidden");
                this._$tabshost.find("#" + params.windowname).addClass("hidden");
            }
        },

        // Sluit alle tabbladen behalve de huidige en behalve _editactive
        closeAll: function(zelf) {
            var tab = FcltMgr._pageManager._$tabshost.find(".ui-tabs-nav li.ui-state-active")
            var activeId = $(tab).attr("aria-controls");
            $(".ui-tabs-nav li", this._$tabshost).each(function(i, j) {
                var thisId = $(this).attr("aria-controls");
                if (i > 0 && thisId != activeId && !FcltMgr._pageManager._panelHot(this)) {
                    FcltMgr._pageManager._closeTab(this);
                }
            });
        },

        // 'Pin' het huidige tabje of geef deze juist vrij
        fnDoSave: function(taboption, params)
        {
            let tab_arr = [];
            let data_arr = { taboption: taboption
                           , menu_key: -1
                           , autoopenttl: []
                           , autoopenurl: []
                           , filters: []
                           , advfilters: []
                           };
            if (taboption == "A")
            {
                var main = $("#maintabs").find("ul");
                $("li", main).each(function()
                    {
                        let $tab = $(this);
                        let v_opener = $tab.attr("openerid");
                        let v_edited = $tab.find("span.tabclose").hasClass("hot");
                        let frm = FcltMgr._pageManager.getFrameState($tab);

                        if (frm.doSave && !frm.waspinned && v_opener && !v_edited)
                        {   // Dit tabblad moet nog gepinned worden.
                            var tab_data = FcltMgr._pageManager.get_tabdata($tab, params.saveRel);
                            tab_arr.push($tab);
                            data_arr.autoopenttl.push(tab_data.ttl);
                            data_arr.autoopenurl.push(tab_data.autoopenurl);
                            data_arr.filters.push(tab_data.filters);
                            data_arr.advfilters.push(tab_data.advfilters);
                        }
                    });
                data_arr.tab_count = tab_arr.length;
            }
            else
            {
                tab_arr.push(params.$tab);
                if ("menu_key" in params) { // Bestaand items
                  data_arr.menu_key = params.menu_key;
                } else {
                  var tab_data = FcltMgr._pageManager.get_tabdata(params.$tab, params.saveRel);
                  data_arr.autoopenttl.push(params.ttl);
                  data_arr.autoopenurl.push(tab_data.autoopenurl);
                  data_arr.filters.push(tab_data.filters);
                  data_arr.advfilters.push(tab_data.advfilters);
                }
            }

            // Opslaan van de pinned tabs en pin op tab zetten.
            protectRequest.dataToken(data_arr);
            $.post( rooturl + "/appl/fac/fac_current_tab_save.asp"
                  , data_arr
                  , FcltCallbackAndThen(function(returndata)
                        {
                            if (returndata.saved)
                            {
                                for (var i = 0; i < tab_arr.length; i++)
                                {
                                    if (data_arr.taboption == "A" || data_arr.taboption == "T")
                                    {
                                        var html = "<span title='" + L("lcl_current_tab_unsave") + "'"
                                                 + " class='tabpinned'"
                                                 + ("menu_key_arr" in returndata ? " data-menu-key='" + (returndata.menu_key_arr[i] || -1) + "'" : "")
                                                 + " onclick='FcltMgr.saveTab(this);'>"
                                                 +      I("fa-thumbtack", { fastyle: "fas", fa_fw: false })
                                                 + "</span>";
                                        $(tab_arr[i]).find("span:first").text(data_arr.autoopenttl[i]).attr("ttl", data_arr.autoopenttl[i]); // Opschrift van de tab en attribuut ttl wijzigen.
                                        $(tab_arr[i]).find(".tabclose").before($(html));
                                    }
                                    else if (data_arr.taboption == "W")
                                    {
                                        var wgt_url = returndata.url;
                                        wgt_url = "../../" + wgt_url + (wgt_url.indexOf("?") > -1 ? "&" : "?") + "has_filters=1&tab_menu_key=" + returndata.menu_key_arr[i];
                                        var data = {
                                            "id": -1,
                                            "type": "FAC",
                                            "name": data_arr.autoopenttl[i].slice(0, 60),
                                            "linkurl": wgt_url
                                        };
                                        protectRequest.dataToken(data);
                                        $.post( rooturl + "/appl/mgt/fac_widget.asp?mode=save&id=-1",
                                                data,
                                                null,
                                                "json"
                                        );
                                    }
                                }
                            }
                            else if (data_arr.taboption == "T") // 1 tab unpinned
                            {
                                $(tab_arr[0]).find(".tabpinned").remove();
                            }
                            FcltMgr._pageManager._resize_forMaxTabs();
                        })
                  , "json"
                  );
        },

        getFrameState: function($tab)
        {
          let target = $tab.find("a").attr("href");
          let $iframe = $(target.slice(target.lastIndexOf("#")));
          let result = { "iframe": $iframe };

          result.waspinned = $tab.find("span.tabpinned").length > 0;
          result.doSave = false;
          if ($iframe.length && !$iframe.attr("is_extern")) {
            result.wnd = $iframe.get(0).contentWindow;
            result.doSave = result.waspinned ||
                            $("body.nopin", $iframe.get(0).contentDocument).length === 0 && $iframe.attr("reusesrc").indexOf("&hmac=") < 0;
          }
          return result;
        },

        get_tabdata: function($tab, saveRel)
        {   // Hier komen alleen nieuwe items
            let result = { "filters": "{}", "advfilters": "{}" };
            result.ttl = $tab.find("span:first").attr("ttl") || $tab.find("span:first").text();

            const target = $tab.find("a").attr("href");
            const $iframe = $(target.slice(target.lastIndexOf("#")));
            const wnd = $iframe.get(0).contentWindow;
            let url = $iframe.attr("reusesrc");
            if (url.indexOf("&fcltfilters=") > 0)
                url = url.substr(0, url.indexOf("&fcltfilters="));
            result.autoopenurl = url;

            if (FcltMgr._frmaccessible(wnd)) {
                url = wnd.location.href; // bij een *net* aangemaakte melding is deze anders
                let href_params = url.split("?")[1].split("&");
                if (href_params.indexOf("verynew=1") > -1) { // De url-parameter verynew verwijderen.
                    href_params.splice(href_params.indexOf("verynew=1"), 1);
                }
                if (href_params.indexOf("has_filters=1") > -1) { // Remove url-parameter has_filters
                    href_params.splice(href_params.indexOf("has_filters=1"), 2);
                }
                result.autoopenurl = url.split("?")[0] + "?" + href_params.join("&");
                if (result.autoopenurl.lastIndexOf("#") > 0) {
                    result.autoopenurl = result.autoopenurl.substr(0, url.lastIndexOf("#"));
                }
                if (wnd.iface && wnd.$("form[name=u2]").length) {
                    result.filters = wnd.iface.formToString(wnd.$("form[name=u2]"), true, saveRel);
                }
                if (typeof wnd.getAdvFlexValues === "function") {
                    result.advfilters = wnd.getAdvFlexValues();
                }
            }
            return result;
        },

        saveTab: function(zelf) {
            if (zelf) {
                var li = $(zelf).parents('li:eq(0)')[0];
                var active = $('li', this._$tabshost).index(li);
            } else {
                var active = FcltMgr._pageManager._$tabshost.find(".ui-tabs-nav li.ui-state-active").prevAll().length;
                if (active == 0 || FcltMgr._pageManager._panelHot(active)) {
                    FcltMgr.alert(L("lcl_tabs_nosave"));
                    return;
                }
            }

            let $tab = $(this._$tabshost.find(".ui-tabs-nav li")[active]);
            let frm = FcltMgr._pageManager.getFrameState($tab);

            if (frm.doSave) {
                const ttl = $tab.find("span:first").attr("ttl") || $tab.find("span:first").text();
                if (frm.waspinned) {
                    const menu_key = $tab.find("span.tabpinned").data("menu-key") || -1;
                    FcltMgr.confirm( L("lcl_current_tab_unsave")
                                   , FcltMgr._pageManager.fnDoSave.bind(null, "T", { "$tab": $tab, "menu_key": menu_key })
                                   );
                } else {
                    const ts = new Date().getTime(); // Timestamp
                    const hasDateFields = $(frm.wnd.document).find("form input.flddate").length > 0;
                    const $dia = $("<div id='pin_" + ts + "' class='pindialog'>"
                                 + "   <script>"
                                 + "     function toggle_ttl(t) {"
                                 + "       $('#pin" + ts + "').prop('disabled', !t);"
                                 + "     }"
                                 + "   </script>"
                                 + "   <input type='text' id='pin" + ts + "' class='fld required' value='" + safe.htmlattr(ttl) + "' maxlength='30'>"
                                 + "   <ul id='pinoptions'>"
                                 + "     <li><label for='pin_T" + ts + "'><input type='radio' name='saveAs" + ts + "' id='pin_T" + ts + "' value='T' onclick='toggle_ttl(1);' checked>" + L("lcl_current_tab_totab") + "</label></li>"
                                 + "     <li><label for='pin_M" + ts + "'><input type='radio' name='saveAs" + ts + "' id='pin_M" + ts + "' value='M' onclick='toggle_ttl(1);'>" + L("lcl_current_tab_tomenu") + "</label></li>"
           + (frm.wnd.canSaveAsWidget ? "<li><label for='pin_W" + ts + "'><input type='radio' name='saveAs" + ts + "' id='pin_W" + ts + "' value='W' onclick='toggle_ttl(1);'>" + L("lcl_current_tab_towidget") + "</label></li>" : "")
                                 + "     <li><label for='pin_A" + ts + "'><input type='radio' name='saveAs" + ts + "' id='pin_A" + ts + "' value='A' onclick='toggle_ttl(0);'>" + L("lcl_current_all_totab") + "</label></li>"
                + (hasDateFields ? "     <br>"
                                 + "     <li><label for='save_rel" + ts + "'><input type='checkbox' name='save_rel" + ts + "' id='save_rel" + ts + "'>" + L("lcl_save_dates_relative") + "</label></li>" : "")
                                 + "   </ul>"
                                 + "   <p>" + L("lcl_tabs_nosave_edit") + "</p>"
                                 + "</div>");
                    $dia.appendTo("body")
                        .dialog({
                            modal: true,
                            title: L("lcl_current_tab"),
                            zIndex: 10000,
                            autoOpen: true,
                            width: "auto",
                            resizable: false,
                            buttons: [{
                                text: L("lcl_submit"),
                                icon: "fal fa-fw fa-check",
                                class: "alertOk",
                                click: function() {
                                    if ($("#pin" + ts + "").val().trim() == "") {
                                        $("#pin" + ts + "").addClass("missing");
                                        FcltMgr.alert(L("lcl_shared_validator_missing"));
                                    } else {
                                        let tabTitle  = $("#pin" + ts).val();
                                        let taboption = $("input[name=saveAs" + ts + "]:checked").val();
                                        let saveRel   = $("#save_rel" + ts).length && $("#save_rel" + ts).prop("checked") ? 1 : 0;
                                        FcltMgr._pageManager.fnDoSave(taboption, { "$tab": $tab, "ttl": tabTitle, "saveRel": saveRel });
                                        $(this).dialog("close");
                                    }
                                }
                            }, {
                                text: L("lcl_cancel"),
                                icon: "fal fa-fw fa-undo",
                                class: "alertCancel",
                                click: function() {
                                    $(this).dialog("close");
                                }
                            }],
                            close: function(event, ui) {
                                $(this).remove();
                            },
                            create: FcltMgr.fnCreateCloseButton($dia)
                        }
                    );
                }
            } else {
                FcltMgr.alert(L("lcl_tabs_nosave"));
            }
        },
        renewTab: function(selector) {
            var $main = $("#maintabs").find("ul");
            var $li_a = $main.find("[aria-labelledby='"+selector+"']"); // gekozen tab

            // Is tab pinned
            var tab = $li_a.find('span').eq(1).attr("data-menu-key") || -1;
            if(tab > 0) {
                // Get tabs in list that have a data-menu-key attribute
                var filteredListItems = $main.find('li').filter(function() {
                    return $(this).find('span').eq(1).is('[data-menu-key]');
                }).last();

                // Move the current tab to the last position (only for tabs that have a data-menu-key attribute)
                if (filteredListItems.length > 0)
                {
                    var previousMenuKey = filteredListItems.find('span').eq(1).attr("data-menu-key");
                    var menuKey = $li_a.find('span').eq(1).attr("data-menu-key");

                    // Only update if more than one pinned tab
                    if(previousMenuKey !== menuKey) {
                        var data = {
                            previous_menu_key: previousMenuKey,
                            menu_key: menuKey,
                            next_menu_key: -1,
                            mgroep: 5
                        };

                        protectRequest.dataToken(data);
                        $.post("fac_sort_menu_save.asp",
                        data,
                        FcltCallback,
                        "json");
                    }
                }
            }

            $main.append($li_a);                                        // Gekozen tab aan het einde toevoegen...
            $li_a.removeAttr("inmore");                                 // ...en weer zichtbaar maken. (show() nu via default.csx)
            $("#"+selector).click();                                    // Activeren van iframe bij tab.
        },

        init: function(tabs_id) {
            this._tabshost = tabs_id;
            this._$tabshost = $("#" + tabs_id);

            var $tabs = this._$tabshost.tabs({
                beforeActivate: function(event, ui) {
                    // Activeer de delay-loaded tabjes
                    var newP = ui.newPanel; // zal een iframe zijn
                    if (newP.length &&
                        newP[0].getAttribute('src').match(/empty\.html$/) &&
                        newP[0].getAttribute('reusesrc')) {
                        newP[0].setAttribute('src', newP[0].getAttribute('reusesrc'));
                    }
                    var ofrm = ui.oldPanel;
                    if (ofrm.length) {
                        var frm = window.frames[ofrm[0].id];
                        if (FcltMgr._frmaccessible(frm)) {
                            frm.wasScroll = $(frm).scrollTop(); // IE11/Win8 onthoudt dit erg slecht
                            for (var i = 0; i in frm; i++) // Ook de subframes
                                if (FcltMgr._frmaccessible(frm[i]) &&
                                    ($(frm[i].frameElement.parentElement).attr("aria-hidden") === "false" ||  // Dit is de div die de tab host
                                     $(frm[i].frameElement.parentElement).attr("aria-hidden") === undefined)) // false voor zichtbare frames, undefined voor notitieframe
                                    frm[i].wasScroll = $(frm[i]).scrollTop();
                        }
                    }
                },
                activate: function(event, ui) {
                    var otab = ui.oldTab;
                    $(".last-active").removeClass("last-active");
                    if (otab.length) { /* Gebeurd niet altijd automatisch, dus dan doen we het wel zelf */
                        $(otab).removeClass("ui-state-focus");
                        $(otab).addClass("last-active");
                    }
                    var ofrm = ui.oldPanel;
                    if (ofrm.length) {
                        var frm = window.frames[ofrm[0].id];
                        if (FcltMgr._frmaccessible(frm) && frm.fcltontabhide) {
                            frm.fcltontabhide();
                        }
                    }
                    var ifrm = ui.newPanel;
                    if (ifrm.length) {
                        var frm = window.frames[ifrm[0].id];
                        if (FcltMgr._frmaccessible(frm)) {
                            $(frm).scrollTop(frm.wasScroll);
                            for (var i = 0; i in frm; i++) { // Ook de subframes
                                if (FcltMgr._frmaccessible(frm[i])) {
                                    if (frm[i].frameElement && $(frm[i].frameElement.parentElement).hasClass("sidebar")) { // Notitieframe?
                                        frm[i].$("textarea#note").trigger("blur"); // Zorgt voor de autogrow
                                    }
                                    if (frm[i].frameElement && frm[i].frameElement.dataset.refreshOnActivate == 1) {
                                        frm[i].location.reload();
                                    } else {
                                        $(frm[i]).scrollTop(frm[i].wasScroll);
                                    }
                                }
                            }
                            var isHot = FcltMgr._pageManager._panelHot(ui.newTab);
                            if (frm.fcltontabshow) {
                                frm.fcltontabshow(isHot);
                            }
                            // Als door 'autoopen' veel tabjes tegelijk zijn geladen
                            // dan is de iframeLoaded niet goed afgegaan. Doe dat alsnog.
                            // AARW#37918 2016.3 speelt dit nog steeds met Edge 38.14393.0.0
                            //  bij veel tab-wisselen
                            $("iframe", frm.document).each(function() {
                                if ($(this).height() == 0)
                                    FcltMgr.iframeLoaded(this);
                                var infrm = frm.frames[this.id];
                                if ($(this)[0].id === "widget_page_frame" && FcltMgr._frmaccessible(infrm) && typeof infrm.resizeOnActivate == "function")
                                    infrm.resizeOnActivate();
                                // Dialogen hadden de neiging te verdwijnen.
                                // Zo (b)lijken ze terug te halen
                                if ($(this).hasClass("fcltmodal")) {
                                    frm.$(this).dialog('widget').show();
                                    frm.FcltMgr._resizeModal(this);
                                }
                            })
                        }
                        FcltMgr._pageManager._timedResizeActiveTab();
                    }
                }
            });

            // // Save sorted dropdown list menu tabs.
            // // Notes:
            // //   1. Decided to not implement this functionality now. Uncomment if requested later.
            // //   2. Does not update 'menu tabs' in GUI without a FcltCallbackRefresh.
            // //       - Might be a hassle to fix because .morelist (contains only items in the dropdown) is a subset list of .ui-tabs-nav (which contains all items).
            // $tabs.find(".morelist").sortable({
            //     items: "div:not([data-menu-key='undefined'])",
            //     axis: "y",
            //     distance: 10,
            //     update: function(event, ui) {
            //         var previousMenuKey = parseInt(ui.item.prev().attr('data-menu-key'), 10) ||  -1;
            //         var menuKey = parseInt(ui.item.attr('data-menu-key'), 10) || -1;
            //         var nextMenuKey = parseInt(ui.item.next().attr('data-menu-key'), 10) || -1;

            //         // Tabs at the left are being 'pushed' down when screen gets smaller.
            //         var data = {
            //             previous_menu_key: nextMenuKey, // list is reversed. New items are being pushed on top (for visual purposes).
            //             menu_key: menuKey,
            //             next_menu_key: previousMenuKey,
            //             mgroep: 5
            //         };

            //         protectRequest.dataToken(data);
            //         $.post("fac_sort_menu_save.asp",
            //         data,
            //         FcltCallback,
            //         "json");
            //     }
            // });

            $tabs.find(".ui-tabs-nav").sortable({
                // Eerste tab ('mijn Facilitor') niet verplaatsen
                items: "li:not([openerid=''])",
                axis: "x",
                distance: 25,
                update: function(event, ui) {
                    var previousMenuKey = parseInt(ui.item.prevAll("li.pinned").first().find("span[data-menu-key]")?.data("menu-key"), 10) || -1;
                    var menuKey = parseInt(ui.item.find("span[data-menu-key]")?.data("menu-key"), 10) || -1;
                    var nextMenuKey = parseInt(ui.item.nextAll("li.pinned").first().find("span[data-menu-key]")?.data("menu-key"), 10) || -1;

                    // If both -1, no need to update
                    if(menuKey > 0 && (previousMenuKey > 0 || nextMenuKey > 0)) {
                        var data = {
                            previous_menu_key: previousMenuKey,
                            menu_key: menuKey,
                            next_menu_key: nextMenuKey,
                            mgroep: 5
                        };

                        protectRequest.dataToken(data);
                        $.post("fac_sort_menu_save.asp",
                        data,
                        FcltCallback,
                        "json");
                    }
                }
            });

            this._$tabshost.on("resize", function() {
                FcltMgr._pageManager._timedResizeActiveTab();
            });
        },

        // Internal to tabbedManager
        _tabshost: null,
        _$tabshost: null,

        // Open een nieuwe tab 'url' in een iframe en 'Titel' als label
        _openTab: function(url, titel, params) {
            params = params || {};
            // Vanuit welk scherm is dit scherm geopend.
            var openerId = '';
            var xx = FcltMgr._pageManager._$tabshost.find(".ui-tabs-nav li.ui-state-active").find("a")[0];

            if (xx)
                openerId = xx.id;

            if (params.reuse) // try to find previous tab
            {
                var found = false;
                var thePanel;
                var nohmac = url;
                var reuse_data = {};
                if (nohmac.indexOf("&hmac=") > 0)
                    nohmac = nohmac.substr(0, url.indexOf("&hmac="))
                // Plaats van de tab opzoeken.
                this._$tabshost.tabs("refresh");
                $('a', this._$tabshost).each(function(i, this_tab)
                {
                    var href = $(this_tab).attr("href");
                    if (href.indexOf("#" > 0))
                        href = "#" + href.split("#")[1];
                    var panel = $(href);
                    var nohmac2 = panel[0].getAttribute("reusesrc");
                    if (nohmac2.indexOf("&hmac=") > 0)
                        nohmac2 = nohmac2.substr(0, nohmac2.indexOf("&hmac="))
                    if (panel.length && nohmac == nohmac2)
                    {
                        found = true;
                        thePanel = panel;
                        reuse_data.tab_index = i;
                        reuse_data.href_nr = href.substr(3); // alleen het volgnummer
                        reuse_data.inmore = (this_tab.parentNode.attributes.inmore ? true: false);
                    }
                });
                if (found)
                {
                    if (!FcltMgr._pageManager._panelHot(reuse_data.href_nr)) // niet als hot
                    {
                        if (!reuse_data.inmore)
                        {
                            // Activeer zichtbare tab.
                            this._$tabshost.tabs("option", "active", reuse_data.tab_index);
                        }
                        else
                        {
                            // Een scherm gevonden die in de dropdown-tab staat.
                            thePanel[0].src = url; // Wel de nieuwe url met nieuwste hmac gebruiken
                            var uiid = $(thePanel).attr("aria-labelledby");
                            FcltMgr.tabbedManager.renewTab(uiid);
                        }

                    }
                    return;
                }
            }

            this._counter++;
            var theId = "ID" + this._counter;
            titel = titel || L("lcl_shared_loading");
            var org_titel = titel
            if (titel.length > 25)
                titel = titel.substr(0, 20) + safe._htmlDecode("&hellip;"); // Wordt later weer encoded, maar de minifier pakt het anders niet goed op

            var tpl = "<li " + (params.showpin ? "class='pinned'" : "") + " openerId='" + openerId + "'><a href='#" + theId + "'><span ttl='" + safe.htmlattr(org_titel) + "'>" + safe.html(titel) + "</span>" +
                (params.showpin ? "<span title='" + L("lcl_current_tab_unsave") + "' class='tabpinned' data-menu-key='" + (params.menu_key || -1) + "' onclick='FcltMgr.saveTab(this)'>" + I("fa-thumbtack", { fastyle: "fas", fa_fw: false }) + "</span>" : "") +
                (params.noclose ? "" : "<span class='tabclose' onclick='FcltMgr._pageManager._closeTab(this)'>" + I("fa-times", { fastyle: "fas", fa_fw: false }) + "</span>") +
                "</a></li>";

            // In eerste instantie met registreren we alleen reusesrc
            // In de beforeActivate nemen we die (pas) over naar src
            var startSrc = url;
            if (params.delayload)
                var startSrc = "../../appl/shared/empty.html"; // Anders doet IE11 moeilijk 'permission denied'
            var panel = "<iframe frameborder='0'" +
                " id='" + theId + "' name='" + theId +
                "' src='" + startSrc.replace(/\'/g, '%27') +
                "' reusesrc='" + url.replace(/\'/g, '%27') +
                (params.is_extern ? "' is_extern='1" : "") +
                "' class='frametab no-select'></iframe>";
            this._$tabshost.find(".ui-tabs-nav").append(tpl);
            // Als mensen dubbelklikken in een lijst, dan selecteren ze de hele nieuwe pagina (en wordt die blauw), de 'no-select' class voorkomt (heel eventjes/wanneer nodig)
            this._$tabshost.addClass("no-select").append(panel).removeClass("no-select");
            this._$tabshost.tabs("refresh");
            if (!params.noactivate)
                this._$tabshost.tabs('option', 'active', -1); //  de laatste

            FcltMgr._pageManager._resize_forMaxTabs();
        },

        _resize_forMaxTabs: function()
        {
            // Afmetingen van een tabje ophalen.
            function __getTabWidth(p_tab, cache)
            {
                if (cache !== false && p_tab.attr("inmore"))
                    return parseFloat(p_tab.attr("inmore"));

                var w = parseFloat(p_tab[0].offsetWidth)
                      + parseFloat(p_tab.css("margin-left"))
                      + parseFloat(p_tab.css("margin-right"));
                return Math.ceil(w);
            }

            var t_ul = this._$tabshost.find("ul");
            var resize_after = false;
            // als de <ul> 2 regels hoog is, dan passen we met de dropdown-lijst de hoogte van de hele pagina aan en dan moet de actieve tab achteraf resized worden.
            if (t_ul.height() >= t_ul.find("li").eq(0).height() * 2)
                resize_after = true;

            var maintabsAvailableWidth = t_ul.get(0).clientWidth;
            var moretabWidth = $("#moretab").attr("inmore") ? __getTabWidth($("#moretab"), false) : 0;

            // fixed_width is de breedte die nooit verborgen wordt
            var fixed_width = parseFloat(t_ul.css("padding-left")) + parseFloat(t_ul.css("padding-right"));

            // Verwijder dropdownmenu van het scherm.
            $("#moretab").removeClass("moretab-active");

            // Tabjes op volgorde, met .last-active als 1-na laatste, en zonder de 'niet verwijderbare'-tabs en zonder de actieve tab
            var $tabslist = $("li:not(.ui-tabs-active, .last-active)", t_ul).filter(function () { return $(this).attr("openerid") != ""; });
            $tabslist = $($($tabslist.get().concat($("li.last-active", t_ul).get())).get().reverse());

            // Eerst de ruimte voor niet verwijderbare knoppen.
            $("li", t_ul).filter("[openerid=''], .ui-tabs-active").each(function()
            {
                fixed_width += __getTabWidth($(this));
            });

            // Van achter naar voren knoppen toevoegen totdat het er niet meer op past.
            // Eerst bepalen of we uberhaupt een dropdown lijst krijgen, want dan moeten we de 'more'-tab breedte bij de fixed_with optellen
            var has_dropdown = false;
            var som_tabs = fixed_width;
            $tabslist.each(function()
            {
                som_tabs += __getTabWidth($(this));
                if (som_tabs > maintabsAvailableWidth)
                {
                    has_dropdown = true;
                    $("#moretab").show().find("span").eq(0).text(L("lcl_more_tabs") + " #"); // Alvast een dummy tekst invullen, voor een preciezere breedte-berekening
                    moretabWidth = __getTabWidth($("#moretab"), false);
                    fixed_width += moretabWidth;
                    return false; // = break uit each()-loop;
                }
            });

            // Dan volgens hetzelfde algoritme daadwerkelijk de tabs wel of niet in de dropdownlijst zetten
            // last-active is het vorige actieve tab, deze wordt pas als allerlaatst weggemoffeld in de dropdown lijst
            var v_hasmore = 0;
            if (has_dropdown)
            {
                som_tabs = fixed_width;
                $tabslist.each(function()
                {
                    var l_tab = __getTabWidth($(this));
                    som_tabs += l_tab;
                    if (som_tabs > maintabsAvailableWidth)
                    {
                        $(this).attr("inmore", l_tab);
                        v_hasmore++;
                    }
                    else
                        $(this).removeAttr("inmore");
                });
            }
            else // if (!has_dropdown) Skip dan de breedte-berekeningen
                $tabslist.removeAttr("inmore");

            // Als er geen tabbladen meer onzichtbaar zijn mag de tab "Nog x V" vooraan ook onzichtbaar worden.
            if (!has_dropdown)
                $("#moretab").attr("inmore", moretabWidth).hide();

            // Het aantal tabs met class=inmore vermelden op de #moretab.
            $("#moretab").find("span").eq(0).text(L("lcl_more_tabs") + " " + v_hasmore);

            if (resize_after)
                this._resizeActiveTab();
        },

        _panelHot: function(tab) // tab mag een 'li' zijn of een index
            {
                $("#moretab").removeClass("moretab-active"); // Verwijder dropdownmenu van het scherm.

                if (typeof tab == "number")
                    var tab = this._$tabshost.find(".ui-tabs-nav li")[tab];
                if (!tab) // Tijdelijke patch AAIT#29057
                    return false;
                var panelId = $(tab).attr("aria-controls");
                var frm0 = window.frames[panelId];
                if (!FcltMgr._frmaccessible(frm0)) // neem aan: access denied kennisbank/externe pdf
                    return false;
                var frm = frm0.window;
                if (!FcltMgr._frmaccessible(frm)) // neem aan: access denied by punchout
                    return false;

                return (frm.FcltMgr &&
                    frm.FcltMgr._editactive)
            },

        // Sluit een tabblad met het kruisje.
        _closeTab: function(zelf, params) {
            var li = $(zelf).closest('li')[0];
            // Opzoeken voordat hij verwijderd is
            var idx = $('li', this._$tabshost).index(li);
            var isCurrent = (idx == this._$tabshost.tabs('option', 'active'));
            var hotwnd = FcltMgr._pageManager._panelHot(li);
            params = params || {};

            var that = this;
            FcltMgr.confirm(L("lcl_shared_closeandignore"), { autoconfirm: params.panelModalCool || !hotwnd }, function() {
                if (hotwnd && hotwnd.onCloseHot)
                    hotwnd.onCloseHot();

                var openerId = $(li).attr("openerId");
                var panelId = $(li).remove().attr("aria-controls");
                FcltMgr._removeIframe($("#" + panelId));

                // Probeer de 'opener' tab te activeren als je zelf actief bent
                if (openerId && isCurrent) {
                    var prevLi = $("a#" + openerId, that._$tabshost);
                    if (prevLi.length) {
                        var idx = $('a', that._$tabshost).index(prevLi[0]);
                        if (idx > -1)
                            that._$tabshost.tabs('option', 'active', idx);
                    }
                }
                that._$tabshost.tabs("refresh");
                FcltMgr._pageManager._resize_forMaxTabs();
            });

            return;
        },

        _timerID: null,

        _timedResizeActiveTab: function(delay) {
            if (this._timerID)
                clearTimeout(this._timerID);
            this._timerID = setTimeout(this._resizeActiveTab, (typeof delay == "number" ? delay : 100));
        },

        _resizeTab: function(ifrm)
        {
            $(ifrm).css("height", $(window).height() - $(ifrm).offset().top);

            // Ook alle subframes
            $(ifrm).contents().find("iframe:visible").each(function() // Subframes
            {
                var subframe_window = this.contentWindow;
                if (subframe_window && "FcltMgr" in subframe_window)
                    subframe_window.FcltMgr.resized();
            });

            // Workaround om te zorgen dat bij Chrome de scrollbars terugkomen
            // http://stackoverflow.com/questions/41964817/why-do-scrollbars-disappear-in-tabbed-iframes-in-chrome
            $(ifrm).queue(function(next) {
                    $(this).css('zoom', '0.99'); // slight shrink to 99%, to 'jog' the screen
                    next();
                })
                .delay(50)
                .queue(function(next) {
                    $(this).css('zoom', '1'); // Back to 100% after short delay
                    next();
                });
        },

        _resizeActiveTab: function() {
            var ifrm = FcltMgr._pageManager._activeTab();
            // PF: constateerde dat via een tragere lijn (vpn) ifrm er niet op tijd kan zijn
            if (ifrm)
                FcltMgr._pageManager._resizeTab(ifrm);
        },
        // levert het IFRAME object van de actieve tab op.
        _activeTab: function() {
            // KR: Deze is niet meer afhankelijk van de <li> volgorde:
            // var active = FcltMgr._pageManager._$tabshost.tabs('option', 'active');
            // var tab = FcltMgr._pageManager._$tabshost.find(".ui-tabs-nav li")[active];
            var tab = FcltMgr._pageManager._$tabshost.find(".ui-tabs-nav li.ui-state-active")
            var panelId = $(tab).attr("aria-controls");
            var ifrm = $("#" + panelId)[0];
            return ifrm;
        }
    },
    /*** EINDE TABBED MANAGER ***/

    _caller: null,

    _doRemove: function(frmid) {
        var $frm = $("#" + frmid);
        $frm.attr('onload', null); // hoeft niet voor empty en bovendien pakte hij wel eens het verkeerde frame
        $frm.attr('src', rooturl + "/appl/shared/empty.html"); // lijkt geheugenlek IE9 te voorkomen
        $frm.remove();
        /* RWSN#26802: crasht IE8 uiteindelijk te veel
                                     UWVA#32572: Toch weer teruggezet, als het probleem van RWSN#26802 zich nog steeds voordoet,
                                       (ik kan het momenteel niet reproduceren) dan moet dat anders worden opgelost.
                                       Dit weglaten zorgt voor meer problemen.
                                     UWVA#39257: IE8 wordt uitgefaseerd, dus laten staan
                                       */
    },

    // De timeout vooromt vreemde IE9 problemen waarbij iface.button.enable klaagt dat iface niet bestaat.
    // of Date dan wel String is undefined. Dit gebeurt als je in een (Ajax) callback een closeDetail doet
    // (bijvoorbeeld opslaan res_edit_multi.asp). Na de Ajax onComplete wil jQuery nog wat opruimwerk doen
    // maar plotseling is de frame met code verdwenen.
    // Verder: de nieuwe Id om te voorkomen dat de verkeerde *volgende* dialoog gesloten worden! (opdr_close==>mld_close)
    _removeIframe: function($frm) {
        // call unloadHandler to handle unfinished work
        if (FcltMgr._frmaccessible($frm[0]) && typeof $frm[0].contentWindow.unloadHandler == "function")
        {
            $frm[0].contentWindow.unloadHandler();
        }
        // Pas op: geen '.' in de newid want dan vindt _doRemove hem niet meer terug!
        var newid = "DELETE" + (new Date).getTime() + Math.floor(10000000 * Math.random());
        $frm.css("display", "none");
        $frm.attr("id", newid).attr("name", newid);
        setTimeout("FcltMgr._doRemove('" + newid + "')", 1100);
    },

    // De allerhoogste te vinden manager
    topmanager: function() {
        try {
            if (parent && parent.FcltMgr && parent.FcltMgr != this)
                return parent.FcltMgr.topmanager();
        } catch (e) { /* FACILITOR in een frame */ };
        return this;
    },

    // Bij de tabbedManaher de hoogste FcltMgr nog net binnen het detail scherm
    // Bij de defaultManager gewoon de hoogste
    detailManager: function() {
        if (FcltMgr.IsTheManager)
            return FcltMgr;
        if (FcltMgr._pageManager.name != "defaultManager" && parent.FcltMgr.IsTheManager)
            return FcltMgr;
        else
            return parent.FcltMgr.detailManager();
    },

    HostTabs: function(tabs_id) // tabs_id moet id van jQuery tabs object zijn
        {
            this._pageManager = this.tabbedManager;
            this._pageManager.init(tabs_id);
        },

    // Open een detailscherm (bijvoorbeeld vanuit een overzicht scherm)
    // Als dit vanuit een tab gebeurt dan wordt een nieuwe tab geopend,
    // anders een nieuw window
    // titel parameter mag weggelaten. Voorkeur is params.title zetten!
    openDetail: function(url, titel, params) {
        // Als titel een object is is het eigenlijk params
        if (typeof titel === "object") {
            params = titel;
            titel = params.titel;
        }
        params = params || {};
        params.orgurl = url;
        titel = titel || "";
        // Vanuit het menu zitten er nog wel eens \n of spaties omheen. Opruimen
        titel = titel.replace(/\n/ig, " ");
        titel = titel.replace(/^\s+|\s+$/g, "");

        if (url[0] == "*")
            return FcltMgr.defaultManager.openDetail(url.substring(1), titel, params);

        if (url.slice(0, 4) != "http") // intern
        {
            if (url.slice(0, 1) == '/')
                url = url.substr(1);
            if ("rooturl" in window)
                url = rooturl + "/" + url;
            else
                url = "../../" + url;
        }

        FcltMgr._pageManager.openDetail(url, titel, params);
    },

    // Als via kruisje gesloten. Geen callback's en dergelijke
    _closeParams: null,
    setCloseParams: function(params) // Handig in combi met alwaysCallback
        {
            parent.FcltMgr._closeParams = params;
        },
    dialogClose: function(result) {
        $("div.suggestautocompleteContainer").hide();
        var orgParams = FcltMgr._modalParams[FcltMgr._modalCount];
        if (orgParams.alwaysCallback) {
            var params = FcltMgr._closeParams || orgParams.alwaysCallbackParams || {};
            params.cancel = true;
            FcltMgr._modalCallback[FcltMgr._modalCount](params, orgParams);
        }

        var $frm = $("iframe#fcltmodal" + FcltMgr._modalCount);
        FcltMgr._removeIframe($frm);
        FcltMgr._modalCount--;
    },

    // Open een Modaal popup detailscherm (bijvoorbeeld vanuit een overzicht scherm)
    // Voor schermen waarvan openDetail te veel eer is.
    // params ondersteunt:
    //  height, width in px;
    //  callback(data) aangeroepen door client dialoog's FcltMgr.CloseDetail(window, data)
    //  params die worden doorgegeven aan de dialoog.
    //      Die kan ze met FcltMgr.dialogArguments() ophalen (let op: clientside!)
    _modalCount: 0,
    _modalCallback: [],
    _modalParams: [],

    _resizeModal: function(frm, params)
    {
        params = params || {};
        try {
            $(frm).contents(); // Hebben we jQuery en hebben we frm ?
        } catch (e) {
            // FcltMgr.alert("Bad resizing");
            return; // IE9 soms als dialoog net gesloten is
        }

        if (typeof frm.contentWindow.spinnerTimeout != "undefined") {
            frm.contentWindow.clearTimeout(frm.contentWindow.overlayTimeout);
            frm.contentWindow.clearTimeout(frm.contentWindow.spinnerTimeout);
        }

        var thisModalCount;
        if ($(frm).attr("name") && $(frm).attr("name").length > 9)
            thisModalCount = $(frm).attr("name").slice(9); // _modalCount op moment van aanmaken, zodat we de juiste modal resizen.
        else
            thisModalCount = FcltMgr._modalCount; // backup (oude manier, overbodig ?)

        // Hoeveel extra moeten we rekenen voor jQuery dialog (title, resize-bars, border)
        var $dialog = $(frm).closest("div.ui-dialog");
        var dialogborderHeight = Math.max(0, $dialog.outerHeight() - $(frm).height());

        var tp = frm.contentDocument.documentElement.scrollTop;

        if (!params.noMasonry && typeof frm.contentWindow.masonryRebuild == "function" && typeof frm.contentWindow.$masonry != "undefined")
        {
            frm.style.height = "10000px"; // Zo heeft masonry de ruimte om eventuele nieuwe (flex) velden opnieuw in te delen
            frm.contentWindow.masonryRebuild();
        }

        frm.style.height = 0;

        /* Dit is de window waarbinnen ons modal zit */
        var $hostWindow = $(frm.contentWindow.parent);

        // Dit wordt de nieuwe iframe hoogte van de modal
        var frmDims = FcltMgr._myDimensions(frm);
        var newHeight = Math.min(frmDims.height, $hostWindow.height() - 100);
        $('iframe#fcltmodal' + thisModalCount).dialog('option', 'height', newHeight + dialogborderHeight);

        var extraWidth = 0;
        frmDims = FcltMgr._myDimensions(frm);
        if (frm.clientHeight < frmDims.height)
        { // scrollbar-y
            // floor en ceil om afrondfouten te voorkomen (dan maar een halve pixel meer)
            var modalWidth = Math.floor($(frm.contentWindow).width());
            var inclScrollbars = Math.ceil(parseFloat($(frm).css("width")));
            extraWidth = inclScrollbars - modalWidth; // = scrollbarWidth
        }

        var newWidth = Math.min(Math.ceil(frmDims.width) + extraWidth, $hostWindow.width() - 10);
        $('iframe#fcltmodal' + thisModalCount).dialog('option', 'width', newWidth);

        frmDims = FcltMgr._myDimensions(frm);
        var newHeight2 = Math.min(frmDims.height, $hostWindow.height() - 100);
        if (newHeight2 > newHeight) // Gebeurde in FireFox wel eens (Komt dit nog steeds voor? 02-2021)
            $('iframe#fcltmodal' + thisModalCount).dialog('option', 'height', newHeight2 + dialogborderHeight);

        frm.contentDocument.documentElement.scrollTop = tp;

        if (FcltMgr.isTouchscreen())
            $('iframe#fcltmodal' + thisModalCount).dialog('option', 'position', { my: "top", at: "top", of: window });
        else
            $('iframe#fcltmodal' + thisModalCount).dialog('option', 'position', { my: "center", at: "center", of: window });
    },

    // Opent een modal dialoog in de huidige 'detailManager'
    // titel parameter mag weggelaten. Voorkeur is params.title zetten!
    openModalDetail: function(url, titel, params) {
        if (url.substr(0, 1) == '*')
            return FcltMgr.openDetail(url.substr(1), titel, params);

        // Als titel een object is is het eigenlijk params
        if (typeof titel === "object") {
            params = titel;
            titel = params.titel;
        }
        var detailMgr = FcltMgr.detailManager();
        if (!detailMgr.window.$.fn.dialog)
            detailMgr = FcltMgr; // Hopen dat we hem zelf hebben

        if (!detailMgr.window.$.fn.dialog)
            FcltMgr.alert("Internal error: jQuery-ui mist mogelijk in " + window.location.href);

        detailMgr._openModalDetail(url, titel, params);
    },

    _openModalDetail: function(url, titel, params) {
        // Niet nog hoger zoeken
        this._modalCount++;
        params = params || {};
        this._modalCallback[FcltMgr._modalCount] = params.callback;
        this._modalParams[FcltMgr._modalCount] = params;
        params.modal = true;
        if (url.match(/queuemail\.asp/)) // Dan is de titel bedoeld voor *in* het frame
        {
            titel = L("lcl_shared_send_email");
        }
        params.title = titel;
        if (!("resizable" in params))
            params.resizable = true;

        if (params.noClose) {
            params.closeOnEscape = false;
            params.create = function(event, ui) {
                $(this).prev(".ui-dialog-titlebar").find(".ui-dialog-titlebar-close")
                                                   .addClass("fcltmodal" + FcltMgr._modalCount).hide(); }
        } else {
            params.create = function(event, ui) {
                var $fclt_icon_close = $("<span class='fclt-icon-close default-clickable-icon' title='" + L("lcl_close_window") + "'>" + I("fa-times fa-xl") + "</span>");
                $(this).prev(".ui-dialog-titlebar").append($fclt_icon_close)
                                                   .addClass("has-fclt-icon-close")
                                                   .find(".ui-dialog-titlebar-close")
                                                   .addClass("fcltmodal" + FcltMgr._modalCount).hide();
                $fclt_icon_close.on("click", function() { FcltMgr.closeDetail(window, { cancel: true, that: this }); });
            }
        }

        params.close = this.dialogClose; // Omdat we het vanuit een Iframe willen doen.
        if (!("width" in params))
            params.width = 500;

        if (FcltMgr.isTouchscreen()) {
            var css_scroll = "overflow:auto; -webkit-overflow-scrolling: touch;";
            params.position = { my: "top", at: "top", of: window };
        } else {
            var css_scroll = "overflow: visible;";
            params.position = { my: "center", at: "center", of: window };
        }
        var css_extra = (params.style ? " " + params.style : "");
        var html = "<iframe class='fcltmodal'" +
            " frameborder='0' onload='FcltMgr._resizeModal(this)'" +
            " id='fcltmodal" + FcltMgr._modalCount + "' name='fcltmodal" + FcltMgr._modalCount + "' modalcount='" + FcltMgr._modalCount + "' style='padding: 0px; " + css_scroll + css_extra + "'" +
            " src='" + url + "'>" +
            "</iframe>";

        var loader_html = "<style>"
                        + "    body {"
                        + "        display: flex;"
                        + "        justify-content: center;"
                        + "        align-items: center;"
                        + "    }"
                        + "    .loader.active {"
                        + "        width: 48px;"
                        + "        height: 48px;"
                        + "        border: 5px solid rgba(0, 0, 0, 0.1);"
                        + "        border-radius: 50%;"
                        + "        border-top-color: #1770f6;"
                        + "        animation: spin 2s linear infinite;"
                        + "    }"
                        + "    .busyoverlay {"
                        + "        position: fixed;"
                        + "        inset: 0;"
                        + "        background-color: rgba(0, 0, 0, .1);"
                        + "    }"
                        + "    @keyframes spin {"
                        + "        0% { transform: rotate(0deg); }"
                        + "        100% { transform: rotate(360deg); }"
                        + "    }"
                        + "</style>"
                        + "<script>"
                        + "  var overlayTimeout = window.setTimeout("
                        + "     _ => { document.querySelector('.loader')?.parentNode.classList?.add('busyoverlay'); }"
                        + " , 500);"
                        + "  var spinnerTimeout = window.setTimeout("
                        + "     _ => { document.querySelector('.loader')?.classList?.add('active'); }"
                        + " , 2000);"
                        + "</script>"
                        + "<div class='loader'></div>";
        $(html).dialog(params).dialog("open").contents().find("body").html(loader_html);
        if (params.modal_hinter) {
            $(".fcltmodal").append('<div id="hinter" style="display:none">' +
                '<div id="hinttext">' +
                I("fa-question-circle fa-2x") +
                L("lcl_hint_select_field") +
                '</div></div>'
            );
            $hint_span = $("<span class='hints default-clickable-icon' title='" + L("lcl_hint") + "'>" + I("fa-headset fa-xl") + I("fa-question fa-sm tinyhint") + "</span>");
            $(".fcltmodal").prev(".ui-dialog-titlebar")
                .append($hint_span);
            $hint_span.on("click", function(event) { FcltMgr._dialogModalHint = FcltMgr.enableModalHint(); });
        }
        // When a modal dialog starts from show mode, the tab is made hot.
        // When the dialog initiated the tab to be hot and the dialog closes, the tab is made cool again.
        if (!FcltMgr.detailManager()._editactive) {
            $("#fcltmodal" + FcltMgr._modalCount).on("dialogclose", function(event) {
                if (FcltMgr.detailManager()._editactive && FcltMgr._panelModalHot) {
                    FcltMgr._panelModalHot = false;
                    FcltMgr.stopEdit(window);
                }
            });
            FcltMgr._panelModalHot = true;
            FcltMgr.startEdit(window, params);
        }
    },

    enableModalHint: function() {
        if (FcltMgr._dialogModalHint == undefined) {
            var modal_hinter = $(".fcltmodal").find("#hinter");
            return $(modal_hinter).dialog({
                title: L("lcl_hint"),
                create: FcltMgr.fnCreateCloseButton(modal_hinter),
                position: { my: "right top", at: "right bottom", of: $(".ui-dialog-titlebar") }
            });
        } else {
            FcltMgr._dialogModalHint.dialog("open");
            return FcltMgr._dialogModalHint;
        }
    },

    setModalHint: function(ttl, hint) {
        if (FcltMgr._dialogModalHint != undefined) {
            FcltMgr._dialogModalHint.dialog("option", "title", L("lcl_hint_field_title").format(ttl));
            $(FcltMgr._dialogModalHint, "div#hinttext").text(hint);
        } else {
            // only if a _dialogModalHint handle is present in the parent window
            var $parent = $(window.parent);
            if ($parent.length > 0) {
                // copy the handle from the parent window that created the handle
                FcltMgr._dialogModalHint = $parent[0].FcltMgr._dialogModalHint;
                $("div span.ui-dialog-title", FcltMgr._dialogModalHint).html(L("lcl_hint_field_title").format(ttl));
            }
        }
    },

    fnCreateCloseButton: function(selector) {
        if (!selector || !$(selector).length)
            return;
        return function() {
            var $fclt_icon_close = $("<span class='fclt-icon-close default-clickable-icon' title='" + L("lcl_close_window") + "'>" + I("fa-times fa-xl") + "</span>");
            $(this).prev(".ui-dialog-titlebar").append($fclt_icon_close)
                                               .addClass("has-fclt-icon-close")
                                               .find(".ui-dialog-titlebar-close")
                                               .addClass("fcltmodal" + FcltMgr._modalCount).hide();
            $fclt_icon_close.on("click", function() { $(selector).dialog("close"); });
        }
    },

    dialogArguments: function() {
        return parent.FcltMgr._modalParams[parent.FcltMgr._modalCount];
    },

    // Sluit een detailscherm als de gebruiker klaar is met bewerken bijvoorbeeld,
    // Als het een tabje is wordt het tabje gesloten
    // Als het een window is wordt geprobeerd het window te sluiten.
    closeDetail: function(thiswindow, params) {
        thiswindow = thiswindow || window;
        params = params || {};
        if (params.warning) {
            FcltMgr.alert(params.warning);
            params.warning = null;
        }

        if (this._modalCount) {
            // first close modal hint, if active
            if (FcltMgr._dialogModalHint != undefined) {
                FcltMgr._dialogModalHint.dialog("close");
                FcltMgr._dialogModalHint.remove();
                FcltMgr._dialogModalHint = null;
            }
            var thisModalCount = $("[id^=fcltmodal]:last").attr("id").slice(9); // _modalCount op moment van sluiten, zodat we de parameters van de juiste modal gebruiken.
            // trigger close of current window
            $(".ui-dialog-titlebar-close.fcltmodal" + thisModalCount).trigger("click"); // doet indirect dialogClose
            var orgParams = this._modalParams[thisModalCount];
            if (params.cancel && !orgParams.alwaysCallback)
                return;
            if (this._modalCallback[thisModalCount])
                this._modalCallback[thisModalCount](params, orgParams);
            //this._modalCount--;
            return;
        }
        // Minstens parent, we kunnen het nooit zelf zijn.
        //if (parent.FcltMgr.detailManager()._modalCount)
        //    return parent.FcltMgr.detailManager().closeDetail(thiswindow, params);

        if (FcltMgr.detailManager()._editactive == thiswindow) {
            if (FcltMgr._frmaccessible(parent) && FcltMgr._safe_parent() !== thiswindow && parent.$)
            {
                parent.$("iframe").not("iframe[name=" + window.name + "]").not(".frametab").parent(".fcltframe").removeClass("hot-overlay");
                parent.$("ul.subtabs .subtab-icon.hot").removeClass("hot");
            }
            FcltMgr.detailManager()._editactive = false;
            FcltMgr.setTitle(null, { hot: false });
        }

        if (thiswindow.name) {
            if (!FcltMgr._frmaccessible(parent)) {
                // We zijn waarschijnlijk een share-and-meet frame
                window.location.href = "../shared/empty.asp?msg=lcl_res_extern_close";
                return;
            }
            var ifrm = parent.document.getElementById(thiswindow.name);
            if (ifrm && ifrm.getAttribute("FcltClose")) {
                var fnName = ifrm.getAttribute("FcltClose");
                if (fnName in parent) {
                    return parent[fnName].call(null, params);
                }
            }
            if (ifrm && ifrm.getAttribute("srcparent")) { // Na verwijderen van child een reload van de parent
                ifrm.setAttribute("src", ifrm.getAttribute("srcparent").replace(/&collapsed=1/gi, ""));
                return;
            }
            if (ifrm && ifrm.getAttribute("orgsrc")) { // Na een reload nooit collapsed
                ifrm.setAttribute("src", ifrm.getAttribute("orgsrc").replace(/&collapsed=1/gi, ""));
                return;
            }
        }
        if (FcltMgr.IsTheManager)
            return FcltMgr._pageManager.closeDetail(window, params);
        else
            return parent.FcltMgr.closeDetail(parent.window, params);
    },

    closeAll: function(zelf) {
        FcltMgr._pageManager.closeAll(zelf);
    },

    saveTab: function(zelf) {
        FcltMgr._pageManager.saveTab(zelf);
    },

    saveFavMenu: function(zelf) {
        const $fav_pin = $(zelf).closest(".menupinned");
        const menu_key = $fav_pin.data("menu-key") || -1;
        FcltMgr.confirm(L("lcl_current_fav_unsave"), function() {
            FcltMgr._pageManager.fnDoSave("M", { "menu_key": menu_key });
            $(zelf).closest("li").remove();
        });
    },

    setPageHeader: function(text, params) {
        var params = params || {};
        params.windowname = FcltMgr.detailManager().window.name;
        FcltMgr.detailManager()._pageManager.setPageHeader(text, params);
    },

    setTitle: function(title, params) {
        params = params || {};
        params.windowname = FcltMgr.detailManager().window.name;
        FcltMgr.detailManager()._pageManager.setTitle(title, params);
    },

    getTitle: function() {
        var params = {};
        params.windowname = FcltMgr.detailManager().window.name;
        return FcltMgr.detailManager()._pageManager.getTitle(params);
    },

    setFrameTitle: function(title, params)
    {
        params = params || {};
        var hasParent = parent && FcltMgr._safe_parent() !== window;

        if (hasParent)
            var $tab = parent.$('#frametabs').find("a[href='#frm_" + window.name + "']");

        if (!title || (window.frameElement && $(window.frameElement.parentElement).hasClass("sidebar")))
            return;

        if (!hasParent || !$tab.length) // Zet hem dan in de frameheader
        {
            var $title_elem = $("<span class='iframer-title'>"+title+"</span>");
            var $fcltframeheader = $(".fcltframeheader");
            if ("id" in params)
                $fcltframeheader = $fcltframeheader.filter("#fcltframeheader" + params.id);
            if ($fcltframeheader.find(".iframer-title").length)
                $fcltframeheader.find(".iframer-title").replaceWith($title_elem);
            else
                $fcltframeheader.prepend($title_elem);
        }

        // Backup indien de LCL nog niet bestaat (zou niet moeten voorkomen)
        else if ($tab.text() === "" || $tab.text().split("??").length > 2) /* Leeg of unknown lcl */
        {
            if ($tab.find(".subtab-icon").length) /* Ik heb al een icon */
            {
                var $contents = $tab.contents();
                if ($contents.length >= 2 && $contents[1].nodeType === 3) /* Tekst volgend op icon */
                    $contents[1].nodeValue = title;
            }
            else
                $tab.html("<span class='subtab-icon'>" + I(params.icon) + "</span>" + title);
        }
    },

    showTab: function(doshow, params) {
        params = params || {};
        params.windowname = FcltMgr.detailManager().window.name;
        FcltMgr.detailManager()._pageManager.showTab(doshow, params);
    },

    // Zet het extra-titelblok in een IFRAMERHEADER
    setHeaderExtraTitle: function(html, params) {
        $(".fcltframeheader #iframerextratitle").show().html(html);
    },

    setSubtabLines: function(number_of_lines, params) {
        params = params || {};
        if (parent && FcltMgr._safe_parent() !== window) {
            var $subtab = $(parent.document).find("ul.subtabs li[aria-controls=frm_" + window.name + "] a");
            var start_lines = parseInt(params.add ? $subtab.attr("data-lines") || 0 : 0, 10);
            var new_lines   = start_lines + parseInt(number_of_lines || 0, 10);
            $subtab.attr("data-lines", new_lines);
            $subtab.attr("data-more", (start_lines === 0 && params.hasMore));
            FcltMgr.setSubtabExtra(new_lines?String(new_lines):"", true);
        }
    },
    setSubtabExtra: function(extra, asbadge) {
        if (parent && FcltMgr._safe_parent() !== window) {
            var $subtab = $(parent.document).find("ul.subtabs li[aria-controls=frm_" + window.name + "] a");
            $subtab.find(".subtabextra").toggleClass("subtab-badge", !!asbadge).html(extra);
        }
    },

    _frmaccessible: function(frm) {
        try {
            if (!frm)
                return false;
            if (!frm.window)
                return false;
            frm.FcltMgr; // Probeer frm te benaderen. Bewust zonder de haakjes
            return true;
        } catch (e) { // neem aan: access denied by punchout of share-and-meet extern tabje
            return false;
        }
    },

    // Aan te roepen door een childwindow als hijzelf resized is (bijvoorbeeld ++bezoekerregels)
    // params: largerOnly (internal)
    _safe_parent: function() {
        try {
            parent.document;
            return parent;
        } catch (e) {}
        return window;
    },

    resized: function(thiswindow, params) {
        params = params || {};
        thiswindow = thiswindow || window;
        if (parent && FcltMgr._safe_parent() !== window) {
            var helpFrame = parent.document.getElementById(thiswindow.name);
            if (helpFrame && typeof jQuery !== "undefined") {
                if ($(helpFrame).hasClass("fcltmodal"))
                {
                    parent.FcltMgr._resizeModal(helpFrame, { noMasonry: (params.noMasonry || false) });
                    if (!params.noMasonry && typeof updateTableHeaders === "function") { updateTableHeaders(); }
                }
                else if ($(helpFrame).hasClass("frametab"))
                {   // Niets doen als via jumpto een list wordt aangeroepen (bv. prs_phonebook_list.asp).
                    // Anders verdwijnt door resizen de scrollbar.
                    var doMasonryRebuild = (!params.noMasonry && typeof helpFrame.contentWindow.masonryRebuild == "function" && typeof helpFrame.contentWindow.$masonry != "undefined");
                    if (doMasonryRebuild) // Deze nog wel
                    {
                        masonryRebuild();
                        if (typeof updateTableHeaders === "function") { updateTableHeaders(); }
                    }
                }
                else if ($(helpFrame).is(":visible"))
                {
                    var doMasonryRebuild = (!params.noMasonry && typeof helpFrame.contentWindow.masonryRebuild == "function" && typeof helpFrame.contentWindow.$masonry != "undefined");
                    FcltMgr._resizeMe(helpFrame, doMasonryRebuild);
                    if (!params.noMasonry && typeof updateTableHeaders === "function") { updateTableHeaders(); }
                }
            }
            if (parent.FcltMgr) {
                parent.FcltMgr.resized(parent, params);
            }
        }
        else if (thiswindow.length === 0) /* Touch */
        {
            if (!params.noMasonry && typeof thiswindow.masonryRebuild == "function" && thiswindow.$masonry)
                thiswindow.masonryRebuild();
            if (!params.noMasonry && typeof updateTableHeaders === "function")
                updateTableHeaders();
        }
    },

    // Aan te roepen vanuit een childwindow als hij bijvoorbeeld niet onzichtbaar wil refreshen
    inInvisibleTab: function() {
        if (!parent || parent == window)
            return false; // gokken op zichtbaar zijn

        var $helpFrame = parent.$("#" + window.name);
        if ($helpFrame.hasClass("frametab"))
        {
            return ($helpFrame.css("display") == "none");
        }
        if (parent.FcltMgr)
            return parent.FcltMgr.inInvisibleTab();
        return false; // gokken op zichtbaar zijn

    },

    // <iframe onload='FcltMgr.iframeLoaded(this)'>
    iframeLoaded: function(elm_iframe, params)
    {
        params = params || {};
        var $elm_iframe = $(elm_iframe);
        try {
            $elm_iframe.contents(); // testing
        } catch (e) {
            return; // Qlikview in een ander frame
        }

        // Zet maar meteen een unload-event op de frame-contents
        // zodat we (veel) later een spinner krijgen als het frame
        // wordt vervangen
        $(elm_iframe.contentWindow).one("beforeunload", function() {
            /* na 500 ms: bij empty.asp zetten we de spinner aan, bij andere
             *            pagina's doen we een overlay
             * na 2000ms: bij gewone pagina's doen we nu pas de spinner
             */

            if (FcltMgr.getData("preventBeforeUnload"))
                return;

            var $body = $("body", elm_iframe.contentDocument);
            elm_iframe.contentWindow.setTimeout(function() {
                if (elm_iframe.contentDocument.location.href.match(/empty\.asp|empty\.html/ig)) {
                    if (!$body.find("div.busyloading").length) {
                        $body.prepend("<div class='busyloading'>" + I("fa-fclt-spinner fa-4x") + "</div>");
                        if ("FcltMgr" in elm_iframe.contentWindow)
                            elm_iframe.contentWindow.FcltMgr.resized();
                    }
                } else if (!$body.find("div.busyoverlay").length) {
                    $body.prepend("<div class='busyoverlay'></div>");
                }
            }, 500);
            elm_iframe.contentWindow.setTimeout(function() {
                if (!$body.find("div.busyloading").length) {
                    $body.prepend("<div class='busyloading'>" + I("fa-fclt-spinner fa-4x") + "</div>");
                    if ("FcltMgr" in elm_iframe.contentWindow)
                        elm_iframe.contentWindow.FcltMgr.resized();
                }
            }, 2000);
        });

        if (!params.noLimitWidth && $(elm_iframe).is(":visible")) {
            // Als resultlist te breed is moet er wel een scrollbar onderin komen.
            // Ietwat getruct alleen voor result-sets, anders willen gewone schermen
            // niet altijd goed resizen van klein naar groot.
            // 1 pixel extra marge voor IE-bug FSN#32255
            if (elm_iframe.parentElement.id == "result" && FcltMgr._myDimensions(elm_iframe).width > $(window).outerWidth() + 1)
                params.widthToo = true;

            var wasScroll = $(window).scrollTop();

            FcltMgr._resizeMe(elm_iframe);

            $(window).scrollTop(wasScroll);

            if (params.widthToo)
            {
                elm_iframe.style.width = 0;                             // trucje om te zorgen dat *versmallen* ook gedetecteerd wordt
                var iframeScrollWidth = FcltMgr._myDimensions(elm_iframe).width;
                if (iframeScrollWidth > $(window).outerWidth() + 1)     // Ben ik breder dan mijn window?
                    elm_iframe.style.width = iframeScrollWidth + "px";  // Maak mijn frame dan breder (voor een horizontale scrollbalk)
                else
                    elm_iframe.style.width = "100%";                    // Anders gewoon 100% breed
            }
            else
                elm_iframe.style.width = "100%";                        // Anders gewoon 100% breed

            if (elm_iframe.contentWindow.parent && elm_iframe.contentWindow.parent.FcltMgr && elm_iframe.contentWindow != elm_iframe.contentWindow.parent)
                elm_iframe.contentWindow.parent.FcltMgr.resized();
        }

        // Scroll resultaatblok automatisch in beeld
        if (wasScroll == 0 &&
            elm_iframe.parentElement && elm_iframe.parentElement.id == 'result' &&
            !elm_iframe.contentDocument.URL.match("empty\.(asp|html)") &&
            !elm_iframe.contentDocument.URL.match("no_autoscroll=1") &&
            !$elm_iframe.attr("skipautoscroll") &&
            !$elm_iframe.attr("keepfilters") &&
            !params.largerOnly &&
            $('#result').offset())
        {
            $('html, body').animate({
                scrollTop: $('#result').offset().top
            }, 400);
        }
        $elm_iframe.removeAttr("skipautoscroll"); // Volgens keer wel gewoon
        if ($elm_iframe.attr("keepfilters")) {
            FcltMgr.resized(window);
        }
    },

    eventLoaded: function() {
        if (parent && parent.FcltMgr)
            parent.FcltMgr.childLoaded(window);
    },

    // Door een child-window aan te roepen vlak voordat hij van show naar edit mode gaat
    startEdit: function(thiswindow, params) {
        if (FcltMgr.mayEdit(thiswindow, params)) {
            FcltMgr.detailManager()._editactive = thiswindow;
            // Let op: soms wordt startEdit aangroepen van een sub-frame (res_show_rsv_ruimte.asp) maar
            //         soms echter ook vanuit de parent (nieuwe reservering-->res_reservering.asp)
            //         Die laatste zou eigenlijk startEdit('window van het subframe') moeten doen
            // De parent.name.match(/^ID.*/) is voor de veiligheid voor als er geen frame omheen zit (touch)
            if (FcltMgr._frmaccessible(parent) && thiswindow.parent && thiswindow.parent.$ &&
                (thiswindow != window || thiswindow.parent.name.match(/^ID.*/))) {
                thiswindow.parent.$("iframe")
                    .not("iframe[name=" + thiswindow.name + "]").not(".frametab, .fcltmodal")
                    .parent(".fcltframe").addClass("hot-overlay");
            }
            // Maak subtab hot, dat kan ook op touch
            if (FcltMgr._frmaccessible(parent) && thiswindow.parent && thiswindow.parent.$) {
                thiswindow.parent.$("li.ui-tabs-active .subtab-icon").addClass("hot");
            }
            FcltMgr.setTitle(null, { hot: thiswindow });
            return true;
        } else
            return false;
    },
    // Door een child-window aan te roepen vlak voordat hij van show naar edit mode gaat
    stopEdit: function(thiswindow, params) {
        if (FcltMgr.detailManager()._editactive == thiswindow) {
            FcltMgr.detailManager()._editactive = false;

            // Let op: soms wordt stopEdit aangroepen van een sub-frame (mld_show_note2.asp) maar
            //         soms echter ook vanuit de parent (nieuwe reservering-->res_reservering.asp)
            //         Die laatste zou eigen stopEdit('window van het subframe') moeten doen
            // De parent.name.match(/^ID.*/) is voor de veiligheid voor als er geen frame omheen zit (touch)
            if (FcltMgr._frmaccessible(parent) && thiswindow.parent && thiswindow.parent.$ &&
                (thiswindow != window || thiswindow.parent.name.match(/^ID.*/))) {
                thiswindow.parent.$("iframe")
                    .not("iframe[name=" + thiswindow.name + "]").not(".frametab,.fcltmodal")
                    .parent(".fcltframe").removeClass("hot-overlay");
            }
            // Subtab (kan ook op touch)
            if (FcltMgr._frmaccessible(parent) && thiswindow.parent && thiswindow.parent.$) {
                thiswindow.parent.$("ul.subtabs .subtab-icon.hot").removeClass("hot");
            }

            FcltMgr.setTitle(null, { hot: false });
            return true;
        } else
            return false;
    },

    // Door een manager-window aan te roepen als hij wil gaan editen
    mayEdit: function(thiswindow, params) {
        if (FcltMgr.detailManager()._editactive && !(params && params.nomessage)) {
            var i;
            if (!thiswindow.parent.$("li.ui-tabs-active .subtab-icon").hasClass("hot") &&           // Het huidige subtab is niet het active subtab
                (i = thiswindow.parent.$("li.ui-tabs-tab").has(".subtab-icon.hot").index()) !== -1) // Er is een actieve subtab aanwezig, met i = index (wordt direct toegewezen)
                thiswindow.parent.$("#frametabs").tabs({ "active": i });                            // Wissel dan naar het actieve subtab
            FcltMgr.alert(L("lcl_shared_othertabactive"));
            return false;
        }
        return true;
    },

    setCaller: function(callerinfo) {
        this.topmanager()._caller = callerinfo;
    },

    getCaller: function() {
        return this.topmanager()._caller;
    },
    setData: function(code, data) {
        this.topmanager().globalData[code] = data;
    },
    getData: function(code) {
        return this.topmanager().globalData[code];
    },

    L: function(lcl, params) {
        var params = params || {};
        if (!(lcl in window.LCL)) // nog niet met de cache meegekomen?
        {
            // Beetje onverwacht dat hij nog niet is gecached.
            // Ophalen met ajax. Dat registreert meteen in onze zelflerende cache
            var url = rooturl + "/appl/shared/load_lcl.asp?lcl=" + escape(lcl);
            if (params.hide_error)
                url += "&hide_error=1";
            url += "&pathname=" + escape(document.location.pathname);
            window.LCL[lcl] = FcltSyncgetJSON(url).lcl;
        }
        return window.LCL[lcl];
    },

    stopPropagation: function(eventHandler) { // This prevents the event from bubbling up to the onclick event handler for the line.
        eventHandler = eventHandler || window.event || {};
        if (eventHandler.stopPropagation)
            eventHandler.stopPropagation();
        // Neen, dan kun je bijvoorbeeld in res_plan_obj geen vinkjes meer zetten
        //            if (eventHandler.preventDefault)
        //                eventHandler.preventDefault();

        eventHandler.cancelBubble = true;

    },

    isTouchscreen: function() {
        var isTouch = false;
        var agent = navigator.userAgent;
        if ((agent.indexOf("iPad") != -1) ||
            (agent.indexOf("iPhone") != -1) ||
            (agent.indexOf("SCH-I800") != -1) ||
            ((agent.indexOf("Android") != -1) && (agent.indexOf("Mobile") == -1))
        ) {
            isTouch = true;
        }
        return isTouch;
    },
    _fillMyFrameHeight: function (frm, selfRect, parentRect)
    {   /* Deze functie set de height van het gegeven iframe zodat het de beschikbare ruimte opvult (en niet meer of minder) */
        var maxH = $(frm.contentWindow.top).height();           // Totale window.top hoogte
        maxH -= (selfRect ? selfRect.top : 0);                  // Eigen .top (relatief aan parent.top)
        maxH -= (selfRect && frm.contentDocument.body ? frm.contentDocument.body.getBoundingClientRect().top : 0); // Soms heeft de <body> zelf nog wat offset t.o.v. zijn eigen <html>
        maxH -= frm.contentDocument.documentElement.scrollTop;  // Als ik gescrolled ben, moet ik dat er ook af trekken.

        /* Alles idem voor de window.parent indien aanwezig */
        if (parentRect)
        {
            maxH -= parentRect.top;
            maxH -= (frm.contentWindow.parent.document.body ? frm.contentWindow.parent.document.body.getBoundingClientRect().top : 0);
            maxH -= frm.contentWindow.parent.document.documentElement.scrollTop;
        }
        maxH -= 1; /* Afronding */
        frm.contentWindow.frameElement.height = maxH;
    },
    _resizeMyHeight: function(elem, largerOnly)
    {
        if (typeof elem == "undefined")
            elem = window;
        if (elem.nodeType == 1 && elem.tagName == "IFRAME")
            elem = elem.contentWindow;

        if (elem && elem.document && elem.document.documentElement &&   // elem heeft een document en een <html>
            elem.location && elem.alert && elem.setInterval &&          // elem is een window
            elem.frameElement)                                          // elem heeft een iframe
        {
            var htmlRectHeight, htmlScrollHeight, htmlHeight, bodyRectHeight, bodyScrollHeight, bodyHeight, result;
            /* Stap 1, benader de hoogte van de content */
            if (!largerOnly)
            {
                elem.frameElement.style.height = "1px";

                htmlRectHeight = elem.document.documentElement.getBoundingClientRect().height;
                htmlScrollHeight = elem.document.documentElement.scrollHeight;
                htmlHeight = Math.round(htmlRectHeight) == htmlScrollHeight ? htmlRectHeight : Math.max(htmlRectHeight, htmlScrollHeight);

                if (elem.document.body)
                {
                    bodyRectHeight = elem.document.body.getBoundingClientRect().height;
                    bodyScrollHeight = elem.document.body.scrollHeight;
                    bodyHeight = Math.round(bodyRectHeight) == bodyScrollHeight ? bodyRectHeight : Math.max(bodyRectHeight, bodyScrollHeight);
                }
                else
                    bodyHeight = 0;

                result = Math.max(bodyHeight, htmlHeight);
            }
            /*  Stap 2, nu we de responsive content redelijk gevormd hebben, bepalen we de echte hoogte.
                Dit is een verfijning op Stap 1, we maken de <iframe> heel groot, en kijken dan nogmaals naar hoe hoog de <html> en de <body> zijn.
                Soms is dit niet mogelijk, bijvoorbeeld als de <html> en/of <body> een style="height: 100%" hebben (dan schaalt deze gewoon mee), of een hoogte in 'vh',
                of als er een plaatje als iframe.src is meegegeven, dan schaalt alle content mee met de hoogte van het <iframe>
                In die gevallen negeren we Stap 2 en houden we gewoon de hoogte van Stap 1 aan.
            */
            if (elem.frameElement.parentElement != null &&
                elem.frameElement.parentElement.className.indexOf("fcltframe") != -1 &&
                result < parseFloat(elem.frameElement.parentElement.style.height))
                elem.frameElement.style.height = elem.frameElement.parentElement.style.height; /* Anders krijg je alsnog 2 scrollbars tijdens het berekenen */
            else
                elem.frameElement.style.height = "25000px";

            htmlRectHeight = elem.document.documentElement.getBoundingClientRect().height;
            if (htmlRectHeight > 24900) /* <html> schaalt mee, negeer deze hoogte */
                htmlRectHeight = 0;

            /* Geen html scrollheight want dat is hier altijd 25000px */

            if (elem.document.body)
            {
                bodyRectHeight = elem.document.body.getBoundingClientRect().height;
                bodyScrollHeight = elem.document.body.scrollHeight;
                bodyHeight = Math.round(bodyRectHeight) == bodyScrollHeight ? bodyRectHeight : Math.max(bodyRectHeight, bodyScrollHeight);
            }
            else
                bodyHeight = 0;

            if (bodyHeight > 24900) /* <body> schaalt mee, negeer deze hoogte */
                bodyHeight = 0;

            var resultHeight = Math.max(result, bodyHeight, htmlRectHeight);
            resultHeight += parseInt(elem.frameElement.style.borderTopWidth || 0, 10);
            resultHeight += parseInt(elem.frameElement.style.borderBottomWidth || 0, 10);

            elem.frameElement.style.height = "";
            elem.frameElement.height = resultHeight;
        }
    },
    _myDimensions: function(elem)
    {   /* Verkrijg de dimensies van een gegeven iframe of window */
        if (typeof elem == "undefined") /* Handig in de console */
            elem = window;
        if (elem.nodeType == 1 && elem.tagName == "IFRAME")
            elem = elem.contentWindow;
        if (elem && elem.document && elem.document.documentElement &&   // elem heeft een document en een <html>
            elem.location && elem.alert && elem.setInterval &&          // elem is een window
            elem.frameElement)                                          // met een iframe
        {
            var htmlRect = elem.document.documentElement.getBoundingClientRect();
            var htmlRectHeight = htmlRect.height;
            var htmlScrollHeight = elem.document.documentElement.scrollHeight;
            var htmlHeight = Math.round(htmlRectHeight) == htmlScrollHeight ? htmlRectHeight : Math.max(htmlRectHeight, htmlScrollHeight);

            var htmlRectWidth = htmlRect.width;
            var htmlScrollWidth = elem.document.documentElement.scrollWidth;
            var htmlWidth = Math.round(htmlRectWidth) == htmlScrollWidth ? htmlRectWidth : Math.max(htmlRectWidth, htmlScrollWidth);

            if (elem.document.body)
            {
                var bodyRect = elem.document.body.getBoundingClientRect();
                var bodyRectHeight = bodyRect.height;
                var bodyScrollHeight = elem.document.body.scrollHeight;
                var bodyHeight = Math.round(bodyRectHeight) == bodyScrollHeight ? bodyRectHeight : Math.max(bodyRectHeight, bodyScrollHeight);

                var bodyRectWidth = bodyRect.width;
                var bodyScrollWidth = elem.document.body.scrollWidth;
                var bodyWidth = Math.round(bodyRectWidth) == bodyScrollWidth ? bodyRectWidth : Math.max(bodyRectWidth, bodyScrollWidth);
                if (elem.document.body.classList.contains("fclt-modal"))
                {
                    var elem_style = elem.getComputedStyle(elem.document.body);
                    var minWidth = parseInt(elem_style.getPropertyValue("min-width"), 10);
                    if (!isNaN(minWidth) && minWidth === bodyWidth)
                    { // jQuery dialog kan de box-sizing: border-box niet goed handlen als we een min-width hebben gedefinieerd -> pak de width incl. scrollbalk
                        let _getScrollbarWidth = () => {

                            // Creating invisible container
                            const outer = elem.document.createElement("div");
                            outer.style.visibility = "hidden";
                            outer.style.overflow = "scroll"; // forcing scrollbar to appear
                            elem.document.body.appendChild(outer);

                            // Creating inner element and placing it in the container
                            const inner = elem.document.createElement("div");
                            outer.appendChild(inner);

                            // Calculating difference between container's full width and the child width
                            const scrollbarWidth = (outer.offsetWidth - inner.offsetWidth);

                            // Removing temporary elements from the DOM
                            elem.document.body.removeChild(outer);

                            return scrollbarWidth;
                        }
                        bodyWidth += _getScrollbarWidth();
                    }
                }
            }
            else
            {
                var bodyHeight = 0;
                var bodyWidth = 0;
            }

            var result = { "height": Math.max(bodyHeight, htmlHeight), "width": Math.max(bodyWidth, htmlWidth) };

            return result;
        }
        return { "height": -1, "width": -1 };
    },
    _resizeMe: function(frm, doMasonryRebuild)
    {   /* Zet de hoogte goed voor alle soorten iFrames;
        1; subframe
            1.1 Naast elkaar (naast noteFrame, of noteFrame)
            1.2 Onder elkaar (bootstrap onder elkaar gedrukt)
            1.3 Enkel breed frame
        2; main-frame / lijst
        3; touch
            3.1 subframe
                3.1.1 Naast elkaar
                3.1.2 Onder elkaar
               	3.1.3 Enkel breed frame
            3.2 mainframe
        */
        if (!frm || !frm.contentDocument || !frm.contentDocument.documentElement)
            return; /* Dan weet ik het ook niet */

        var thisRect        = frm.contentWindow.frameElement.getBoundingClientRect();
        var $iframe_parent  = $(frm).parent(".fcltframe");
        var scrollingElem, orgScrollTop;
        if ($iframe_parent.length) // Is een subframe in een div
        {
            scrollingElem   = frm.contentDocument.documentElement; /* Default scrolling element = self */
            orgScrollTop    = scrollingElem.scrollTop;

            if (doMasonryRebuild) /* Alleen hier nodig */
                masonryRebuild();

            if (frm.contentWindow.parent === window.top) /* 3.1 Touch */
            {
                var parentRect = window.top.document.body.getBoundingClientRect();
                if (parentRect.width - thisRect.width < 30) /* 3.1.2 of 3.1.3 */
                {
                    if ($iframe_parent.siblings(".fcltframe:visible").length) /* 3.1.2 Touch subframes onder elkaar */
                    {
                        window.top.document.documentElement.style.overflow = "auto";

                        /* Hier is de parent het scrollende element */
                        scrollingElem = window.top.document.documentElement;
                        orgScrollTop = scrollingElem.scrollTop;

                        FcltMgr._resizeMyHeight(frm);
                    }
                    else /* 3.1.3 Touch subframe 100% breed */
                    {
                        FcltMgr._fillMyFrameHeight(frm, thisRect);
                    }
                }
                else /* 3.1.1 Touch subframes naast elkaar */
                {
                    window.top.document.documentElement.scrollTop = 0;
                    window.top.document.documentElement.style.overflow = "hidden";
                    FcltMgr._fillMyFrameHeight(frm, thisRect, parentRect);
                }
            }
            else /* Volgens mij vrijwel een exact kopie van de if tak, maar fijn om gescheiden te hebben tijdens debuggen bijv. */
            {
                var parentRect = frm.contentWindow.parent.frameElement.getBoundingClientRect();
                if (parentRect.width - thisRect.width < 30) /* 1.2 of 1.3 */
                {
                    if ($iframe_parent.siblings(".fcltframe:visible").length) /* 1.2 Subframes onder elkaar */
                    {   /* resizehandlers hiden gebeurd in de css */
                        frm.contentWindow.parent.document.documentElement.style.overflow = "auto";

                        /* Hier is de parent het scrollende element */
                        scrollingElem = frm.contentWindow.parent.document.documentElement;
                        orgScrollTop = scrollingElem.scrollTop;

                        FcltMgr._resizeMyHeight(frm);
                    }
                    else /* 1.3 Subframe 100% breed*/
                    {
                        FcltMgr._fillMyFrameHeight(frm, thisRect, parentRect);
                    }
                }
                else /* 1.1 Subframes naast elkaar (notitieframe) */
                {
                    frm.contentWindow.parent.document.documentElement.scrollTop = 0;
                    frm.contentWindow.parent.document.documentElement.style.overflow = "hidden";
                    FcltMgr._fillMyFrameHeight(frm, thisRect, parentRect);
                }
            }
        }
        else
        {
            scrollingElem   = frm.contentWindow.parent.document.documentElement; /* Default scrolling element = parent */
            orgScrollTop    = scrollingElem.scrollTop;

            if (frm.contentWindow.parent === window.top)
            { /* 3.2 Touch */ }
            else
            { /* 2. Main frame / lijst */
                var parentRect = frm.contentWindow.parent.frameElement.getBoundingClientRect();
            }

            if (frm.contentDocument.location.href.match(/empty\.asp|empty\.html/ig))
            {
                FcltMgr._fillMyFrameHeight(frm, thisRect, parentRect);
            }
            else
            {
                FcltMgr._resizeMyHeight(frm);
            }
        }
        if (orgScrollTop) /* Behoud de scroll-hoogte */
            scrollingElem.scrollTop = orgScrollTop;
    },
    mydialog: function(tekst, params) {
        try {
            params.keepfocus = document.activeElement; // probeer die terug te zetten
        } catch (e) { /* unspecifed error in IE11 */ }
        this.topmanager()._mydialog(tekst, params);
    },
    _mydialog: function(tekst, params) {
        params = params || {};
        if (!params.ishtmlsafe) {
            tekst = safe.html(tekst);
        }
        var $dia = $("<div>" + tekst + "</div>");
        params.buttons = params.buttons || [];
        params.buttons.unshift( // Ok is er altijd
            {
                "text": params.oktekst || L("lcl_ok"),
                "icon": "fal fa-fw fa-check",
                "click": function() {
                    if (params.fncallback)
                        return params.fncallback($(this).closest("div.ui-dialog"));
                },
                "class": "alertOk"
            });
        // Doe overal automatisch een 'close' achteraan
        for (var i = 0; i < params.buttons.length; i++) {
            var button = params.buttons[i];
            button.click = function(wasclick) {
                return function() {
                    let close;
                    if (wasclick) {
                        close = wasclick.apply(this, arguments);
                    }
                    if (close !== false) { // Als de callback functie niets returned dan is hij dus ook niet false
                        $dia.dialog("close");
                    }
                    if (params.keepfocus) {
                        params.keepfocus.focus();
                    }

                    if (params.fnafterclose) {
                        params.fnafterclose();
                    }
                }
            }(button.click);
        }

        var wWidth = $(window).width();
        var dWidth = wWidth * 0.8;
        $dia.appendTo('body')
            .dialog({
                modal: true,
                title: params.title,
                zIndex: 10000,
                autoOpen: true,
                width: 'auto',
                closeOnEscape: false, // die doen we zelf om params.fncancel te kunnen aanroepen
                create: function() {
                    $(this).css('maxWidth', dWidth);
                    $(this).dialog("widget").find('.ui-dialog-titlebar-close').remove();
                    $(this).closest('.ui-dialog').on('keydown', function(ev) {
                        if (ev.keyCode === $.ui.keyCode.ESCAPE) {
                            if (params.fncancel)
                                params.fncancel();
                            $dia.dialog("close");
                        }
                    });
                    /* Custom close icon/button */
                    var $fclt_icon_close = $("<span class='fclt-icon-close default-clickable-icon' title='" + L("lcl_close_window") + "'>" + I("fa-times fa-xl") + "</span>");
                    $(this).prev(".ui-dialog-titlebar").append($fclt_icon_close)
                                                    .addClass("has-fclt-icon-close")
                                                    .find(".ui-dialog-titlebar-close").hide();
                    $fclt_icon_close.on("click", function() {
                        if (params.fncancel)
                            params.fncancel();
                        $dia.dialog("close");
                    });
                    /* Voor als je in een notificatie-popup op een linkje klikt */
                    if (params.closeOnClick)
                        $(this).find("span").on("click", function() { $dia.dialog("close"); });
                },
                resizable: false,
                buttons: params.buttons,
                close: function(event, ui) {
                    $dia.remove();
                }
            });
    },
    windowopen: function(url, name, specs) {
        var wnd;
        if (typeof name == "undefined") {
            wnd = window.open(url, "_blank", "noopener");
        } else { // still unsafe with specs (so use only for internal URL's)
            wnd = window.open(url, name, specs || "noopener");
        }
        try {
            wnd.focus();
        } catch (e) {
            // FcltMgr.alert(e);
        }
        return wnd;
    },
    confirm: function(tekst, params, fncallback) {
        if (typeof params == "function") { // shift
            fncallback = params;
            params = {};
        }
        if (params.autoconfirm) {
            fncallback();
            return;
        }
        if (window.fcltmobile || !this.topmanager().window.$.fn.dialog) // fallback als jQuery-ui niet is geladen
        {
            if (confirm(tekst)) {
                fncallback();
            } else {
                if (params.fncancel)
                    params.fncancel();
            }
            return;
        }
        params.title = params.title || L("lcl_confirm_title");
        params.fncallback = fncallback;
        params.buttons = [{
            "text": params.canceltekst || L("lcl_cancel"),
            "icon": "fal fa-fw fa-undo",
            "click": function() {
                if (params.fncancel)
                    params.fncancel();
            },
            "class": "alertCancel"
        }];

        this.mydialog(tekst, params);
    },
    alert: function(tekst, params) {
        params = params || {};
        if (window.fcltmobile || !this.topmanager().window.$.fn.dialog) // fallback als jQuery-ui niet is geladen
        {
            alert(tekst);
            return;
        }
        this.mydialog(tekst, params);
    }
}

FcltMgr.url = window.location.href;
FcltMgr.window = window;
FcltMgr._pageManager = FcltMgr.defaultManager;
FcltMgr.IsTheManager = true;
try { // Als een van beide op /-1 eindigt niet al te hard klagen
    if (this.userId && !String(this.userId).match(/\/\-1$/) &&
        parent && parent.userId && !String(parent.userId).match(/\/\-1$/) &&
        this.userId != parent.userId) {
        FcltMgr.confirm("Unexpected change from user " + parent.userId + " to " + this.userId + "\nReload Facilitor?", function() {
            top.location.reload();
        });
    }

    if (parent && parent.FcltMgr && parent.FcltMgr != FcltMgr) {
        FcltMgr._pageManager = parent.FcltMgr._pageManager;
        FcltMgr.IsTheManager = false;
    }
} catch (e) {
    /* Kan falen als FACILITOR in een frame draait */
}