<% /*
     $Revision$
     $Id$

    File:               res_plan_room_v2.inc
    Description:        Het echte genereren van het planbordje voor ruimtes
                        via functie make_plan_room() die een kale HTML-table oplevert

    Context:            Indirect ajax in read_load_plan_room.asp
                        of rechtstreeks in res_search_plan_room_v2.asp
    Notes:              Zie res_load_cat.asp voor de catering en res_plan_obj_v2.inc voor ruimtes

*/

FCLTHeader.Requires({js: [rooturl+"/appl/res/res_plan.js", "date.js"]});

var initcollapsed = (typeof initcollapsed == "undefined" ? false : initcollapsed);
var collapseThreshold = (typeof collapseThreshold == "undefined" ? 5 : collapseThreshold);

// params: res_van, res_tot (en vis)
// Wijzigt(!) plan_rooms_info en voegt overal een will_fit aan toe
function set_will_fit(plan_rooms_info, plan_bezet_info, params)
{
  var any_nofit = false;
  for (var i_room in plan_rooms_info)
  {
    var is_nofit = false;
    var room = plan_rooms_info[i_room];

    var ar = plan_bezet_info[room.res_ruimte_key];

    var df = params.res_van.getHours() + params.res_van.getMinutes()/60;
    var dt = params.res_tot.getHours() + params.res_tot.getMinutes()/60;
    if (df < room.begintijd || dt > room.eindtijd)
    {
        is_nofit = true; // Buiten openingstijden
    }
    else if (room.begintijdblok && room.eindtijdblok && df < room.eindtijdblok && dt > room.begintijdblok)
    {
        is_nofit = true; // Tijdens lunch
    }

    if (!is_nofit)
    {
        for (var ci=0; ar != null && ci<ar.length; ci++)
        {
          if (ar[ci].rsv_ruimte_key == params.rsv_ruimte_key)
          { // we zijn het zelf
            is_selected = true;
          }
          else
          {
              // We willen graag de max(preposttime) van de catalogus waarin we nu reserveren, en de existing_rsv catalogus
              var this_prepost_time = Math.max(room.prepost_time, (ar[ci].rsv_preposttime || 0));

              // this_pre_time = Max van this_prepost_time en existing_rsv.activity.post_time
              room.pre_time  = (ar[ci].act_post_time ? Math.max(this_prepost_time, ar[ci].act_post_time) : this_prepost_time);
              room.post_time = Math.max(this_prepost_time, params.act_post_time);

              if (params.res_van.getTime() - room.pre_time *60*60*1000 < ar[ci].res_tot.getTime()
               && params.res_tot.getTime() + room.post_time*60*60*1000 > ar[ci].res_van.getTime())
                  is_nofit = true; // Overlap!
          }
        }
    }

    room.will_fit = !is_nofit;
    room.existing_res = ar;
    any_nofit |= is_nofit;
  }
  return any_nofit;
}

// Converts plan_bezet_info[x].res_opstel_tijd into .pre_opstel_tijd and .post_opstel_tijd
// taking into account the "opstelling" of the previous and next reservations.
function addPrePostOpstelTijd(plan_rooms_info, plan_bezet_info, params)
{
  var room;
  var all_rsv;
  var curr_rsv;
  var this_rsv;
  var prev_rsv;
  var next_rsv;
  var checkPrevNext;

  for (var i_room in plan_rooms_info)
  {
    room = plan_rooms_info[i_room];
    all_rsv = plan_bezet_info[room.res_ruimte_key];

    if (all_rsv == null) continue;

    // Efficiency check, het sorteren is veruit het zwaarst, dus voorkomen waar mogelijk.
    checkPrevNext = false;
    for (var x = 0; all_rsv != null && x < all_rsv.length; x++)
    {
        curr_rsv = all_rsv[x];
        if ("res_opstel_tijd" in curr_rsv && curr_rsv.res_opstel_tijd > 0 && curr_rsv.fo_status != 3  &&
            (!("prepost_time" in room) || curr_rsv.res_opstel_tijd > room.prepost_time)               &&
            (!("rsv_preposttime" in curr_rsv) || curr_rsv.res_opstel_tijd > curr_rsv.rsv_preposttime))
        {
            checkPrevNext = true;
            break;
        }
    }

    if (!checkPrevNext) continue;

    all_rsv.sort(function(a, b) { return new Date(a.res_van) - new Date(b.res_van) });

    for (var x = 0; all_rsv != null && x < all_rsv.length; x++)
    {
        this_rsv = all_rsv[x];

        if (!("res_opstel_tijd" in this_rsv)) continue;

        // Hier weegt de res_opstel_tijd toch niet mee
        if (!this_rsv.res_opstel_tijd || this_rsv.fo_status == 3                                ||
            (room.prepost_time        && this_rsv.res_opstel_tijd <= room.prepost_time)         ||
            (this_rsv.rsv_preposttime && this_rsv.res_opstel_tijd <= this_rsv.rsv_preposttime))
        {
            delete this_rsv.res_opstel_tijd;
            continue;
        }

        prev_rsv = null;
        next_rsv = null;

        // Verschillende ruimtes hebben nooit dezelfde res_ruimte_opstelling_key
        if (x > 0                && ("na_koppel" in this_rsv) == ("na_koppel" in all_rsv[x-1]))
            prev_rsv = all_rsv[x-1];

        if (x+1 < all_rsv.length && ("na_koppel" in this_rsv) == ("na_koppel" in all_rsv[x+1]))
            next_rsv = all_rsv[x+1];

        if (prev_rsv == null || !("res_opstel_key" in prev_rsv) || this_rsv.res_opstel_key != prev_rsv.res_opstel_key)
            this_rsv.pre_opstel_tijd = this_rsv.res_opstel_tijd;

        if (next_rsv == null || !("res_opstel_key" in next_rsv) || this_rsv.res_opstel_key != next_rsv.res_opstel_key)
            this_rsv.post_opstel_tijd = this_rsv.res_opstel_tijd;

        delete this_rsv.res_opstel_tijd;
    }
  }
}

function empty_grid_line(row, room, nr_days)
{
    var index;
    var tb_index;
    var html = '';

    for (tb_index = 0; tb_index < tijdbalken_kop.tijdbalk_arr().length; tb_index++)
    {
        if (nr_days < S('res_plantable_condensed_from')) // >= S('res_plantable_condensed_from') is compact view
        {
            for (index = tijdbalken_kop.tijdbalk_arr()[tb_index].start_column() + 1; index <= tijdbalken_kop.tijdbalk_arr()[tb_index].end_column(); index++)
            {
                // column 3 is empty
                if (index != 3)
                {
                    // first row is location
                    html += '<div class="rc hidden" data-row="' + row + '" style="grid-area: ' + row + '/' + index + ';"></div>';
                }
            }
            html += '<div class="hidden" style="grid-area: ' + row + '/' + index + ';"' + ' data-row="' + row + '"></div>';
        }
        else
        {
            html += '<div class="rc-compact hidden" data-row="' + row + '" style="grid-area: ' + row + '/' + Number(tijdbalken_kop.tijdbalk_arr()[tb_index].start_column() + 1) + '/' + row + '/' + tijdbalken_kop.tijdbalk_arr()[tb_index].end_column() + ';"></div>';
        }
    }
    return html;
}

function select_and_planbord_options(par, opt)
{   // Voor reserveren/bekijken ruimte op planbord.
    var forSelectRoom = par.forSelectRoom || false;
    var res =  (  (!forSelectRoom && (S("res_planbord_options") & opt) == opt)
               || (forSelectRoom && (S("res_forselect_options") & opt) == opt)
               );
    return res;
}

// What info to display in a cell
function res_load_title_rsv(pparams, picons) // static version of the ajax-one
{
    var title_rsv = {};
    var safe_icons = "";

    if (pparams.flag_status && pparams.flag_status > 0)
        safe_icons += "<span title='" + safe.html(L("lcl_res_flag" + pparams.flag_status)) + "' class='resflag" + pparams.flag_status + " ress'>" + I("fa-fclt-flag") + "</span>";

    if (picons)
    {
        // Indicatie iconen
        if (pparams.aantalM && pparams.aantalM > 0) // Res heeft meldingen
            safe_icons += I("fa-digging");
        if (pparams.aantalD && pparams.aantalD > 0) // Res heeft voorzieningen
            safe_icons += I("fa-projector");
        if (pparams.aantalA && pparams.aantalA > 0) // Res heeft catering
            safe_icons += I("fa-utensils-alt");
    }
    if (pparams.dirtlevel && pparams.dirtlevel > 0) // Ongeldig
        safe_icons += I("fa-exclamation-triangle");
    if (safe_icons)
    {
        safe_icons = "<div class='ric'>" + safe_icons + "</div>";
        title_rsv.safe_icons = "<div class='ric'>" + safe_icons + "</div>";
    }

    // Get config(opstelling) based on pparams.res_opstel_key
    var opst = "";
    if (pparams.res_opstel_key > 0)
    {
        var sql = "SELECT " + lcl.xsqla('ro.res_opstelling_omschrijving','ro.res_opstelling_key') + ", res_ruimte_opstel_default"
                + "  FROM res_ruimte_opstelling rro, res_opstelling ro"
                + " WHERE rro.res_ruimte_opstel_key = " + pparams.res_opstel_key
                + "   AND rro.res_opstelling_key = ro.res_opstelling_key";
        var oRs = Oracle.Execute(sql);
        if (oRs("res_ruimte_opstel_default").Value != 1) {
            opst = oRs("res_opstelling_omschrijving").Value;
        }
        oRs.Close();
    }

    var showTooltip = (pparams.extern ? 3 : planbordTooltipShowing(pparams.rsv_ruimte_key));
    switch (showTooltip) // Welke gegevens mogen worden getoond.
    {
        case 0: // geen geheim!
                title_rsv.info = [ { tclass: "rdk",          tval: pparams.fo_status==3?L("lcl_blokkade"):pparams.activiteit }
                                 , { tclass: "time-from-to", tval: toTimeString(new Date(pparams.res_van)) + "-" + toTimeString(new Date(pparams.res_tot)) }
                                 , { tclass: "",             tval: ((pparams.dirtlevel & res.dirtlevel.ruimte.notavailable) ? L("lcl_res_dirtyroom_short") : "") }
                                 , { tclass: "rdt",          tval: (pparams.intern && pparams.intern.omschrijving ? pparams.intern.omschrijving : "") }
                                 , { tclass: "opst",         tval: (opst != "" ? opst : "") }
                                 ];
                break;
        case 1: // geheim voor FE!
                title_rsv.info = [ { tclass: "", tval: toTimeString(new Date(pparams.res_van)) + "-" + toTimeString(new Date(pparams.res_tot)) }
                                 ];
                break;
        case 2: // geheim voor tooltip FE!
                title_rsv.info = [ { tclass: "", tval: pparams.res_key + "/" + pparams.res_volgnr
                                                                       + " " + toTimeString(new Date(pparams.res_van))
                                                                       + "-" + toTimeString(new Date(pparams.res_tot))
                                   }
                                 ];
                break;
        case 3: // Voor externen
                title_rsv.info = [ { tclass: "", tval: pparams.extern.res_nr + " " + toTimeString(pparams.res_van)
                                                                             + "-" + toTimeString(pparams.res_tot)
                                                                             + "\n" + pparams.extern.omschrijving
                                               , turl: pparams.extern.deepurl
                                   }
                                 ];
                break;
    }

    if (showTooltip != 1 && showTooltip != 3)
    {
        if (pparams.host_key)
        {
            var sql = "SELECT prs_perslid_naam_friendly"
                    + "  FROM prs_v_perslid_fullnames_all"
                    + " WHERE prs_perslid_key = " + pparams.host_key;
            var oRs = Oracle.Execute(sql);
            var hostname = oRs("prs_perslid_naam_friendly").Value;
            oRs.Close()
        }
        // ik weet uit res.inc dat contact_key alleen gevuld is als die anders is dan host_key
        if (pparams.contact_key)
        {
            var sql = "SELECT prs_perslid_naam_friendly"
                    + "  FROM prs_v_perslid_fullnames_all"
                    + " WHERE prs_perslid_key = " + pparams.contact_key;
            var oRs = Oracle.Execute(sql);
            var contactname = oRs("prs_perslid_naam_friendly").Value;
            oRs.Close()
        }
        if (hostname || (false && pparams.contact_key))
        {
            title_rsv.info.push( { tclass: "rdn", tval: hostname + (false && pparams.contact_key ? " (" + contactname + ")" : "")} );
        }

        if (pparams.bezoekers && pparams.bezoekers > 1)  // 1 bezoeker geloof ik ook wel (vaak concentratieruimte oid)
        {
            title_rsv.info.push( { tclass: "rbz", tval: pparams.bezoekers + " " + L("lcl_res_visitors") } );
        }
    }

    // Maak de inline en tooltip.
    var safe_rsv_inline = "";
    var safe_rsv_tooltip = "";
    var safe_rsv_deepurl = "";
    if (title_rsv && title_rsv.info)
    {
        for (var i=0; i<title_rsv.info.length; i++)
        {
            if (title_rsv.info[i].tval != "")
            {
                safe_rsv_inline += ( title_rsv.info[i].tclass
                              ? "<span class='" + title_rsv.info[i].tclass + "'>" + safe.html(title_rsv.info[i].tval) + "</span>"
                              : safe.html(title_rsv.info[i].tval)
                              );
                safe_rsv_tooltip += (safe_rsv_tooltip != "" ? "\n" : "") + safe.htmlattr(title_rsv.info[i].tval);
                safe_rsv_deepurl += (title_rsv.info[i].turl ? safe.htmlattr(title_rsv.info[i].turl) : "");
            }
        }
        safe_rsv_tooltip = " title='" + safe_rsv_tooltip + "'" + (safe_rsv_deepurl != "" ? " deepurl='" + safe_rsv_deepurl + "'" : "");
    }

    var hasTooltip = select_and_planbord_options(pparams, 2);
    var noInline   = select_and_planbord_options(pparams, 8);
    return { safe_inline:  "<div class='rd'>" + safe_icons + (noInline ? "" : safe_rsv_inline) + "</div>"
           , safe_tooltip: (hasTooltip ? safe_rsv_tooltip : "")
           };
}


// De echte planningblokjes van een regel
function make_plan_regel(room, ar, params, nr_days, row, hour_px)
{
  var vanmidnight = params.res_van.midnight().getTime(); // Eén keer uitrekenen
  var vanofs = params.res_van.getTimezoneOffset();
  var html = []; // het resultaat
  html.push(empty_grid_line(row, room, nr_days));
  var is_nofit = false; // zouden we hier passen?
  var tijdbalk;
  for (dagen = 0; dagen < nr_days; dagen++)
  {
      tijdbalk = tijdbalken_kop.tijdbalk_arr()[dagen];
      if (room.begintijd > S("res_t1"))
      {
        html.push('<div class="begintijd res_blok hidden" data-row="' + row + '" style="' + tijdbalk.gridtimeposition(S('res_t1'), room.begintijd) + ' grid-row: ' + row + ';">&nbsp;</div>');
      }
      if (room.eindtijd < S("res_t2"))
      {
        html.push('<div class="eindtijd res_blok hidden" data-row="' + row  + '" style="' + tijdbalk.gridtimeposition(room.eindtijd, S('res_t2')) + ' grid-row: ' + row + ';">&nbsp;</div>');
      }
      if (room.begintijdblok && room.eindtijdblok)
      {
          var div = "<div class='gepland hidden res_blok res_lunchblok' data-row='" + row  + "' style='" + tijdbalk.gridtimeposition(room.begintijdblok, room.eindtijdblok) + " grid-row: " + row + ";'>&nbsp;</div>";
          html.push("\n" + div);
      }
      for (ci = 0; ar && ci < ar.length; ci++)
      {
        var h_start = (ar[ci].res_van.getTime() - vanmidnight) /1000/60/60 - 24*dagen;
        var h_end   = (ar[ci].res_tot.getTime() - vanmidnight) /1000/60/60 - 24*dagen;
        // Correctie voor zomer-/wintertijd
        var arvanofs = ar[ci].res_van.getTimezoneOffset();
        var artotofs = ar[ci].res_tot.getTimezoneOffset();
        if (vanofs > arvanofs)
            h_start ++;
        else if (vanofs < arvanofs)
            h_start --;
        if (vanofs > artotofs)
            h_end ++;
        else if (vanofs < artotofs)
            h_end --;

        if (h_start>24 || h_end < 0)
            continue;

        var not_available = false;

        // if we filter on host key we do not want to show the other reservations
        // where our host is not the host.

        var contact_key = ar[ci].contact_key || ar[ci].host_key;
        if ((params.host_key != -1  && params.host_key != ar[ci].host_key) ||
            (params.contact_key != -1  && params.contact_key != contact_key) ||
            (params.urole=='fe' && !user.isCollega(contact_key)))
        {
            not_available = true; // FE en niet een eigen reservering
        }

        if (ar[ci].extern) // uitzondering
            not_available = false;

        if ((params.host_key > 0 && not_available) ||
            (params.contact_key > 0 && not_available) ||
             ar[ci].na_koppel == 1)
          statClass = "res_nietvrij hidden";
        else
        {
          var eigen_res = user.isCollega(contact_key);
          statClass = { 1 : (eigen_res ? "res_optie_eigen" : "res_optie"),
                        2 : (eigen_res ? "res_definitief_eigen" : "res_definitief"),
                        3 : "res_blok",
                        4 : "res_vervallen"}[ar[ci].fo_status];
          statClass = (ar[ci].bo_status == 5 ? "res_afgemeld" : statClass);
        }

        if (ar[ci].rsv_ruimte_key && ar[ci].rsv_ruimte_key == params.rsv_ruimte_key)
          statClass += " res_current";

        var theClass = statClass + " gepland";


        if (ar[ci].dirtlevel & res.dirtlevel.ruimte.notavailable)
          theClass += " res_fatal";
        var overflowleft = "";
        var overflowright = "";
        if (h_start<S("res_t1"))
        {
            h_start = S("res_t1") - S("res_h");
            if (dagen > 0)
                theClass += " continueleft";
            else
            {
                theClass += " overflowleft";
                overflowleft = '<div class="wrap-arrowleft"><div class="arrowleft"></div></div>';
            }
        }
        px_start = (h_start-S("res_t1"))*hour_px;
        var startplanblok = h_start;

        var contright = false;
        if (h_end>S("res_t2"))
        {
            h_end = S("res_t2") + S("res_h");
            if (dagen < nr_days-1)
            {
                theClass += " continueright";
                contright = true;
            }
            else
            {
                theClass += " overflowright";
                overflowright = '<div class="wrap-arrowright"><div class="arrowright"></div></div>';
            }
        }
        px_end = (h_end-S("res_t1"))*hour_px;
        var endplanblok = h_end;

        px_width = px_end - px_start;

        var theStyleLeft = ""; // voor cleaning blokje afmeting
        var theStyleRight = "";

        // We willen graag de max(preposttime) van de catalogus waarin we nu reserveren, en de existing_rsv catalogus
        var this_prepost_time = Math.max(room.prepost_time, (ar[ci].rsv_preposttime || 0));

        // Perhaps cleaning time, alleen optie en definitief
        // this_post_time >= this_prepost_time (always)
        var this_pre_time  = Math.max(this_prepost_time, (ar[ci].pre_opstel_tijd || 0));
        var this_post_time = Math.max(this_prepost_time, (ar[ci].post_opstel_tijd || 0), (ar[ci].act_post_time || 0));

        var preclean = 0;
        var postclean = 0;
        var gridcolumns = tijdbalk.gridtimecolumns(startplanblok, endplanblok);
        var unitLength = tijdbalk.unit_length();

        if ((this_pre_time || this_post_time) && (ar[ci].fo_status == 1 || ar[ci].fo_status == 2))
        {
            theClass += " res_cleaning";

            // maximaal kwartier 'doorschieten' links/rechts
            preclean = Math.min(this_pre_time, h_start - S("res_t1") + 0.25);
            postclean = Math.min(this_post_time, S("res_t2") - h_end + 0.25);
            if ( Math.round(hour_px * postclean - 1) > 0 )
            {
                theClass += " poc";
                // widen reservation with whole postclean time units
                gridcolumns.indexend += Math.ceil(postclean / unitLength);
                gridcolumns.indexdiff += Math.ceil(postclean / unitLength);
            }
            if ( preclean > 0 )
            {
                var precleanpx = Math.round(hour_px * preclean - 1);
                if (precleanpx > 0)
                {
                    theClass += " prc";
                    // widen reservation with whole preclean time units
                    gridcolumns.indexstart -= Math.ceil(preclean / unitLength);
                    gridcolumns.indexdiff += Math.ceil(preclean / unitLength);
                }
                if (px_start >= 0)
                {
                   px_start -= precleanpx; // border is 1px minder, maar wel altijd een kwartier
                   px_width += 1; // border is 1px minder
                }
            }
        }

        if (!preclean)
            theClass += " res_nopre";
        if (!postclean)
            theClass += " res_nopost";

        if (ar[ci].extern_api) {
            theClass += " res_extern click";
            if (ar[ci].extern_readonly)
                theClass += " extern_readonly";
        }
        else if (!ar[ci].na_koppel && !not_available && !params.forSelectRoom)
            theClass += " click";

        ar[ci].forSelectRoom = (params.forSelectRoom || false);
        var safetxt = res_load_title_rsv(ar[ci], !(ar[ci].na_koppel == 1 || params.urole=="fe"), params.forSelectRoom);
        var div = "<div class='"+theClass+" hidden'";
        if (ar[ci].rsv_ruimte_key)
            div += " rsv_key='" + ar[ci].rsv_ruimte_key + "'";
        if (ar[ci].act_color)
            div += " data-custom-color='true'";
        div += " data-act-key='" + ar[ci].act_key + "'";
        div += (postclean ? " poc=" + postclean : "");
        div += ' data-row="' + row + '"';
        div += (preclean ? " prc=" + preclean : "");
        div += " style='" + tijdbalk.gridtimeposition(startplanblok, endplanblok, gridcolumns) + " grid-row: " + row + " ;'";
        div += safetxt.safe_tooltip;
        div += ">";
        div += (nr_days < S("res_plantable_condensed_from") ? safetxt.safe_inline : "")
             + overflowleft + overflowright + "</div>";

        html.push("\n"+div);
      }
  }
  return html.join("");
}

function make_plan_room(plan_rooms_info, plan_bezet_info, params)
{
  var nnregels = 1;
__Log("start make_plan_room");
  if (typeof capSort == "undefined") {
      var capSort = true;
      var roomSort = true;
  }
  if (params.srtact)
    lcl.set_dialect(params.srtact, "RES_SRTACTIVITEIT_KEY");

  // Iterate through all reservable rooms (vertically) and show schedule for them (horizontally)
  var res_ruimte_key, free;

  if (plan_rooms_info.length == 0)
  {
    Response.Write("<table style='width:500px'><tr><td>" + L("lcl_res_none_inplanbord") + "</td></tr></table>");
    if (params.isSelection)
    {
        if (params.bld_key_arr.length > 0) {
            %><div class="resplanbordlimited" onclick="window.location.href=window.location.href.replace('&gebouw_key=<%=params.bld_key_arr[0]%>','');">
            <a><%=L("lcl_res_show_room_avail_loc")%></a></div><%
        }
        else if (params.loc_key_arr.length > 0)
        {
            %><div class="resplanbordlimited" onclick="window.location.href=window.location.href.replace('&locatie_key=<%=params.loc_key_arr[0]%>','');">
            <a><%=L("lcl_res_show_room_avail_all")%></a></div><%
        }
    }
        return; // quick exit
  }

  var nr_days = Math.round(1 + (params.res_tot.midnight() - params.res_van.midnight()) / 24 / 60 / 60 / 1000);
  // Bereken dynamisch breedte blokjes
  var hour_px = res.hour_px(width_px - 400, nr_days);

  var onclick = "";
  var onmouse = "";
  if (!params.forSelectRoom && authparams.PRSwritelevel < 9 && authparams.ALGwritelevel < 9)
  {
      var dt = params.res_van.midnight().getTime();
      onclick = "onclick=\"room_table_click(event,new Date("+dt+"), " + hour_px + ", "+safe.jsfloat(S("res_t1"))+",'"+params.urole+"', [" + params.act_key_arr.join(",") + "])\"";
  }

  if (!params.forSelectRoom)
  {
    addPrePostOpstelTijd(plan_rooms_info, plan_bezet_info, params);
  }

  var xfunc = user.func_enabled2("CAD", { isOptional: true });
  var cad_button = (xfunc && xfunc.canRead("WEB_CADUSE"));

%>
    <script type='text/javascript'>
        var tijdbalken = [];
    <%
        tijdbalken_kop = new Tijdbalken(params.res_van, nr_days, S("res_t1"), S("res_t2"), params.fnStep);
        tijdbalken_kop.render();
        var index_tb_render;
        for (index_tb_render = 0; index_tb_render < tijdbalken_kop.tijdbalk_arr().length; index_tb_render++)
        { %>
            tijdbalken.push(<%=tijdbalken_kop.tijdbalk_arr()[index_tb_render].render_object()%>);
    <%  }

  if (!params.forSelectRoom && authparams.PRSwritelevel < 9 && authparams.ALGwritelevel < 9)
  { %>
        function roomGridClick(evt, urole, activiteit)
        {
            var target_object = find_datetime_from_target(evt.target);
            var $target = $(evt.target);
            var $labelgrid = $target.prevAll("div.labelgrid").first();
            if (target_object)
            {
                if (window.beingdragged)
                {
                    window.beingdragged = false;
                    return;
                }
                var res_ruimte_key = $labelgrid.attr("id");
                var res_van = new Date(target_object.res_date);
                res_van.setHours(target_object.h, target_object.m, 0, 0);
                // Gewoon intern
                var cmd = "appl/res/res_reservering.asp"
                        + "?res_ruimte_key=" + res_ruimte_key
                        + "&res_van=" + res_van.getTime()
                        + (activiteit.length == 1 ? "&activiteit_key=" + activiteit : "")
                        + "&urole=" + urole;
                FcltMgr.openDetail(cmd);
            }
        }
<%  } %>
        function beforeprint() {
            // a4 is 842px at 72dpi
            set_borders_margins(tijdbalken[0], 842 / $(document).width());
        }
        function afterprint() {
            set_borders_margins(tijdbalken[0]);
        }
        function toggleNoFit(visible)
        {
            // show sort icons on the top row (in case a res_nofit is the top row)
            if ($("div.labelgrid i[data-type=room_up]").length > 1)
            {
                $("i[data-type=room_up].will_fit, i[data-type=room_down].will_fit, i[data-type=cap_up].will_fit, i[data-type=cap_down].will_fit").toggle(visible);
            }
            return $("div.grid > div.body div.labelgrid.res_nofit").each(function() {
                var $this = $(this);
                $("div[data-row=" + $this.attr("data-row") + "]", $this.closest("[data-loc]"))
                    .removeClass("res_nofit_hidden")
                    .addClass(visible ? "res_nofit_hidden" : "");
            });
        }

        function doFilter(event)
        {
            var valThis = $autofilter.val().toLowerCase();
            if (valThis != window.lastFilter)
            {
                showLoader();
                delayed(500, _doFilter);
            }
            function _doFilter()
            {
                function textFromCollection(collection)
                {
                    var _text = "";
                    collection.each(function () { _text += $(this).text().trim(); });
                    return _text.toLowerCase();
                }
                var $more = $("div.resplanbordlimited > a");
                var $gridrows;
                if ($more.length > 0 && $more.first().is(":visible"))
                {
                    $gridrows = $("div.grid > div.body div.labelgrid").not(".res_nofit");
                }
                else
                {
                    $gridrows = $("div.grid > div.body div.labelgrid");
                }
                var valThis = $autofilter.val().toLowerCase();
                $gridrows.each(function() {
                    var $this = $(this);
                    var $grid = $this.closest("div.grid");
                    var found = (valThis.trim().length == 0 || textFromCollection($("div[data-row=" + $this.attr("data-row") + "]", $grid).not(".rc")).indexOf(valThis) > -1);
                    $("div[data-row=" + $this.attr("data-row") + "]", $grid).toggle(found);
                });
                FcltMgr.resized();
                hideLoader();
                window.lastFilter = valThis;
            }
        }

        var $rc_first;
        var $clonedH2;
        var $originalGridHeader;
        var $clonedGridHeader;
        var $autofilter;
        function sortClicked(evt) {
            if (parent.fnSortPlanbord)
            {
                var type = $(this).attr("data-type") || "";
                // replace the sort with a different sort
                if (!!type) {
                    FcltMgr.stopPropagation(evt);
                    parent.fnSortPlanbord(type);
                }
                return;
            }
            var sort = "<%=getQParamSafe("sort", "")%>";
            var type = $(this).attr("data-type") || "";
            // replace the sort with a different sort
            if (!!type) {
                FcltMgr.stopPropagation(evt);
                if (!!sort)
                {
                    window.location.href = window.location.href.replace("&sort=" + sort, "&sort=" + type);
                }
                // add sort to the query parms
                else
                {
                    window.location.href = window.location.href + "&sort=" + type;
                }
            }
        }
        $(function()
        {
            $("i[data-type=cap_up], i[data-type=cap_down], i[data-type=room_up], i[data-type=room_down]").on("click", sortClicked);
            toggleNoFit(true);
            $rc_first = $("div.rc").first();
            $('body').append('<div id="timetip" style="display: none;"></div>');
            $('body').append("<div class='gridsearch busyloading' style='display:none;'>" + I("fa-fclt-spinner fa-4x") + "</div>");
            $("div.planbordgrid").on("mouseenter mousemove mouseout" + (typeof roomGridClick == "function" ? " click" : ""), tooltipDateTime);
            var $locDivs = $("div.locatiegrid").on("click", toggleLoc);
            if ($locDivs.length > 2 && !<%=(initcollapsed ? "true" : "false")%>)
            {
                $locDivs.each(
                    function ()
                    {
                        var $this = $(this);
                        if ($("span.badge", $this).text() <= <%=collapseThreshold%>)
                        {
                            $this.click();
                        }
                    }
                );
            }
            else
            {
                $locDivs.first().trigger("click");
            }
            set_borders_margins(tijdbalken[0]);
            setTimeout(function() { $(window).on("resize", resize_reservations); }, 500);
            var $originalFrameheader = $("div.fcltframeheader");
            var $clonedH2 = $originalFrameheader.clone(true);
            $originalFrameheader.css("visibility", "hidden");
            $clonedH2.addClass("floating");
            $originalGridHeader = $("div.grid div.header").first();
            $clonedGridHeader = $originalGridHeader.clone(true);
            $clonedGridHeader.addClass("floating");
            $(window).on("resize", function() {$clonedGridHeader.width($originalGridHeader.width());}); // give room-selector time to resize
            $("body").prepend($clonedH2);
            $("div.grid").first().prepend($clonedGridHeader).css("grid-template-rows", "50px 1fr");
            // resize after the extra header is set
            FcltMgr.resized();
            $(window).on("beforeprint", beforeprint);
            $(window).on("afterprint", afterprint);
            $(window.parent).on("scroll", function() {
                var formHeight = $(window.frameElement).offset().top; // FcltMgr.getElement("form[name=u2]").height();
                var root = document.documentElement;
                root.style.setProperty('--toppos',
                    Math.max($(window.parent).scrollTop() - formHeight, 0) + "px");
            });
            // autofilter
            if ($("div.grid > div.body div.labelgrid").length > 4)
            {
                var $autofilterAfter = $("h2.fcltframeheader").find("#iframerextratitle");
                if ($autofilterAfter.length == 0)
                {
                    $autofilterAfter = $("div.grid");
                    $autofilter = $("<input id='autofilter' type='text' placeholder='"+L("lcl_autofilter")+"' style='align-self:center;grid-area: 1/1/3/1;'>");
                    $("div.grid div.header.floating div.planbordgrid").append($autofilter);
                }
                else
                {
                    $autofilter = $("<input id='autofilter' type='text' placeholder='"+L("lcl_autofilter")+"'>");
                    $autofilterAfter.after($autofilter);
                }
                $autofilter.click(function (e) { e.stopPropagation() })
                                .keyup(function (e) {
                                    if (e.which == 27) {
                                        $("h2.floating #autofilter").val("");
                                    }
                                    doFilter();
                                });
            }
            // tooltip for reservations on room selector
<%
            if (select_and_planbord_options(params, 2) || (nr_days >= S("res_plantable_condensed_from")))
            {
%>
                $("div.planbordgrid").on("mouseover", "div.gepland", function (e)
                {
                    var obj = e.currentTarget;
                    rsv_title(obj);
                });
<%          }  %>
        });
        function resize_reservations()
        {
            set_borders_margins(tijdbalken[0]);
            // reset resizable en draggable when div.rc.first hasClass.droppable
            if ($rc_first.hasClass("ui-droppable"))
            {
                res_change();
            }
            $clonedGridHeader.width($originalGridHeader.width());
        }
        var lasteventtype;
        function tooltipDateTime(evt)
        {
            if ($(evt.target).hasClass("rc"))
            {
                if (evt.type == "mouseenter" || lasteventtype == "mouseout")
                {
                    tooltipDateTimeMouseenter(evt);
                }
                else if (evt.type == "mousemove")
                {
                    tooltipDateTimeMousemove(evt);
                }
                else if (evt.type == "mouseout")
                {
                    tooltipDateTimeMouseout(evt);
                }
                if (evt.type == "click")
                {
                    roomGridClick(evt, '<%=params.urole%>', [<%=params.act_key_arr.join(",")%>]);
                }
                lasteventtype = evt.type;
            }
        }
        // find date and time on target's grid-column
        function find_datetime_from_target(target)
        {
            var target_column = target.style.gridArea.split("/")[1];
            var index;
            var result = {};
            var start_time;
            var h;
            var m;
            var tb;
            for (index = 0; index < tijdbalken.length; index++)
            {
                tb = tijdbalken[index];
                if (tb._start_column <= target_column && tb._end_column >= target_column)
                {
                    start_time = tb._p_res_t1 + ((target_column - tb._start_column ) * tb._unit_length);
                    result.h = Math.floor(start_time);
                    result.m = Math.floor((start_time - result.h)*60);
                    result.res_date = tb._res_date;
                    break
                }
            }
            return result;
        }
        // render date and time on target's grid-column
        function render_datetime_from_target(target)
        {
            var target_object = find_datetime_from_target(target);
            var text = "";
            if (target_object)
            {
              var h = target_object.h;
              if (h < 10) h = "0" + h;
              var m = target_object.m;
              if (m < 10) m = "0" + m;
              text = toDateString(new Date(target_object.res_date)) + ' ' + h + ':' + m;
            }
            return text;
        }
        function tooltipDateTimeMouseenter(evt)
        {
                $("#timetip").html(render_datetime_from_target(evt.target))
                            .css({ left: parseInt(evt.pageX, 10) + 10 + "px",
                                    top: parseInt(evt.pageY, 10) - 25 + "px"
                                  })
                            .show();
        }
        function tooltipDateTimeMousemove(evt)
        {
            $("#timetip").css({ left: parseInt(evt.pageX, 10) + 10 + "px",
                                top: parseInt(evt.pageY, 10) - 25 + "px"
                              });
        }
        function tooltipDateTimeMouseout(evt)
        {
            $("#timetip").hide();
        }
        function openFG(evt, res_ruimte_key, titel)
        {
            var url = '../cad/ShowRoom.asp?res_ruimte_key='+res_ruimte_key;
            FcltMgr.openModalDetail(url, titel );
            FcltMgr.stopPropagation(evt);
        }
    </script>
<%
    function writeLocationRow(rows, locatie, loc_key, end_column, warning, total_rows, fit_rows)
    {
        var beschikbaar = (fit_rows >= 0 ? fit_rows + "/" : "") + rows + " " + L("lcl_obj_available");
        var withStyle = (fit_rows == 0 ? " style='background-color:#FFBF00;'" : "");
        Response.Write('<div class="locatiegrid collapsed" data-row="1" style="grid-column: 1 / ' + end_column + ';grid-row: 1;">'
                      + safe.html(locatie) + '<span class="badge"' + withStyle + '>' + beschikbaar + '</span>'
                      + (total_rows > S("qp_maxgridrows2") ? '&nbsp<span class="maxgridrows">'+ L("lcl_qp_maxgridrows2").format(S("qp_maxgridrows2")) + '</span>'
                        : (warning ? '&nbsp<span class="maxgridrows">'+ L("lcl_qp_maxrows2").format(S("qp_maxgridrows")) + '</span>' : ''))
                      + '</div>');

    }

  var any_nofit = false;
  if (params.forSelectRoom)
  {
    any_nofit = set_will_fit(plan_rooms_info, plan_bezet_info, params);
  }
  var loc_key = -1;
  var row = 0;
  tijdbalken_kop = new Tijdbalken(params.res_van, nr_days, S("res_t1"), S("res_t2"), params.fnStep);
  var tijdbalk_header = tijdbalken_kop.render();
  var total_rows = 0;
  var shown_rows = 0
  for (i_room in plan_rooms_info)
  {
    var room = plan_rooms_info[i_room];
    // show location on own line
    if (room.loc_key != loc_key)
    {
        if (loc_key > -1)
        {
            var fit_rows = ((typeof room.will_fit == "undefined") ? -1 : shown_rows);
            writeLocationRow(Math.min((row -2), S("qp_maxgridrows")), prev_locatie, loc_key, tijdbalken_kop.end_column()+1, row > S("qp_maxgridrows"), total_rows, fit_rows);
            total_rows++;
%>
        </div> <!-- close planbordgrid -->
    </div> <!-- close body -->
    <div class="footer">
        <div id="<%=params.id%>-footer" cellspacing="0" class="planbordgrid compressed planbord multiday"></div>
    </div>
</div> <!-- close grid -->
<%
        }
        row = 2;
        var prev_locatie = room.locatie;
%>
<div class="grid" data-loc="<%=room.loc_key%>">
    <div class="header">
        <div id="<%=params.id%>-header" cellspacing="0" class="planbordgrid compressed planbord multiday">
    <%
        // write only first header
        if (tijdbalk_header)
        {
           Response.Write(tijdbalk_header);
           tijdbalk_header = null;
        }
    %>
        </div>
    </div>
    <div class="body">
        <div id="<%=params.id%>-body" cellspacing="0" class="planbordgrid compressed planbord multiday">
<%
        loc_key = room.loc_key;
        shown_rows = 0;
        nnregels++;
    }
    total_rows++;
    if (row < S("qp_maxgridrows") && total_rows < S("qp_maxgridrows2"))
    {
      var bezet_info = plan_bezet_info[room.res_ruimte_key];
      // no_fit is al bepaald met set_will_fit

      var zelfde_datum = true;
      if (params.cur_van)
      {
        var oude_datum = new Date(params.cur_van).setHours(0, 0, 0, 0);
        var nieuwe_datum = new Date(params.res_van).setHours(0, 0, 0, 0);
        zelfde_datum = (oude_datum == nieuwe_datum);
      }

      var cls = "";
      if (params.forSelectRoom)
      {
        if (params.res_ruimte_key==room.res_ruimte_key && zelfde_datum)
        {
          cls = " selected";
        }
        else
        {
          if (!room.will_fit)
          {
            cls = " res_nofit";
          }
          else
          {
            shown_rows++;
          }
        }
      }
%>
      <div class='labelgrid hidden<%=cls%>' data-row='<%=row%>' style='grid-column: 1;grid-row: <%=row%>;<%=params.forSelectRoom ? " cursor: pointer;" : ""%>'
        id='<%=room.res_ruimte_key%>'
<%      if (select_and_planbord_options(params, 4)) { %>
            OnMouseOver="showOpmerking('#remark2_<%=room.res_ruimte_key%>', event, true);"
            OnMouseOut="$('#remark2_<%=room.res_ruimte_key%>').hide();"
<%      }
        if (room.deepurl)
        {
            Response.Write(" deepurl='"+safe.htmlattr(room.deepurl)+"'");
            Response.Write(" disc_key='"+params.disc_key+"'");
        }
        if (params.forSelectRoom) {
            Response.Write(" onClick='selectRoomGrid(this);'");
            Response.Write(" data-extern='" + (room.extern_id ? "true" : "false") + "'");
            Response.Write(" data-room-extern-meeting='" + room.res_ruimte_extern_meeting + "'");
        }
%>
      >
<%
      var capc_txt = ((room.minbezoekers > 0 && room.minbezoekers != room.bMax) ? room.minbezoekers + "-" : ((room.bMin != room.bMax)? room.bMin + "-" : ""))
                   + room.bMax
                   + " " + L("lcl_prs_pers")

      var resroomdescription = ((room.omschrijving && room.omschrijving.replace(/^\s+|\s+$/g,"") != room.res_ruimte_nr) ? room.omschrijving : "");
      var resroomimage = "";
      if (room.image)
      {
        if (!room.image_url)
        {
            room.image_url = S("res_image_path") + room.image;
        }
      }
      var imageClass = "";
      if (room.image_url)
      {
        imageClass = " rrphoto";
      }
%>
        <label>
            <%=(room.catimage ? I(room.catimage + " fa-2x", { fastyle: "far" }) + "&nbsp;" : "")%><%=safe.html(room.res_ruimte_nr)%>
        </label>
        <i<%=((cad_button && !params.extern) ? " title='" + L("lcl_room_cad") + "' class='far fa-fw fa-map-marker' onclick='openFG(event, " + room.res_ruimte_key + ", \"" + safe.jsstring(room.res_ruimte_nr) + "\")'>" : ">")%></i>
<%
        // sort icon only on first row of first location and on first row of visible location
        if ((row == 2 || room.will_fit) && roomSort == true)
        {
%>
            <i class="far fa-fw fa-arrow-up<%=room.will_fit ? " will_fit" : ""%>" data-type="room_up" title="<%=L("lcl_sort")%>"></i>
            <i class="far fa-fw fa-arrow-down<%=room.will_fit ? " will_fit" : ""%>" data-type="room_down" title="<%=L("lcl_sort")%>"></i>
<%
            if (room.will_fit)
            {
                roomSort = false;
            }
        }
        if (nr_days < S('res_plantable_condensed_from'))
        {
            // show image on search room (forSelectRoom) or when enabled (options bit 1 is [off])
            var imageClassStyle = '';
            if (!select_and_planbord_options(params, 1))
            {
              imageClassStyle = (room.image_url ? " style='background-image: url(\"" + safe.htmlattr(room.image_url) + "\");'" : "");
            }
            else
            { // no image so no image class
              imageClass = "";
            }
%>

            <div class="rrdet<%=imageClass%>"<%=imageClassStyle%> id="remark<%=room.res_ruimte_key%>" style="z-index:2;">
<%          if (resroomdescription)
            {
              if (S("fac_html_strictness") == 0)
              {
                var safe_oms = resroomdescription.replace(/\n/g, "<br>");
              }
              else // strict mode
              {
                var safe_oms = safe.html(resroomdescription);
              }
               if (select_and_planbord_options(params, 4))
               {
%>
                    <div class="res_remark" style='text-align:center' id="remark2_<%=room.res_ruimte_key%>">
                        <%=safe_oms%>
                    </div>
<%             } else { %>
                <div class="rrdesc-wrapper">
                    <div class="rrdesc">
                        <%=safe_oms%>
                    </div>
                </div>
            <% } %>
<%          } %>
            </div>
<%      } %>
        </div>
        <div class="capaciteitgrid hidden" data-row='<%=row%>' style='grid-column: 2;grid-row: <%=row%>;'><%=capc_txt%>
<%
        // sort icon only on first row of first location and on first row of visible location
        if ((row == 2 || room.will_fit) && capSort == true)
        {
%>
            <i class="far fa-fw fa-arrow-up<%=room.will_fit ? " will_fit" : ""%>" data-type="cap_up" title="<%=L("lcl_sort")%>"></i>
            <i class="far fa-fw fa-arrow-down<%=room.will_fit ? " will_fit" : ""%>" data-type="cap_down" title="<%=L("lcl_sort")%>"></i>
<%
            if (room.will_fit)
            {
                capSort = false;
            }
        }
%>
        </div>
<%
      Response.Write(make_plan_regel(room, bezet_info, params, nr_days, row, hour_px));
    }
    row++;
    nnregels ++;
  }
  var fit_rows = ((typeof room.will_fit == "undefined") ? -1 : shown_rows);
  writeLocationRow(Math.min((row -2), S("qp_maxgridrows")), prev_locatie, loc_key, tijdbalken_kop.end_column()+1, row > S("qp_maxgridrows"), total_rows, fit_rows);
%>
        </div> <!-- close planbordgrid -->
    </div> <!-- close body -->
    <div class="footer">
        <div id="<%=params.id%>-footer" cellspacing="0" class="planbordgrid compressed planbord multiday"></div>
    </div>
</div> <!-- close grid -->
<% if (!params.noclicker) { %>
  <script>
    $(function () {

      $("div.res_nopre, div.res_nopost").each(function()
      {
          var MARGIN = 2;

          var color = $(this).css("backgroundColor");
          var pix = $(this).width() - MARGIN;

      });

      $(".planbordgrid").on("click", "div.click", function (e)
      {
          var obj = e.currentTarget;
          rsv_click(obj, '<%=params.urole%>');
      });
      toggleNoFit(true);
    });
  </script>
<% } %>
<%
  if (any_nofit)
  {
    %><div class="resplanbordlimited" onclick="$('.planbordgrid').toggleClass('compressed');$(this).toggle();$(this).next().toggle();toggleNoFit(false);FcltMgr.resized()">
       <a><%=L("lcl_res_show_full_room_catalogus")%></a></div>
      <div class="resplanbordlimited" style='display:none' onclick="$('.planbordgrid').toggleClass('compressed');$(this).toggle();$(this).prev().toggle();toggleNoFit(true);FcltMgr.resized()">
       <a><%=L("lcl_res_show_half_room_catalogus")%></a></div>
      <div class="mapcontainer"></div><%
  }
  if (params.isSelection)
  {
    if (params.bld_key_arr.length > 0) {
        %><div class="resplanbordlimited" onclick="window.location.href=window.location.href.replace('&gebouw_key=<%=params.bld_key_arr[0]%>','');">
        <a><%=L("lcl_res_show_room_avail_loc")%></a></div><%
    }
    else if (params.loc_key_arr.length > 0)
    {
        %><div class="resplanbordlimited" onclick="window.location.href=window.location.href.replace('&locatie_key=<%=params.loc_key_arr[0]%>','');">
        <a><%=L("lcl_res_show_room_avail_all")%></a></div><%
    }
  }
__Log("done make_plan_room");
  return nnregels;
}
%>
