<% /*
     $Revision$
     $Id$

     File:               shared/header.inc
     Description:        Generieke serverside header functie.
                         Stelt de HTML header samen op basis van de meegegeven eisen.

     Parameters:         params:
                            title
                            outputmode  (default 0=screen)
                            plugins []
                            js []
     Context:           Eenmalig per ASP, automatisch via shared.inc
     Result:
     Note:              Gebruikt veel de globale rooturl die tegenwoordig autodetect is
                        Deprecated aanroepen als volgt kunnen gewoon verwijderd worden
                            FCLTHeader.Generate({ root: "../../appl/" });
                            FCLTHeader.Set({root: "../../../appl/"});

*/

/************************************************************/

// Huidige jQuery versie: 3.6.0

// Huidige jQuery-ui versie: 1.13.2

if (typeof FCLTHeader != "undefined")
{
    Response.Write("common.inc is indirect dubbel included. Dat mag niet.")
    Response.End;
}

FCLTHeader =
{
    filters: null,
    advfilters: null,

    _plugins : [],

    _params: { title: getQParam("_titel_", null),
                outputmode: 0,
                plugins: [], js: [], css: [], extrahead: [] },

    _myPush: function (arr, itm)
             {
                 var j;
                 for (j = 0; j < arr.length; j++)
                 {
                     if (typeof itm == "string" && arr[j].toLowerCase() == itm.toLowerCase()
                         || arr[j] == itm)
                         return; // hij is er al
                 }
                 arr.push(itm);
             },

    Title: function(title)
    {
        // TODO: Titel van het tabje zetten.
        FCLTHeader._params.title = title;
    },

    Set: function(params)
    {
        var i;
        for (i in params)
            FCLTHeader._params[i] = params[i];
    },

    Requires: function(params)
              {
                  if (JSON_Result)
                  {
                    __SafeLog("<span style='background-color:#0ff'>JSON_Result staat aan, dan is FCLTHeader.Requires erg onlogisch.</span>"
                             + "<br>Als er maar geen FCLTHeader.Generate() volgt gaat het in de praktijk wel goed.");
                    __Log(params);
                    return;
                  }

                  if (!params) params = {};

                  var i, j;
                  // 'Explodeer' de plugins naar onderdelen
                  if (params.plugins)
                      for (i = 0; i < params.plugins.length; i++)
                      {
                          this._plugins[params.plugins[i]] = true;
                          var i;
                          switch (params.plugins[i])
                          {
                              case "bootstrap":
                                  FCLTHeader.Requires({css: [rooturl + "/appl/localscripts/bootstrap/css/bootstrap.min.css"],
                                                        js: [rooturl + "/appl/localscripts/bootstrap/js/bootstrap.bundle.min.js" ]});
                              case "masonry":
                                  FCLTHeader.Requires({js: [rooturl + "/appl/localscripts/masonry/masonry.min.js"]});
                              case "jQuery":
                                  FCLTHeader.Requires({js: ["jQuery.js", "FcltJquery.js"]});
                                  FCLTHeader.Requires({js: ["jquery.toast.min.js"],
                                                       css: [rooturl + "/appl/localscripts/jquery.toast.min.css"]});
                                  break;
                              case "suggest":
                                  FCLTHeader.Requires({js: [rooturl + "/appl/shared/suggest/suggest.js"],
                                                      css: [rooturl + "/appl/shared/suggest/suggest.css"]});
                                  break;
                              case "kenmerk":
                                  FCLTHeader.Requires({js: ["jquery-ui.js", "modal_kenmerken.js", "date.js", rooturl + "/appl/shared/kenmerk_js.asp", "jquery.timepicker-table.js"],
                                                      css: ["timePicker-table.css"]});
                                  break;
                              case "modernizr":
                                  FCLTHeader.Requires({js: [rooturl + "/appl/Localscripts/modernizr/modernizr.js"]});
                                  break;
                              case "vis-network":
                                  FCLTHeader.Requires({js: [rooturl + "/appl/Localscripts/vis-network/vis-network.min.js",
                                                            rooturl + "/appl/Localscripts/vis-network/vis-network-utils.js"],
                                                      css: [rooturl + "/appl/Localscripts/vis-network/vis-network.css"]});
                                  break;
                              default: _AiAi("INTERNAL_ERROR_PLUGIN_"+params.plugins[i]+"_IS_ONBEKEND");
                          }
                      }

                  if (params.js)
                      for (i = 0; i < params.js.length; i++)
                      {
                        if (params.js[i].toLowerCase() != "fcltmgr.js")
                          FCLTHeader._myPush(FCLTHeader._params.js, params.js[i]);
                      };

                  if (params.css)
                      for (i = 0; i < params.css.length; i++)
                      {
                          FCLTHeader._myPush(FCLTHeader._params.css, params.css[i]);
                      };

                  if (params.extrahead)
                    FCLTHeader._myPush(FCLTHeader._params.extrahead, params.extrahead);
              },

    _fso:Server.CreateObject("Scripting.FileSystemObject"),
    AddTimeStamp: function(fPath, isOptional)
        {
            fPath = fPath.toLowerCase(); // canonical can improve caching
            if (fPath.indexOf(rooturl.toLowerCase()) == 0)
                fPath = rooturl + fPath.substring(rooturl.length); // Afblijven van rooturl-deel, cookies verdwijnen anders
            try
            {
                var f = this._fso.GetFile(Server.MapPath(fPath));
                var ts=new Date(f.DateLastModified);
                return fPath + "?" + Math.floor(ts.getTime()/1000);
            }
            catch(e)
            {
                if (isOptional)
                    return false; // niet erg
                Response.Write("\"></script></style></head><p><b>Internal error: kan bestand " + fPath + " niet vinden/openen</b></p>");
                Response.Write(e.description);
                Response.End;
            }
        },
    AddStylesheet: function(fPath, isOptional, embedformail)
        {
            if (getQParamInt("formail", 0) == 1)
            {
                if (!embedformail)
                    return;

                var fcss = this._fso.OpenTextFile(Server.MapPath(fPath));
                var tekst = fcss.ReadAll();
                fcss.Close();
                Response.Write("\n<style>/*embedding shared/default-print.css*/\n" + tekst + "\n</style>");
                return;
            }
            var timedpath = this.AddTimeStamp(fPath, isOptional);
            if (timedpath)
            {
                %>
         <link rel="stylesheet" href="<%=timedpath%>" type="text/css"><%
            }
        },

    _custCssPath: function (cust) {
        return rooturl + "/cache/" + customerId + (cust ? "_cust" : "") + ".css";
    },

    // params
    //   cssmode: 0: is normale customer css
    //            1: is default css
    //            2: concept css uit params.csstemplate
    //   forceInlineExcel: geen native xlsx maar inline
    Generate: function (params)
    {
        if (!params) params = {};

        var now = new Date();
        var timestamp = now.getFullYear() + "-" +
                    ("0" + (now.getMonth() + 1)).slice(-2) + "-" +
                    ("0" + now.getDate()).slice(-2) + " " +
                    ("0" + now.getHours()).slice(-2) + "_" +
                    ("0" + now.getMinutes()).slice(-2) + "_" +
                    ("0" + now.getSeconds()).slice(-2);

        if (params.outputmode == 2 || params.outputmode == 6) // Excel (incl. flex)
        {
          if (S("excel_mode") == 1)
          {
            params.outputmode = 4;
          }
          else if (params.forceInlineExcel || S("excel_mode") != 2)
          {
            Response.ContentType = "application/vnd.ms-excel";
            Response.AddHeader ("content-disposition", "inline; filename=Facilitor "+timestamp+".xls");
%>     <meta http-equiv="Content-Type" content="text/html;charset=<%=Response.Charset%>"><%
            return; // Excel is heel kaal
          }
        }

        if (params.outputmode == 4) // csv
        {
          var filename = (params.filename ? params.filename : "Facilitor") + " " + timestamp;
          Response.ContentType = "text/csv";
          Response.AddHeader ("content-disposition", "attachment; filename=" + filename +".csv");
          return; // csv is heel kaal
        }

%>     <meta http-equiv="Content-Type" content="text/html;charset=<%=Response.Charset%>">
     <%=S("http_meta_head")%>
     <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
 <%
         // FSN#14103 Voorkom back-button problemen
         // user_lang en rooturl resp. voor date.js en FcltMgr.js
%>         <script type="text/javascript">
<% if (!params.mobile) { %>
                window.history.forward(1);
<% } %>
                var user_lang = "<%=safe.jsstring(user_lang)%>";
                var rooturl = "<%=safe.jsstring(rooturl)%>";
                var S_fontawesomestyle = '<%=safe.jsstring(S("fontawesomestyle"))%>';
<% if (Response.Expires <= 0) { /* niet als gecached, te irritant */ %>
                var userId = "<%=safe.jsstring(customerId)+"/"+user_key%>";

<% } %>
<%              var ttl_prefix = "";
                if (Application("otap_environment") == "O")
                    ttl_prefix = customerId + " ";
%>
                var ttl_prefix = "<%=safe.jsstring(ttl_prefix) %>";
                var LCL = {}; // Bouw het clientside LCL-array voor teksten die in js gebruikt worden
                var RVT_token = "<%=safe.jsstring(protectRequest.theToken())%>";
<%          lcl.buildClientLCL();
            if (getQParamInt("has_filters", 0) == 1)
            {
                var fsql = " SELECT fac_menu_filter, fac_menu_advfilter"
                          + "   FROM fac_menu"
                          + "  WHERE (prs_perslid_key = " + user_key
                          + "      OR fac_menu_altgroep = 9)" // Widgets zijn niet persoonsgebonden
                          + "    AND fac_menu_key = " + getQParamInt("tab_menu_key");
                var foRs = Oracle.Execute( fsql );
                if (foRs.Eof)
                {
                    Response.Write("</script>");
                    FCLTHeader.AddStylesheet(rooturl + "/appl/fontawesome/css/fontawesome.min.css");
                    FCLTHeader.AddStylesheet(rooturl + "/appl/fontawesome/css/light.min.css");
                    FCLTHeader.AddStylesheet(rooturl + "/appl/fontawesome/css/regular.min.css");
                    FCLTHeader.AddStylesheet(rooturl + "/appl/fontawesome/css/solid.min.css");
                    Response.Write("</head><body>" + L("lcl_shared_pin_removed_error") + "</body></html>");
                    Response.End;
                }
                else
                {
                    this.filters = foRs("fac_menu_filter").Value || "";
                    this.advfilters = foRs("fac_menu_advfilter").Value || "";
                }
                foRs.Close();
            }
            if (this.filters)
            {
                try {
                    this.filters = JSON.parse(this.filters);
                    for (x in this.filters) {
                        if (typeof this.filters[x] === "string" && ("show_" + x in this.filters) && this.filters[x].match(/^:[dwmqy][pn]?\d{0,6}$/i)) {
                            var parsedDate = defaultDate(this.filters[x].substr(1));
                            this.filters[x] = new Date(parsedDate).getTime();
                            this.filters["show_" + x] = toDateString(parsedDate);
                        }
                    }
                    this.filters = JSON.stringify(this.filters);
                } catch (e) {
                    /* Dat kan haast niet, we maken dit object immers zelf (clientside) */
                    __DoLog("INTERNAL_ERROR parsing filters; " + e.description);
                }
                Response.Write("\nvar fcltfilters = '" + safe.jsstring(this.filters) + "';");
            }
            if (this.advfilters)
            {
                Response.Write("\nvar advfilters = '" + safe.jsstring(this.advfilters) + "';");
            }

            if (typeof placeholder_icons !== "undefined") /* uit icons.inc, is aanwezig indien iface.inc */
            { /* Stel dan de placeholder iconen ook clientside beschikbaar, voor iface.js */ %>
                var placeholder_icons = {
<%              for (i in placeholder_icons)
                {
%>                    "<%=i%>": "<%=placeholder_icons[i]%>",
<%              } /* Trailing comma in object literals is allowed in supported browsers */
%>                }; <%
            } %>

           </script>
<%

        if (FCLTHeader._params.title)
        {
          %><title><%=safe.html(FCLTHeader._params.title)%></title><%
        }
        // FcltMgr.js altijd
%>        <script type="text/javascript" src="<%=this.AddTimeStamp(rooturl + '/appl/LocalScripts/FcltMgr.js')%>"></script><%

        FCLTHeader.AddStylesheet(rooturl + "/appl/fontawesome/css/fontawesome.min.css");
        FCLTHeader.AddStylesheet(rooturl + "/appl/fontawesome/css/light.min.css");
        FCLTHeader.AddStylesheet(rooturl + "/appl/fontawesome/css/regular.min.css");
        FCLTHeader.AddStylesheet(rooturl + "/appl/fontawesome/css/solid.min.css");
        if (params.outputmode == 1 || params.outputmode == 5) {       // Printen is vrij kaal
            FCLTHeader.AddStylesheet(rooturl+"/appl/shared/default-print.css", false, true); // embedformail
            if (getQParam('autosortby', "") || getFParam('autosortby', "")) // Deze zijn wel nodig bij printen
            {
                %><script type="text/javascript" src="<%=this.AddTimeStamp(rooturl+'/appl/LocalScripts/jquery.min.js')%>"></script><%
                %><script type="text/javascript" src="<%=this.AddTimeStamp(rooturl+'/appl/LocalScripts/FcltJquery.js')%>"></script><%
                %><script type="text/javascript" src="<%=this.AddTimeStamp(rooturl+'/appl/LocalScripts/modernizr/modernizr.js')%>"></script><%
                %><script type="text/javascript" src="<%=this.AddTimeStamp(rooturl+'/appl/LocalScripts/iface.js')%>"></script><%
                %><script type="text/javascript" src="<%=this.AddTimeStamp(rooturl+'/appl/LocalScripts/sorttable.js')%>"></script><%
            }
            return;
        }
        var theme = // S("cssfordesktop") ||
                    "/appl/Localscripts/theme/jquery-ui.min.css";
        FCLTHeader.AddStylesheet(rooturl + theme);

        for (var i = 0; i < FCLTHeader._params.css.length; i++)
        {
            var pad = FCLTHeader._params.css[i];
            if (pad.indexOf("/") == -1)
                pad = rooturl+"/appl/shared/" + pad;
            FCLTHeader.AddStylesheet(pad);
        }

        FCLTHeader.AddStylesheet(rooturl+"/appl/shared/variables.css");
        if (params.mobile) {
            FCLTHeader.AddStylesheet(rooturl+"/appl/pda/default.css");
        } else {
            FCLTHeader.AddStylesheet(rooturl+"/appl/shared/default.css");
        }

        this.testTemplateCssRefresh();
        if (!this._fso.FileExists(Server.MapPath(this._custCssPath()))) {
            this.generateTemplateCss(undefined, undefined);
        }

        FCLTHeader.AddStylesheet(this._custCssPath(), true); // Deze altijd, technisch gezien optioneel
        if (!params.mobile && Session("interface") != "mobile" && (params.cssmode || 0) == 0) {
            FCLTHeader.AddStylesheet(this._custCssPath(true), true);
        }

        if (params.mobile)
        {
            // Deprecated
            // FCLTHeader.AddStylesheet(getCustCss("m"), true);
        }
        else
        {
            // 0=normaal, 1=kale default, 2=concept
            var previewfile = rooturl + "/cache/" + customerId + "_concept.css";
            switch (params.cssmode)
            {
                case 2:
                    this.generateTemplateCss(params.csstemplate, previewfile);
                    FCLTHeader.AddStylesheet(previewfile);
                    break
                case 1:
                    this.generateTemplateCss(FCLTHeader.defaultTemplate, previewfile);
                    FCLTHeader.AddStylesheet(previewfile);
                    break
            }
            if (Session("interface") == "touch")
                FCLTHeader.AddStylesheet(rooturl+"/appl/shared/touch.css");
            if (Session("interface") == "mobile")
                FCLTHeader.AddStylesheet(rooturl+"/appl/pda/default.css");
            if ((params.cssmode || 0) == 0) {
                FCLTHeader.AddStylesheet(getCustCss(), true); // optional
                FCLTHeader.AddStylesheet(custpath + "/custenv.css", true); // optional, handig voor testomgevingen
            }
        }

        /* Javascript's */
        for (var i = 0; i < FCLTHeader._params.js.length; i++)
        {
            var pad = FCLTHeader._params.js[i];
            if (__Logging == 0)
            {
                if (pad.toLowerCase() == "jquery.js") {
                    pad = "jquery.min.js";
                }
                if (pad.toLowerCase() == "jquery-ui.js")
                    pad = "jquery-ui.min.js";
            }
            else
            {
                if (pad.toLowerCase() == "jquery.js") {
                    pad = "jquery.js";
                }
                if (pad.toLowerCase() == "jquery-ui.js")
                    pad = "jquery-ui.js";
            }

            if (pad.indexOf("/") == -1)
                pad = rooturl+"/appl/LocalScripts/" + pad;
%>
        <script type="text/javascript" src="<%=this.AddTimeStamp(pad)%>"></script><%
        }

        for (var i = 0; i < FCLTHeader._params.extrahead.length; i++)
        {
            FCLTHeader._params.extrahead[i]();
        }
    },

    defaultTemplate : // De defaults
    {
        "main-color":               "#051163",
        "accent-color":             "#086DFB",

        "headerimage":              "banner.png",
        "headerbackgroundcolor":    "#FFFFFF",

        "loginbgimage":             "loginbg.jpg",
        "loginbackgroundcolor":     "#062889",
        "logincolor":               "#FFFFFF",
        "loginiconcolor":           "#FF7F62"
    },

    // tpl en cssfile worden alleen gebruikt bij concept/preview, anders zijn ze leeg
    generateTemplateCss : function(tpl, cssfile)
    {
        function _generateCustCss(custCssFile) {
            if (S("csscust"))
            {
                custCssFile.WriteLine("\n\n/*********************");
                custCssFile.WriteLine(" * Setting 'csscust' *");
                custCssFile.WriteLine(" *********************/");
                custCssFile.WriteLine(S("csscust"));
            }
            if (S("cssenvcust"))
            {
                custCssFile.WriteLine("\n\n/************************");
                custCssFile.WriteLine(" * Setting 'cssenvcust' *");
                custCssFile.WriteLine(" ************************/");
                custCssFile.WriteLine(S("cssenvcust"));
            }
        }

        // Contrast ratio between two colors
        function contrast(hex1, hex2) {
            var L1 = luminance(hex1);
            var L2 = luminance(hex2);
            if (L1 < L2) {
                var t = L1; L1 = L2; L2 = t;
            }
            return (L1 + 0.05) / (L2 + 0.05);
        }

        // Compute WCAG relative luminance
        function luminance(hex) {
            var c = hexToRgb(hex);
            var r = c.r / 255, g = c.g / 255, b = c.b / 255;

            r = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
            g = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
            b = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);

            return 0.2126 * r + 0.7152 * g + 0.0722 * b;
        }

        // Convert #RRGGBB to {r,g,b} (0–255)
        function hexToRgb(hex) {
            var value = parseInt(hex.substring(1), 16);
            return {
                r: (value >> 16) & 255,
                g: (value >> 8) & 255,
                b: value & 255
            };
        }

        // Chooses one of two colors (colorA or colorB) based on which has better contrast with a given base color
        function _pickContrastColor(baseColor, colorList) {

            // Iterate through all colors and find the one with the highest contrast
            var bestColor = null;
            var bestContrast = 0;
            for (var i = 0; i < colorList.length; i++) {
                var c = colorList[i];
                var diff = contrast(baseColor, c);
                if (diff > bestContrast) {
                    bestContrast = diff;
                    bestColor = c;
                }
            }

            return bestColor;
        }

        function _getFileByFilename(filename) {
            try
            {
                var outFile = this._fso.OpenTextFile(filename, 2, true); // 2=write, true=create?
                return outFile;
            }
            catch (e)
            {
                Response.Write("ERROR: Unable to write " + filename + "<br>" + e.description);
                if (this._fso.FileExists(filename))
                    Response.Write("<br>File owner: " + getFileOwner(filename));
                Response.Write("<br>Make sure " + getUserContext() + " user has modify permissions to this folder on the webserver");
                Response.End;
            }
        }

        if (!this._fso.FolderExists(Server.MapPath(rooturl + "/cache/")))
        {
            Response.Write("ERROR: Folder {0} does not exist on the Webserver".format(Server.MapPath(rooturl + "/CACHE")));
            Response.Write("<br>Please create it and make sure IUSR user has modify permissions to this folder on the webserver (just like on TEMP)");
            Response.End;
        }
        if (typeof tpl == "undefined")
            var custtemplate = S("csstemplate");
        else
            var custtemplate = tpl;

        var outfname = Server.MapPath(cssfile || this._custCssPath());
        __Log("Regenerating template {0}".format(outfname));

        var inCss = ":root {\n";
        for (itm in this.defaultTemplate) // Vervang alle bekende variabelen
        {
            var val = this.defaultTemplate[itm];
            if (itm in custtemplate)
                val = custtemplate[itm];
            if (val) {
                if (itm.slice(-5) === "image") {
                    if (itm in custtemplate) {
                        if (custtemplate[itm] === "## grijstint ##") { /* TODO; Dit kan er dan ook uit toch ? */
                            val = rooturl + "/APPL/Pictures/nobg.jpg";
                        } else if (custtemplate[itm] === "## transparant ##") {
                            val = rooturl + "/APPL/Pictures/transparant.png";
                        } else {
                            val = S("style_image_path") + sharedTrim(val); // Hier staan de custom plaatjes
                        }
                    } else {
                        val = rooturl + "/APPL/Pictures/" + val; // Hier staan de default plaatjes
                    }
                    inCss += "--" + itm + ": url(\"" + val + "\");\n";
                } else {
                    inCss += "--" + itm + ": " + val + ";\n";
                }
            }
        }

        var white       = "#FFFFFF"
        var aareonBlack = "#081326";
        var mainColor   = custtemplate["main-color"] || this.defaultTemplate["main-color"];
        var accentColor = custtemplate["accent-color"] || this.defaultTemplate["accent-color"];

        var mainColorContrastOptions = [white, aareonBlack, accentColor];
        var mainColorContrastIndex = custtemplate["main-color-contrast"] || 0;
        var mainColorContrast;
        if (mainColorContrastIndex) {
            mainColorContrast = mainColorContrastOptions[mainColorContrastIndex - 1];
        } else {
            mainColorContrast = _pickContrastColor(mainColor, mainColorContrastOptions);
        }

        var accentColorContrastOptions = [white, aareonBlack, mainColor];
        var accentColorContrastIndex = custtemplate["accent-color-contrast"] || 0;
        var accentColorContrast;
        if (accentColorContrastIndex) {
            accentColorContrast = accentColorContrastOptions[accentColorContrastIndex - 1];
        } else {
            accentColorContrast = _pickContrastColor(accentColor, accentColorContrastOptions);
        }

        inCss += "--main-color-contrast: " + mainColorContrast + ";\n";
        inCss += "--accent-color-contrast: " + accentColorContrast + ";\n";

        var frameHeaderBackgroundColorOptions = [
            "#F2F2F2",
            "color-mix(in srgb, var(--main-color) 10%, rgb(255, 255, 255))",
            "color-mix(in srgb, var(--main-color) 50%, rgb(255, 255, 255))",
            "color-mix(in srgb, var(--main-color) 90%, rgb(255, 255, 255))"
        ];
        var frameHeaderBackgroundColorIndex = custtemplate["frameheader-bg-color"] || 0;
        var frameHeaderBackgroundColor = frameHeaderBackgroundColorOptions[frameHeaderBackgroundColorIndex];

        var frameHeaderColorOptions = [
            "var(--headertextcolor)",
            "var(--headertextcolor)",
            "var(--headertextcolor)",
            "var(--main-color-contrast)"
        ];
        var frameHeaderColor = frameHeaderColorOptions[frameHeaderBackgroundColorIndex];

        inCss += "--frameheaderbackgroundcolor: " + frameHeaderBackgroundColor + ";\n";
        inCss += "--frameheadercolor: " + frameHeaderColor + ";\n";

        var menuBackgroundColor = "#F2F2F2"; // Zie ook: variables.css
        var menuiconColor = mainColor;
        if (contrast(menuBackgroundColor, menuiconColor) < 3) { // Voldoet dit wel aan de WCAG? (>= 3.0)
            menuiconColor = accentColor;
        }
        if (contrast(menuBackgroundColor, menuiconColor) < 3) { // Nu dan?
            menuiconColor = aareonBlack;
        }
        inCss += "--menuiconcolor: " + menuiconColor + ";\n";

        inCss += "}";

        var outFile = _getFileByFilename.call(this, outfname);

        outFile.WriteLine("/*********************************");
        outFile.WriteLine(" * GENERATED FILE, DO NOT MODIFY *");
        outFile.WriteLine(" *********************************/\n");

        outFile.WriteLine("/*************************");
        outFile.WriteLine(" * Global CSS variables  *");
        outFile.WriteLine(" *************************/");

        outFile.Write(inCss);

        if (cssfile) { // Custom css in dezelfde (preview-)template
            _generateCustCss(outFile);
            __Log("Template file {0} has been generated.".format(outfname));
            outFile.Close();
        } else {
            __Log("Template file {0} has been generated.".format(outfname));
            outFile.Close();

            outfname = Server.MapPath(this._custCssPath(true)); // In een eigen bestandje, deze willen we mobile bijv. niet

            outFile = _getFileByFilename.call(this, outfname);

            _generateCustCss(outFile); // Custom css in eigen bestandje zodat we deze kunnen uitsluiten
            __Log("Template file {0} has been generated.".format(outfname));
            outFile.Close();
        }
    },

    // Purpose : Checks if the custom CSS file is outdated and regenerates it.
    // Logic   : Compares timestamps of the CSS file, header.inc, and the latest
    //           setting change. If either is newer, CSS is rebuilt.
    testTemplateCssRefresh: function () {
        // Initialize timestamps
        var tset = 0;
        var tcss = 0;

        // Resolve file paths
        var cssPath = Server.MapPath(this._custCssPath());
        var headerPath = Server.MapPath(rooturl + "/appl/shared/header.inc");

        // Check if custom CSS file exists
        if (this._fso.FileExists(cssPath)) {
            var fcss = this._fso.GetFile(cssPath);
            tcss = new Date(fcss.DateLastModified).getTime();
        }

        // Get header.inc last modified time (for possible changes to this.defaultTemplate)
        var hinc = this._fso.GetFile(headerPath);
        var tinc = new Date(hinc.DateLastModified).getTime();

        // Check settings if header is not newer (for possible styling changes)
        if (tinc <= tcss) {
            var sql = "SELECT MAX(fa.fac_audit_datum) AS last_changed"
                    + " FROM fac_audit fa"
                    + " JOIN fac_setting fs ON fs.fac_setting_key = fa.fac_audit_tabelkey"
                    + " WHERE fa.fac_audit_tabelnaam = 'fac_setting'"
                    + " AND fa.fac_audit_veldnaam = 'fac_setting_pvalue'"
                    + " AND fs.fac_setting_name IN ('csstemplate', 'csscust')";
            var oRs = Oracle.Execute(sql);
            var lastChanged = oRs("last_changed").Value;
            if (lastChanged === null) {
                lastChanged = 0;
            }
            tset = new Date(lastChanged).getTime();
            oRs.Close();
        }

        // Regenerate CSS if outdated
        if (tinc > tcss || tset > tcss) {
            this.generateTemplateCss(undefined, undefined);
        }
    }
}
%>
