// Global constants
var NR_PERIOD_DEFS = 13;        // The number of input lines for a period definition
var NR_RESPONSE_ATTEMPTS = 3;   // The maximum number of attempts to contact a client by phone

// Basic global variables
var question_about_options = {};

var tab_parts = {};

var issue_search = {
   page_size: 20,
   orderby: 'cdate',
   start: 0,
   order: 'DESC'
};
var clientissue_search = {
   page_size: 20,
   orderby: 'cdate',
   start: 0,
   order: 'DESC'
};
var message_search = {
   page_size: 20,
   orderby: 'cdate',
   start: 0,
   order: 'DESC'
};
var merger_search = {
   page_size: 10,
   start: 0,
   total: 0
};

var user = {
   loggedin: false,         // Are we logged in?
   type: '',                // The type of user
   is_bbb: false,           // Logged in as BAS, BackOffice or Admin
   changing_details: false, // Set to true while changing user info, to prevent double posts
   dont_search_user: false  // Set to true when a button in the user details tab is clicked, to prevent searching user details
};
var safari = true;
var focused_element = null;
var isCtrl = false;
var app;
var drag_info = {
   leftframe_pos: 0,
   rightframe_pos: 0,
   dragme_pos: 0,
   leftwidth: 0,
   rightwidth: 0
};
var uitvraag_data = {
   active: [], // The fields that are visible (i.e. excluding values from dead branches)
   filled: {}, // The currently filled in values
   last: ''    // Name of the last field that was changed
};
var uitvraag_cache = null;
var uitvraag_periods = {};
// Whether to show a captcha:
//   'check': ask the server first
//   'yes':   show the captcha
//   'no':    don't show the captcha
var uitvraag_show_captcha = 'check';
// Random number to prevent browser caching of captchas
var uitvraag_captcha_rand = 0; 
var select_hidden = {};
var help_cache = {};

/**
 * Clean up when leaving the site. This clears any edit locks still held
 * by the user, and cleans up the Google map. The latter is mainly (only?)
 * for the benefit of IE6, where garbage collection is... less than optimal.
 */
function cleanupAll()
{
//   if (user.loggedin && user.is_bbb)
//      basClient.releaseLocks();

   document.onkeyup = null;
   document.onkeydown = null;

   focused_element = null;
   cleanMap();
}

/**
 * Translate certain terms and just returns the translation
 *
 * @param term   string   Contains the term the switch checks against for translation
 * @return       string   Translation
 */
function getTranslation(term)
{
   term = term.toUpperCase();
   switch(term) {
   case '18R':
   case '36L':
      return term+' (Polderbaan)';
   case '18C':
   case '36C':
      return term+' (Zwanenburgbaan)';
   case '09':
   case '27':
      return term+' (Buitenveldertbaan)';
   case '18L':
   case '36R':
      return term+' (Aalsmeerbaan)';
   case '24':
   case '06':
      return term+' (Kaagbaan)';
   case '22':
   case '04':
      return term+' (Oostbaan)';
   case 'LANDING':
      return 'Landen';
   case 'TAKEOFF':
      return 'Opstijgen';
   case 'YES':
      return 'Ja';
   case 'NO':
      return 'Nee';
   case 'WEB':
      return 'Web';
   case 'PHONE':
      return 'Telefoon';
   case 'FAX':
      return 'Fax';
   case 'VOICEMAIL':
      return 'Voicemail';
   case 'LETTER':
      return 'Post';
   case 'RESPONSE':
      return 'Antwoord';
   case 'SERVICE':
      return 'Service';
   case 'MAILING':
      return 'Mailing';
   case 'ILIST_POLL':
   case 'POLL':
      return 'Enquête';
   case 'BESTPRACTICE':
      return (user.is_bbb ? 'Best practice' : 'Antwoord');
   case 'ATTACHMENT':
      return 'Bijlage';
   case 'COMPLAINT':
      return 'Melding';
   case 'QUESTION':
      return 'Vraag';
   case 'GENERIC':
      return 'Overige';
   case 'PERIOD':
      return 'Periode';
   case 'SPECIFIC':
      return 'Specifieke';
   case 'ALL':
      return 'Alles';
   case 'OPEN':
      return 'In behandeling';
   case 'BAS':
      return (user.is_bbb ? 'Actie Bas' : 'In behandeling');
   case 'NEW':
      return (user.is_bbb ? 'Nieuw' : 'In behandeling');
   case 'LVNL':
      return (user.is_bbb ? 'Actie LVNL' : 'In behandeling');
   case 'AAS':
      return (user.is_bbb ? 'Actie AAS' : 'In behandeling');
   case 'RIJK':
      return (user.is_bbb ? 'Actie rijk' : 'In behandeling');
   case 'COUPLED':
      return (user.is_bbb ? 'Gekoppeld' : 'In behandeling');
   case 'NOTCOUPLED':
      return (user.is_bbb ? 'Niet gekoppeld' : 'In behandeling');
   case 'RESPOND':
      return (user.is_bbb ? 'Terugmelden' : 'In behandeling');
   case 'INVESTIGATE':
      return (user.is_bbb ? 'Te onderzoeken' : 'In behandeling');
   case 'CLOSED':
      return 'Afgehandeld';
   case 'DELETED':
      return 'Niet te koppelen';
   case 'REJECTED':
      return 'Niet ontvankelijk';
   case 'PUBLIC':
      return 'Publiek';
   case 'CLIENT':
      return 'Klant';
   case 'FINAL':
      return 'Afgerond';
   case 'SENT':
      return 'Verzonden';
   case 'SENDING':
      return 'Bezig met verzenden';
   case 'FINAL,SENT':
      return 'Afgerond';
   case 'DRAFT':
      return 'Concept';
   case 'URL':
      return 'Enquête script';
   case 'T_VALID':
      return 'Beschikbaar van';
   case 'T_INVALID':
      return 'Beschikbaar tot';
   case 'T_START':
      return 'Zichtbaar van';
   case 'T_END':
      return 'Zichtbaar tot';
   case 'ID':
      return 'Bericht-id';
   case 'AUTHOR':
      return 'Medewerker';
   case 'KEYWORDS':
      return 'Kernwoorden';
   case 'SCOPE':
      return 'Scope';
   case 'TYPE':
      return 'Type';
   case 'QUERY_ID':
      return 'Query-id';
   case 'QUERY_TITLE':
      return 'Selectie';
   case 'STATE':
      return 'Status';
   case 'SENDSTATE':
      return 'Verzendstatus';
   case 'CDATE':
      return 'Aanmaak datum';
   case 'ILIST_COMPLAINT':
      return 'Klacht';
   case 'ILIST_QUESTION':
      return 'Vraag';
   case 'SCOPE_PUBLIC':
      return 'Publiek';
   case 'SCOPE_CLIENT':
      return 'Klant';
   case 'SCOPE_BACKOFFICE':
      return 'Backoffice';
   case 'SCOPE_BAS':
      return 'Bas';
   case 'COUPLING_MANUAL':
      return 'Handmatig';
   case 'COUPLING_AUTO':
      return 'Automatisch';
   case 'QUALITY':
      return 'Kwaliteit';
   default:
      return 'Onbekend';
   }
}

/**
 * Grab the xml for the 'uitvraagscript'
 *
 * @param xml   string   Contains all the xml
 * @param role  string   bas/client in order to get the right script
 */
function getUitvraagXml(role)
{
   var now = new Date();
   if (!uitvraag_cache || now >= uitvraag_cache.t_invalid)
   {
      basScript.getCurrentScript(role);
   }
   else
   {
      uitvraag_data.filled = {};
      uitvraag_data.last = '';
      resetUitvraagScriptHTML('blc_client_1');
   }
}

/**
 * Throws away the uitvraagscript and reloads the correct one
 */
function resetUitvraagScript()
{
   if (uitvraag_cache && uitvraag_cache.script_type == 'POLL')
   {
      uitvraag_data.filled = {};
      uitvraag_data.last = '';
      document.getElementById('blc_client_1').innerHTML = '';
      resetUitvraagScriptHTML('message_details_static');
      uitvraag_cache.forceReload = true;  // so the regular script is reloaded on tab switch
   }
   else if (user.type == 'BAS' || user.type == 'ADMIN')
   {
      getUitvraagXml('BAS');
   }
   else
   {
      // Make sure we get a new captcha every time the script is reset
      changeCaptcha(false);
      uitvraag_show_captcha = 'check';
      getUitvraagXml('CLIENT');
   }
}

/**
 * Create the HTML data for the uitvraagscript from the XML script. This
 * function is called when new script data is retrieved from the server.
 */
function setUitvraagScript(script)
{
   var xmlDoc;

   if (document.implementation && document.implementation.createDocument)
   {  // FF
      var parser = new DOMParser();
      xmlDoc = parser.parseFromString(script.script, "text/xml");
   }
   else if (window.ActiveXObject)
   {  // IE
      xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
      xmlDoc.loadXML(script.script);
   }
   else
   {  // It's not working (obviously needs testing since we don't actually want to see this)
      alert('Error (xml)! Uw browser kan niet met deze pagina overweg');
      return;
   }

   // Keep the current script cached, but no longer than 24 hours
   var t_str = script.t_invalid.replace(/(\d{4})-(\d\d)-(\d\d)/, '$1/$2/$3');
   var t_invalid = new Date(t_str);
   var tomorrow = new Date();
   tomorrow.setDate(tomorrow.getDate()+1);
   if (t_invalid > tomorrow)
   {
      t_invalid = tomorrow;
   }

   uitvraag_cache = {
      script_id: script.id,
      t_invalid: t_invalid,
      script: xmlDoc.documentElement,
      script_type: script.type
   };
   if (script.message)
   {
      uitvraag_cache.message = script.message;
   }

   resetUitvraagScript();
}

/**
 * Create new HTML for the uitvraagscript based on the values already
 * filled in. This traces the branches using the provided values, and halts
 * after the first input element that was not filled in.
 * @return string HTML code for the uitvraagscript
 */
function createUitvraagScript()
{
   uitvraag_data.active = [];

   if (user.type == 'BAS' && !$('#tlc_client').find('[name=id]').val())
   {
      // No client selected
      return 'Selecteer hierboven een klant';
   }

   var res = createUitvraagNodes(uitvraag_cache.script, uitvraag_data, false);
   var html = res.html;

   // Anti-point and click script measure. Insert an empty line at random
   // at the end of input.
   if (user.type == 'CLIENT' && Math.random() < 0.2)
   {
      html += '<div class="uitvraag_fill"></div>';
   }

   return html;
}

/**
 * Create HTML code for all children of the provided uitvraagscript XML node,
 * halting after the first child input that was not filled in.
 * @param object node   XML parent node
 * @param array uv_data The currently filled in uitvraagscript fields
 * @param bool wait     If true, generate upto the first unanswered question
 * @return array with the generated HTML, a boolean which is true if
 *    all child fields were already filled in, and the name of the last
 *    filled element.
 */
function createUitvraagNodes(node, uv_data, wait)
{
   var html = '';
   var filled = true;
   var name = '';

   var cond = node.getAttribute('cond');
   if (!cond || checkUitvraagCondition(cond, uv_data))
   {
      for (var i = 0; i < node.childNodes.length; i++)
      {
         var child = node.childNodes[i];
         if (child.nodeType != 1)
         {
            continue;
         }

         var c_res = createUitvraagNode(child, uv_data, wait);
         if (c_res.html)
         {
            html += c_res.html;
         }
         name = c_res.name;
         if (!c_res.filled || (name && wait))
         {
            filled = false;
            break;
         }
         if (name == uv_data.last)
         {
            wait = true;
         }
      }
   }

   return {html: html, filled: filled, name: name};
}

/**
 * Create HTML code for an uitvraagscript node. The node can be an 'ask'
 * element which represents a single input, a 'switch' element that represents
 * a series of choices, or a 'send' element for the submit button.
 * @param object node   The XML node for which to generate HTML code
 * @param array uv_data The currently filled in uitvraagscript fields
 * @param bool wait     If true, only show the next node if this node
 *    is fully answered
 * @return array with the generated HTML, a boolean which is true if
 *    all child fields were already filled in, and the name of the last
 *    filled element
 */
function createUitvraagNode(node, uv_data, wait)
{
   var cond = node.getAttribute('cond');
   if (!cond || checkUitvraagCondition(cond, uv_data))
   {
      switch (node.tagName)
      {
      case 'ask':
         return createUitvraagQuestion(node, uv_data, wait);
      case 'switch':
         return createUitvraagSwitch(node, uv_data, wait);
      }
   }

   return {html: '', filled: true, name: ''};
}

/**
 * Create HTML code for a question in the uitvraagscript.
 * @param object node   The XML node containing the question
 * @param array uv_data The currently filled in uitvraagscript fields
 * @param bool wait     If true, only show the next node if this question
 *    is answered
 * @return array with the generated HTML, a boolean which is true if
 *    this question was answered by the user, and the name of the last
 *    filled element.
 */
function createUitvraagQuestion(node, uv_data, wait)
{
   var id = node.getAttribute('id');
   uv_data.active.push(id);
   var check = node.getAttribute('check');
   if (!check)
   {
      check = '';
   }

   var optional = node.getAttribute('optional');
   optional = optional ? (optional == 'true') : false;

   var html = '';
   var filled = (uv_data.filled[id] === undefined) ? false : true;

   var attr = node.getAttribute('ok');
   var orig_ok = attr ? attr : 'ok';
   var ok = (!filled || wait) ? orig_ok : 'no';

   var defval = node.getAttribute('default');
   var value = filled ? uv_data.filled[id] : (defval ? defval : null);
   var lastname = id;
   var optional_shown = false;
   var res;
   for (var i = 0; i < node.childNodes.length; i++)
   {
      var child = node.childNodes[i];
      if (child.nodeType != 1)
      {
         continue;
      }

      // If the question is optional, add a message saying so before
      // the first input element
      if (optional && !optional_shown && child.tagName != 'note')
      {
         html += '<div>(<i>Optioneel</i>)</div>';
         optional_shown = true;
      }

      switch (child.tagName)
      {
      case 'note':
         html += createUitvraagNote(child);
         break;
      case 'enum':
         res = createUitvraagEnum(child, id, value, check, ok, orig_ok,
            uv_data);
         html += res.html;
         lastname = res.name;
         // Always show a send button when required by the selected option
         if (ok != 'no' || res.ok == 'send')
         {
            ok = res.ok;
         }
         if (!res.filled)
         {
            filled = false;
         }
         break;
      case 'set':
         html += createUitvraagSet(child, id, value, check, ok);
         break;
      case 'line':
         html += createUitvraagLine(child, id, value, optional, check,
                                    ok, uv_data);
         break;
      case 'text':
         html += createUitvraagText(child, id, value, optional, check,
                                    ok, uv_data);
         break;
      case 'date':
         res = createUitvraagDate(child, id, value, check, ok, uv_data);
         html += res.html;
         if (!res.filled)
         {
            value = '';
         }
         break;
      case 'time':
         html += createUitvraagTime(child, id, value, check, ok, uv_data);
         break;
      case 'periods':
         html += createUitvraagPeriods(child, id, value, check, ok, uv_data);
         break;
      case 'captcha':
         html += createUitvraagCaptcha(child, id, value, ok);
         break;
      }
   }
   html = '<div class="uitvraagrow">' + html + '</div>';

   if (ok != 'no')
   {
      var attrs = { id: 'uitvraag_ok' };
      var label = ok == 'send' ? 'Verzenden' : 'OK';
      var bt_class;
      if (optional || value)
      {
         // make sure we go to the next question once we click OK
         uv_data.last = id;
         uv_data.filled[id] = value ? value : '';

         if (check)
         {
            attrs.onclick = 'checkUitvraagValue("' + id + '","' + value
               + '","' + check + '","' + ok + '"); return false;';
         }
         else if (ok == 'send')
         {
            attrs.onclick = 'submitUitvraagScript(); return false;';
         }
         else
         {
            attrs.onclick = 'resetUitvraagScriptHTML(); return false;';
         }
         bt_class = '';
      }
      else
      {
         attrs.onclick = 'pleaseSelectSomething(); return false;';
         bt_class = 'inactive';
      }
      html += '<div class="uitvraag_ok">'
            +    createButton(label, '#', attrs, bt_class)
            + '</div>';
   }

   return {html: html, filled: filled, name: lastname};
}

function pleaseSelectSomething()
{
   alert("Wilt u iets invullen alstublieft?");
}

/**
 * Generate HTML code for static uitvraagscript text
 * @param object node The XML containing the test to display
 * @return string containing the HTML code with the text
 */
function createUitvraagNote(node)
{
   var text = xmlText(node).replace(/\n/g, '<br/>');
   return '<div>' + text + '</div>';
}

/**
 * Toggle the action and label of the confirmation button in the script.
 * @param string ok The action of the button: 'ok' for an OK button leading
 *   to the next question, or 'send' for a button that sends the script data
 *   to the server to create a new issue.
 */
function toggleUitvraagOk(ok)
{
   if (ok == 'ok')
   {
      changeButtonText('uitvraag_ok', 'OK');
      activateButton('uitvraag_ok',
         {onclick: 'resetUitvraagScriptHTML(); return false;'});
   }
   else if (ok == 'send')
   {
      changeButtonText('uitvraag_ok', 'Verzenden');
      activateButton('uitvraag_ok',
         {onclick: 'submitUitvraagScript(); return false;'});
   }
   return true;   
}

/**
 * Generate HTML code for an enumeration input field in the uitvraagscript.
 * @param object node   The XML node containing the enum
 * @param string name   The name of the associated uitvraagscript field
 * @param string check  Name of a validator function for the input
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 * @param string origok The original value of the OK button from the XML
 *    source. Used when ok='no' is ignored because changing the selected
 *    options changes the action of the OK button.
 * @param string value  If provided, the initial value of the enum
 * @param array uv_data The currently filled in uitvraagscript fields
 * @return array with the generated HTML, and a boolean which is true if
 *    all an option of the enum was already selected by the user
 */
function createUitvraagEnum(node, name, value, check, ok, origok, uv_data)
{
   var result = {
      html: '',
      filled: true,
      name: name,
      ok: ok
   };
   var horizontal = node.getAttribute('layout') && node.getAttribute('layout') == 'horizontal';
   var i;

   // Check if the action of the 'OK' button should change depending on
   // the option chosen
   var options = node.getElementsByTagName('elem');
   var toggle_ok = false;
   for (i = 0; i < options.length; i++)
   {
      if (options[i].getAttribute('ok'))
      {
         toggle_ok = true;
         break;
      }
   }

   for (i = 0; i < options.length; i++)
   {
      var option = options[i];
      var cond = option.getAttribute('cond');
      if (cond && !checkUitvraagCondition(cond, uv_data))
      {
         continue;
      }

      var helpname = option.getAttribute('help');
      var help = helpname ? createInlineHelpLink(helpname) : '';

      var elemok = ok;
      var optval = option.getAttribute('value');
      if (toggle_ok)
      {
         var elemokval = option.getAttribute('ok');
         if (!elemokval)
         {
            elemokval = origok;
         }
         if (optval == value)
         {
            result.ok = elemokval;
         }
         if (ok != 'no')
         {
            elemok = elemokval;
         }
      }

      var children = option.getElementsByTagName('ask');
      if (children.length > 0)
      {
         elemok = 'no';
      }

      var id = 'uv_' + name + '_' + i;
      var onclick = '';
      if (toggle_ok && ok != 'no')
      {
         onclick = "toggleUitvraagOk('" + elemok + "');";
      }
      onclick += "setUitvraagValue('" + name + "','" + optval
               +    "','" + check + "','" + elemok + "');";
      var checked = optval == value ? ' checked="checked"' : '';

      if (horizontal)
      {
         if (i === 0)
         {
            result.html += '<div><label for="' + id + '">' + xmlText(option) + '</label>';
         }

         result.html += '<input type="radio" id="' + id + '" name="' + name + '" '
                     +    'value="' + optval + '" onclick="' + onclick + '" '
                     +    checked + '/>';

         if (i > 0)
         {
            result.html += '<label for="' + id + '">' + xmlText(option) + '</label>';
         }
         if (i == options.length - 1)
         {
            result.html += '</div>';
         }
      }
      else
      {
         result.html += '<div>'
                     +    '<input type="radio" id="' + id + '" name="' + name + '" '
                     +       'value="' + optval + '" onclick="' + onclick + '" '
                     +       checked + '/>'
                     +    '<label for="' + id + '">' + xmlText(option) + '</label>'
                     +    help
                     + '</div>';
      }

      if (optval == value)
      {
         for (var j = 0; j < children.length; j++)
         {
            var child_res = createUitvraagNode(children[j], uv_data, ok != 'no');
            result.html += child_res.html;
            result.name = child_res.name;
            if (child_res.filled)
            {
               if (uv_data.last == name)
               {
                  uv_data.last = result.name;
               }
            }
            else
            {
               result.filled = false;
               break;
            }
         }
      }
   }

   return result;
}

/**
 * Generate HTML code for a multiple-choice input field in the uitvraagscript.
 * @param object node   The XML node containing the set
 * @param string name   The name of the associated uitvraagscript field
 * @param string value  If provided, the initial values of the set
 * @param string check  Name of a validator function for the input
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 * @return string with the HTML code for the set
 */
function createUitvraagSet(node, name, value, check, ok)
{
   var html = '';

   var set_values = value ? value.split(',') : [];
   var onclick = "setUitvraagSet('" + name + "','" + check + "','"
               +    ok + "');";
   var options = node.getElementsByTagName('elem');
   for (var i = 0; i < options.length; i++)
   {
      var option = options[i];
      var optval = option.getAttribute('value');
      var id = name + '_' + i;
      var checked = arrayContains(set_values, optval) ? ' checked="checked"' : '';
      html += '<div>'
            +    '<input type="checkbox" id="' + id + '" name="' + name + '" '
            +       'value="' + optval + '" onclick="' + onclick + '" '
            +       checked + '/>'
            +    '<label for="' + id + '">' + xmlText(option) + '</label>'
            + '</div>';
   }

   return html;
}

/**
 * Create HTML code for a text input line in the uitvraagscript
 * @param object node   The XML node in the script for the text line
 * @param string name   The name of the associated uitvraagscript field
 * @param string value  If provided, the initial value of the input line
 * @param bool optional If true, the line can be left empty
 * @param string check  Name of a validator function for the input
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 * @param array uv_data The currently filled in uitvraagscript fields
 * @return string with the HTML code for the text line
 */
function createUitvraagLine(node, name, value, optional, check, ok, uv_data)
{
   var callback = "setUitvraagText('" + name + "',this.value," + optional
                + ",'" + check + "','" + ok + "');";

   // If an OK button appears, we want it to turn green as soon as text is
   // entered, so we set the callback on the keyup event. Otherwise,
   // reset the script only when the text area loses focus.
   var action = '';
   if (ok != 'no')
   {
      action = 'onkeyup="' + callback + '"';
   }
   else
   {
      action = 'onchange="' + callback + '"';
   }

   var valstr = value ? value : '';
   var html = '<input type="text" name="' + name + '" '
            +    'value="' + valstr + '" ' + action + '/>';

   return html;
}

/**
 * Create HTML code for a textarea input in the uitvraagscript
 * @param object node   The XML node in the script for the textarea
 * @param string name   The name of the associated uitvraagscript field
 * @param string value  If provided, the initial value of the input
 * @param bool optional If true, the text area can be left empty
 * @param string check  Name of a validator function for the input
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 * @param array uv_data The currently filled in uitvraagscript fields
 * @return string with the HTML code for the textarea
 */
function createUitvraagText(node, name, value, optional, check, ok, uv_data)
{
   var callback = "setUitvraagText('" + name + "',this.value," + optional
               + ",'" + check + "','" + ok + "');";
   var valstr = value ? value : '';

   // If an OK button appears, we want it to turn green as soon as text is
   // entered, so we set the callback on the keyup event. Otherwise,
   // reset the script only when the text area loses focus.
   var action = '';
   if (ok != 'no')
   {
      action = 'onkeyup="' + callback + '"';
   }
   else
   {
      action = 'onchange="' + callback + '"';
   }

   var html = '<div>'
            +    '<textarea name="' + name + '" ' + action + '>'
            +       valstr
            +    '</textarea>'
            + '</div>';

   return html;
}

/**
 * Delete a value in the uitvraagscript and regenerate the HTML
 * @param string name The name of the value to clear
 */
function clearUitvraagValue(name)
{
   delete uitvraag_data.filled[name];
   resetUitvraagScriptHTML();
}

/*!
 * Reset the value of a date field in the uitvraagscript. First removes
 * the value from the collected data, then regenerates the HTML, puts the
 * old value back in, and finally triggers a keyup event to validate the
 * value. All this to ensure that the user presses the OK button again
 * before continuing, and to keep the old value available at the same
 * time.
 * @param string name The name of the date field in the script
 */
function resetUitvraagDate(name)
{
   var id = 'uv_' + name;
   var value = uitvraag_data.filled[name];
   delete uitvraag_data.filled[name];
   resetUitvraagScriptHTML();
   if (value)
   {
      dateFieldSetValue(id, value);
   }
   // trigger a check on the date
   $('#' + id + ' [name=day]').keyup();
}

/**
 * Create HTML code for a date input field in the uitvraagscript
 * @param object node  The XML node in the script for the date field
 * @param string name  The name of the associated uitvraagscript field
 * @param string value If provided, the initial value of the date
 * @param string check Name of a validator function for the input
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 * @param array uv_data The currently filled in uitvraagscript fields
 * @return string with the HTML code for the date field
 */
function createUitvraagDate(node, name, value, check, ok, uv_data)
{
   var id = 'uv_' + name;
   var callback = "setUitvraagDate(\\'" + name + "\\',\\'" + check + "\\',\\'" + ok + "\\');";
   var reset = "resetUitvraagDate('" + name + "');";

   var attrs = {
      onkeyup: callback
   };
   if (value)
   {
      attrs.value = value;
   }
   if (ok == 'no' && uv_data.filled[name])
   {
      attrs.day_attrs = {'onclick': reset};
      attrs.month_attrs = {'onclick': reset};
      attrs.year_attrs = {'onclick': reset};
   }

   var html = createDateField(id, attrs);

   var children = node.getElementsByTagName('setter');
   for (var i = 0; i < children.length; i++)
   {
      var setnode = children[i];

      var setter_val = setnode.getAttribute('value');
      var text = xmlText(setnode);
      var setcb = 'dateFieldSetValue("' + id + '","' + setter_val
                + '"); setUitvraagDate("' + name + '","' + check + '","' + ok
                + '"); return false';
      html = '<div>'
           +    html
           +    '<div style="position:absolute;left:50%;top:0;">'
           +       createButton(text, '#', {onclick: setcb})
           +    '</div>'
           + '</div>';
   }

   var filled = value ? /\d{4}\-?\d{2}\-?\d{2}/.test(value) : false;
   return {html: html, filled: filled};
}

/*!
 * Reset the value of a time field in the uitvraagscript. As
 * resetUitvraagDate(), but for a time field.
 * @param string name The name of the time field in the script
 */
function resetUitvraagTime(name)
{
   var id = 'uv_' + name;
   var value = uitvraag_data.filled[name];
   delete uitvraag_data.filled[name];
   resetUitvraagScriptHTML();
   if (value)
   {
      timeFieldSetValue(id, value);
   }
   // trigger a check on the date
   $('#' + id + ' [name=hour]').keyup();
}

/**
 * Create HTML code for a time input field in the uitvraagscript
 * @param object node  The XML node in the script for the time field
 * @param string name  The name of the associated uitvraagscript field
 * @param string value If provided, the initial value of the time
 * @param string check Name of a validator function for the input
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 * @param array uv_data The currently filled in uitvraagscript fields
 * @return string with the HTML code for the time field
 */
function createUitvraagTime(node, name, value, check, ok, uv_data)
{
   var id = 'uv_' + name;
   var callback = "setUitvraagTime(\\'" + name + "\\',\\'" + check + "\\',\\'"
                +    ok + "\\');";
   var reset = "resetUitvraagTime('" + name + "')";

   var attrs = {
      onkeyup: callback
   };
   if (value)
   {
      attrs.value = value;
   }
   if (ok == 'no' && uv_data.filled[name])
   {
      attrs.hour_attrs = {'onclick': reset};
      attrs.minute_attrs = {'onclick': reset};
   }

   return createTimeField(id, attrs);
}

/**
 * Create HTML code for the list of periods. This first looks up the
 * date in the filled in script data. If the period definitions for this
 * date hav already been loaded in this session, checkboxes are
 * generated for each of the periods. Otherwise a request for the
 * definitions is sent to the server, and a waiting message is displayed
 * instead. Once the server responds, the script HTML is regenerated with
 * the resulting period definitions.
 * @param object node   The XML node in the script for the periods
 * @param string name   The name of the associated uitvraagscript field
 * @param string value  If provided, the initially selected periods
 * @param string check  Name of a validator function for the input
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 * @param array uv_data The currently filled in uitvraagscript fields
 * @return string with the generated HTML code
 */
function createUitvraagPeriods(node, name, value, check, ok, uv_data)
{
   var date_id = node.getAttribute('date');
   if (!uv_data.filled[date_id])
   {
      return '';
   }

   var now = new Date();
   var date = uv_data.filled[date_id];
   var is_today = (date == dateToString(now));
   var nowtime = '24:00';
   if (is_today)
   {
      var hour = now.getHours();
      var minute = now.getMinutes();
      if (hour < 10)
      {
         hour = '0' + hour;
      }
      if (minute < 10)
      {
         minute = '0' + minute;
      }
      nowtime = hour + ':' + minute;
   }

   var html;
   if (uitvraag_periods[date])
   {
      html = '';

      var set_values = value ? value.split(',') : [];
      var onclick = "setUitvraagSet('" + name + "','" + check + "','"
                  +    ok + "');";
      for (var i = 0; i < uitvraag_periods[date].length; i++)
      {
         var period = uitvraag_periods[date][i];
         var pval = period.from + '-' + period.upto;
         var id = name + '_' + i;
         var checked = arrayContains(set_values, pval) ? ' checked="checked"' : '';
         html += '<div>'
               +    '<input type="checkbox" id="' + id + '" '
               +       'name="' + name + '" value="' + pval + '" '
               +       'onclick="' + onclick + '" ' + checked + '/>'
               +    '<label for="' + id + '">' + pval + '</label>'
               + '</div>';

         // Only show checkboxes up to the current time for complaints
         // of today
         if (is_today && period.upto > nowtime)
         {
            break;
         }
      }
   }
   else
   {
      basIssue.getPeriods(date);
      html = '<div><i>Periodes worden geladen</i></div>';
   }

   return html;
}

/**
 * If required, create a CAPTCHA image and an input field to enter the
 * CAPTCHA code. If we are still waiting for the server to respond to
 * our question whether we should show a CAPTCHA, show a message saying so
 * instead.
 *
 * Currently, there can be only one CAPTCHA field in the script, and its
 * name should be 'captcha'.
 *
 * @param object node   The XML node in the script for the CAPTCHA
 * @param string name   The name of the associated uitvraagscript field
 * @param string value  If provided, the CAPTCHA code the user entered
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 * @return string with the generated HTML code
 */ 
function createUitvraagCaptcha(node, name, value, ok)
{
   if (uitvraag_show_captcha == 'check')
      return '<i>Bezig te controleren of code ingevuld moet worden</i>';

   var id = 'uv_' + name;

   var callback = "setUitvraagText('" + name
                + "',this.value,false,'checkUitvraagCaptcha','"  + ok + "');";
   var action = '';
   if (ok != 'no')
   {
      action = 'onkeyup="' + callback + '"';
   }
   else
   {
      action = 'onchange="' + callback + '"';
   }
   var valstr = value ? value : ''

   var html = '<div>'
            +    '<img src="captcha.php?' + uitvraag_captcha_rand
            +       '" alt="Controle code" />'
            + '</div>'
            + '<div style="height:25px">'
            +    createButton('Andere code', '#',
                     { onclick: 'changeCaptcha(true, "' + name + '"); return false;' })
            +    '&nbsp;'
            + '</div>'
            + '<div>'
            +    '<input type="text" id="' + id + '" value="' + valstr + '" '
            +       action + '></input>'
            + '</div>';

   return html;
}

/**
 * Return whether we should show a CAPTCHA. If not yet known, ask the server
 * and return true. The function creating the CAPTCHA should check the
 * value of uitvraag_show_captcha to determine whether to actually show a
 * CAPTCHA or tell the user that it's stille waiting for the server to respond.
 * @return true if a CAPTCHA (or message) should be shown, false if the
 *    CAPTCHA should be skipped.
 */
function shouldShowCaptcha()
{
   if (uitvraag_show_captcha == 'check')
   {
      basIssue.shouldShowCaptcha();
      return true;
   }
   else if (uitvraag_show_captcha == 'yes')
   {
      return true;
   }
   else
   {
      return false;
   }
}

/**
 * This function is called when the server responds to our query on whether
 * to show a CAPTCHA. It sets the corresponding global variable that is
 * checked by shouldShowCaptcha(), and regenerates the scipt HTML code.
 */
function setShouldShowCaptcha(res)
{
   uitvraag_show_captcha = res ? 'yes' : 'no';
   resetUitvraagScriptHTML();
}

/**
 * Ask the server to change our CAPTCHA code and make sure it also changes
 * in the browser. This is done when the script is reset, or when the
 * user cannot read the CAPTCHA and presses the button to generate a new one.
 * @param bool reset  If true, recreate the uitvraagscript when the server
 *   responds. Necessary on button press.
 * @param string name The name of the CAPTCHA field being reset.
 */
function changeCaptcha(reset, name)
{
   // Make sure we don't show a cached version
   uitvraag_captcha_rand = Math.random();
   if (name)
      // Make sure we don't send a previously validated code to the server
      delete uitvraag_data.filled[name];
   // And reset the captcha on the server
   basIssue.resetCaptcha(reset);
}

/**
 * Send a request to the server to check the CAPTCHA code entered by the client.
 * @param string name  The name of the CAPTCHA field
 * @param string value The code entered by the user
 * @param string ok    The action of the OK button
 */
function checkUitvraagCaptcha(name, value, ok)
{
   basIssue.checkCaptcha(name, value, ok);
}

/**
 * This function is called when the server responds to a request for
 * checking a CAPTCHA code. If the user enetered the correct code, it
 * is stored in the script array, and the script is regenerated or submitted.
 * Otherwise, an error message is shown, and the user has to enter the
 * correct code to be able to continue.
 * @param bool res     The result of the check, true if the code was correct
 * @param string name  The name of the CAPTCHA field
 * @param string value The code entered by the user
 * @param string ok    The action to be taken on succes: if 'send' submit
 *    the script results, otherwise regenerate the script HTML to move on to
 *    the next question.
 */
function uitvraagCaptchaResult(res, name, value, ok)
{
   if (res)
   {
      uitvraag_data.filled[name] = value;
      if (ok == 'send')
      {
         submitUitvraagScript();
      }
      else
      {
         resetUitvraagScriptHTML();
      }
   }
   else
   {
      alert("De ingevulde code is onjuist");
   }
}

/**
 * Generate HTML code for an uitvraagscript switch. This scans the switch
 * for the first case of which the condition is matched by the currently
 * filled in fields, and returns the HTML code for that case.
 * @param object node   The uitvraagscript XML node for the switch
 * @param array uv_data The currently filled in uitvraagscript fields
 * @param bool wait     If true, only show the next node if this switch
 *    is fully answered
 * @return array with the generated HTML, a boolean which is true if
 *    all child fields of the matching case were already filled in, and
 *    the name of the last filled element.
 */
function createUitvraagSwitch(node, uv_data, wait)
{
   for (var opt = node.firstChild; opt; opt = opt.nextSibling)
   {
      if (opt.nodeType == 1 && opt.nodeName == 'case')
      {
         var cond = opt.getAttribute('cond');
         if (!cond || checkUitvraagCondition(cond, uv_data))
         {
            return createUitvraagNodes(opt, uv_data, wait);
         }
      }
   }

   // Oops, nothing matches. Ignore this switch completely and hope
   // nothing breaks.
   return {html: '', filled: true, name: ''};
}

/**
 * Get all text content of an XML node. This scans all text child nodes of
 * the provided node, trims their contents of whitespace, and concatenates
 * the results.
 * @param object node The XML node for which to get the text
 * @return string with the text content
 */
function xmlText(node)
{
   var res = '';

   for (var i = 0; i < node.childNodes.length; i++)
   {
      var child = node.childNodes[i];
      if (child.nodeType == 3)
      {
         res += child.data.trim();
      }
   }

   return res;
}

/**
 * Set or clear the value of a field in the script, and regenerate the
 *    script HTML.
 * @param string name  The name of the field to set
 * @param string value The new value of the field, or null to clear the field
 * @param string check Name of a validator function for the input
 * @param string ok    The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 */
function setUitvraagValue(name, value, check, ok, onnotok)
{
   uitvraag_data.last = name;

   var onclick;
   if (value === null)
   {
      delete uitvraag_data.filled[name];
      if (ok != 'no')
      {
         onclick = onnotok
            ? onnotok
            : function() {
                 pleaseSelectSomething();
                 return false;
              };
         deactivateButton('uitvraag_ok', { onclick: onclick });
      }
      else
      {
         resetUitvraagScriptHTML();
      }
   }
   else
   {
      if (ok != 'no')
      {
         onclick = function() {
            checkUitvraagValue(name, value, check, ok);
            return false;
         };
         activateButton('uitvraag_ok', {onclick: onclick});
      }
      else
      {
         checkUitvraagValue(name, value, check, ok);
      }
   }
}

/**
 * If a validation function was defined for the input field, execute it. If
 * this function returns success (or if no check was defined), proceed
 * to the next question or submit the script depending on the value of ok.
 * @param string name  The name of the input field
 * @param string value The answer given to the question
 * @param string check Name of the validation function
 * @param string ok    The action of the OK button ('ok' or 'send')
 */
function checkUitvraagValue(name, value, check, ok)
{
   if (!check || window[check](name, value))
   {
      uitvraag_data.filled[name] = value;
      if (ok == 'send')
      {
         submitUitvraagScript();
      }
      else
      {
         resetUitvraagScriptHTML();
      }
   }
}

/**
 * Set the value of a set field in the script. The values of the ticked
 * checkboxes in the set are joined by commas to form the value. The checkbox
 * values are retrieved from the DOM tree.
 * @param string name  The name of the set field to set
 * @param string check Name of a validator function for the input
 * @param string ok    The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 */
function setUitvraagSet(name, check, ok)
{
   var values = [];
   $('#' + uitvraag_cache.target).find(':checkbox[name=' + name + ']:checked').each(function() {
      values.push($(this).val());
   });
   var value = values.length > 0 ? values.join(',') : null;
   setUitvraagValue(name, value, check, ok);
}

/**
 * Function to call when pushing a deactivated OK button after a date
 * field. Used instead of the default "Please select something callback.
 * @param name The name of the date field in the uitvraagscript
 */
function errorUitvraagDate(name)
{
   var id = 'uv_' + name;
   try
   {
      var date = dateFieldGetValue(id);
   }
   catch (exc)
   {
      handleError([exc], ["$('#" + id + " input')"]);
   }
}

/**
 * Set the value of a time input field in the script. The value is retrieved
 * from the DOM tree.
 * @param string name  The name of the time field to set
 * @param string check Name of a validator function for the input
 * @param string ok    The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 */
function setUitvraagDate(name, check, ok)
{
   var id = 'uv_' + name;
   try
   {
      var date = dateFieldGetValue(id);
      if (date)
      {
         setUitvraagValue(name, date, check, ok);
         basReport.getIssueDayReport(date);
      }
   }
   catch (exc)
   {
      var onnotok = new Function(
         'errorUitvraagDate("' + name + '"); return false;'
      );
      setUitvraagValue(name, null, check, ok, onnotok);
   }
}

/**
 * Function to call when pushing a deactivated OK button after a date
 * field. Used instead of the default "Please select something callback.
 * @param name The name of the date field in the uitvraagscript
 */
function errorUitvraagTime(name)
{
   var id = 'uv_' + name;
   try
   {
      var date = timeFieldGetValue(id);
   }
   catch (exc)
   {
      handleError([exc], ["$('#" + id + " input')"]);
   }
}

/**
 * Set the value of a time input field in the script. The value is retrieved
 * from the DOM tree.
 * @param string name  The name of the time field to set
 * @param string check Name of a validator function for the input
 * @param string ok    The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 */
function setUitvraagTime(name, check, ok)
{
   var id = 'uv_' + name;
   try
   {
      var time = timeFieldGetValue(id);
      if (time)
      {
         setUitvraagValue(name, time, check, ok);
      }
   }
   catch (exc)
   {
      var onnotok = new Function(
         'errorUitvraagTime("' + name + '"); return false;'
      );
      setUitvraagValue(name, null, check, ok, onnotok);
   }
}

/**
 * Set the value of a text or textarea input element in the script. When
 * the output consists of blanks only, it is considered empty, and the value
 * is not set.
 * @param string name   The name of the script field to set
 * @param string value  The text provided by the user
 * @param bool optional If true, the text can be left empty
 * @param string check  Name of a validator function for the input
 * @param string ok     The action of the OK button ('ok' or 'send'),
 *    or 'no' for no OK button
 */
function setUitvraagText(name, value, optional, check, ok)
{
   var str = value.trim();
   if (!str && !optional)
   {
      str = null;
   }
   setUitvraagValue(name, str, check, ok);
}

/**
 * Set the available periods for a certain date an regenerate the script HTML.
 * This function is called when the server responds to a request for said
 * periods.
 * @param string date   The date for which to set the periods
 * @param array periods The valid periods for date date
 */
function setPeriods(date, periods)
{
   uitvraag_periods[date] = periods;
   resetUitvraagScriptHTML();
}

/**
 * Check for a condition on a script node. Currently conditions are very
 * simple: a set of one or more sub-conditions, separated by commas. The
 * condition is only fulfilled if all sub-conditions are fulfilled. Each
 * sub-condition can be of the form:
 * name=value: true if the (enum) element name is set and has value value.
 * name!=value: true if the (enum) element name does not have value value,
 *    or is not set.
 * name[field]: true if element field in set name is checked.
 * !name[field]: true if element field in set name is not checked.
 * @param string cond   The condition to check
 * @param array uv_data The current script output
 * @return bool true if the condition is fulfilled, false otherwise
 */
function checkUitvraagCondition(cond, uv_data)
{
   var res = checkUitvraagConditionAnd(cond, uv_data);
   if (res.result)
   {
      return true;
   }
   if (res.cond && res.cond.charAt(0) == '|')
   {
      return checkUitvraagCondition(res.cond.substr(1), uv_data);
   }
   return false;
}

function checkUitvraagConditionAnd(cond, uv_data)
{
   var res = checkUitvraagConditionNot(cond, uv_data);
   while (res.cond && res.cond.charAt(0) == ',')
   {
      var tres = checkUitvraagConditionNot(res.cond.substr(1), uv_data);
      res.result |= tres.result;
      res.cond = tres.cond;
   }
   return res;
}

function checkUitvraagConditionNot(cond, uv_data)
{
   if (!cond)
   {
      return {result: false, cond: ''};
   }
   if (cond.charAt(0) == '!')
   {
      var res = checkUitvraagConditionNot(cond.substr(1), uv_data);
      res.result = !res.result;
      return res;
   }
   return checkUitvraagConditionTerm(cond, uv_data);
}

function checkUitvraagConditionTerm(cond, uv_data)
{
   var match = cond.match(/^\w+=\w+/);
   var idx, name, res;

   if (match)
   {
      var eq = match.toString();
      idx = eq.indexOf('=');
      name = eq.substr(0, idx);
      res = {cond: cond.substr(eq.length)};
      if (uv_data.filled[name] === undefined)
      {
         res.result = false;
      }
      else
      {
         var value = eq.substr(idx+1);
         res.result = uv_data.filled[name] == value;
      }
      return res;
   }

   match = cond.match(/^\w+\.\w+/);
   if (match)
   {
      var opt = match.toString();
      idx = opt.indexOf('.');
      name = opt.substr(0, idx);
      res = {cond: cond.substr(opt.length)};
      if (uv_data.filled[name] === undefined)
      {
         res.result = false;
      }
      else
      {
         var set_values = uv_data.filled[name].split(',');
         res.result = arrayContains(set_values, opt.substr(idx+1));
      }
      return res;
   }

   match = cond.match(/^[a-zA-Z_]\w+\(\)/);
   if (match)
   {
      var fun = match.toString();
      res = {cond: cond.substr(fun.length), result: !!eval(fun)};
      return res;
   }

   return {result: false, cond: ''};
}

/**
 * Send the output of the script to the server to create a new issue. Only the
 * basic script answers and the client ID are sent, the server takes care of
 * interpretation and filling in any needed defaults.
 */
function submitUitvraagScript()
{
   var client_id = $('#tlc_client').find('[name=id]').val();
   var script_output = {};

   for (var i = 0; i < uitvraag_data.active.length; i++)
   {
      var name = uitvraag_data.active[i];
      if (uitvraag_data.filled[name])
      {
         script_output[name] = uitvraag_data.filled[name];
      }
   }
   if (uitvraag_cache.script_type == 'POLL')
   {
      script_output.type = 'POLL';
      script_output.poll_message = uitvraag_cache.message;
   }

   basIssue.createIssue(client_id, uitvraag_cache.script_id, script_output);
}

/**
 * Set the contents of the uitvraagscript tab with the HTML generated from
 * the XML script and scroll to the bottom
 */
function resetUitvraagScriptHTML(target)
{
   if (!target)
   {
      target = uitvraag_cache.target;
   }
   else
   {
      uitvraag_cache.target = target;
   }

   var tgt_div = document.getElementById(target);
   tgt_div.innerHTML = createUitvraagScript();

   $(tgt_div).find(':input').each(function() {
      this.onfocus = new Function('focused_element = this;');
   });
   $(tgt_div).find('.uitvraagrow:last').find(':input:first').focus();

   var outDiv = $(tgt_div).parents('.content').get().shift();
   var height = Math.max(outDiv.scrollHeight, outDiv.clientHeight);
   outDiv.scrollTop = height - outDiv.clientHeight;
}

/**
 * Callback executed when an issue has successfully been inserted
 * @param array result The result information returned by the server
 */
function handleIssueCreated(result)
{
   var cdate = formatDBDate(result.cdate);
   var close_attrs = {
      onclick: 'resetUitvraagScript(); return false;'
   };
   var msg = '<div class="uitvraagrow">'
           +    'Wij danken u voor uw melding.<br/>'
           +    'De volgende details zijn opgeslagen:<br/><br/>'
           +    'Melding nummer: ' + result.id + '<br/>'
           +    'Indien datum: ' + cdate + '<br/>'
           +    'Huidige status: ' + getTranslation(result.state)
           + '</div>';
   if (uitvraag_cache.target == 'blc_client_1')
   {
      msg += '<div class="uitvraagrow">'
           +    createButton("Sluiten", '#', close_attrs)
           + '</div>';
   }

   document.getElementById(uitvraag_cache.target).innerHTML = msg;

   countIssues();
   if (user.type == 'BAS')
   {
      countIssuesBl2();
   }

   if (result.poll_message)
   {
      // In fact we only need to remove the row for this message ID, but
      // the message counter also has to be updated. Simply reload the
      // messages.
      searchMessages();
   }
}

function checkComplaintDate(name, date)
{
   try
   {
      if (dateBefore('today', date))
      {
         throw 'Ingevoerde datum ligt in de toekomst';
      }

      // Allow complaints to be filed up to 10 days after the event,
      // or 14 for BAS employees
      var nr_days = user.type == 'BAS' || user.type == 'ADMIN' ? 14 : 10;
      var begin_date = new Date();
      begin_date.setDate(begin_date.getDate()-nr_days);
      if (dateBefore(date, begin_date, false))
      {
         var msg = 'Ingevoerde datum/tijd ligt te ver in het verleden.\\n\\n'
                 + 'Een klacht dient binnen ' + nr_days
                 + ' kalenderdagen na de ondervonden hinder te zijn '
                 + 'ingediend bij Bas.\\n'
                 + 'Zie hiervoor ook het klachtenreglement.';
         throw msg;
      }
   }
   catch (exc)
   {
      handleError([exc], ["$('#uv_" + name + " input')"]);
      // reset the field to the old value if we have it
      if (uitvraag_data.filled[name])
      {
         setTimeout('dateFieldSetValue("uv_' + name + '","' + uitvraag_data.filled[name] + '");', 650);
      }
      return false;
   }

   return true;
}

/**
 * Check if the time given in the uitvraagscript is valid.
 * XXX FIXME: this also uses the date given in the uitvraagscript,
 * assuming that it was entered under the name 'date'. This should
 * be given in the uitvraagscript XML itself, but we currently have
 * no way of passing extra parameter to a check function.
 */
function checkComplaintTime(name, time)
{
   try
   {
      var date = uitvraag_data.filled.date;
      if (!date || dateBefore(date, 'today'))
      {
         return true;
      }
      date += ' ' + time;
      if (dateBefore('today', date, true))
      {
         throw 'Ingevoerde tijd ligt in de toekomst';
      }
   }
   catch (exc)
   {
      handleError([exc], ["$('#uv_" + name + " input')"]);
      // reset the field to the old value if we have it
      if (uitvraag_data.filled[name])
      {
         setTimeout('timeFieldSetValue("uv_' + name + '","' + uitvraag_data.filled[name] + '");', 650);
      }
      return false;
   }

   return true;
}

/**
 * Switches pages in a tab with a list
 * @param string id        ID of the table to switch pages on
 * @param string direction 'prev' for paging backward, 'next' for forward
 */
function togglePage(id, direction)
{
   if (id == 'issues')
   {
      if (direction == 'prev')
      {
         issue_search.start -= issue_search.page_size;
      }
      else if (direction == 'next')
      {
         issue_search.start += issue_search.page_size;
      }

      searchIssues();
   }
   else if (id == 'clientissues')
   {
      if (direction == 'prev')
      {
         clientissue_search.start -= clientissue_search.page_size;
      }
      else if (direction == 'next')
      {
         clientissue_search.start += clientissue_search.page_size;
      }

      searchIssuesBl2();
   }
   else if (id == 'messages')
   {
      if (direction == 'prev')
      {
         message_search.start -= message_search.page_size;
      }
      else if (direction == 'next')
      {
         message_search.start += message_search.page_size;
      }

      searchMessages();
   }
   else if (id == 'merger_list')
   {
      if (direction == 'prev')
      {
         merger_search.start -= merger_search.page_size;
      }
      else if (direction == 'next')
      {
         merger_search.start += merger_search.page_size;
      }

      listMergeableRecords();
   }
}

/**
 * Set the information needed for the drag button
 */
function setDragInfo()
{
   drag_info.leftframe_pos = $('#leftframe').width();
   drag_info.rightframe_pos = $('#rightframe').width();
   drag_info.dragme_pos = $('#dragme').offset().left;
   // CHANGE THESE TWO FOR MIN-WIDTH!
   drag_info.leftwidth = 508;
   drag_info.rightwidth = 288;
   $('.drag_containment').width($('.container').width() - drag_info.leftwidth - drag_info.rightwidth).css('left', drag_info.leftwidth);
}

function dragSlider()
{
   // Rightframe_width should be 3px lower than here above due to IE bug
   if ($('#leftframe').width() >= drag_info.leftwidth
       && $('#rightframe').width() >= drag_info.rightwidth-3)
   {
      var change = $('#dragme').offset().left - drag_info.dragme_pos;
      $('#rightframe').width(drag_info.rightframe_pos - change);
      $('#leftframe').width(drag_info.leftframe_pos + change);
   }
}

/**
 * Sets basic vars, functions and listener functions once the document is loaded.
 *
 * @param document
 */
$(document).ready(function()
{
   var elem = null;

   app = fApplication.singleton();
   app.initialize({
      rpc: { decodeISO8601: false }
   });

   window.onbeforeunload = cleanupAll;

   document.onkeyup = KeyCheck;
   document.onkeydown = KeyCheck2;

   $('li').map(function(){
      var id = $(this).attr('id');
      tab_parts[id] = $(this).parents('.part').attr('id');
   });

   setEventListeners();

   setDragInfo();
   $('#dragme').draggable({
      containment: '.drag_containment',
      axis: 'x',
      drag: dragSlider
   });

   if ($.browser.msie && $.browser.version == '6.0')
   {
      $('#tlc_reports').find('div.title,div.title2').attr('onclick','')
         .removeClass().addClass('title3').next().show();
   }
   // Add elements for the report settings
   resetReportSettings();

   if (!document.getElementById('busy'))
   {
      elem = document.createElement('div');
      elem.id = 'busy';
      elem.innerHTML = "<div id='busy_box'>"
                     + "<div id='busy_text'>Bezig met laden</div>"
                     + "<div id='busy_img'/>"
                     + "</div>";
      document.body.appendChild(elem);
   }
   if (window.location.search)
   {
      var search = window.location.search;
      if (search.charAt(0) == '?')
      {
         search = search.substr(1);
      }

      var params = search.split('&');
      if (arrayContains(params, 'fullscreen'))
      {
         setFullScreen();
      }
   }

   if ($.browser.safari)
   {
      $('div#content_topleft').attr('style','overflow-y: hidden');
   }

   elem = document.createElement('div');
   elem.id = 'same_address_temp';
   document.getElementById('debug').appendChild(elem);

   elem = null;
 });

/**
 * Break out of the staging frame, and indicate we want the page full screen
 */
function redirectFullScreen()
{
   var parts = window.location.href.split('?');
   if (parts[1])
   {
      parts[1] += '&fullscreen';
   }
   else
   {
      parts[1] = 'fullscreen';
   }
   window.top.location = parts[0] + '?' + parts[1];
}

/**
 * Turns Bas into fullscreen
 */
function setFullScreen()
{
   var height = screen.availHeight-250;
   var percent = height / 100;
   $('div.container').height(height);
   $('div.container').width('100%');
   var topleft = Math.round(percent * 46.66666666667);
   var bottomleft = Math.round(percent * 45);
   var topright = Math.round(percent * 53.333333333);
   var bottomright = Math.round(percent * 38.3333333);

   if((topleft + bottomleft) > (topright + bottomright))
   {
      topright -= ((topleft + bottomleft) - (topright + bottomright));
   }
   else if((topright + bottomright) > (topleft + bottomleft))
   {
      topright -= ((topright + bottomright) - (topleft + bottomleft));
   }

   if($.browser.msie)
   {
      $('#googlemaps').width('100%');
   }
   $('#topleft').height(topleft + 'px');
   $('#content_topleft').height(topleft-20 + 'px');
   $('#bl_client').height(bottomleft + 'px');
   $('#bl_issues').height(bottomleft + 'px');
   $('#bl_messages').height(bottomleft + 'px');
   $('#bl_reports').height(bottomleft + 'px');
   $('#bl_admin').height(bottomleft + 'px');
   $('#topright').height(topright + 'px');
   $('#googlemaps').height(topright-20 + 'px');
   $('#bottomright').height(bottomright + 'px');
   $('#content_bottomright').height(bottomright-20 + 'px');

   setDragInfo();

   $('#fullscreenlink').remove();
}

/**
 * Swings the 'legend-div' in and out
 */
function toggleLegend()
{
   /*if($('.maps_legend_content').width() < 5)
   {
      $('.maps_legend_content').show();
      $('.maps_legend_content').animate( { width: $('#googlemaps').width()-105 },
         1200, 'swing', function(){}); // Empty function here, solves IE 'syntax error'
   }
   else
   {
      $('.maps_legend_content').animate( { width: "1px" }, 1200, 'swing', function() {
         $('.maps_legend_content').hide();
      });
   }*/
}

/**
 * If an email address is filled in on the login tab, ask the server
 * if that address is still available, otherwise proceed directly to
 * the registration tab.
 */
function registerIfEmailAvailable()
{
   var email = $('#tlc_login').find('[name=username]').val();
   if (email)
   {
      basClient.checkEmailAvailable(email);
   }
   else
   {
      loginSettings('register');
   }
   return false;
}

/**
 * Helper class for the login stuff contains several functions for the different types,
 *    showing/hiding the correct divs.
 *
 * @param type   object   Contains the role(s) - ATM only one role can be had
 */
function loginSettings(type)
{
   if (!type)
   {
      type = 'login';
   }

   // They all want to see these
   $('#leftframe').find('div.innercontent:not([role])').show();
   $('#leftframe').find('li:not([role])').show();

   if (user.loggedin)
   {
      // GV: Shouldn't happen?
      return false;
   }

   $('div#report_klagers').hide();

   if (!type.roles)
   {
      if (type == 'register')
      {
         typeSwitchRegister();
      }
      else
      {
         typeSwitchLogin();
      }
   }
   else
   {
      // GV: Find the user role. Since multiple roles can be set, some care has
      // to be taken here. Check in order ADMIN, BAS, BACKOFFICE and CLIENT,
      // switching to the first available interface.
      var usertype = '';
      var alltypes = ['ADMIN', 'BAS', 'BACKOFFICE', 'CLIENT'];

      $('[role=always]').show();

      for (var i = 0; i < alltypes.length; i++)
      {
         if (arrayContains(type.roles, alltypes[i]))
         {
            usertype = alltypes[i];
            break;
         }
      }
      switch (usertype)
      {
      case 'CLIENT':
         typeSwitchUser(type);
         break;
      case 'BAS':
         typeSwitchBas(type);
         break;
      case 'ADMIN':
         typeSwitchAdmin();
         break;
      case 'BACKOFFICE':
         var backoffice_type = '';
         if (arrayContains(type.roles, 'LVNL'))
         {
            backoffice_type = 'lvnl';
         }
         else if (arrayContains(type.roles, 'AAS'))
         {
            backoffice_type = 'aas';
         }
         else if (arrayContains(type.roles, 'RIJK'))
         {
            backoffice_type = 'rijk';
         }
         typeSwitchBackoffice(backoffice_type);
         break;
      default:
         typeSwitchLogin();
      }
   }
   return false;
}

/**
 * Recursively remove all child nodes that have a role attribute that
 * occurs in the given array.
 * @param array roles Names of the roles to remove
 * @param object node The parent node from which to remove matching child nodes
 */
function removeNodeRoles(roles, node)
{
   for (var i = node.childNodes.length - 1; i >= 0; i--)
   {
      var child = node.childNodes[i];
      if (child.nodeType == 1)
      {
         var role = child.getAttribute('role');
         if (role && arrayContains(roles, role))
         {
            node.removeChild(child);
         }
         else
         {
            removeNodeRoles(roles, child);
         }
      }
   }
}

/**
 * Remove all elements from the tree that have a role attribute that
 * occurs in the given array.
 * @param array roles Names of the roles to remove
 */
function removeRoles(roles)
{
   removeNodeRoles(roles, document.documentElement);
   for (var tab_id in tab_parts)
   {
      if (!document.getElementById(tab_id))
      {
         delete tab_parts[tab_id];
      }
   }
}

/**
 * Show/hide/set all the divs/inputs etc for normal users
 *
 * @param type   object
 */
function typeSwitchUser(type)
{
   user.type = 'CLIENT';
   user.is_bbb = false;
   user.loggedin = true;

   getUitvraagXml('CLIENT');

   removeRoles(['login', 'bbb', 'basadm', 'bas', 'admin']);

   $('#leftframe').find('li[role="user"]').show();

   changeButtonText('client_confirm', 'Wijzigen');
   basClient.findClient(type.id);
   fillIssueSearchState('user');
   countIssues();
   countMessages();
   $('#holder_selecties').show();
   $('#selectbar_report').show();
   showReportDiv('#report_klagers');

   toggleTab('tl_client');
   $('div#blc_client_1').show();
   $('div#nav_bl_client').show();

   if($.browser.msie && $.browser.version == '6.0')
   {
      $('div#report_klagers').show();
   }

   basMessage.listServiceMessages();
   addInactivityTimer();
}

/**
 * get the User's data
 *
 * @param result   object
 */
function getUserData(result)
{
   if (result.clients.length == 1)
   {
      showUserDetails(result.clients[0]);
   }
}

/**
 * Show the client-data belonging to the issue
 *
 * @param result   object
 */
function showIssueClient(result)
{
   var phone1, phone2;
   var clientData = '<table>';

   if (result.clients[0])
   {
      var client = result.clients[0];

      clientData += '<tr><td>Naam:</td><td colspan="3">' + (client.firstName
                                                  ? (client.firstName + ' ')
                                                  : '');
      clientData += (client.surNamePrefix
                        ? (client.surNamePrefix + ' ')
                        : '');
      clientData += client.surName + '</td></tr>';
      clientData += '<tr><td>Adres:</td><td colspan="3">' + (client.street
                                                   ? (client.street + ' ' + client.number)
                                                   : '') + '</td></tr>';
      clientData += '<tr><td>Postcode:</td><td>' + client.postalCode;
      clientData += '</td><td>Plaats:</td><td>' + client.city + '</td></tr>';
      if(client.phone1 || client.phone2)
      {
         phone1 = client.phone1 ? client.phone1 : '';
         phone2 = client.phone2 ? client.phone2 : '';
         if (isValidPhoneNumber(phone1))
         {
            phone1 = formatPhoneNumber(phone1);
         }
         if (isValidPhoneNumber(phone2))
         {
            phone2 = formatPhoneNumber(phone2);
         }

         clientData += '<tr><td>Tel (vast):</td><td>' + phone1 + '</td>';
         clientData += '<tr><td>Tel (mobiel):</td><td>' + phone2 + '</td>';
      }
      if (client.email)
      {
         clientData += '<tr><td>Email:</td><td colspan="3">'
                     + client.email
                     + '</td></tr>';
      }
      if (client.basComments)
      {
         clientData += '<tr><td>Opmerkingen:</td><td colspan="3">'
                     + client.basComments.replace(/\n/g, '<br>')
                     + '</td></tr>';
      }
   }
   else
   {
      clientData += '<tr><td>Geen klant gevonden</td></tr>';
   }
   clientData += '</table>';

   document.getElementById('blc_issues_2').innerHTML = clientData;
}

/**
 * Show a issue/message counter @id
 *
 * @param result   into   amount
 * @param id       string counter-id
 */
function showCounter(result, id)
{
   document.getElementById(id).innerHTML = '(' + result + ')';
}

/**
 * Show the user's details
 *
 * @param result   array
 */
function showUserDetails(result)
{
   var clientform = $('#tlc_client');
   for (var x in result)
   {
      var val = result[x] ? result[x] : '';
      if ((x == 'phone1' || x == 'phone2') && isValidPhoneNumber(val))
      {
         val = formatPhoneNumber(val);
      }

      if (user.type == 'ADMIN')
      {
         if (x == 'roles')
         {
            selectorBoxSetValue('roles', result.roles);
         }
         else if (x == 'active')
         {
            $('input[name=active]').attr('checked', result.active);
         }
         else if (x == 'id')
         {
            document.getElementById('clientid').innerHTML = val;
         }
      }

      if (x == 'mailnews' || x == 'mailactual')
      {
         clientform.find('[name='+x+']').attr('checked', result[x]);
      }
      else
      {
         clientform.find('[name='+x+']').val(val);
      }

      if(x == 'email' && (user.type == 'BAS' || user.type == 'ADMIN'))
      {
         activateResetPassword();
      }
   }

   if (user.type == 'BAS' || user.type == 'ADMIN')
   {
      hideOverlay('report');
   }
   if (result.postalCode && result.number)
   {
      var options = { createMarker: true };
      if (!result.street || !result.city)
      {
         options.getAddress = true;
      }
      getAddress(result.postalCode, result.number, options);
   }

   if (user.type == 'BAS')
   {
      if (result.active)
      {
         resetUitvraagScript();
      }
      else
      {
         document.getElementById('blc_client_1').innerHTML = 'Klant is inactief';
      }
   }
}

/**
 * Show/hide/set all the divs/inputs etc for Bas employees
 *
 * @param type   object
 */
function typeSwitchBas(type)
{
   user.type = 'BAS';
   user.is_bbb = true;
   user.loggedin = true;

   getUitvraagXml('BAS');
   basScript.getQuestionClasses();
   removeRoles(['login', 'user', 'admin']);

   $('[role=bbb],li[role=bas],a[role=bas],a[role=basadm],li[role=basadm]').show();
   $('div#holder_sql').show();

   fillIssueSearchState('bas');
   var option = '<option value="NUM_CLIENTS_ISSUES">Melders en meldingen</option>';
   $('#report_resultaat').find('select[name=report_result_sum]').append(option);

   countIssues();
   countMessages();
   $('#holder_selecties').show();
   $('#selectbar_report').show();

   resetClientForm();

   $('div#blc_client_1').show();
   $('div#nav_bl_client').show();

   showReportDiv('#report_klagers');
   $('#report_klagers').find('div.formrow').show();
   toggleTab('tl_client');
   if($.browser.msie && $.browser.version == '6.0')
   {
      $('div#report_klagers').show();
   }
   // Poll the server every 5 minutes to remove locks that are held
   // for too long
   setInterval(basClient.clearOldLocks, 300000);

   basMessage.listServiceMessages();
   basOverlay.listOverlays();
}

/**
 * Show/hide/set all the divs/inputs etc for admins
*/
function typeSwitchAdmin()
{
   user.type = 'ADMIN';
   user.is_bbb = true;
   user.loggedin = true;

   removeRoles(['login', 'user', 'bas']);
   $('[role=bbb],[role=basadm],[role=admin]').show();

   $('#holder_selecties').show();
   $('#selectbar_report').show();

   fillIssueSearchState('admin');
   countIssues();
   countMessages();
   toggleTab('tl_admin');

   if ($.browser.msie && $.browser.version == '6.0')
   {
      showAdminPage('beheer_1_nav');
      $('#holder_sql').css('top','-110px');
      $('div#report_klagers').show();
   }

   setInterval(basClient.clearOldLocks, 300000);

   basMessage.listServiceMessages();
   basOverlay.listOverlays();
}

/**
 * Show/hide/set all the divs/tabs for backoffice
 * @param string type The type of backoffice (LVNL, AAS or RIJK)
*/
function typeSwitchBackoffice(type)
{
   user.type = 'BACKOFFICE';
   user.is_bbb = true;
   user.loggedin = true;

   $('#tl_client').remove();
   removeRoles(['login', 'user', 'basadm', 'bas', 'admin']);

   $('[role=bbb],[role=backoffice]').show();
   $('#holder_selecties').show();
   $('#selectbar_report').show();

   fillIssueSearchState(type);
   countIssues();
   countMessages();

   toggleTab('tl_issues');

   if($.browser.msie && $.browser.version == '6.0')
   {
      $('div#report_klagers').show();
   }

   // Poll the server every 5 minutes to remove locks that are held
   // for too long
   setInterval(basClient.clearOldLocks, 300000);

   basMessage.listServiceMessages();
   basOverlay.listOverlays();
}

/**
 * Show/hide/set all the divs/inputs etc for users registering for the first time
*/
function typeSwitchRegister()
{
   $('div#leftframe').find('div[class=innercontent]').map(function() { $(this).hide(); } );
   $('div#leftframe').find('div[class=innercontent2]').map(function() { $(this).hide(); } );
   $('div#tlc_register').show().removeClass().addClass('innercontent');

   // Copy email address from login tab, if provided
   var email = $('#tlc_login').find('[name=username]').val();
   if (email)
   {
      $('#tlc_register').find('[name=email]').val(email);
   }
   var password = $('#tlc_login').find('[name=password]').val();
   if (password)
   {
      $('#tlc_register').find('[name=newpassword]').val(password);
   }

   $('#tlc_client').parents('.part').find('li[id^=tl_]').map(function(){
      if ($(this).attr('id') != 'tl_register'
         && $(this).attr('id') != 'tl_reports'
         && $(this).attr('id') != 'tl_login')
      {
         $(this).hide();
      }
      else
      {
         $(this).show();
      }
   });
   document.getElementById('logoutlink').style.display = 'none';
   toggleTab('tl_register');
}

/**
 * Show a message when a client registers
 *
 * @param result
 */
function handleClientRegistered(result)
{
   // Clear the registration input fields
   $('#tlc_register').find(':input').val('');

   var msg = "Bedankt voor het registreren bij BAS.\n\n"
      + "Een email met activeringscode is verzonden naar het opgegeven email "
      + "adres. Na activering kunt u inloggen op de BAS site met uw email "
      + "adres en het door u gekozen wachtwoord.";
   alert(msg);
   typeSwitchLogin();
}

/**
 * Show/hide/set all the divs/inputs etc for users when logging in
 */
function typeSwitchLogin()
{
   $('div#leftframe').find('div[class=innercontent]').hide();
   $('#tlc_login').parents('.part').find('li[id^=tl]').map(function(){
      if($(this).attr('id') == 'tl_login' || $(this).attr('id') == 'tl_reports')
      {
         $(this).show();
      }
      else
      {
         $(this).hide();
      }
   });
   $('div#report_klagers').show();
   $('div#tlc_login').show().removeClass().addClass('innercontent');
   document.getElementById('logoutlink').style.display = 'none';
   toggleTab('tl_login');

   var email = getCookie('email');
   var password = getCookie('password');
   if (email)
   {
      $('#tlc_login').find('[name=username]').val(email);
      $('#tlc_login').find('[name=remember_me]').attr('checked', true);
   }
   if (password)
   {
      $('#tlc_login').find('[name=password]').val(password);
   }
}

/**
 * Adds the available states to the state select depending on role
 *
 * @param role string
 */
function fillIssueSearchState(role)
{
   var data;

   switch(role) {
   case 'user':
      data = '<select name="issue_search_state" onchange="searchIssues(false,0);">'
           + '<option value="ALL">Alles</option>'
           + '<option value="OPEN">In behandeling</option>'
           + '<option value="CLOSED">' + getTranslation('CLOSED') + '</option>'
           + '</select>';
      break;
   case 'lvnl':
   case 'aas':
   case 'rijk':
      data = '';
      break;
   case 'admin':
      basScript.listScripts();
      basAdmin.listQueries();
      basAdmin.listPeriodDefinitions();
      basAdmin.listSettings();
   case 'bas':
      $('[name=message_search_type] option[value=SERVICE]').attr('selected', true);
      basMessage.listStoredQueries();
   default:
      data = '<select name="issue_search_state" onchange="searchIssues(false,0);">'
           + '<option value="ALL">' + getTranslation('ALL') + '</option>'
           + '<option value="NEW">' + getTranslation('NEW') + '</option>'
           + '<option value="NOTCOUPLED">' + getTranslation('NOTCOUPLED') + '</option>'
           + '<option value="INVESTIGATE">' + getTranslation('INVESTIGATE') + '</option>'
           + '<option value="BAS" selected>' + getTranslation('BAS') + '</option>'
           + '<option value="AAS">' + getTranslation('AAS') + '</option>'
           + '<option value="LVNL">' + getTranslation('LVNL') + '</option>'
           + '<option value="RIJK">' + getTranslation('RIJK') + '</option>'
           + '<option value="RESPOND">' + getTranslation('RESPOND') + '</option>'
           + '<option value="CLOSED">' + getTranslation('CLOSED') + '</option>'
           + '<option value="REJECTED">' + getTranslation('REJECTED') + '</option>'
           + '<option value="DELETED">' + getTranslation('DELETED') + '</option>'
           + '</select>';
      break;
   }
   document.getElementById('logoutlink').style.display = 'inline';
   $('td[name=issue_search_state]').html(data);
}

/**
 * Shows the stores queries in the select box
 *
 * @param result
 */
function showStoredQueries(result)
{
   if (result)
   {
      var data = '';
      for (var x in result)
      {
         data += '<option value="' + result[x].id + '">' + result[x].title + '</option>';
      }

      if(data.length > 0)
      {
         $('select#message_queries').append(data);
      }
   }
}

/**
 * Change the class of this sort button to current, and return all other
 * sort buttons in the same row to normal.
 * @param object The button to make current
 */
function toggleSortButton(button)
{
   $(button).parents('tr:first').find('.up_cur').removeClass('up_cur').addClass('up');
   $(button).parents('tr:first').find('.down_cur').removeClass('down_cur').addClass('down');
   if ($(button).hasClass('up'))
   {
      $(button).removeClass('up').addClass('up_cur');
   }
   else
   {
      $(button).removeClass('down').addClass('down_cur');
   }
}

/**
 * Search the issues ordering up or down by a column, and make the
 * corresponding sort button current.
 * @param object button  The sort button to make current
 * @param string orderby The field to sort by
 * @param string order   The order to sort in ('ASC' or 'DESC')
 */
function toggleIssueSearch(button, orderby, order)
{
   toggleSortButton(button);

   issue_search.orderby = orderby;
   issue_search.order = order;
   searchIssues(false, 0);
}

function searchIssuesDate(date)
{
   searchIssues(false, 0);
}

/**
 * Get a count of the total number of issues matching the current
 * search settings
 */
function countIssues()
{
   searchIssues(true);
}

/**
 * Performs a search through the issuelist
 */
function searchIssues(countonly, start)
{
   if (typeof start != 'undefined')
   {
      issue_search.start = start;
   }

   var issue = {};
   try
   {
      var cdate = dateFieldGetValue('issue_search_cdate', true);
      if (cdate)
      {
         issue.cdate = cdate;
      }
   }
   catch (exc)
   {
      handleError([exc],["$('#issue_search_cdate input')"]);
   }
   try
   {
      var date = dateFieldGetValue('issue_search_date', true);
      if (date)
      {
         issue.date = date;
      }
   }
   catch (exc)
   {
      handleError([exc],["$('#issue_search_date input')"]);
   }
   var pc = $('input[name=issue_search_postalCode]').val();
   if (pc)
   {
      issue.postalCode = pc;
   }
   var type = $('select[name=issue_search_type] option:selected').val();
   if (type)
   {
      issue.type = type;
   }
   var fb = $('select[name=issue_search_feedback] option:selected').val();
   if (fb)
   {
      issue.feedback = fb;
   }
   var state = $('select[name=issue_search_state] option:selected').val();
   if (state)
   {
      issue.state = state;
   }

   document.getElementById('blc_issues_1').innerHTML = 'Selecteer hierboven een melding';
   if (user.type == 'BAS')
   {
      document.getElementById('blc_issues_2').innerHTML = 'Klantgegevens';
   }

   if (countonly)
   {
      basIssue.countIssues(issue, false);
   }
   else
   {
      basIssue.listIssues(issue, issue_search.orderby, issue_search.order,
         issue_search.start, issue_search.page_size, false);
      toggleTab('br_4');
      $('#br_8').hide();
      $('#br_3').hide();
   }
}

/**
 * Search the issues for the currently selected user ordering up or down
 * by a column, and make the corresponding sort button current.
 * @param object button  The sort button to make current
 * @param string orderby The field to sort by
 * @param string order   The order to sort in ('ASC' or 'DESC')
 */
function toggleIssueSearchBl2(button, orderby, order)
{
   toggleSortButton(button);

   clientissue_search.orderby = orderby;
   clientissue_search.order = order;
   searchIssuesBl2(false, 0);
}

/**
 * Count the total number of issues for the currently selected user
 */
function countIssuesBl2()
{
   searchIssuesBl2(true);
}

/**
 * Searches for issues for the bottomleft Issuelist
 *
 * @param orderby
 * @param start
 * @param order
 */
function searchIssuesBl2(countonly, start)
{
   if (typeof start != 'undefined')
   {
      clientissue_search.start = start;
   }

   var client_id = '';
   if ($('form#client_form').find('#user_id').val())
   {
      client_id = $('form#client_form').find('#user_id').val();
   }

   var issue = {client: client_id, type: '', state: 'ALL'};
   if (countonly)
   {
      basIssue.countIssues(issue, true);
   }
   else
   {
      basIssue.listIssues(issue, clientissue_search.orderby,
         clientissue_search.order, clientissue_search.start,
         clientissue_search.page_size, true);
   }
}

/**
 * Search the messages ordering up or down by a column, and make the
 * corresponding sort button current.
 * @param object button  The sort button to make current
 * @param string orderby The field to sort by
 * @param string order   The order to sort in ('ASC' or 'DESC')
 */
function toggleMessageSearch(button, orderby, order)
{
   toggleSortButton(button);

   message_search.orderby = orderby;
   message_search.order = order;
   searchMessages(false, 0);
}

function searchMessagesDate(date)
{
   searchMessages(false, 0);
}

/**
 * Count the total number of messages matching the current search settings
 */
function countMessages()
{
   searchMessages(true);
}

/**
 * Performs a search through the messagelist
 *
 * @param orderby
 */
function searchMessages(countonly, start)
{
   if (typeof start != 'undefined')
   {
      message_search.start = start;
   }

   var message = {};
   try
   {
      var cdate = dateFieldGetValue('message_search_date', true);
      if (cdate)
      {
         message.cdate = cdate;
      }
   }
   catch (exc)
   {
      handleError([exc],["$('#message_search_date input')"]);
   }
   var type = $('select[name=message_search_type] option:selected').val();
   if (type)
   {
      message.type = type;
   }
   var title = $('input[name=message_search_title]').val();
   if (title)
   {
      message.title = $('input[name=message_search_title]').val();
   }
   var state = $('select[name=message_search_state] option:selected').val();
   if (state)
   {
      message.state = state;
   }

   clearMessageDetails();

   if (countonly)
   {
      basMessage.countMessages(message);
   }
   else
   {
      basMessage.listMessages(message, message_search.orderby,
         message_search.order, message_search.start, message_search.page_size);
   }
}

/**
 * Display the list of service messages returned by the server
 * @param array $result The list of service messages
 */
function showServiceMessages(result, empty_msg)
{
   if (!empty_msg)
   {
      empty_msg = 'Geen berichten gevonden';
   }

   var messagelist = '';
   for (var i = 0; i < result.messages.length; i++)
   {
      var title = result.messages[i].title ? result.messages[i].title : '';
      var content = '';
      if (result.messages[i].content)
      {
         content = result.messages[i].content.replace(/\n/g, '<br>');
      }
      if (result.messages[i].url)
      {
         content += '<a href="' + result.messages[i].url + '">' + result.messages[i].url + '</a>';
      }

      messagelist += '<tr id="servicemsg_' + result.messages[i].id + '" class="titlerow">'
                   +    '<td>' + title + '</td>'
                   + '</tr>'
                   + '<tr class="contentrow">'
                   +    '<td>' + content + '</td>'
                   + '</tr>';
   }

   if (!messagelist)
   {
      if (result.count === 0)
      {
         messagelist = '<tr class="contentrow"><td><i>' + empty_msg + '</i></td></tr>';
      }
      else
      {
         messagelist = '<tr class="contentrow"><td><i>Fout bij het laden van berichten</i></td></tr>';
      }
   }

   var table = '<table class="servicemsg">' + messagelist + '</table>';
   document.getElementById('brc_4').innerHTML = table;
}

/**
 * Create all the event listeners
 *
 * TBD : The input-listeners need to be expanded to contain all given overlays
 * TBD : Form-input listeners need to be expanded to validate/query stuff,
 *       perhaps merge into one input.blur
 */
function setEventListeners()
{
   // Form input elements

   $('input').each(function() {
      this.onblur = new Function('focused_element = null;');
      this.onfocus = new Function('focused_element = this;');
   });
   $('#tlc_client,#tlc_register').find('input').each(function() {
      this.onblur = new Function('inputBlur(this);');
      this.onfocus = new Function('focused_element = this; user.dont_search_user = false;');
   });
   document.getElementById('username').onkeypress = usernameKeyPress;
   document.getElementById('password').onkeypress = passwordKeyPress;

   // Show/hide the chosen overlay
   $('div.maps_options').find('[type=checkbox]').click(function() {
      if (!$(this).attr('checked'))
      {
         hideOverlay($(this).attr('name'));
      }
      return false;
   });
}

/**
 * Build the table information shown at the top of the issue details tab.
 * @param array issue The issue details returned by the server
 * @return object containing the table
 */
function buildIssueDetailsTitleTable(issue)
{
   var table = document.createElement('table');

   var row = table.insertRow(-1);
   if (issue.date)
   {
      var date = formatDBDate(issue.date);

      if (issue.time)
      {
         date += ' ' + issue.time;
      }
      row.insertCell(-1).innerHTML = '<label>Datum:</label>';
      row.insertCell(-1).innerHTML = date;
   }
   else
   {
      row.insertCell(-1);
      row.insertCell(-1);
   }
   if (issue.postalCode)
   {
      row.insertCell(-1).innerHTML = '<label>Postcode:</label>';
      row.insertCell(-1).innerHTML = issue.postalCode;
   }

   row = table.insertRow(-1);
   if (issue.id)
   {
      row.insertCell(-1).innerHTML = '<label>ID:</label>';
      row.insertCell(-1).innerHTML = issue.id;
   }
   else
   {
      row.insertCell(-1);
      row.insertCell(-1);
   }
   if (issue.type)
   {
      row.insertCell(-1).innerHTML = '<label>Melding type:</label>';
      var type = issue.type;
      switch (issue.type)
      {
      case 'COMPLAINT':
         if (issue.complaintType)  // PERIOD, SPECIFIC, GENERIC
         {
            type = getTranslation(issue.complaintType) + ' klacht';
         }
         else
         {
            type = 'Klacht';
         }
         break;
      case 'QUESTION':
         type = 'Vraag';
         break;
      case 'COMMENT':
         type = 'Opmerking';
         break;
      case 'POLL':
         type = 'Enquête';
         break;
      }
      row.insertCell(-1).innerHTML = type;
   }

   row = table.insertRow(-1);
   if (issue.state)
   {
      row.insertCell(-1).innerHTML = '<label>Huidige status:</label>';
      row.insertCell(-1).innerHTML = getTranslation(issue.state);
   }
   else
   {
      row.insertCell(-1);
      row.insertCell(-1);
   }
   if (issue.source)
   {
      row.insertCell(-1).innerHTML = '<label>Ingediend via:</label>';
      row.insertCell(-1).innerHTML = getTranslation(issue.source);
   }

   if (user.type == 'BAS' && issue.scriptOutput
      && issue.scriptOutput.vraag_over)
   {
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Vraag over:</label>';
      row.insertCell(-1).innerHTML = question_about_options[issue.scriptOutput.vraag_over];
   }

   if (issue.scriptOutput && issue.scriptOutput.comment)
   {
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Opmerkingen:</label>';
      var cell = row.insertCell(-1);
      cell.colSpan = 3;
      cell.innerHTML = issue.scriptOutput.comment;
   }

   return table;
}

/**
 * Build the table containing BAS-only information for an issue (issue
 * creation time, time of last update, and issue log).
 * @param array issue The issue details returned by the server
 * @return object containing the details table
 */
function buildIssueDetailsDetailsDiv(issue)
{
   var table = document.createElement('table');
   var row;

   if (issue.udate)
   {
      var udate = formatDBDate(issue.udate);
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Laatste wijziging:</label>';
      row.insertCell(-1).innerHTML = udate;
   }
   if (issue.cdate)
   {
      var cdate = formatDBDate(issue.cdate);
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Aangemaakt op:</label>';
      row.insertCell(-1).innerHTML = cdate;
   }
   if (issue.transponderCode)
   {
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Transponder code:</label>';
      row.insertCell(-1).innerHTML = issue.transponderCode;
   }
   if (issue.log)
   {
      var log = issue.log.replace(/\n/g, '<br/>');
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Log:</label>';
      row.insertCell(-1).innerHTML = log;
   }

   var outerDiv = document.createElement('div');

   var legendDiv = document.createElement('div');
   legendDiv.className = 'legend';
   legendDiv.innerHTML = 'Details';
   outerDiv.appendChild(legendDiv);

   var contentDiv = document.createElement('div');
   contentDiv.className = 'issue_detail_content';
   contentDiv.id = 'issue_detail_content';
   contentDiv.appendChild(table);
   outerDiv.appendChild(contentDiv);

   return outerDiv;
}

/**
 * Build the table with script output for an issue.
 * @param array script The script output as returned by the server.
 * @return object containing the script output
 */
function buildIssueDetailsScriptDiv(script)
{
   var dontShow = ['date', 'day', 'month', 'year', 'time', 'hour', 'minute',
                   'medium', 'feedback', 'type', 'complaintType',
                   'poll_message', 'vraag_over'];

   var table = document.createElement('table');
   var row;
   if (script.date)
   {
      var date = formatDBDate(script.date);
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Datum:</label>';
      row.insertCell(-1).innerHTML = date;
   }
   else if (script.day || script.month && script.year)
   {
      // Old script output, day/month/year stored separately
      var day = parseInt(script.day, 10);
      if (day < 10)
      {
         day = '0' + day;
      }
      var month = parseInt(script.month, 10);
      if (month < 10)
      {
         month = '0' + month;
      }
      var year = parseInt(script.year, 10);
      if (year < 70)
      {
         year += 2000;
      }
      else if (year < 1970)
      {
         year += 1900;
      }

      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Datum:</label>';
      row.insertCell(-1).innerHTML = day + '-' + month + '-' + year;
   }

   if (script.time)
   {
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Tijd:</label>';
      row.insertCell(-1).innerHTML = script.time;
   }
   else if (script.hour)
   {
      // Old script output, hour/minute stored separately
      var hour = parseInt(script.hour, 10);
      if (hour < 10)
      {
         hour = '0' + hour;
      }
      var minute = parseInt(script.minute, 10);
      if (minute < 10)
      {
         minute = '0' + minute;
      }

      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Tijd:</label>';
      row.insertCell(-1).innerHTML = hour + ':' + minute;
   }

   var normalTypes = ['QUESTION', 'COMPLAINT', 'COMMENT', 'POLL'];
   if (user.is_bbb && script.type && !arrayContains(normalTypes, script.type))
   {
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Melding type:</label>';
      row.insertCell(-1).innerHTML = script.type;
   }

   for (var x in script)
   {
      if (arrayContains(dontShow, x))
      {
         continue;
      }

      row = table.insertRow(-1);
      switch (x)
      {
      case 'comment':
         row.insertCell(-1).innerHTML = '<label>Opmerkingen:</label>';
         var cell = row.insertCell(-1);
         cell.colSpan = 3;
         cell.innerHTML = script[x];
         break;
      case 'question':
         row.insertCell(-1).innerHTML = '<label>Vraag:</label>';
         row.insertCell(-1).innerHTML = script[x];
         break;
      case 'periods':
         row.insertCell(-1).innerHTML = '<label>Periodes:</label>';
         row.insertCell(-1).innerHTML = script[x];
         break;
      default:
         var newTitle = x.charAt(0).toUpperCase() + x.substr(1).replace(/_/g, ' ');
         var newString = script[x].charAt(0) + script[x].substr(1).replace(/_/g, ' ');
         row.insertCell(-1).innerHTML = '<label>' + newTitle + ':</label>';
         row.insertCell(-1).innerHTML = newString;
      }
   }

   var outerDiv = document.createElement('div');

   var legendDiv = document.createElement('div');
   legendDiv.className = 'legend';
   legendDiv.innerHTML = 'Ingevoerde meldinggegevens';
   outerDiv.appendChild(legendDiv);

   var contentDiv = document.createElement('div');
   contentDiv.className = 'issue_script_content';
   contentDiv.id = 'issue_script_content';
   contentDiv.appendChild(table);
   outerDiv.appendChild(contentDiv);

   return outerDiv;
}

/**
 * Build the selector for changing the state of an issue
 * @param array issue The issue for which to build the selector
 * @return object containing the selector
 */
function buildStateChangeSelect(issue)
{
   var actions;
   var texts;
   if (issue.state == 'INVESTIGATE')
   {
      actions = ['BAS', 'AAS', 'LVNL', 'RIJK'];
      texts = ['BAS', 'AAS', 'LVNL', 'RIJK'];
   }
   else if (issue.state == 'RESPOND')
   {
      actions = ['CLOSED', 'DELETED', 'REJECTED'];
      texts = ['Afgehandeld', 'Niet te koppelen', 'Niet ontvankelijk'];
   }
   else if (issue.state == 'BAS' && (user.type == 'BAS' || user.type == 'ADMIN'))
   {
      actions = ['BAS', 'LVNL', 'AAS', 'RIJK'];
      texts = ['BAS', 'LVNL', 'AAS', 'RIJK'];
   }
   else if (user.type == 'BAS')
   {
      actions = ['BAS', 'AAS', 'LVNL', 'RIJK'];
      texts = ['BAS', 'AAS', 'LVNL', 'RIJK'];
   }
   else
   {
      actions = ['BAS', issue.state];
      texts = ['BAS', issue.state];
   }
   var radio = '';
   for (var i = 0; i < actions.length; i++)
   {
      radio += '<input type="radio" name="change_state" value="' + actions[i] +
               '" ' + (actions[i] == issue.state ? 'checked' : '') + '>' + texts[i];
   }

   return radio;
}

/**
 * Check if an issue state is closed, i.e. one of 'CLOSED', 'DELETED'
 * or 'REJECTED'.
 * @param string state The state to check
 * @return bool true if state is a closed state, false otherwise
 */
function isClosedIssueState(state)
{
   var closedStates = ['CLOSED', 'DELETED', 'REJECTED'];
   return arrayContains(closedStates, state);
}

/**
 * Build the selector for classifying the questions
 * @return strin containing HTML for the radio buttons
 */
function buildQuestionAbout(issue)
{
   var sel_opt = '';
   if (issue.scriptOutput && issue.scriptOutput.vraag_over)
   {
      sel_opt = issue.scriptOutput.vraag_over;
   }

   var ncol = 2;
   var icol = 0;
   var buttons = '';
   for (var key in question_about_options)
   {
      var checked = (key == sel_opt ? ' checked="checked"' : '');
      if (icol === 0)
      {
         // start a new row
         buttons += '<tr>';
      }

      buttons += '<td><input type="radio" name="question_about" value="'
         + key + '" ' + checked + '/></td><td>' + question_about_options[key]
         + '</td>';
      if (++icol == ncol)
      {
         // end this row
         buttons += '</tr>';
         icol = 0;
      }
   }
   if (icol > 0)
   {
      // Close the last row
      buttons += '</tr>';
   }

   return '<table id="question_about_buttons">' + buttons + '</table>';
}

/**
 * Set the number of response attempts, and hide or show GUI elements
 * depending on the value. The first two bits of count are reserved for
 * a succesful contact attempt: if the first bit is set, a card was sent
 * to the client, if the second bit is set the client was contacted by
 * telephone. The remaining 6 bits indicate the number of failed contact
 * attempts.
 * @param int count The response count as returned by the server
 */
function setNrResponseAttempts(count)
{
   var delivered = '';
   if (count & 0x40)
      delivered = ' (telefonisch bereikt)';
   else if (count & 0x80)
      delivered = ' (kaartje gestuurd)';
   count &= 0x3f;

   document.getElementById('nr_response_attempts').innerHTML = count + delivered;
   if (!delivered)
   {
      if (count < NR_RESPONSE_ATTEMPTS)
      {
         document.getElementById('response_buttons_row').style.display = '';
         document.getElementById('response_failed_row').style.display = 'none';
      }
      else
      {
         document.getElementById('response_buttons_row').style.display = 'none';
         document.getElementById('response_failed_row').style.display = '';
      }
      document.getElementById('change_state_row').style.display = 'none';
      document.getElementById('issue_change_buttons').style.display = 'none';
   }
   else
   {
      document.getElementById('response_buttons_row').style.display = 'none';
      document.getElementById('response_failed_row').style.display = 'none';
      document.getElementById('change_state_row').style.display = '';
      document.getElementById('issue_change_buttons').style.display = '';
   }
}

/**
 * Build the table containing the issue state and causes, with selector
 * boxes to change these if the user is authorised to do so.
 * @param array issue The issue details as returned by the server
 * @return object conataining the table with state and causes
 */
function buildIssueDetailsStateTable(issue)
{
   // Causes
   var cause_fields = [];
   var certainty = null;
   var coupling = null;
   var cause, fields, i;

   if (issue.complaintType != 'PERIOD' && issue.causes
       && issue.causes.length > 0)
   {
      cause = issue.causes[0];
      fields = [];
      if (cause.runway)
      {
         fields.push('Baan:&nbsp;' + getTranslation(cause.runway));
      }
      if (cause.direction)
      {
         fields.push('Richting:&nbsp;' + getTranslation(cause.direction));
      }
      if (cause.groundNoise)
      {
         fields.push('Grondlawaai:&nbsp;' + getTranslation(cause.groundNoise));
      }
      cause_fields.push(fields.join(', '));
      if (cause.certainty)
      {
         certainty = Math.round(100*cause.certainty) + '%';
      }
      if (cause.coupling)
      {
         coupling = getTranslation('COUPLING_' + cause.coupling);
      }
   }
   else if (issue.complaintType == 'PERIOD' && issue.period_causes
            && issue.period_causes.length > 0)
   {
      for (i = 0; i < issue.period_causes.length; i++)
      {
         cause = issue.period_causes[i];
         fields = [];
         if (cause.runway)
         {
            fields.push('Baan:&nbsp;' + getTranslation(cause.runway+''));
         }
         if (cause.direction)
         {
            fields.push('Richting:&nbsp;' + getTranslation(cause.direction));
         }
         if (cause.groundNoise)
         {
            fields.push('Grondlawaai:&nbsp;' + getTranslation(cause.groundNoise));
         }
         cause_fields.push(cause.period + ': ' + fields.join(', '));
      }
   }

   var editable = user.is_bbb && !isClosedIssueState(issue.state);
   var show_buttons = (editable || user.type == 'ADMIN');

   var table = document.createElement('table');
   var table2, buttons;
   var row, cell;
   if (show_buttons)
   {
      buttons = '';
   }
   var nr_response_attempts = null;

   if ((issue.complaintType == 'PERIOD' || issue.complaintType == 'SPECIFIC')
       && issue.state != 'NOTCOUPLED' && issue.state != 'NEW')
   {
      row = table.insertRow(-1);
      row.insertCell(-1).innerHTML = '<label>Oorzaak:</label>';
      row.insertCell(-1).innerHTML =
         cause_fields.length > 0 ? cause_fields.join('<br/>') : '<i>onbekend</i>';

      if (certainty !== null && user.is_bbb)
      {
         row = table.insertRow(-1);
         row.insertCell(-1).innerHTML = '<label>Zekerheid:</label>';
         row.insertCell(-1).innerHTML = certainty;
      }

      if (coupling !== null && user.is_bbb)
      {
         row = table.insertRow(-1);
         row.insertCell(-1).innerHTML = '<label>Koppeling:</label>';
         row.insertCell(-1).innerHTML = coupling;
      }
   }

   if (issue.state == 'RESPOND')
   {
      row = table.insertRow(-1);
      row.id = 'issue_feedback_block';
      cell = row.insertCell(-1);
      cell.innerHTML = '<label>Terugmelden:</label>';
      cell = row.insertCell(-1);
      cell.id = 'issue_feedback_value';

      if (issue.feedback == 'YES' && issue.prefFeedback)
      {
         switch (issue.prefFeedback)
         {
         case 'EMAIL':
            cell.innerHTML = 'Per email';
            break;
         case 'POST':
            cell.innerHTML = 'Per post';
            break;
         case 'PHONE':
            var fb;
            if (issue.phone1 && isValidPhoneNumber(issue.phone1))
            {
               fb = formatPhoneNumber(issue.phone1);
            }
            else if (issue.phone2 && isValidPhoneNumber(issue.phone2))
            {
               fb = formatPhoneNumber(issue.phone2);
            }
            else
            {
               fb = 'Per telefoon (<i>nummer onbekend</i>)';
            }
            cell.innerHTML = fb;
            break;
         default:
            cell.innerHTML = getTranslation(issue.prefFeedback);
         }
      }
      else if (issue.feedback)
      {
         cell.innerHTML = getTranslation(issue.feedback);
      }
      else
      {
         cell.innerHTML = '<i>onbekend</i>';
      }

      if (issue.feedback && issue.feedback == 'YES'
         && issue.prefFeedback == 'PHONE'
         && issue.responseAttempts)
      {
         nr_response_attempts = parseInt(issue.responseAttempts, 10);

         row = table.insertRow(-1);
         row.insertCell(-1).innerHTML = '<label>Contactpogingen:</label>';
         row.insertCell(-1).innerHTML = '<span id="nr_response_attempts"></span>';

         row = table.insertRow(-1);
         row.id = 'response_buttons_row';
         row.insertCell(-1);
         var onclick_inc   = 'basIssue.incResponseAttempts(' + issue.id + '); return false;'
         var onclick_phone = 'basIssue.setResponseDelivered(' + issue.id + ', "PHONE"); return false;'
         var onclick_card  = 'basIssue.setResponseDelivered(' + issue.id + ', "CARD"); return false;'
         row.insertCell(-1).innerHTML
            = '<div class="input">'
            +    createButton('Neemt niet op', '#', {onclick: onclick_inc})
            + '</div>'
            + '<div class="input">'
            +    createButton('Klant bereikt', '#', {onclick: onclick_phone})
            + '</div>';

         row = table.insertRow(-1);
         row.id = 'response_failed_row';
         row.insertCell(-1);
         row.insertCell(-1).innerHTML
            = '<div class="input">'
            +    createButton('Kaartje gestuurd', '#', {onclick: onclick_card})
            + '</div>';
      }
   }

   if (editable)
   {
      var have_unassessed_messages = false;
      if (user.type == 'BAS' && issue.messages)
      {
         for (i = 0; i < issue.messages.length; i++)
         {
            var message = issue.messages[i];
            if (message.office && message.office != 'BAS' && !message.quality)
            {
               have_unassessed_messages = true;
               break;
            }
         }
      }

      var show_question_about
         = user.type == 'BAS' && issue.type == 'QUESTION'
            && issue.state == 'BAS';
      var question_about_unset = show_question_about
         && !(issue.scriptOutput && issue.scriptOutput.vraag_over);
      var show_change_state
         = !have_unassessed_messages
            && !question_about_unset
            && issue.state != 'NOTCOUPLED'
            && issue.state != 'NEW';

      if (show_question_about || show_change_state)
      {
         if (show_question_about)
         {
            row = table.insertRow(-1);
            row.insertCell(-1).innerHTML = '<label>Vraag over:</label>';
            row.insertCell(-1).innerHTML = buildQuestionAbout(issue);
         }
         if (show_change_state)
         {
            row = table.insertRow(-1);
            row.id = 'change_state_row';
            row.insertCell(-1).innerHTML = '<label>'+(issue.state == 'RESPOND' ? 'Status melding' : 'Actiehouder')+':</label>';
            row.insertCell(-1).innerHTML = buildStateChangeSelect(issue);
         }

         var onclick = '';
         if (show_question_about && show_change_state)
         {
            onclick = 'setQuestionAbout(' + issue.id + ', $("[name=change_state]:checked").val()); return false';
         }
         else if (show_question_about)
         {
            onclick = 'setQuestionAbout(' + issue.id + '); return false';
         }
         else
         {
            onclick = 'changeState(' + issue.id + ', $("[name=change_state]:checked").val()); return false';
         }

         buttons += '<div class="input">'
                  +    createButton("Opslaan", '#', { onclick: onclick })
                  + '</div>';
      }

      if (!have_unassessed_messages)
      {
         var attrs;
         if (!question_about_unset && issue.state == 'BAS')
         {
            var msgtabs = buildIssueMessages(issue.id, issue.messages);
            if (msgtabs.replies || msgtabs.attachments || msgtabs.bestpractices)
            {
               attrs = {
                  id: 'feedback_issue',
                  onclick: 'changeState(' + issue.id + ', "RESPOND"); return false;'
               };
               buttons += '<div class="input">'
                        +    createButton('Klaar voor terugmelden', '#', attrs)
                        + '</div>';
            }
         }
         if (issue.complaintType == 'SPECIFIC'
            && (issue.state == 'NOTCOUPLED' || issue.state == 'NEW'))
         {
            attrs = {
               id: 'link_auto_issue',
               onclick: 'linkFlight(' + issue.id + '); return false;'
            };
            buttons += '<div class="input">'
                     +    createButton('Koppel vlucht', '#', attrs)
                     + '</div>';
         }
         if (issue.state == 'NOTCOUPLED')
         {
            attrs = {
               id: 'investigate_issue',
               onclick: 'changeState(' + issue.id + ', "INVESTIGATE"); return false;'
            };
            buttons += '<div class="input">'
                     +    createButton('Onderzoeken', '#', attrs)
                     + '</div>';

            attrs = {
               id: 'onjuist_issue',
               onclick: 'changeState(' + issue.id + ', "DELETED"); return false;'
            };
            buttons += '<div class="input">'
                     +    createButton('Niet te koppelen', '#', attrs)
                     + '</div>';
         }
         if (issue.state == 'INVESTIGATE' || issue.state == 'NEW')
         {
            attrs = {
               id: 'false_issue',
               onclick: 'changeState(' + issue.id + ', "REJECTED"); return false;'
            };
            buttons += '<div class="input">'
                     +    createButton('Niet ontvankelijk', '#', attrs)
                     + '</div>';
         }
         if (!question_about_unset && issue.type == 'QUESTION' &&
            (issue.state == 'INVESTIGATE' || issue.state == 'BAS'))
         {
            attrs = {
               id: 'close_issue',
               onclick: 'changeState(' + issue.id + ', "CLOSED"); return false;' // TBD Closed/rejected
            };
            buttons += '<div class="input">'
                     +    createButton('Sluiten zonder melding', '#', attrs)
                     + '</div>';
         }
      }
   }

   if (user.type == 'ADMIN' && isClosedIssueState(issue.state))
   {
      var reopen_notcoupled_attrs = {
         id: 'reopen_notcoupled_issue',
         onclick: 'changeState(' + issue.id + ', "NOTCOUPLED"); return false;'
      };
      var reopen_investigate_attrs = {
         id: 'reopen_investigate_issue',
         onclick: 'changeState(' + issue.id + ', "INVESTIGATE"); return false;'
      };

      if(issue.complaintType == 'SPECIFIC')
      {
         buttons += '<div class="input">'
                  +    createButton('Heropen', '#', reopen_notcoupled_attrs)
                  + '</div>';
      }
      else if (issue.complaintType == 'PERIOD'
         || issue.complaintType == 'GENERIC'
         || issue.type == 'QUESTION')
      {
         buttons += '<div class="input">'
                  +    createButton('Heropen','#',reopen_investigate_attrs)
                  + '</div>';
      }
   }

   var stateDiv = document.createElement('div');
   stateDiv.appendChild(table);
   if (show_buttons)
   {
      row = table.insertRow(-1);
      row.id = 'issue_change_buttons';
      row.insertCell(-1);
      row.insertCell(-1).innerHTML = buttons;
   }

   if (nr_response_attempts !== null)
   {
      setTimeout(function() {setNrResponseAttempts(nr_response_attempts)}, 50);
   }

   return stateDiv;
}

/**
 * Build the lists of messages attached to an issue. This creates tables
 * for the different types of messages that are shown in the issue details tab.
 * The exception are the service messages which are shown in a different
 * tab and are handled by showServiceMessages().
 * @param int issue_id   The ID of the issue
 * @param array messages The messages belonging to this issue
 * @return array with table objects for reply, attachment and best practice
 *    messages, and a new array with service messages.
 */
function buildIssueMessages(issue_id, messages)
{
   var tables = {
      replies: null,
      bestpractices: null,
      services: {count: 0, messages: []}
   };

   var row, cell;
   for (var z in messages)
   {
      var message = messages[z];

      var content = '';
      if (message.content)
      {
         content = message.content.replace(/\n/g, '<br>');
      }
      var title = '<label>' + (message.title ? message.title : '') + '</label>';
      var scope = '';
      if (user.is_bbb && message.scope)
      {
         var scopes = message.scope.split(',');
         for (var i in scopes)
         {
            scopes[i] = getTranslation('SCOPE_' + scopes[i]);
         }
         scope = '(' + scopes.join(', ') + ')';
      }

      switch (message.type)
      {
      case 'RESPONSE':
      case 'ATTACHMENT':
         if (!tables.replies)
         {
            tables.replies = document.createElement('table');
         }
         row = tables.replies.insertRow(-1);
         row.insertCell(-1).innerHTML = title;
         row.insertCell(-1).innerHTML = scope;
         if (content)
         {
            row = tables.replies.insertRow(-1);
            cell = row.insertCell(-1);
            cell.colSpan = 2;
            cell.innerHTML = content;
         }
         if (message.attachments && message.attachments.length > 0)
         {
            for (var y in message.attachments)
            {
               row = tables.replies.insertRow(-1);
               cell = row.insertCell(-1);
               cell.colSpan = 2;
               cell.innerHTML =
                    '<a class="textlink" href="attachment.php?id=' + message.attachments[y].id + '" target="_blank">'
                  +    message.attachments[y].title
                  + '</a>';
            }
         }
         if (user.type == 'BAS' && message.office && message.office != 'BAS')
         {
            row = tables.replies.insertRow(-1);
            row.insertCell(-1).innerHTML = '<label>' + getTranslation('quality') + '</label>';
            var name = 'response_quality_' + message.id;
            var checked = {
               OK: '',
               INSUFFICIENT: '',
               RETURNED: ''
            };
            if (message.quality)
            {
               checked[message.quality] = ' checked="checked"';
            }
            row.insertCell(-1).innerHTML
               = '<input type="radio" name="' + name + '"' + checked.OK + ' value="OK" />'
               +    translateResponseQuality('OK') + ' '
               + '<input type="radio" name="' + name + '"' + checked.INSUFFICIENT + ' value="INSUFFICIENT" />'
               +    translateResponseQuality('INSUFFICIENT') + ' '
               + '<input type="radio" name="' + name + '"' + checked.RETURNED + ' value="' + message.office + '" />'
               +    translateResponseQuality('RETURNED');

            row = tables.replies.insertRow(-1);
            row.insertCell(-1);
            var save_attrs = {
               onclick: 'setResponseQuality(' + issue_id + ',' + message.id + '); return false;'
            };
            row.insertCell(-1).innerHTML = createButton('Opslaan', '#', save_attrs);
         }
         break;
      case 'BESTPRACTICE':
         if (!tables.bestpractices)
         {
            tables.bestpractices = document.createElement('table');
         }
         row = tables.bestpractices.insertRow(-1);
         row.insertCell(-1).innerHTML = '<label>' + title + '</label>';
         row = tables.bestpractices.insertRow(-1);
         row.insertCell(-1).innerHTML = content;
         break;
      case 'SERVICE':
         tables.services.messages.push(message);
         tables.services.count++;
      }
   }

   return tables;
}

/**
 * Set the quality of a response given by a backoffice.
 * @param int id The ID of the message for which to set the quality.
 */
function setResponseQuality(issue_id, message_id)
{
   var quality = $('#blc_issues_1 [name=response_quality_' + message_id + ']:checked').val();
   if (!quality)
   {
      alert('Er is geen waarde voor de kwaliteit geselecteerd');
   }
   else if (quality == 'OK' || quality == 'INSUFFICIENT')
   {
      basMessage.setResponseQuality(issue_id, message_id, quality);
   }
   else
   {
      basMessage.setResponseQuality(issue_id, message_id, 'RETURNED', quality);
   }
}

/**
 * Function called when a response arrives from the server after updating the
 * quality assesment of a backoffice response. This reloads the issue, or
 * when te issue has been returned to backoffice, the issue list.
 * @param array result Result returned by the server, containing the
 *   issue and message IDs, and the new quality value.
 */
function responseQualitySet(result)
{
   if (result.quality == 'RETURNED')
   {
      // This issue has been returned to the backoffice. Reload the
      // list of issues
      searchIssues();
   }
   else
   {
      // Reload the issue itself
      basIssue.getIssue(result.issue);
   }
   alert('De wijziging is opgeslagen');
}

/**
 * Create the popup window for adding attachments to an issue.
 * @param int issue_id The ID of the issue for which the window is created
 * @return object The DOM nodes for the popup
 */
function buildNewAttachmentPopup(issue_id)
{
   var add_attrs = {
      onclick: 'uploadFile("issue",' + issue_id + '); issueShowDiv("issue_attachment_div"); return false;'
   };

   var popup = document.createElement('div');
   popup.className = 'issue_sub_div';
   popup.id = 'issue_attachment_div';
   popup.innerHTML = getCancelButton()
                   + '<table>'
                   +    '<tr>'
                   +       '<td>Titel:</td>'
                   +       '<td>'
                   +          '<input type="text" name="issue_attachment_title" id="issue_attachment_title"></input>'
                   +          '<input type="hidden" id="issue_attachment_id" name="issue_attachment_id" '
                   +             'value="' + issue_id + '"></input>'
                   +       '</td>'
                   +    '</tr>'
                   +    '<tr>'
                   +       '<td>Bestand:</td>'
                   +       '<td>'
                   +          '<input type="file" id="issue_attachment_file" name="issue_attachment_file"></input>'
                   +       '</td>'
                   +    '</tr>'
                   +    '<tr>'
                   +       '<td colspan="2">'
                   +          createButton('Voeg toe', '#', add_attrs)
                   +       '</td>'
                   +    '</tr>'
                   + '</table>';

   return popup;
}

/**
 * Create the popup window for adding best practices to an issue.
 * @return object The DOM nodes for the popup
 */
function buildAddBestPracticePopup()
{
   var popup = document.createElement('div');
   popup.className = 'issue_sub_div';
   popup.id = 'best_practices';
   popup.innerHTML = getCancelButton()
                   + 'Best practices worden geladen';

   return popup;
}

/**
 * Create the popup window for adding replies to an issue.
 * @param int issue_id The ID of the issue for which the window is created
 * @return object The DOM nodes for the popup
 */
function buildAddResponsePopup(issue_id)
{
   var add_attrs = {
      onclick: 'addResponse(' + issue_id + '); issueShowDiv("response_div"); return false;'
   };

   var popup = document.createElement('div');
   popup.className = 'issue_sub_div';
   popup.id = 'response_div';
   var content = getCancelButton()
               + '<table>'
               +    '<tr>'
               +       '<td>Titel: <input type="text" name="response_title"></input></td>';
   if (user.type == 'BAS' || user.type == 'ADMIN')
   {  // If we are bas-employee or admin we can add a scope to it
      content +=       '<td>Scope: '
               +          '<select name="response_scope">'
               +             '<option value="CLIENT" selected>Klant</option>'
               +             '<option value="BACKOFFICE">Backoffice</option>'
               +             '<option value="BAS">Bas</option>'
               +          '</select>'
               +       '</td>';
   }
   content    +=    '</tr>'
               +    '<tr>'
               +       '<td colspan="2"><textarea name="response_content"></textarea></td>'
               +    '</tr>'
               +    '<tr>'
               +       '<td>'
               +          createButton('Voeg toe', '#', add_attrs)
               +       '</td>'
               +    '</tr>'
               + '</table>';
   popup.innerHTML = content;

   return popup;
}

function showIssueCauses(issue)
{
   var runways = [];
   var sids = [];

   var date, time, i;

   if (!issue.isupdate)
   {
      time = formatDBDate(issue.date);
      if (issue.time)
      {
         time += ' ' + issue.time.match(/^\d+:\d+/);
      }
      else if (issue.periods)
      {
         var periods = [];
         for (i = 0; i < issue.periods.length; i++)
         {
            var from = issue.periods[i].from.match(/^\d+:\d+/);
            var upto = issue.periods[i].upto.match(/^\d+:\d+/);
            periods.push(from + '-' + upto);
         }
         time += ' ' + periods.join(', ');
      }
      document.getElementById('flight_time').innerHTML = time;
   }

   var table, row;
   var direction, runway, flights;
   if (issue.complaintType == 'SPECIFIC')
   {
      table = document.getElementById('issue_specific_flights');
      deleteContentRows(table,
         ['contentrow', 'contentrow_current', 'page_buttons']);
      document.getElementById('issue_period_flights').style.display = 'none';
      table.style.display = '';

      flights = issue.flights ? issue.flights : {};

      for (i = 0; i < flights.length; i++)
      {
         var flight = flights[i];

         date = '';
         time = '';
         if (flight.date)
         {
            var parts = flight.date.replace(/(\d{4})-(\d+)-(\d+) (\d+):(\d\d).*/,
               '$3-$2 $4:$5').split(' ');
            date = parts[0];
            time = parts[1];
         }
         direction = (flight.direction ? (flight.direction == 'LANDING' ? 'L' : 'S') : '');
         var type = (flight.type ? flight.type : '');
         var callsign = (flight.callsign ? flight.callsign : '');
         var fl = (flight.fl !== undefined ? flight.fl : '');
         runway = (flight.runway ? flight.runway : '');
         var sid = (flight.sidstar ? flight.sidstar : '');
         var dest = (flight.depdest ? flight.depdest : '');

         row = table.insertRow(-1);
         row.className = 'contentrow';
         row.id = 'flight_' + flight.id;
         row.insertCell(-1).innerHTML = date;
         row.insertCell(-1).innerHTML = time;
         row.insertCell(-1).innerHTML = direction;
         row.insertCell(-1).innerHTML = type;
         row.insertCell(-1).innerHTML = callsign;
         row.insertCell(-1).innerHTML = fl;
         row.insertCell(-1).innerHTML = runway;
         row.insertCell(-1).innerHTML = sid;
         row.insertCell(-1).innerHTML = dest;
         row.onclick = new Function(
            'selectFlight(' + flight.id + '); return false;'
         );

         if (!arrayContains(runways, runway))
         {
            runways.push(runway);
         }
         if (!arrayContains(sids, sid))
         {
            sids.push(sid);
         }
      }
   }
   else if (!issue.isupdate)
   {
      table = document.getElementById('issue_period_flights');
      deleteContentRows(table,
         ['contentrow', 'contentrow_current', 'page_buttons']);
      document.getElementById('issue_specific_flights').style.display = 'none';
      table.style.display = '';

      if (!issue.causes)
      {
         return;
      }

      date = issue.date.replace(/\d{4}-(\d\d)-(\d\d).*/, '$2-$1');
      for (i = 0; i < issue.causes.length; i++)
      {
         var cause = issue.causes[i];
         if (cause.period < 0)
         {
            continue; // overall cause
         }

         direction = (cause.direction ? (cause.direction == 'LANDING' ? 'L' : 'S') : '');
         runway = cause.runway;

         row = table.insertRow(-1);
         row.className = 'contentrow';
         row.onclick = new Function(
            'selectFlightPeriod(this,' + issue.id + ',' + cause.period + '); return false;'
         );
         row.insertCell(-1).innerHTML = date;
         row.insertCell(-1).innerHTML = periodToTime(cause.period);
         row.insertCell(-1).innerHTML = direction;
         row.insertCell(-1).innerHTML = runway;

         if (!arrayContains(runways, runway))
         {
            runways.push(runway);
         }
      }
   }

/* XXX DEBUG ONLY.
   if (issue.isall)
   {
      document.getElementById('show_all_flights').style.display = 'none';
      document.getElementById('show_matching_flights').style.display = 'inline';
   }
   else
   {
      document.getElementById('show_all_flights').style.display = 'inline';
      document.getElementById('show_matching_flights').style.display = 'none';
   }
// END DEBUG
*/

   // If we have flights, draw them. If we don't and this is the first
   // time the function is called for this issue, also call drawIssueTracks
   // to remove existing tracks.
   if (issue.flights || !issue.isupdate)
   {
      flights = issue.flights ? issue.flights : {};
      var circles = issue.circles ? issue.circles : [];
      var onselect = issue.complaintType == 'SPECIFIC' ? selectFlight : null;
      drawIssueTracks(flights, circles, onselect);
   }

   // Update the visible overlays with the current runways and SIDs
   selectAllOverlaysRunwaySid(runways, sids);
}

function showConditionsInformation(issue)
{
   var time = formatDBDate(issue.date);
   var i, j;

   if (issue.time)
   {
      time += ' ' + issue.time.match(/^\d+:\d+/);
   }
   else if (issue.periods)
   {
      var periods = [];
      for (i = 0; i < issue.periods.length; i++)
      {
         var from = issue.periods[i].from.match(/^\d+:\d+/);
         var upto = issue.periods[i].upto.match(/^\d+:\d+/);
         periods.push(from + '-' + upto);
      }
      time += ' ' + periods.join(', ');
   }
   document.getElementById('conditions_time').innerHTML = time;

   var table = document.getElementById('conditions_list');
   var row;
   deleteContentRows(table,
      ['contentrow', 'contentrow_current', 'page_buttons']);

   if (issue.conditions)
   {
      for (i = 0; i < issue.conditions.length; i++)
      {
         var condition = issue.conditions[i];

         var winddirection = condition.winddir
                           ? condition.winddir + '\u00b0' : '';
         var windspeed     = condition.windspeed
                           ? condition.windspeed + ' kts' : '';
         var temp          = condition.temp
                           ? condition.temp + '\u00b0C' : '';
         var visibility    = condition.visibility
                           ? condition.visibility + ' m' : '';
         var cloudbase     = condition.cloudbase
                           ? condition.cloudbase : '';

         row = table.insertRow(-1);
         row.className = 'contentrow';
         row.insertCell(-1).innerHTML = winddirection;
         row.insertCell(-1).innerHTML = windspeed;
         row.insertCell(-1).innerHTML = temp;
         row.insertCell(-1).innerHTML = visibility;
         row.insertCell(-1).innerHTML = cloudbase;
      }
   }

   table = document.getElementById('runway_list');
   deleteContentRows(table,
      ['contentrow', 'contentrow_current', 'page_buttons']);

   if (issue.runways)
   {
      for (i = 0; i < issue.runways.length; i++)
      {
         var runway = issue.runways[i];

         time = runway.time ? formatDBDate(runway.time) : '';
         var landing, takeoff;

         if (runway.landing)
         {
            var landings = [
               runway.landing.substr(0,3),
               runway.landing.substr(3,3),
               runway.landing.substr(6)
            ];
            for (j = 0; j < landings.length; j++)
            {
               if (landings[j] != '---')
               {
                  landings[j] = landings[j].replace(/\-+$/, '');
               }
            }
            landing = landings.join(' ');
         }
         else
         {
            landing = '';
         }

         if (runway.takeoff)
         {
            var takeoffs = [
               runway.takeoff.substr(0,3),
               runway.takeoff.substr(3,3),
               runway.takeoff.substr(6)
            ];
            for (j = 0; j < takeoffs.length; j++)
            {
               if (takeoffs[j] != '---')
               {
                  takeoffs[j] = takeoffs[j].replace(/\-+$/, '');
               }
            }
            takeoff = takeoffs.join(' ');
         }
         else
         {
            takeoff = '';
         }

         row = table.insertRow(-1);
         row.className = 'contentrow';
         row.insertCell(-1).innerHTML = time;
         row.insertCell(-1).innerHTML = landing;
         row.insertCell(-1).innerHTML = takeoff;
      }
   }
}

/**
 * Request all flights crossing the area around the postal code of a
 * period complaint in a selected 15-minute interval
 * @param object node   The DOM node of the selected row
 * @param int    id     The id of the complaint
 * @param int    period The interval code of the 15-minute period
 */
function selectFlightPeriod(row, id, period)
{
   basIssue.findFlightsPeriod(id, period);
   $(row).siblings('.contentrow_current').attr('class', 'contentrow');
   $(row).attr('class', 'contentrow_current');
}

function showFlights(id, complaintType, res, all)
{
   var issue = {
      id : id,
      complaintType: complaintType,
      flights: res.flights,
      circles: []
   };
   if (res.center && res.radii)
   {
      for (var i = 0; i < res.radii.length; i++)
      {
         issue.circles.push({center: res.center, radius: res.radii[i]});
      }
   }
   issue.isall = all;
   issue.isupdate = true;
   showIssueCauses(issue);
}

/**
 * Show the selected issue. This function is called after the server
 * responds to a request for issue details.
 * @param array issue The issue details to be shown
 */
function showIssueDetails(issue)
{
   $('#issues_'+issue.id).siblings('tr.contentrow_current').attr('class', 'contentrow');
   $('#issues_'+issue.id).attr('class', 'contentrow_current');
   $('.clientissues').find('tr.contentrow_current').attr('class', 'contentrow');
   var details = document.getElementById('blc_issues_1');
   // clear the previous content of the tab
   details.innerHTML = '';

   details.appendChild(buildIssueDetailsTitleTable(issue));
   details.appendChild(buildIssueDetailsStateTable(issue));
   if (issue.scriptOutput)
   {
      details.appendChild(buildIssueDetailsScriptDiv(issue.scriptOutput));
   }

   var legendDiv;

   var msgtabs = buildIssueMessages(issue.id, issue.messages);
   if (user.is_bbb && !isClosedIssueState(issue.state))
   {
      legendDiv = document.createElement('div');
      legendDiv.className = 'legend';
      if((user.type == 'BAS' && arrayContains(['BAS', 'RESPOND'], issue.state))
         || (user.type == 'BACKOFFICE' && arrayContains(['AAS','LVNL','RIJK'], issue.state)))
      {
         legendDiv.innerHTML =
              'Antwoorden '
            + '<a class="addlink" id="add_response" href="#" onclick="issueShowDiv(\'response_div\'); return false;">'
            +    '[voeg antwoord toe]'
            + '</a> '
            + '<a class="addlink" id="add_attachment" href="#" onclick="issueShowDiv(\'issue_attachment_div\'); return false;">'
            +    '[voeg bijlage toe]'
            + '</a>';
      }
      else
      {
         legendDiv.innerHTML = 'Antwoorden';
      }
      details.appendChild(legendDiv);
      details.appendChild(buildAddResponsePopup(issue.id));
      details.appendChild(buildNewAttachmentPopup(issue.id));
      if (msgtabs.replies)
      {
         details.appendChild(msgtabs.replies);
      }

      legendDiv = document.createElement('div');
      legendDiv.className = 'legend';
      if((user.type == 'BAS' && arrayContains(['BAS', 'RESPOND'], issue.state))
         || (user.type == 'BACKOFFICE' && arrayContains(['AAS','LVNL','RIJK'], issue.state)))
      {
         legendDiv.innerHTML =
              'Best practice '
            + '<a class="addlink" id="add_best_practices" href="#" onclick="issueShowDiv(\'best_practices\',' + issue.id + '); return false;">'
            +    '[voeg toe]'
            + '</a>';
      }
      else
      {
         legendDiv.innerHTML =
            'Best practice ';
      }
      details.appendChild(legendDiv);
      details.appendChild(buildAddBestPracticePopup());
      if (msgtabs.bestpractices)
      {
         details.appendChild(msgtabs.bestpractices);
      }
   }
   else if (msgtabs.replies || msgtabs.attachments || msgtabs.bestpractices)
   {
      legendDiv = document.createElement('div');
      legendDiv.className = 'legend';
      legendDiv.innerHTML = 'Antwoord';

      details.appendChild(legendDiv);
      if (msgtabs.replies)
      {
         details.appendChild(msgtabs.replies);
      }
      if (msgtabs.attachments)
      {
         details.appendChild(msgtabs.attachments);
      }
      if (msgtabs.bestpractices)
      {
         details.appendChild(msgtabs.bestpractices);
      }
   }

   showServiceMessages(msgtabs.services, 'Geen relevante berichten gevonden');

   if (user.is_bbb)
   {
      details.appendChild(buildIssueDetailsDetailsDiv(issue));
      var client_id = user.type == 'BAS' ? issue.client : null;
      // Add markers for the day of the complaint
      basReport.getIssueDayReport(issue.date, client_id);
      // Center on the postal code of the complaint
      basLocation.getCoordinates(issue.postalCode);

/* XXX DEBUG ONLY
      document.getElementById('show_all_flights').onclick =
         new Function('basIssue.findFlights(' + issue.id + ', true, "'
            + issue.complaintType + '"); return false;');
      document.getElementById('show_matching_flights').onclick =
         new Function('basIssue.findFlights(' + issue.id + ', false, "'
            + issue.complaintType + '"); return false;');
*/
   }

   if (user.is_bbb && (issue.conditions || issue.runways))
   {
      showConditionsInformation(issue);
      $('li#br_8').show();
   }
   else
   {
      $('li#br_8').hide();
   }

   if (user.is_bbb
       && issue.type == 'COMPLAINT'
       && (issue.complaintType == 'SPECIFIC' || issue.complaintType == 'PERIOD'))
   {
      showIssueCauses(issue);
      document.getElementById('br_3').style.display = 'inline';

      if(user.type == 'BAS' && (issue.state == 'NOTCOUPLED' || issue.state == 'NEW'
                                   || issue.state == 'INVESTIGATE' || issue.state == 'BAS')) {
         if($('#br_8:visible').attr('id')) {
         }
         else {
            toggleTab('br_3');
         }
      }
      else {
         if($('#br_8:visible').attr('id'))
         {
         }
         else {
            toggleTab('br_4');
         }
      }
   }
   else
   {
      if (user.is_bbb)
      {
         document.getElementById('br_3').style.display = 'none';

         if($('#br_8:visible').attr('id'))
         {
         }
         else
         {
            toggleTab('br_4');
         }
      }
   }
   toggleTab('bl_issues_1');
}

/**
 * Toggle the div's in the reports section
 *
 * @param id      int   id of div to toggle
 */
function showReportDiv(id)
{
   // Don't change titlebar class in IE6, since the sections
   // cannot be collapsed anyway
   if (!$.browser.msie || $.browser.version != '6.0')
   {
      $(id).toggle();
      if ($(id).is(':visible'))
      {
         $(id).prev().attr('class','title2');
      }
      else
      {
         $(id).prev().attr('class','title');
      }
   }
}

/**
 * Show the 'add stuff'-divs for issues (and message-attachments)
 *
 * @param string id    The div to show/hide
 * @param int issue_id The ID of the issue for which this div is shown
 */
function issueShowDiv(id, issue_id)
{
   if (id == 'best_practices')
   {
      if ($('#best_practices').is(':hidden'))
      {
         basMessage.listBestPractices(issue_id);
      }
      $('#response_div').hide();
      $('#best_practices').toggle();
   }
   else if(id == 'attachment_div')
   {
      $('#attachment_div').find('[name=attachment_title]').attr('value', '');
      $('#attachment_div').find('[name=attachment_file]').attr('value', '');
      $('#attachment_div').toggle();
      $('#attachment_div').find('[name=attachment_title]').focus();
   }
   else if(id == 'issue_attachment_div')
   {
      $('#issue_attachment_div').find('[name=issue_attachment_title]').attr('value', '');
      $('#issue_attachment_div').find('[name=issue_attachment_file]').attr('value', '');
      $('#issue_attachment_div').toggle();
      $('#issue_attachment_div').find('[name=issue_attachment_title]').focus();
   }
   else
   {
      $('#best_practices').hide();
      $('#response_div').toggle();
   }
}

/**
 * Fill the best practices div after the server returns with the current
 * list of best practices.
 * @param array bps The list of best practices
 * @param int id    The ID of the issue to which to add best practices
 */
function fillBestPractices(bps, id)
{
   var bp_rows    = '<tr><td colspan="2"><h2>Best Practices</h2></td></tr>';
   if (bps.length > 0)
   {
      bp_rows    += '<tr>'
                  +    '<td colspan="2">'
                  +       '<select name="best_practices" multiple>';
      for (var i = 0; i < bps.length; i++)
      {
         bp_rows +=          '<option value="' + bps[i].id + '">'
                  +             bps[i].title
                  +          '</option>';
      }
      bp_rows    +=       '</select>'
                  +    '</td>'
                  + '</tr>';

      var add_attrs = {
         onclick: 'addResponse(' + id + '); issueShowDiv("best_practices"); return false;'
      };
      bp_rows    += '<tr>'
                  +    '<td>'
                  +       createButton('Voeg toe', '#', add_attrs)
                  +    '</td>'
                  + '</tr>';
   }
   else
   {
      bp_rows    += '<tr>'
                  +    '<td colspan="2">'
                  +       '<i>Geen best practices gevonden</i>'
                  +    '</td>'
                  + '</tr>';
   }

   document.getElementById('best_practices').innerHTML =
      getCancelButton() + '<table>' + bp_rows + '</table>';
}

/**
 * Change cause/message/state of an issue
 *
 * @param int id       The ID the issue to change
 * @param bool respond If true, also send responses to the user
 */
function amendIssue(id, respond)
{
   var issue = { id: id };
   if ($('#blc_issues_1').find('input[name=change_state]:checked').attr('name'))
   {
      issue.state = $('#blc_issues_1').find('input[name=change_state]:checked').val();
   }

   if ($('#blc_issues_1').find('select[name=causes_runway]').attr('name'))
   {
      var runway = $('#blc_issues_1').find('select[name=causes_runway] option:selected').val();
      var groundNoise = $('#blc_issues_1').find('select[name=causes_groundnoise] option:selected').val();
      var direction = $('#blc_issues_1').find('select[name=causes_direction] option:selected').val();

      if (runway || groundNoise || direction)
      {
         var cause = {};
         if (runway)
         {
            cause.runway = runway;
         }
         if (groundNoise)
         {
            cause.groundNoise = groundNoise;
         }
         if (direction)
         {
            cause.direction = direction;
         }
         issue.causes = [cause];
      }
   }

   if ($('#blc_issues_1').find('div#response_div:visible').attr('id'))
   {
      issue.messages = [];
      if ($('#blc_issues_1').find('textarea[name=response_content]').val().length > 0)
      {
         var scope = $('#blc_issues_1').find('select[name=response_scope]').val();
         if (!scope)
         {
            scope = 'BACKOFFICE';
         }
         var message = {
            message: {
               title:   $('#blc_issues_1').find('input[name=response_title]').val(),
               scope:   scope,
               content: $('#blc_issues_1').find('textarea[name=response_content]').val(),
               type:    'RESPONSE'
            }
         };
         issue.messages.push(message);
      }
   }
   if ($('#blc_issues_1').find('div#best_practices:visible').attr('id'))
   {
      issue.messages = [];
      $('#blc_issues_1').find('select[name=best_practices] option:selected').map(function(){
         issue.messages.push ( {id: $(this).val() } );
      });
   }
   basIssue.amendIssue(issue, respond);
}

function linkFlight(issue_id)
{
   var flight = $('#issue_specific_flights .contentrow_current').attr('id');
   if (!flight || flight === '')
   {
      alert('Er is geen vlucht geselecteerd');
      return false;
   }
   var flight_id = flight.split('_')[1];
   basIssue.coupleFlight(issue_id, flight_id);
}

function changeState(id, state)
{
   if (state)
   {
      basIssue.changeState(id, state);
   }
   else
   {
      alert('Selecteer een status');
   }
}

/**
 * Callback function executed when the server responds to a request to
 * update an issue.
 * @param array result The result of the request as returned by the server
 */
function handleIssueAmended(result)
{
   var issue = result.issue;
   if (issue)
   {
      showIssueDetails(issue);
      // (GV) XXX FIXME: should the next line ever happen? Don't think so.
      if (user.type == 'BACKOFFICE' && !arrayContains(['AAS','LVNL','RIJK'], issue.state))
      {
         searchIssues();
      }
      else
      {
         var tr_state = getTranslation(issue.state);
         $('#issues_' + issue.id).find('td:eq(6)').html(tr_state);
         if (user.type == 'BAS')
         {
            $('#clientissues_' + issue.id).find('td:eq(6)').html(tr_state);
         }
      }
   }
   else
   {
      // we are no longer allowed to view the issue, update the list
      searchIssues(false, 0);
      toggleTab('br_4');
      $('#br_3').hide();
      $('#br_8').hide();
      basClient.releaseLocks();
      drawIssueTracks({}, []);
      hideOverlay('issue');
      hideOverlay('report');
   }

   if (result.message)
   {
      alert(result.message);
   }
   else
   {
      alert('Aanpassing succesvol');
   }
}

/**
 * Update the issue state in the issue lists after it has been changed
 * @param int id       The ID of the updated issue
 * @param string state The new state of the issue
 */
function updateTableIssueState(id, state)
{
   var tr_state = getTranslation(state);
   $('#issues_' + id).find('td:eq(6)').html(tr_state);
   if (user.type == 'BAS')
   {
      $('#clientissues_' + id).find('td:eq(6)').html(tr_state);
   }
}

/**
 * Callback function, used when the server responds to a request to
 * change the state of an issue.
 * @param array issue    The ID and new state of the updated issue
 * @param string message A possible error message from the server
 */
function handleIssueStateChanged(issue, message)
{
   if (message)
   {
      alert(message);
   }
   else
   {
      alert('Aanpassing succesvol');
   }

   if (user.type == 'BACKOFFICE' || !issue.state)
   {
      // Retrieve the whole issue list again, the lazy way
      toggleTab('tl_issues');
   }
   else
   {
      updateTableIssueState(issue.id, issue.state);
      basIssue.getIssue(issue.id);
   }
}

/**
 * Add a response or one or more best practices to an issue
 * @param int id The id of the issue to which to add
 */
function addResponse(id)
{
   var issue = { id: id };

   issue.messages = [];
   if ($('#response_div').is(':visible'))
   {
      var text = $('#response_div').find('textarea[name=response_content]').val();
      if (text.trim().length > 0)
      {
         var scope = $('#response_div').find('select[name=response_scope]').val();
         if (!scope)
         {
            scope = 'BACKOFFICE';
         }
         var message = {
            message: {
               title:   $('#response_div').find('input[name=response_title]').val(),
               scope:   scope,
               content: text,
               type:    'RESPONSE'
            }
         };
         issue.messages.push(message);
      }
   }
   if ($('#best_practices').is(':visible'))
   {
      $('#best_practices').find('select[name=best_practices] option:selected').map(function(){
         issue.messages.push ( {id: $(this).val() } );
      });
   }

   basIssue.amendIssue(issue, false);
}

/**
 * Add a cancel/close button to a div
 *    Needs to be placed within the parent that has to be closed
 *
 * @return   The html to show the button
 */
function getCancelButton()
{
   return '<div class="cancel_button" onclick="$(this).parent().hide(); return false;">X</div>';
}

/**
 * Update the details of a message in the message list
 * @param message The details of the updated message
 */
function updateMessageListEntry(message)
{
   var elem = document.getElementById('message_' + message.id);
   if (elem)
   {
      var cdate = message.cdate ? formatDBDate(message.cdate) : '';

      elem.cells[0].innerHTML = message.id;
      elem.cells[1].innerHTML = cdate;
      elem.cells[2].innerHTML = getTranslation(message.type);
      elem.cells[3].innerHTML = message.title ? message.title : '';
      elem.cells[4].innerHTML = getTranslation(message.state);
   }
}

/**
 * Build the list of attachments in the message details output
 * @param array The list of attachments
 * @return string with HTML code for the attachment list
 */
function messageDetailsAttachments(attachments, msg_id, state)
{
   if (!attachments)
   {
      attachments = [];
   }
   var editable = (user.is_bbb && state == 'DRAFT');
   if (attachments.length === 0 && !editable)
   {
      return '';
   }

   var ttext = 'Bijlagen';
   if (editable)
   {
      ttext += ' <a class="addlink" id="add_attachment" href="#" onclick="issueShowDiv(\'attachment_div\'); return false;">'
             +     '[voeg bijlage toe]'
             +  '</a>';
   }
   var title = '<div class="legend">' + ttext + '</div>';

   var links = '';
   if (attachments.length > 0)
   {
      var rows = '';
      for (var i in attachments)
      {
         var attachment = attachments[i];
         rows += '<tr>'
               +    '<td>'
               +       '<a class="textlink" href="attachment.php?id=' + attachment.id + '" target="_blank">'
               +          attachment.title
               +       '</a>'
               +    '</td>'
               + '</tr>';
      }
      links = '<table>' + rows + '</table>';
   }

   var edit = '';
   if (editable)
   {
      var link_attrs = {
         onclick: 'uploadFile("message",' + msg_id + '); issueShowDiv("attachment_div"); return false;'
      };
      edit = '<div class="issue_sub_div" id="attachment_div">'
           +    getCancelButton()
           +    '<table>'
           +       '<tr>'
           +          '<td>Titel:</td>'
           +          '<td>'
           +             '<input type="text" name="attachment_title" id="attachment_title"></input>'
           +             '<input type="hidden" id="attachment_id" name="attachment_id" value="'
           +                msg_id + '"></input>'
           +          '</td>'
           +       '</tr>'
           +       '<tr>'
           +          '<td>Bestand:</td>'
           +          '<td>'
           +             '<input type="file" id="attachment_file" name="attachment_file"></input>'
           +          '</td>'
           +       '</tr>'
           +       '<tr>'
           +          '<td colspan="2">'
           +             createButton('Voeg toe', '#', link_attrs)
           +          '</td>'
           +       '</tr>'
           +    '</table>'
           + '</div>';
   }

   var res = title + links + edit;
   return res;
}

/*
 * Callback function for displaying a non-editable message
 * @param array message The message details as returned by the server
 */
function showStaticMessageDetails(message)
{
   var link = '';
   if (user.type == 'BAS' || user.type == 'ADMIN')
   {
      link = '<div>'
           +    '<a href="#" class="textlink" onclick="newMessage(); return false;">'
           +       'Maak een nieuw bericht'
           +    '</a>'
           + '</div>';
   }

   var title = '<div style="font-weight:bold">' + message.title + '</div>';
   var content = '';
   if (message.content)
   {
      content = '<div>' + message.content.replace(/\n/g, '<br>') + '</div>';
   }
   var issue = '';
   if (message.issue)
   {
      issue = '<div>'
            +    '<a class="textlink" id="show_issue" href="#" onclick="basIssue.getIssue(' + message.issue + '); toggleTab(\'tl_issues\'); return false;">'
            +       'Klik hier om de bijbehorende melding te bekijken'
            +    '</a>'
            + '</div>';
   }
   var msg_str = '<div class="message_details">'
               +    link + title + content + issue
               + '</div>';

   var details = '';
   var i;
   if (user.is_bbb)
   {
      var ignore_fields = ['title', 'content', 'attachments', 'issue',
                           'query_id'];
      var det_rows = [];

      for (var x in message)
      {
         if (arrayContains(ignore_fields, x))
         {
            continue;
         }

         var label = '<td>' + getTranslation(x) + ':</td>';
         var value = message[x];
         switch(x)
         {
         case 't_valid':
         case 't_invalid':
         case 't_start':
         case 't_end':
            if (message.type == 'SERVICE')
            {
               var date = formatDBDate(value);
               det_rows.push(label + '<td>' + date + '</td>');
            }
            break;
         case 'cdate':
            var cdate = formatDBDate(value);
            det_rows.push(label + '<td>' + cdate + '</td>');
            break;
         case 'type':
         case 'state':
            det_rows.push(label + '<td>' + (value ? getTranslation(value) : value) + '</td>');
            break;
         case 'scope':
            var tr_scopes = [];
            var scopes = value.split(',');
            for (var y in scopes)
            {
               switch (scopes[y])
               {
                  case 'PUBLIC':     tr_scopes.push('Publiek'); break;
                  case 'CLIENT':     tr_scopes.push('Klant'); break;
                  case 'BACKOFFICE': tr_scopes.push('Backoffice'); break;
                  case 'BAS':        tr_scopes.push('Bas'); break;
                  default:           tr_scopes.push(scopes[y]);
               }
            }
            det_rows.push(label + '<td>' + tr_scopes.join(',') + '</td>');
            break;
         case 'url':
            if (value)
            {
               if (message.type == 'POLL')
               {
                  title = value;
                  if (user.type == 'BAS' || user.type == 'ADMIN')
                  {  
                     var select = document.getElementById('message_url');
                     for (i = 0; i < select.length; i++)
                     {
                        if (select.options[i].value == value)
                        {
                           title = select.options[i].text;
                           break;
                        }
                     }
                  }
                  det_rows.push(label + '<td>' + title + '</td>');
               }
               else
               {
                  det_rows.push(label + '<td><a class="textlink" href="' + value + '">' + value + '</a></td>');
               }
            }
            break;
         case 'query_title':
            if (value)
            {
               det_rows.push(label + '<td>' + value + '</td>');
            }
            break;
         case 'sendstate':
            if((message.type == 'MAILING' || message.type == 'POLL') && (message.state == 'SENDING' || message.state == 'SENT'))
            {
               var sendclick = "showMessageSendState(); return false";
               det_rows.push(label + '<td><a class="textlink" href="#" onclick="' + sendclick + '">Details</a></td>');
            }
            break;
         case 'quality':
            if (value !== null)
            {
               det_rows.push(label + '<td>' + translateResponseQuality(value) + '</td>');
            }
            break;
         default:
            det_rows.push(label + '<td>' + value + '</td>');
         }
      }
      if (message.state == 'FINAL' && (message.type == 'MAILING' || message.type == 'POLL'))
      {
         var button_link_attrs = {id: 'message_queue', onclick: 'queueMessage(' + message.id + ',"static"); return false;'};
         det_rows.push(createButton('Versturen', '#', button_link_attrs));
      }

      details = '<div class="message_detail_content">'
              +    '<div class="legend">Details</div>'
              +    '<div class="message_detail_content">'
              +       '<table>'
              +          '<tr>' + det_rows.join('</tr><tr>') + '</tr>'
              +       '</table>'
              +    '</div>'
              + '</div>';
   }


   var sendstate = '';
   if((message.type == 'MAILING' || message.type == 'POLL')
      && (message.state == 'SENDING' || message.state == 'SENT')
      && message.sendstate
      && message.sendstate.length > 0)
   {
      sendstate = '<div class="issue_sub_div" id="messageSendStateDiv" style="'
                + 'display:none; overflow-y: auto; overflow-x: hidden; width:440px; left: 20px; height:200px;">'
                + '<div class="cancel_button" onclick="$(this).parent().hide(); return false;"'
                + ' style="margin-right:16px;">X</div>'
                + '<table style="position:absolute; width:94%; top:16px;">'
                + '<tr class="titlerow"><th style="width: 240px;">Email</th>'
                + '<th style="width: 100px">Verzonden op</th><th style="width: 100px">Verzendstatus</th></tr>';

      for (i = 0; i < message.sendstate.length; i++)
      {
         var line = message.sendstate[i];

         var recipient = line.recipient.replace(/</,'^').replace(/>/,'^')
            .replace(/^s:\d+:"([a-zA-Z0-9_\-]+) ([a-zA-Z0-9_\-]+)/,'').replace(/";/,'').replace(/\^/,'').replace(/\^/,'');
         sendstate += '<tr><td>' + recipient + '</td><td>';

         var sendfailed = false;
         var senddate;
         if (line.sent_time)
         {
            senddate = formatDBDate(line.sent_time, true);
         }
         if(senddate)
         {
            sendstate += senddate;
         }
         else
         {
            sendfailed = true;
         }
         sendstate += '</td><td>';

         if (line.try_sent == '0')
         {
            sendstate += 'Niet verzonden';
         }
         else if (sendfailed && line.try_sent == '1')
         {
            sendstate += 'Fout bij verzenden';
         }
         else
         {
            sendstate += 'Verzonden';
         }
         sendstate += '</td></tr>';
      }

      sendstate += '</table></div>';
   }

   if (user.type == 'BAS' || user.type == 'ADMIN')
   {
      document.getElementById('message_details_edit').style.display = 'none';
   }
   document.getElementById('message_details_static').innerHTML =
      msg_str + details + sendstate;
   document.getElementById('message_details_static').style.display = 'block';

   document.getElementById('message_details_attachments').innerHTML =
      messageDetailsAttachments(message.attachments, message.id, message.state);
}

function showMessageSendState()
{
   if ($('#messageSendStateDiv').attr('id'))
   {
      if($('#messageSendStateDiv:visible').attr('id'))
      {
         $('#messageSendStateDiv').hide();
      }
      else
      {
         $('#messageSendStateDiv').show();
      }
   }
}

/*
 * Callback function for displaying an editable message
 * @param array message The message details as returned by the server
 */
function showEditableMessageDetails(message)
{
   $('#message_details_edit [name=message_type]').val(message.type).
      attr('disabled', 'disabled');

   $('#message_details_edit [name=message_final]').attr('checked',
      message.state && message.state == 'FINAL');
   document.getElementById('message_mailing_detail').innerHTML = '0';

   var scope = 'CLIENT';
   if (message.scope)
   {
      var bits = message.scope.split(',');
      var allscopes = ['PUBLIC', 'CLIENT', 'BACKOFFICE', 'BAS'];
      for (var i = 0; i < allscopes.length; i++)
      {
         if (arrayContains(bits, allscopes[i]))
         {
            scope = allscopes[i];
            break;
         }
      }
   }
   $('#message_details_edit [name=message_scope]').val(scope);

   var title = message.title ? message.title : '';
   $('#message_details_edit [name=message_title]').val(title);

   var content = message.content ? message.content : '';
   $('#message_details_edit [name=message_content]').val(content);

   if (message.type == 'POLL')
   {
      $('#message_details_edit [name=message_url]').val(message.url);
      $('#message_url_row').show();
   }
   else
   {
      $('#message_url_row').hide();
   }

   var keywords = message.keywords ? message.keywords : '';
   $('#message_details_edit [name=message_keywords]').val(keywords);

   var query = message.query_id ? message.query_id : '';
   $('#message_details_edit [name=message_queries]').val(query);

   if(message.type == 'MAILING' || message.type == 'POLL')
   {
      mailingSelectionQuery(message.query_id);
      $('#message_queue').show();
      $('tr#mailing_send_state').show();
   }
   else
   {
      $('tr#mailing_send_state').hide();
   }

   if (message.type == 'SERVICE')
   {
      dateFieldSetValue('msg_t_valid', message.t_valid);
      dateFieldSetValue('msg_t_invalid', message.t_invalid);
      dateFieldSetValue('msg_t_start', message.t_start);
      dateFieldSetValue('msg_t_end', message.t_end);
      $('#valid_date').show();
   }
   else
   {
      $('#valid_date').hide();
   }

   document.getElementById('message_details_static').style.display = 'none';
   document.getElementById('message_details_edit').style.display = 'block';
   document.getElementById('message_submit').onclick =
      new Function('amendMessage(' + message.id + '); return false;');
   if (message.type == 'MAILING' || message.type == 'POLL') {
      document.getElementById('message_queue').onclick =
         new Function('queueMessage(' + message.id + '); return false;');
   }

   document.getElementById('message_details_attachments').innerHTML =
      messageDetailsAttachments(message.attachments, message.id, message.state);
}

/**
 * Send a request to the server for the list of users that match a certain
 * query.
 * @param int query_id The ID of the query
 */
function mailingSelectionQuery(query_id)
{
   if($('select[name=message_type]').val() == "MAILING" || $('select[name=message_type]').val() == "POLL")
   {
      basMessage.getMailingUserSelection(query_id);
   }
}

function showMailingSelectionDiv()
{
   if($('#message_queries').val())
   {
      $('#mailingUserListPopup').show();
   }
}

/**
 * Callback function to show a Message
 * @param array message The message details returned by the server
 */
function showMessageDetails(message)
{
   if ((user.type == 'BAS' || user.type == 'ADMIN') && message.state == 'DRAFT')
   {
      showEditableMessageDetails(message);
   }
   else
   {
      showStaticMessageDetails(message);
   }
}

/**
 * Clear the message input fields, and show them
 */
function newMessage()
{
   document.getElementById('message_submit').onclick = function() {
      addMessage();
      return false;
   };
   document.getElementById('message_queue').onclick = function() {
      queueMessage();
      return false;
   };
   $('#message_details_edit').find(':text,textarea').val('');
   $('#message_details_edit [name=message_type]').removeAttr('disabled');
   $('#message_details_edit [name=message_type]').val('SERVICE');
   $('#message_details_edit [name=message_final]').attr('checked', false);
   $('#message_details_edit [name=message_scope]').val('PUBLIC');
   $('#message_url_row').hide();
   $('#message_details_edit [name=message_queries]').val('');

   setMessageType('SERVICE');
   document.getElementById('message_details_static').style.display = 'none';
   document.getElementById('message_details_edit').style.display = 'block';
}

/**
 * Toggle the visibility of message input fields after a change of
 * message type.
 */
function setMessageType(type)
{
   if (type == 'SERVICE')
   {
      $('#valid_date').attr('style','display:table');
      if ($.browser.msie) // IE6 doesnt want to hide the input fields
      {
         $('#valid_date').attr('style','top:0px; position:relative;');
      }
   }
   else
   {
      $('#valid_date').attr('style','display:none');
      if ($.browser.msie)
      {
         $('#valid_date').attr('style','top:-1000px; position:absolute;');
      }
   }

   if (type == 'POLL')
   {
      $('#message_url_row').attr('style','display:table-row');
   }
   else
   {
      $('#message_url_row').attr('style','display:none');
   }

   if (type == 'MAILING' || type == 'POLL')
   {
      $('#mailing_send_state').attr('style','display:table-row');
      document.getElementById('message_mailing_detail').innerHTML = '0';
      $('#message_queue').attr('style', 'display:block');
      if($.browser.msie)  // IE doesn't hide the button so force it
      {
         if ($('#message_queue').find('div.left_bt_hidden').attr('class'))
         {
            $('#message_queue').find('div.left_bt_hidden').attr('class','left_bt');
         }
         if ($('#message_queue').find('div.right_bt_hidden').attr('class'))
         {
            $('#message_queue').find('div.right_bt_hidden').attr('class','right_bt');
         }
      }
   }
   else
   {
      $('#mailing_send_state').attr('style','display:none');
      $('#message_queue').attr('style', 'display:none');

      if($.browser.msie)  // IE doesn't hide the button so force it
      {
         if ($('#message_queue div.left_bt').attr('class'))
         {
            $('#message_queue div.left_bt').attr('class','left_bt_hidden');
         }
         if ($('#message_queue div.right_bt').attr('class'))
         {
            $('#message_queue div.right_bt').attr('class','right_bt_hidden');
         }
      }
   }
}

/**
 * Fill the best practices div after the server returns with the current
 * list of best practices.
 * @param array bps The list of best practices
 * @param int id    The ID of the issue to which to add best practices
 */
function fillUserMailingSelection(result)
{
   var bp_rows    = '<h2>Klanten</h2><table>';
   if (result.length > 0)
   {
      for (var x in result)
      {
         bp_rows +=          '<tr><td>' + (result[x].firstName ? result[x].firstName+' ' : '')
                  +             (result[x].surName ? result[x].surName+' ' : '') + '</td>'
                  +             '<td>' + (result[x].postalCode ? result[x].postalCode+' ' : '') + '</td>'
                  +             '<td>' + (result[x].email ? result[x].email : '') + '</td>'
                  +          '</tr>';
      }
   }
   else
   {
      bp_rows    += '<tr>'
                  +    '<td colspan="2">'
                  +       '<i>Geen klanten gevonden</i>'
                  +    '</td>'
                  + '</tr>';
   }

   document.getElementById('mailingUserListPopup').innerHTML =
      getCancelButton() + bp_rows + '</table>';
   document.getElementById('message_mailing_detail').innerHTML = result.length;
}

/**
 * Uploads a file to the server
 * @param string where  The type of file to store ('message', 'issue'
 *    or 'overlay')
 * @param int id        For message or issue attachments, the ID of the
 *    corresponding message or issue
 */
function uploadFile(where, id)
{
   var type_data = {
      message: {
         id: 'attachment_file',
         extra: ['attachment_title', 'attachment_id']
      },
      issue: {
         id: 'issue_attachment_file',
         extra: ['issue_attachment_title', 'issue_attachment_id']
      },
      overlay: {
         id: 'overlay_file',
         extra: ['overlay_title', 'overlay_isgroup2', 'overlay_runway', 'overlay_sid']
      }
   };

   if (!type_data[where])
   {
      handleError(['Onbekend bestandselector ID'], []);
      return false;
   }

   var elem_id = type_data[where].id;
   if (!$('#' + elem_id).val())
   {
      handleError(['Bestandsnaam is leeg'],["$('#" + elem_id + "')"]);
      return;
   }
   $.ajaxFileUpload
   (
      {
      url:'upload.php',
      secureuri:false,
      fileElementId: elem_id,
      extra_fields: type_data[where].extra,
      dataType: 'json',
      success: function (data, status)
               {
                  if (data.error)
                  {
                     alert(data.error.message);
                  }
                  else
                  {
                     if (where == 'message')
                     {
                        basMessage.getMessage(id);
                     }
                     else if (where == 'issue')
                     {
                        basIssue.getIssue(id);
                     }
                     else
                     {
                        basOverlay.listOverlays();
                     }
                     alert('Aanpassing succesvol');
                  }
               },
      error: function (data, status, e)
             {
                alert(e);
             }
      }
   );

   return false;
}

/**
 * Toggle the div containing the messageDetails
 *
 * @param id   string   div-id
 * @param type string   what are we toggling
 */
function toggleItemContent(id, type)
{
   if(type == 'message')
   {
      if($('#message_detail_' + id + ':visible').attr('id'))
      {
         $('#message_detail_' + id).hide();
         $('.message_detail_bar a').html('Klik hier voor details');
      }
      else 
      {
         $('#message_detail_' + id).show();
         $('.message_detail_bar a').html('Verberg details');
      }
   }
   else if(type == 'issue_details')
   {
      if($('#issue_detail_content:visible').attr('id'))
      {
         $('#issue_detail_content').hide();
      }
      else 
      {
         $('#issue_detail_content').show();
      }
   }
   else if(type == 'issue_script')
   {
      if($('#issue_script_content:visible').attr('id'))
      {
         $('#issue_script_content').hide();
      }
      else
      {
         $('#issue_script_content').show();
      }
   }
}

/**
 * Checks the given input element when focus moves away
 * *TBD : Validate stuff here or at least call another function here?
 *
 * @param input   object
 */
function inputBlur(input)
{
   focused_element = null;

   if (user.dont_search_user || !input.name)
   {
      return;
   }

   if (input.name == 'number')
   {
      var form = ($('#tlc_register').is(':visible')
         ? $('#tlc_register') : $('#tlc_client'));

      var postalCode = form.find('input[name=postalCode]').attr('value');
      var number = form.find('input[name=number]').attr('value');
      var options = { ignoreExc: true, getAddress: true };
      getAddress(postalCode, number, options);

      // Only focus when tabbing out of number, not when selecting from list
      if (!isCtrl && $('#tlc_client').find('[name=number]').val() !== '')
      {
         document.getElementById('phone1').focus();
      }
   }

   if ($('#tlc_client_subdiv2').is(':visible'))
   {
      var search_fields = ['surName', 'postalCode', 'number', 'email',
                           'street', 'city'];
      if (arrayContains(search_fields, input.name))
      {
         var searchFor = {};
         var count = 0;
         if ($('#tlc_client').find('[name=surName]').val().length > 0)
         {
            searchFor.surName = '*'+$('#tlc_client').find('[name=surName]').val().trim()+'*';
            count++;
         }
         if($('#tlc_client').find('[name=firstName]').val().length > 0)
         {
            searchFor.firstName = '*'+$('#tlc_client').find('[name=firstName]').val().trim()+'*';
            count++;
         }
         if($('#tlc_client').find('[name=postalCode]').val().length > 0)
         {
            searchFor.postalCode = '*'+$('#tlc_client').find('[name=postalCode]').val().trim()+'*';
            count++;
         }
         if($('#tlc_client').find('[name=number]').val().length > 0)
         {
            searchFor.number = $('#tlc_client').find('[name=number]').val().trim();
            count++;
         }
         if($('#tlc_client').find('[name=email]').val().length > 0)
         {
            searchFor.email = '*'+$('#tlc_client').find('[name=email]').val().trim()+'*';
            count++;
         }
         if($('#tlc_client').find('[name=street]').val().length > 0)
         {
            searchFor.street = '*'+$('#tlc_client').find('[name=street]').val().trim()+'*';
            count++;
         }
         if($('#tlc_client').find('[name=city]').val().length > 0)
         {
            searchFor.city = '*'+$('#tlc_client').find('[name=city]').val().trim()+'*';
            count++;
         }

         if (count > 0 && ($('#tlc_client_subdiv2').attr('id')
             && (!$('#tlc_client_subdiv3').attr('id'))))
         {
            basClient.findClientList(searchFor, 10);
         }
         else
         {
            listUsersFound({count: 0});
            changeButtonText('client_confirm', 'Toevoegen');
         }
      }
   }
}

/**
 * Show a list of all users found
 *
 * @param   result   object
 */
function listUsersFound(result)
{
   var count = Math.min(result.count, 10);

   var select = document.getElementById('same_address');
   while (select.length > 0)
   {
      select.remove(0);
   }
   select.size = count > 1 ? count : count+1;
   for (var i = 0; i < count; i++)
   {
      var client = result.clients[i];

      var label = '(' + i + ') ';
      if (client.firstName)
      {
         label += client.firstName + ' ';
      }
      if (client.surNamePrefix)
      {
         label += client.surNamePrefix + ' ';
      }
      if (client.surName)
      {
         label += client.surName + ' ';
      }
      if (client.postalCode && client.number)
      {
         label += '- ' + client.postalCode + ' ' + client.number + ' ';
      }
      if (client.email)
      {
         label += '- ' + client.email;
      }
      if (!client.active)
      {
         label += ' (inactief)';
      }

      var option = new Option(label, client.id);
      try
      {
         select.add(option, null);
      }
      catch (exc)
      {
         select.add(option);
      }
   }
   select.selectedIndex = -1;

   document.getElementById('nr_searchresults').innerHTML =
      (result.count > 0 ? result.count : 'Geen') + ' zoekresultaten:';
}

/**
 * Get the address and location of of a postal code / house number combination.
 * @param string postalCode The postal code of the address
 * @param string number     The house number of the address
 * @param array options     Extra options. These can include:
 *    createMarker: add a marker on the map for the address
 *    ignoreExc   : ignore an exception when the address cannot be found
 *    keepIssues  : if createMarker is set, don't remove the current address
 *                  markers from the map when adding the new marker
 *    getAddress  : get the street name and city in addition to the
 *                  postal code location.
 * @return true if a request for the address was sent to the server, false
 *    the postal code or number was empty.
 */
function getAddress(postalCode, number, options)
{
   if (!postalCode || !number)
   {
      return false;
   }

   if (options.getAddress)
   {
      basLocation.getAddressAndCoordinates(postalCode, number, options);
   }
   else
   {
      basLocation.getLocation(postalCode, options);
   }

   return true;
}

/**
 * Get coordinates by the given postalCode
 *    (and support wildcards)
 *
 * @param postalcode
 */
function getPostalCodeLocation(postalcode)
{
   var strip1 = postalcode.replace(/[\*\?]/g, '');
   if (strip1.length < 4)
   {
      for (var i = strip1.length; i < 4; i++)
      {
         strip1 += '0';
      }
   }
   else if (strip1.length == 5)
   {
      strip1 += 'A';
   }

   basLocation.getCoordinates(strip1);
}

/**
 * Handle the 'scrolling' for the overlay-legend box
 *
 * @param direction   string   Are we going up or down?
 */
function gMapOptions(direction)
{
   var rows = $('div.maps_options').find('div.checkboxrow');
   var count = rows.length;
   var current = rows.filter(':visible').attr('id');
   var currentnr = current.substring(8,10);
   var currentnrdown = rows.filter(':visible').attr('id').substring(8,10);
   var i;
   currentnr = parseFloat(currentnr);
   currentnrdown = parseFloat(currentnrdown)+3;

   for (i = 0; i <= count; i++)
   {
      $('div.maps_options').find('div.checkboxrow[id=checkbox'+i+']').hide();
      $('div.maps_legend_content').find('div.checkboxrow[id=checkbox'+i+'_l]').hide();
   }

   if(direction == 'up')
   {
      if (currentnr < 4)
      {
         currentnr = 4;
      }
      for (i = currentnr-3; i < currentnr; i++)
      {
         $('div.maps_options').find('div.checkboxrow[id=checkbox'+i+']').show();
         $('div.maps_legend_content').find('div.checkboxrow[id=checkbox'+i+'_l]').show();
      }
   }
   else
   {
      if (currentnrdown > count)
      {
         currentnrdown = currentnrdown-3;
      }

      var h = 0;
      for (i = currentnrdown; i <= count; i++)
      {
         if (h != 3)
         {
            $('div.maps_options').find('div.checkboxrow[id=checkbox'+i+']').show();
            $('div.maps_legend_content').find('div.checkboxrow[id=checkbox'+i+'_l]').show();
            h++;
         }
      }
   }
}

/**
 * This function is called when a report marker on the map is clicked.
 * It fills in the marker's postal code, and selects all complaints for
 * that postal code area taking all other filter settings into account.
 * @param string postcode The postal code to search for
 */
function showComplaint(postcode)
{
   $('#report_postcode').val(postcode);
   getReport(true);
   toggleTab('tl_reports');
   toggleTab('br_5');
}

/**
 * This function is called when a marker is clicked that was created
 * after clicking on an issue. It fills in the marker's postal code and
 * the date of the issue, and generates an overview of the number of
 * complaints per hour for that day and postal code.
 */
function showDateComplaint(postcode, date)
{
   $('#report_postcode').val(postcode);
   var filters = {
      startdate: date,
      enddate: date,
      postalCode: postcode
   };
   basReport.getReport(filters, null, 'NUM_ISSUES', ['HOUR'], {getgeo: false});
   toggleTab('tl_reports');
   toggleTab('br_5');
}

/**
 * Keypress handler for the username field on the login screen. Toggles the
 * focus to the password tab when Enter is hit, and prevents Opera from copying
 * the username and password in the location bar.
 * @param object e The keypress event structure
 */
function usernameKeyPress(e)
{
   e = (window.event) ? event : e;
   var KeyID = e.keyCode;

   if (KeyID == 13) // Enter
   {
      if ($.browser.opera || $.browser.safari)
      {
         e.preventDefault();
      }
      $('#password').focus();
      return false;
   }
}

/**
 * Keypress handler for the password field on the login screen. Sends the login
 * information if enter is pressed.
 * @param object e The keypress event structure
 */
function passwordKeyPress(e)
{
   var KeyID = (window.event ? event.keyCode : e.keyCode);

   if (KeyID == 13) // Enter
   {
      loginClient();
      return false;
   }
}

/**
 * onKeyDown
 *
 * @param e
 */
function KeyCheck2(e)
{
   var KeyID = (window.event) ? event.keyCode : e.keyCode;

   if(KeyID == 17)
   {
      isCtrl = true;
   }
   else if (KeyID == 90 && isCtrl)
   {
      if ($('#tlc_client').is(':visible') && (user.type == 'BAS' || user.type == 'ADMIN'))
      {
         $('#same_address').focus();
         focused_element = null;
      }
   }
}

/**
 * Toggle between tabs depending on key pressed
 *
 * @param key
 */
function KeyTab(key)
{
   var length, next, prev;

   switch(key)
   {
      case 335: // the END key
         length = $('.current').parents('div.container').find('.part').length;
         next = $('.current').parents('div.part').nextAll(':visible').attr('id');

         if($('.current').parents('div.part').attr('id') == 'bottomright')
         {
            next = 'topleft';
         }

         if (!next || next == 'undefined' || next === '')
         {
            if ($('.current').parents('div.part').attr('id').substr(0, 3) == 'bl_')
            {
               next = 'bottomright';
            }
            else
            {
               next = $('.current').parents('.container').find('.part:first').attr('id');
            }
         }

         if (next.substr(0, 3) == 'bl_')
         {
            if (next != 'bl_client' && next != 'bl_issues')
            {
               next = 'bottomright';
            }
         }

         toggleContent(next);

         $('.current').removeClass().addClass('open') ;
         $('#'+next).find('li[class=open]').removeClass().addClass('current');
         $('.current').parents('div.part').find('.innercontent').find(':input:visible:first').focus();
         break;
      case 336:  // PageDown key
         length = $('.current').parents('ul').find('li:visible').length;
         next = $('.current').nextAll(':visible:eq(0)').attr('id');

         if(!next)
         {
            next = $('.current').parents('ul').find('li:visible:first').attr('id');
         }

         $('.current').removeClass();
         $('#'+next).addClass('current');
         toggleContent(next);

         toggleTab(next);

         if($('.current').parents('.part').attr('id') == 'topleft'){
            //var nr = $('.current').attr('id').substring(3,4);
            //$('[id^=bl_]').hide();
            //$('[id=bl_'+nr+']').show();
         }
         $('.current').parents('div.part').find('.innercontent').find(':input:visible:first').focus();
         break;
      case 337: // PageUp key
         length = $('.current').parent('ul').find('li:visible').length;
         prev = $('.current').prevAll(':visible:eq(0)').attr('id');

         if (!prev || prev == 'undefined' || prev === '')
         {
            prev = $('.current').parents('ul').find('li:visible:last').attr('id');
         }

         $('.current').removeClass();
         $('#'+prev).addClass('current');
         toggleContent(prev);

         toggleTab(prev);

         if($('.current').parents('.part').attr('id') == 'topleft'){
            //var nr = $('.current').attr('id').substring(3,4);
            //$('[id^=bl_]').hide();
            //$('[id=bl_'+nr+']').show();
         }

         $('.current').parents('div.part').find('.innercontent').find(':input:visible:first').focus();
         break;
   }
}

/**
 * Performs actions depending on the pressed key
 *
 * @param e
 */
function KeyCheck(e)
{
   isCtrl = false;
   var KeyID = (window.event) ? event.keyCode : e.keyCode;
   switch(KeyID)
   {
   case 40:
      break;
   case 27:
      if ($('#change_password').is(':visible'))
      {
         toggleChangePassword();
      }
      else if ($('.current').attr('id') == 'tl_client')
      {
         resetClientForm();
      }
      $('.issue_sub_div').hide();
      $('#reportdiv_opslaan').hide();
      break;
   case 26:
      break;
   case 48: // 0
   case 49: // 1
   case 50: // 2
   case 51: // 3
   case 52: // 4
   case 53: // 5
   case 54: // 6
   case 55: // 7
   case 56: // 8
   case 57: // 9
      if ($('#nav_topleft').find('.current').attr('id') == 'tl_client')
      {
         if (!focused_element)
         {
            var idx = KeyID-48;
            var select = document.getElementById('same_address');
            if (idx < select.length)
            {
               select.selectedIndex = idx;
               selectUser(select.options[idx].value);
            }
         }
      }
      break;
   }
}

/**
 * The handler for the client selector. Called when a user is selected
 * by a BAS employee or administrator from the list of clients matching
 * the search criteria.
 */
function selectUser(id)
{
   basClient.findClient(id);
   if (user.type == 'BAS')
   {
      getIssueListBl2(id);
   }
   changeButtonText("client_confirm", "Wijzigen");
}

/**
 * Toggle the tabinterface
 *
 * @param id
 */
function toggleTab(id)
{
   if($('#tl_messages').attr('class') == 'current'
      || $('#tl_messages').attr('class') == 'open')
   {
      clearMessageDetails();
   }

   var part_id = tab_parts[id];
   if (part_id == 'topleft')
   {
      if (user.is_bbb
          && ($('#tl_issues').attr('class') == 'current' || $('#tl_issues').attr('class') == 'open'))
      {
         basClient.releaseLocks();
         drawIssueTracks({}, []);
         hideOverlay('issue');
         hideOverlay('report');
      }
      // If this is the topleft frame that's changing, then we have to change
      // the bottomleft content as well
      var tab_type = id.substr(3);
      var bl_types = ['client', 'issues', 'messages', 'reports', 'admin',
         'runwayuse'];
      for (var i = 0; i < bl_types.length; i++)
      {
         var type = bl_types[i];
         var disp = type == tab_type ? 'block' : 'none';
         document.getElementById('bl_' + type).style.display = disp;
      }

      // If a tab was selected in the now open bottom left frame, show it,
      // otherwise open the first one.
      // Make sure the bottomleft div and tabs get shown correctly as well
      var bl_tab_id = $('#bl_' + tab_type).find('li.open,li.current').attr('id');
      var bl_tab_nr = (bl_tab_id ? bl_tab_id.substr(3, 4) : 1);
      $('#blc_' + tab_type + '_' + bl_tab_nr).show();
      if (id != 'tl_issues')
      {
         basClient.releaseLocks();
      }
   }

   for (var tab_id in tab_parts)
   {
      var tab_part = tab_parts[tab_id];
      var tab_class = document.getElementById(tab_id).className;

      if (tab_class == 'open')
      {
         // If this tabitem was open, close it if it is in this frame
         if (tab_part == part_id)
         {
            document.getElementById(tab_id).className = '';
         }
      }
      else if (tab_class == 'current')
      {
         // If it is current, see if it is in this frame, either close it or set it to 'open'
         document.getElementById(tab_id).className =
            (tab_part == part_id ? '' : 'open');
      }
   }
   if (part_id == 'topleft')
   {
      if ((user.type == 'BAS' || user.type == 'ADMIN')
          && ((id == 'tl_client' && select_hidden.same_address)
              || (id != 'tl_client' && !select_hidden.same_address)))
      {
         var n1 = document.getElementById('same_address');
         var n2 = document.getElementById('same_address_temp');
         try
         {
            n1.swapNode(n2);
         }
         catch (exc)
         {
            var p1 = n1.parentNode;
            var p2 = n2.parentNode;
            var n1c = n1.cloneNode(true);
            p2.replaceChild(n1c, n2);
            p1.replaceChild(n2, n1);
            p2.replaceChild(n1, n1c);
            n1c = null;
         }
         select_hidden.same_address = !select_hidden.same_address;
         if($('table#clientissues').find('tr.contentrow_current').attr('id'))
         {
            $('table#clientissues').find('tr.contentrow_current').attr('class','contentrow');
         }
      }

      if (id == 'tl_issues')
      {
         searchIssues();
      }
      else if (id == 'tl_messages')
      {
         searchMessages();
      }
      else if (id == 'tl_reports')
      {
         toggleTab('bl_reports_2');
         if ($('#tl_client').is(':visible') || user.is_bbb)
         {
            basReport.listReports('PRIVATE');
            basReport.listReports('PUBLIC');
         }
      }
      else if (id == 'tl_admin' && !($.browser.msie))
      {
         showAdminPage('beheer_1_nav');
      }
      else if (id == 'tl_client' && uitvraag_cache && uitvraag_cache.forceReload)
      {
         uitvraag_cache = null;
         resetUitvraagScript();
      }
   }
   else if (part_id == 'bl_client')
   {
      if (id == 'bl_client_1')
      {
         resetUitvraagScript();
      }
      else if (id == 'bl_client_2')
      {
         getIssueListBl2();
      }
   }
   if(id == 'br_5')
   {
      $('#br_8').hide();
      $('#br_3').hide();
   }

   // Show the reports tab when reports are select in the upper left or
   // lower left part. Otherwise, show the service messages.
   var cname;
   if (part_id == 'bl_reports' || id == 'tl_reports')
   {
      cname = document.getElementById('br_4').className;
      document.getElementById('br_4').style.display = 'none';
      document.getElementById('br_5').style.display = 'block';
      if (cname == 'open' || cname == 'current')
      {
         document.getElementById('br_5').className = 'open';
         toggleContent('br_5');
      }
   }
   else if (part_id != 'bottomright')
   {
      cname = document.getElementById('br_5').className;
      document.getElementById('br_5').style.display = 'none';
      if (id != 'bl_issues_2' && id != 'bl_issues_1')
      {
         if(user.is_bbb)
         {
            document.getElementById('br_3').style.display = 'none';
         }
         document.getElementById('br_8').style.display = 'none';
         toggleTab('br_4');
      }
      document.getElementById('br_4').style.display = 'block';
      if (cname == 'open' || cname == 'current')
      {
         document.getElementById('br_4').className = 'open';
         toggleContent('br_4');
      }
   }

   document.getElementById(id).className = 'current';
   toggleContent(id);

   // Focus on the first input
   if (id == 'tl_client')
   {
      $('#tlc_client').find('[name=postalCode]').focus();
   }
   else
   {
      $('#' + part_id).find('.innercontent:visible').find(':input:visible:first').focus();
   }

   if (safari == 100 && !$('#tlc_client').is(':visible'))
   {
      $('#content_topleft').attr('style','overflow-y: auto');
      safari = false;
   }
   if ($.browser.safari && id == 'tl_client')
   {
      safari = 100;
   }
}

/**
 * Grab the issues for this specific user
 *
 * @param userid   int
 */
function getIssueListBl2(userid)
{
   var id = null;
   if (userid)
   {
      id = userid;
   }
   else if ($('div.form').find('input[name=id]').val())
   {
      id = $('div.form').find('input[name=id]').val();
   }

   if (id)
   {
      var issue = {client: id, state: 'ALL'};
      basIssue.listIssues(issue, clientissue_search.orderby,
         clientissue_search.order, 0, clientissue_search.page_size, true);
   }
}

/**
 * Function called when an issue is selected from the list. This sets the
 * display class for the list entry and its neighbours, and requests the
 * desired issue details from the server.
 * @param string id The id of the list entry
 */
function selectIssue(id)
{
   drawIssueTracks({}, []);
   hideOverlay('issue');
   hideOverlay('report');
   var parts = id.split('_');
   var issue_id = parts[1];
   basIssue.getIssue(issue_id);
   return false;
}

/**
 * Function called when an issue is selected from the list of issues of a
 * selected client. This sets the display class for the list entry and its
 * neighbours, requests the desired issue details from the server, and switches
 * to the issue details tab.
 * @param string id The id of the list entry
 */
function selectClientIssue(id)
{
   var parts = id.split('_');
   var issue_id = parts[1];
   $('#'+id).siblings('tr.contentrow_current').attr('class', 'contentrow');
   $('#'+id).attr('class','contentrow_current');
   $('.issues').find('tr.contentrow_current').attr('class', 'contentrow');
   basIssue.getIssue(issue_id);
   toggleTab('tl_issues');
   toggleTab('bl_issues_1');
   return false;
}

/**
 * Function called when a message is selected from the list. This sets the
 * display class for the list entry and its neighbours, and requests the
 * desired message details from the server.
 * @param string id    The id of the list entry
 * @param bool is_poll True if the message refers to a poll, false otherwise
 */
function selectMessage(id, is_poll)
{
   var parts = id.split('_');
   var message_id = parts[1];
   var row = $('#'+id);
   row.siblings('tr.contentrow_current').attr('class', 'contentrow');
   row.attr('class', 'contentrow_current');
   if (!is_poll || user.is_bbb)
   {
      basMessage.getMessage(message_id);
   }
   else
   {
      basMessage.getPoll(message_id);
   }
   toggleTab('bl_messages_1');
   return false;
}

/**
 * Function called when a flight is selected from the list of matching
 * flights for an issue. It makes the selected row current, and highlights
 * the corresponding track.
 * @param int id The flight ID of the selected flight
 */
function selectFlight(id)
{
   $('#flight_'+id).siblings('tr.contentrow_current').attr('class', 'contentrow');
   $('#flight_'+id).attr('class','contentrow_current');
   selectTrack(id);
}

/**
 * Remove rows matching certain class names from a table before new
 * content is inserted.
 * @param object table  The DOM node for which to remove the rows
 * @param array classes The class names of the rows to be deleted
 */
function deleteContentRows(table, classes)
{
   for (var i = table.rows.length-1; i >= 0; i--)
   {
      if (arrayContains(classes, table.rows[i].className))
      {
         table.deleteRow(i);
      }
   }
}

/**
 * Add a message row with a message (such as 'no issues found') to an issue
 * or message list.
 * @param object table The DOM node to add the message to
 * @param string msg   The message to add
 * @param int    span  The number of columns to be reserved for the message
 */
function addListMessage(table, msg, span)
{
   var row = table.insertRow(-1);
   row.className = 'contentrow_current';
   row.insertCell(-1);
   var cell = row.insertCell(-1);
   cell.colSpan = span;
   cell.innerHTML = msg;
}

/**
 * Add a next and or previous page button row to an issue or message list.
 * @param object table    The DOM node to add the buttons to
 * @param string which    The buttons to add: 'prev', 'next' or 'both'
 * @param int    prevspan The number of columns for the prev button
 * @param int    nextspan The number of columns for the next button
 * @param string id       The name of the table to flip through
 */
function addPageButtons(table, which, prevspan, nextspan, id)
{
   var row = table.insertRow(-1);
   row.className = 'page_buttons';

   var cell = row.insertCell(-1);
   cell.colSpan = prevspan;
   if (which == 'prev' || which == 'both')
   {
      cell.innerHTML = '<a href="#" class="textlink" onclick="togglePage(\'' + id + '\',\'prev\'); return false;">'
                     +    'Vorige pagina'
                     + '</a>';
   }
   if (which == 'next' || which == 'both')
   {
      cell = row.insertCell(-1);
      cell.colSpan = nextspan;
      cell.innerHTML = '<a href="#" class="textlink" onclick="togglePage(\'' + id + '\',\'next\'); return false;">'
                     +    'Volgende pagina'
                     + '</a>';
   }
}

/**
 * Show a list of issues
 *
 * @param result
 * @param bas
 */
function showIssueList(result, bas)
{
   var id = bas ? 'clientissues' : 'issues';
   var table = document.getElementById(id);

   deleteContentRows(table,
      ['contentrow', 'contentrow_current', 'page_buttons']);

   var selectfun = bas ? 'selectClientIssue' : 'selectIssue';
   var current = $('#blc_issues_1').find('input[name=issue_id]').val();
   if (current) current = '';
   for (var i = 0; i < result.issues.length; i++)
   {
      var issue = result.issues[i];

      var cdate = '';
      if (issue.cdate)
      {
         cdate = formatDBDate(issue.cdate, true);
      }
      var date = '';
      if (issue.date)
      {
         date = formatDBDate(issue.date);
         if (issue.complaintType == 'PERIOD')
         {
            date += '&nbsp;per.';
         }
         else if (issue.complaintType == 'GENERIC')
         {
            date += '&nbsp;alg.';
         }
         else if (issue.time)
         {
            date += '&nbsp;' + issue.time.substr(0, 5);
         }
      }

      var row = table.insertRow(-1);
      row.id = id + '_' + issue.id;
      row.className = issue.id == current ? 'contentrow_current' : 'contentrow';
      row.onclick = new Function(selectfun + '(this.id);');
      row.insertCell(-1).innerHTML = issue.id;
      row.insertCell(-1).innerHTML = cdate;
      row.insertCell(-1).innerHTML = date;
      if (user.is_bbb)
      {
         row.insertCell(-1).innerHTML = issue.postalCode;
      }
      row.insertCell(-1).innerHTML = getTranslation('ILIST_' + issue.type);
      if (user.is_bbb)
      {
         row.insertCell(-1).innerHTML = getTranslation(issue.feedback);
      }
      row.insertCell(-1).innerHTML = getTranslation(issue.state);
   }

   if (result.issues.length === 0)
   {
      var msg = result.count === 0 ? 'Geen meldingen gevonden' : 'Fout bij het laden van meldingen';
      var len = $('#' + id).find('.titlerow').find('th').length;
      addListMessage(table, msg, len-1);
   }
   else if (result.count > result.issues.length)
   {
      var start = bas ? clientissue_search.start : issue_search.start;
      var which = start <= 0
         ? 'next'
         : (start + result.issues.length < result.count ? 'both' : 'prev');
      addPageButtons(table, which, 4, 2, id);
   }

   showCounter(result.count, bas ? 'client_issue_counter': 'issue_counter');
}

/**
 * Show a list of messages
 *
 * @param result
 */
function showMessageList(result)
{
   var table = document.getElementById('messages');

   deleteContentRows(table,
      ['contentrow', 'contentrow_current', 'page_buttons']);

   for (var i = 0; i < result.messages.length; i++)
   {
      var message = result.messages[i];

      var cdate = '';
      if (message.cdate)
      {
         cdate = formatDBDate(message.cdate);
      }

      var row = table.insertRow(-1);
      var is_poll = message.type == 'POLL' ? 'true' : 'false';
      row.id = 'message_' + message.id;
      
      row.className = 'contentrow';
      row.onclick = new Function('selectMessage(this.id, ' + is_poll + ');');
      row.insertCell(-1).innerHTML = message.id;
      row.insertCell(-1).innerHTML = cdate;
      row.insertCell(-1).innerHTML = getTranslation(message.type);
      row.insertCell(-1).innerHTML = message.title ? message.title : '';
      if (user.is_bbb)
      {
         row.insertCell(-1).innerHTML = getTranslation(message.state);
      }
   }

   if (result.messages.length === 0)
   {
      var msg = result.count === 0 ? 'Geen berichten gevonden' : 'Fout bij het laden van berichten';
      var len = $('#messages').find('.titlerow').find('th').length;
      addListMessage(table, msg, len-1);
   }
   else if (result.count > result.messages.length)
   {
      var which = message_search.start <= 0
         ? 'next'
         : (message_search.start + result.messages.length < result.count ? 'both' : 'prev');
      addPageButtons(table, which, 3, 1, 'messages');
   }

   showCounter(result.count, 'message_counter');
}

/**
 * Toggle the content depending on the tab
 *
 * @param id
 */
function toggleContent(id)
{
   if (id == 'bl_admin_1')
   {
      $('#beheer_6').show();
      $('#beheer_8').hide();
   }
   else if (id == 'bl_admin_2')
   {
      $('#beheer_8').show();
      $('#beheer_6').hide();
      $('div#blc_admin').css('overflow-x','auto');
   }
   else if (id == 'nav_merger_list' || id == 'nav_merger_records')
   {
      var cont_id = '#beheer_' + id.split('_').slice(1).join('_');
      $(cont_id).show().siblings().hide();
   }
   else
   {
      var type = id.split('_').slice(1).join('_');
      $('#'+tab_parts[id] + ' .innercontent').each(function(){
         var cid = $(this).attr('id');
         var ctype = cid.split('_').slice(1).join('_');
         var disp = ctype == type ? 'block' : 'none';
         document.getElementById(cid).style.display = disp;
      });
   }
}

/**
 * Show an administration page
 * @param string id The ID of the page to show
 */
function showAdminPage(id)
{
   $('#' + id).addClass('current').siblings().removeClass('current');

   var parts = id.split('_');
   parts.pop();
   var content_id = parts.join('_');

   // If the SQL console or the record merger was selected, show the
   // corresponding tab bar. Otherwise, hide them.
   if (content_id == 'beheer_6')
   {
      document.getElementById('nav_bl_admin').style.display = 'block';
      document.getElementById('nav_merger').style.display = 'none';
      document.getElementById('bl_admin_1').className = 'open';
      document.getElementById('bl_admin_2').style.display = 'none';
      document.getElementById('blc_admin').style['overflow-x'] = 'hidden';
   }
   else if (content_id == 'beheer_merger_list')
   {
      document.getElementById('nav_bl_admin').style.display = 'none';
      document.getElementById('nav_merger').style.display = 'block';
      document.getElementById('nav_merger_list').className = 'open';
      document.getElementById('nav_merger_records').style.display = 'none';
   }
   else
   {
      document.getElementById('nav_bl_admin').style.display = 'none';
      document.getElementById('nav_merger').style.display = 'none';
   }

   $('#blc_admin').find('div[id^=beheer]').map(function(){
      if($(this).attr('id') == content_id)
      {
         $(this).show();
      }
      else
      {
         $(this).hide();
      }
   });

   return false;
}

/**
 * Show the right search-div in the right place (and/or hide it)
 *
 * @param type
 */
function showSearch(type)
{
   if($('#searchdiv_'+type).is(':visible')) {
      $('#searchdiv_'+type).hide();
   }
   else
   {
      if($('#searchdiv_'+type).parent().attr('id') == 'div.search') {
         $('#searchdiv_'+type).show();
      }
      else
      {
         $('#searchbar_'+type).append($('#holder_'+type).contents());
         $('#searchdiv_'+type).show();
      }
   }
}

/**
 * Show the right search-div in the right place (and/or hide it)
 *
 * @param type
 */
function showReportSelect(type, tostate)
{
   if(tostate == 'hide'
      || $('#reportdiv_'+type).is(':visible') && tostate != 'show')
   {
      $('#reportdiv_'+type).hide();
   }
   else
   {
      $('#reportdiv_'+type).show();
   }
}

/**
 * Get the current setting in the report tab, as entered by the user,
 * for storing or retrieving a report.
 * @return array with the settings
 */
function getReportSettings()
{
   var output = null;
   var groupby = [];
   var filters = {};
   var sql = null;

   $('.reportselect_content').find('[name=groupby]').map(function(){
      groupby.push($(this).val());
   });
   $('.reportselect_content').find('select[name=report_result_sum]').map(function(){
      output = $(this).val();
   });

   $('.reportselect_content').find('div[id^=holder][id!=holder_resultaat]').map(function(){
      if($(this).attr('id') != 'holder_opslaan' && $(this).attr('id') != 'holder_selecties'){
         $(this).find(':text').map(function(){
            var name = $(this).attr('name');
            var val = $(this).val();

            if (val && !arrayContains(['day', 'month', 'year'], name))
            {
               filters[name] = val;
            }
         });
         $(this).find('input:checked').map(function(){
            var name = $(this).attr('name');
            if (!filters[name])
            {
               filters[name] = [];
            }
            filters[name].push($(this).val());
         });
         if (user.is_bbb)
         {
            $(this).find('select[name=certainty]').map(function(){
               if ($(this).val())
               {
                  filters[$(this).attr('name')] = $(this).val();
               }
            });
         }
         $(this).find('textarea').map(function(){
            if($(this).val())
            {
               filters[$(this).attr('name')] = $(this).val();
            }
         });
      }
   });

   try
   {
      var startdate = dateFieldGetValue('report_startdate', true);
      if (startdate)
      {
         filters.startDate = startdate;
      }
   }
   catch (exc)
   {
      handleError([exc], ["$('#report_startdate').find('input')"]);
      return;
   }
   try
   {
      var enddate = dateFieldGetValue('report_enddate', true);
      if (enddate)
      {
         filters.endDate = enddate;
      }
   }
   catch (exc)
   {
      handleError([exc], ["$('#report_enddate').find('input')"]);
      return;
   }

   if (filters.report_result_sql)
   {
      sql = filters.report_result_sql;
      delete filters.report_result_sql;
   }

   return {
      filters: filters,
      output: output,
      groupby: groupby,
      sql: sql
   };
}

/**
 * Send a request to the serve to save the current report settings.
 */
function storeReport()
{
   var report = {
      title: ($('[name=report_opslaan_titel]').val()
         ? $('[name=report_opslaan_titel]').val()
         : false),
      scope: ($('[name=report_scope]:checked').val()
         ? $('[name=report_scope]:checked').val()
         : null)
   };
   if(!report.title)
   {
      return false;
   }

   var settings = getReportSettings();
   if (settings.filters)
   {
      basReport.storeReport(report.title, report.scope, settings.filters,
         settings.sql, settings.output, settings.groupby);
   }
}

/**
 * Request a report with the current report settings from the server.
 * @param bool nogeo If true, don't ask for geographical distribution of the
 */
function getReport(nogeo)
{
   var settings = getReportSettings();
   if (settings.filters)
   {
      var options = {};
      if (nogeo)
      {
         options.getgeo = false;
      }
      basReport.getReport(settings.filters, settings.sql, settings.output,
         settings.groupby, options);
   }
}

/**
 * Reset all report filters to their default values
 */
function resetReportSettings()
{
   // Set all checkboxes
   $('#holder_klachten').find(':checkbox').attr('checked', true);
   selectorBoxSetText('runway');
   $('#holder_resultaat').find(':checked').attr('checked', false);

   // Clear text fields
   $('#holder_klagers').find(':text').val('');
   $('#holder_sql').find(':input').val('');

   // Set selectors to default values
   $('#holder_klachten').find('[name=certainty] option[value=0]').attr('selected', true);
   $('#holder_resultaat').find('[name=report_result_sum] option[value=NUM_ISSUES]')
      .attr('selected', true);
   $('#holder_resultaat').find('[name=groupby][value=WEEKDAY]').attr('checked', true);

   // Default values for dates and times (past month)
   var today = new Date();
   var prev_month = new Date(today.getFullYear(), today.getMonth()-1, today.getDate());
   if (prev_month.getMonth() == today.getMonth())
   {
      prev_month.setDate(0);
   }
   dateFieldSetValue('report_startdate', dateToString(prev_month));
   dateFieldSetValue('report_enddate', dateToString(today));
}

/**
 * Reset the report filters with the values returned by the server
 *
 * @param array filters The new filter settings
 * @param string output The quantity to count
 * @param array groupby The values to group by
 */
function setReportSettings(filters, output, groupby)
{
   resetReportSettings();
   if (filters)
   {
      for (var name in filters)
      {
         var value = filters[name];

         switch (name)
         {
         case 'startdate':
         case 'enddate':
            dateFieldSetValue('report_' + name, value);
            break;
         case 'starttime':
         case 'endtime':
            var hour = value.substring(0, 2);
            var minute = value.substring(3, 5);
            $('#report_' + name + '_h').val(hour);
            $('#report_' + name + '_m').val(minute);
            break;
         case 'complainttype':
         case 'direction':
         case 'groundnoise':
         case 'timeofday':
            // First clear all checkboxes for this filter, then set the ones
            // returned by the server
            $('#holder_klachten').find('[name=' + name + ']').attr('checked', false);
            for (var i = 0; i < value.length; i++)
            {
               $('#holder_klachten').find('[name=' + name + '][value=' + value[i] + ']').attr('checked', true);
            }
            break;
         case 'runway':
            selectorBoxSetValue('runway', value);
            break;
         case 'certainty':
            $('#holder_klachten').find('[name=' + name + ']').val(value);
            break;
         case 'postalcode':
         case 'number':
         case 'city':
            $('#report_klagers').find('[name=' + name + ']').val(value);
            break;
         }
      }
   }

   if (output)
   {
      $('#holder_resultaat [name=report_result_sum] option[value=' + output + ']').attr('selected', true);
   }

   if (groupby && groupby.length > 0)
   {
      //$('#holder_resultaat [name=groupby]:checked').attr('checked', false);
      //for (var i = 0; i < groupby.length; i++)
      //   $('#holder_resultaat [name=groupby][value=' + groupby[i] + ']').attr('checked', true);
      $('#holder_resultaat [name=groupby]').val(groupby[0]);
   }
}

/**
 * Display the result of the report-search
 *
 * @param result   object
 */
function handleReport(result, nogeo)
{
   document.getElementById('blc_reports_2').innerHTML = result.table;

   if (result.graph)
   {
      document.getElementById('blc_reports_1').innerHTML = result.graph;
      $('#bl_reports_1').show();
   }
   else
   {
      $('#blc_reports_1').hide();
      $('#bl_reports_1').hide();
      if ($('#bl_reports').is(':visible'))
      {
         toggleTab('bl_reports_2');
      }
   }

   var pc = $('#report_postcode').val();
   if (result.geo && !nogeo)
   {
      hideOverlay('issue');
      hideOverlay('report');
      gMapMarkersOverlay(result.geo, 'report');
      if (result.filters && result.filters.postalcode)
      {
         pc = result.filters.postalcode;
      }
   }
   if (pc)
   {
      getPostalCodeLocation(pc);
   }

   if (result.filters || result.output || result.groupby)
   {
      setReportSettings(result.filters, result.output, result.groupby);
   }
}

/**
 * Display the result of the report-search
 *
 * @param result   object
 */
function handleDayIssues(result, date)
{
   //if(result.sql)
      //$('#report_result_sql').val(result.sql);
   if (result.geo)
   {
      hideOverlay('issue');
      hideOverlay('report');
      gMapMarkersOverlay(result.geo, 'issue', date);
   }
}

/**
 * Handle the result of storing a report. This simply updates the
 *    relevant report list.
 *
 * @param result The result of storing the report as returned by the server
 */
function handleReportStored(result)
{
   showReportSelect('opslaan', 'hide');
   $('[name=report_opslaan_titel]').val('');
   if (result.toUpperCase() == 'PUBLIC')
   {
      basReport.listReports('PUBLIC');
   }
   else
   {
      basReport.listReports('PRIVATE');
   }
}

/**
 * Collect the fields of a message being edited, and return them
 * @param int id If set, the fields correspond to an existing message
 *    being edited, and the type is not included in the fields. Otherwise
 *    the message is a new one.
 * @return array with the message, or boolean false when an error occured
 */
function getMessageDetails(id)
{
   var message = {
      title: $('#message_details_edit [name=message_title]').val(),
      keywords: $('#message_details_edit [name=message_keywords]').val(),
      scope: $('#message_details_edit [name=message_scope]').val(),
      content: $('#message_details_edit [name=message_content]').val(),
      query_id: $('#message_details_edit [name=message_queries]').val()
   };
   if ($('#message_details_edit [name=message_final]').is(':checked'))
   {
      message.state = 'FINAL';
   }
   var type = $('#message_details_edit [name=message_type]').val();
   if (typeof id == 'undefined')
   {
      message.type = type;
   }
   else
   {
      message.id = id;
   }

   var error = [];
   var err_field = [];

   if (!message.title)
   {
      error.push('Titel is leeg');
      err_field.push("$('#message_details_edit [name=message_title]')");
   }
   if (!message.content)
   {
      error.push('Bericht is leeg');
      err_field.push("$('#message_details_edit [name=message_content]')");
   }
   if (type == 'MAILING' || type == 'POLL')
   {
      if (!message.query_id)
      {
         error.push('Geen selectie van ontvangers opgegeven');
         err_field.push("$('#message_details_edit [name=message_queries]')");
      }
   }

   if (type == 'POLL')
   {
      message.url = $('#message_details_edit [name=message_url]').val();
   }
   else if (type == 'SERVICE')
   {
      try
      {
         message.t_valid = dateFieldGetValue('msg_t_valid');
      }
      catch (exc)
      {
         error.push(exc);
         err_field.push("$('#msg_t_valid input')");
      }
      try
      {
         message.t_invalid = dateFieldGetValue('msg_t_invalid');
      }
      catch (exc)
      {
         error.push(exc);
         err_field.push("$('#msg_t_invalid input')");
      }
      try
      {
         message.t_start = dateFieldGetValue('msg_t_start');
      }
      catch (exc)
      {
         error.push(exc);
         err_field.push("$('#msg_t_start input')");
      }
      try
      {
         message.t_end = dateFieldGetValue('msg_t_end');
      }
      catch (exc)
      {
         error.push(exc);
         err_field.push("$('#msg_t_end input')");
      }

      if (error.length === 0)
      {
         if (message.t_invalid < message.t_valid)
         {
            error.push('\\"Geldig van\\" datum is later dan \\"Geldig tot\\" datum');
            err_field.push("$('#msg_t_valid,#msg_t_invalid').find('input')");
         }
         if (message.t_end < message.t_start)
         {
            error.push('\\"Zichtbaar van\\" datum is later dan \\"Zichtbaar tot\\" datum');
            err_field.push("$('#msg_t_start,#msg_t_end').find('input')");
         }
      }
   }

   if (error.length > 0)
   {
      handleError(error, err_field);
      return false;
   }

   return {message: message, type: type};
}

/**
 * Add a new message
 */
function addMessage()
{
   var details = getMessageDetails();
   basMessage.addMessage(details.message, details.type);
}

/**
 * Queue a mailing
 */
function queueMessage(id, message)
{
   if (!message || message == 'static')
   {
      var details = message == 'static' ? { message: {} } : getMessageDetails();
      var type = details.type ? details.type : '';
      details.message.mailtype = 'MAILING';
      details.message.state = 'SENDING';
      if (id)
      {
         details.message.id = id;
         delete details.message.type;
         basMessage.amendMessage(details.message, type);
      }
      else
      {
         basMessage.addMessage(details.message, type);
      }
   }
   else
   {
      if (!message.query_id)
      {
         alert('Geen selectie gekozen');
         return false;
      }
      else
      {
         message.id = id;
         basMessage.queueMessage(message);
      }
   }

}

/**
 * Edit a message
 * @param int id The ID of the message being edited
 */
function amendMessage(id)
{
   var details = getMessageDetails(id);
   if (details)
   {
      basMessage.amendMessage(details.message, details.type);
   }
}

/**
 * Show a list of stored reports.
 * @param array result The list of stored reports
 * @param string scope The scope of the report: 'PUBLIC' or 'PRIVATE'
 */
function showReportList(result, scope)
{
   var id = (scope == 'PUBLIC' ? 'public_reports' : 'client_reports');
   var table = document.getElementById(id);
   var row;

   deleteContentRows(table, ['reportsrow', 'reportsrow_current']);

   if (result.length === 0)
   {
      row = table.insertRow(-1);
      row.className = 'reportsrow';
      row.insertCell(-1).innerHTML = '<i>Geen overzichten gedefinieerd</i>';
   }
   else
   {
      for (var i = 0; i < result.length; i++)
      {
         var report = result[i];

         row = table.insertRow(-1);
         row.className = 'reportsrow';
         row.id = 'report_' + report.id;
         var cell = row.insertCell(-1);
         cell.className = 'report';
         cell.innerHTML = report.title;
         cell.onclick = new Function('basReport.getStoredReport(' + report.id + ')');
         if (scope == 'PRIVATE' && report.isowner)
         {
            row.insertCell(-1).innerHTML =
                  '<a href="#" onclick="confirmDeleteReport(' + report.id + '); return false;" class="textlink">'
               +    'Verwijder'
               + '</a>';
         }
      }
   }
}

/**
 * Open a confirmation window for deleting a report
 * @param int id  The ID of the report to delete
 */
function confirmDeleteReport(id)
{
   $('#report_delete_link').unbind('click');
   $('#report_delete_link').click(function() {
      basReport.deleteReport(id);
      $('#report_delete').hide();
      return false;
   });
   $('#report_delete').show();
}

/**
 * Remove a stored report entry from the list. Called when the server
 * reports success after a request to delete a report.
 * @param int id The ID of the deleted report
 */
function handleReportDeleted(id)
{
   var node = document.getElementById('report_' + id);
   node.parentNode.removeChild(node);
}

/**
 * Handle error messages, show them and color the given elements red
 *
 * @param message   Array   Contains the error messages
 * @param element   Array   Contains the elements the error belongs to
 */
function handleError(message,element)
{
   var hideString = '';
   if (typeof(element) == 'object')
   {
      for (var x in element)
      {
         eval(element[x]).animate({backgroundColor: '#F54959'}, 1000);
         hideString += element[x] + '.animate({backgroundColor: \'#FFFFFF\'}, 1000);';
      }
   }

   var unique_msg = [];
   for (var i = 0; i < message.length; i++)
   {
      var msg = message[i];
      if (!arrayContains(unique_msg, msg))
      {
         unique_msg.push(msg);
      }
   }
   var error = unique_msg.join('\\n');

   if (setTimeout('alert("' + error + '")', 650))
   {
      eval(hideString);
      $('#exception_content_div').hide();
   }
   return false;
}

/**
 * Reset the message details tab. Sets a default message in the static
 * content div, and hides the edit fields.
 */
function clearMessageDetails()
{
   var content = '';
   if (user.type == 'BAS' || user.type == 'ADMIN')
   {
      document.getElementById('message_details_edit').style.display = 'none';
      content = 'Selecteer hierboven een bericht, of<br/><br/>'
              + '<a href="#" class="textlink" onclick="newMessage(); return false;">'
              +    'Maak een nieuw bericht'
              + '</a>';
   }
   else
   {
       content = 'Selecteer hierboven een bericht';
   }

   document.getElementById('message_details_static').innerHTML = content;
   document.getElementById('message_details_static').style.display = 'block';
   document.getElementById('message_details_attachments').innerHTML = '';
}

/**
 * When logged in as BAS or backoffice employee, the client checks occasionally
 *    if it should release a selected issue or message so that another employee
 *    can edit the object. This function does the releasing.
 *
 * @param array tables Array with objects to release. Entries can me 'Message'
 *                     to unselect the messages, or 'Issue' to release an issue.
 */
function unselectTables(tables)
{
   for (var i = 0; i < tables.length; i++)
   {
      var table = tables[i];
      if (table == 'Message')
      {
         clearMessageDetails();
      }
      else if (table == 'Issue')
      {
         document.getElementById('blc_issues_1').innerHTML = 'Selecteer een melding';
      }
   }
}

/**
 * Check if a filter becomes empty after toggling off an option, and if so
 *    select all available options
 * @param string name The name of the filter to check
 */
function checkEmptyFilter(name)
{
   if ($('#reportdiv_klachten').find('[name=' + name + ']:checked').length === 0)
   {
      $('#reportdiv_klachten').find('[name=' + name + ']').attr('checked', true);
   }
}

/**
 * Callback function that is executed when one of the complaint type
 * options in the reports tab is clicked. Checks if the filter becomes
 * empty, and if so, selects all available options. Also hides the certainty
 * selector when the 'period' option is checked, and hides it otherwise.
 */
function toggleReportComplaintType()
{
   checkEmptyFilter('complainttype');
   if (user.is_bbb)
   {
      var row = document.getElementById('report_complaint_certainty');
      if (document.getElementById('report_type2_specifiek').checked)
      {
         row.style.display = 'block';
      }
      else
      {
         row.style.display = 'none';
      }
   }
}

/**
 * Check if the surname provided looks valid, and if not issue
 *    a warning.
 *
 * @param string name The name to check
 */
function checkSurName(name)
{
   if (!isValidSurName(name))
   {
      alert("Achternaam bevat ongeldige tekens");
   }
}

/**
 * Check if a field contains a valid phone number, here defined as
 *    any combination of non-alpha characters containing 10 digits, and
 *    if not, alert the user.
 *
 * @param object field The phone number field to be checked
 */
function checkPhoneNumber(field)
{
   var nr = field.value.replace(/^\s*/, '');
   if (nr)
   {
      if (!isValidPhoneNumber(nr))
      {
         alert('"' + nr + '" is geen geldig telefoonnummer');
      }
      else
      {
         field.value = formatPhoneNumber(nr);
      }
   }
}

/**
 * Create an attribute string of name='value' pairs from the associative array
 * @param array attrs An array with attribute names as keys and attribute
 *                    values as values
 * @return The attribute string
 */
function createAttributeString(attrs)
{
   var res = "";
   if (attrs)
   {
      for (var name in attrs)
      {
         if (attrs[name] === null)
         {
            res += " " + name;
         }
         else
         {
            res += " " + name + "='" + attrs[name] + "'";
         }
      }
   }
   return res;
}

/**
 * Create code for a new button
 *
 * @param string text The text on the button
 * @return string with the HTML code for the button
 */
function createButton(text, link, link_attrs, type)
{
   var left = 'left_bt';
   var right = 'right_bt';
   if (type && type == 'inactive')
   {
      left = 'left_bt_inactive';
      right = 'right_bt_inactive';
   }

   var button = "<div class='" + left + "'></div>"
              + "<div class='" + right + "'>" + text + "</div>";
   if (link)
   {
      button = "<a href='" + link + "'" + createAttributeString(link_attrs) + ">"
             + button + "</a>";
   }
   return button;
}

/**
 * Change the label on a text button
 * @param string id   The id of the button
 * @param string text The new button text
 */
function changeButtonText(id, text)
{
   var button = document.getElementById(id);
   if (button)
   {
      button.childNodes[1].innerHTML = text;
   }
}

/**
 * Activate a button (make it green again), and change its onclick handler
 * @param id       The id of the button
 * @param handlers Updated event handlers of the button
 * @param label    Ig given, the new label of the button
 */
function activateButton(id, handlers)
{
   var button = document.getElementById(id);
   if (button)
   {
      for (var i = 0; i < button.childNodes.length; i++)
      {
         var child = button.childNodes[i];
         if (child.className == 'left_bt_inactive')
         {
            child.className = 'left_bt';
         }
         else if (child.className == 'right_bt_inactive')
         {
            child.className = 'right_bt';
         }
      }

      for (var e in handlers)
      {
         button[e] = handlers[e];
      }
   }
}

/**
 * Deactivate a button (grey it out), and change its onclick handler
 * @param id       The id of the button
 * @param handlers Updated event handlers of the button
 */
function deactivateButton(id, handlers)
{
   var button = document.getElementById(id);
   if (button)
   {
      for (var i = 0; i < button.childNodes.length; i++)
      {
         var child = button.childNodes[i];
         if (child.className == 'left_bt')
         {
            child.className = 'left_bt_inactive';
         }
         else if (child.className == 'right_bt')
         {
           child.className = 'right_bt_inactive';
         }
      }

      for (var e in handlers)
      {
         button[e] = handlers[e];
      }
   }
}

/**
 * Callback function for the register button. This checks if all required
 * fields are filled in, and passes the request to register a new account
 * to the server.
 */
function registerNewClient()
{
   var surName = $('#tlc_register [name=surName]').val();
   var postalCode = $('#tlc_register [name=postalCode]').val();
   var number = $('#tlc_register [name=number]').val();
   var email = $('#tlc_register [name=email]').val();
   var newpassword = $('#tlc_register [name=newpassword]').val();
   var confirmpassword = $('#tlc_register [name=confirmpassword]').val();
   var phone1 = $('#tlc_register [name=phone1]').val();
   var phone2 = $('#tlc_register [name=phone2]').val();

   var error = [];
   var msg = [];

   if(!surName || !postalCode || !number || !email || !newpassword
      || !confirmpassword)
   {
      msg[0] = 'Vul alle verplichte velden in';
      if (!surName)
      {
         error.push("$('#tlc_register [name=surName]')");
      }
      if (!postalCode)
      {
         error.push("$('#tlc_register [name=postalCode]')");
      }
      if (!number)
      {
         error.push("$('#tlc_register [name=number]')");
      }
      if (!email)
      {
         error.push("$('#tlc_register [name=email]')");
      }
      if (!newpassword)
      {
         error.push("$('#tlc_register [name=newpassword]')");
      }
      if (!confirmpassword)
      {
         error.push("$('#tlc_register [name=confirmpassword]')");
      }
   }

   if (!isValidSurName(surName))
   {
      msg.push('Achternaam bevat ongeldige tekens');
      error.push("$('#tlc_register [name=surName]')");
   }

   if (newpassword != confirmpassword)
   {
      msg.push('Wachtwoorden komen niet overeen');
      error.push("$('#tlc_register [name=newpassword],#tlc_register [name=confirmpassword]')");
   }

   if (phone1 && !isValidPhoneNumber(phone1))
   {
      msg.push("'" + phone1 + "' is geen geldig telefoonnummer");
      error.push("$('#tlc_register [name=phone1]')");
   }
   if (phone2 && !isValidPhoneNumber(phone2))
   {
      msg.push("'" + phone2 + "' is geen geldig telefoonnummer");
      error.push("$('#tlc_register [name=phone2]')");
   }

   if (error.length > 0)
   {
      handleError(msg, error);
      return false;
   }

   basClient.registerClient(
      { id : null,
        firstName : $('#tlc_register').find('[name=firstName]').val(),
        surName : surName,
        surNamePrefix : $('#tlc_register').find('[name=surNamePrefix]').val(),
        street : $('#tlc_register').find('[name=street]').val(),
        number : number,
        postalCode : postalCode,
        city : $('#tlc_register').find('[name=city]').val(),
        phone1 : phone1,
        phone2 : phone2,
        email : email,
        newpassword : newpassword,
        confirmpassword : confirmpassword,
        sex : $('#tlc_register').find('[name=sex]').val(),
        prefFeedback : ($('#tlc_register').find('[name=prefFeedback]').attr('id')
                     ? $('#tlc_register').find('[name=prefFeedback]').val()
                     : null)
      }
   );

   return false;
}

/**
 * Create an inline-help link
 * @param string name The name of the help item
 * @return String containing the help text
 */
function createInlineHelpLink(name)
{
   var onmouseover = "showHelpItem('" + name + "');";
   var onmouseout = "hideHelpItem('" + name + "');";
   var result = '<div class="tiplink" onmouseover="' + onmouseover + '" '
              +    'onmouseout="' + onmouseout + '">'
              +    '<img src="images/help.png" alt="Uitleg"/>'
              +    '<div class="tip" id="tip_' + name + '" '
              +       'style="display:none;"></div>'
              + '</div>';
   return result;
}

/**
 * Display the help text for a tooltip. If the content is present in the
 * help cache it is displayed directly, otherwise a request for the item
 * text is sent to the server, and displayed once the server responds.
 * @param string name The name of the help item to show
 */
function showHelpItem(name)
{
   if (help_cache[name])
   {
      var id = 'tip_' + name;
      document.getElementById(id).innerHTML = help_cache[name];
      $('#' + id).css('display', 'inline');
   }
   else
   {
      basHelp.getHelpItem(name);
   }
}

/**
 * Hide the help text for a tooltip.
 * @param string name The name of the help item to hide
 */
function hideHelpItem(name)
{
   $('#tip_' + name).hide();
}

/**
 * Store the help text for a tooltip in the cache, and display the item.
 * This function is called after the server responds to a request for a
 * help text.
 * @param string name    The name of the help item to set
 * @param string content The actual help content
 */
function setHelpItem(name, content)
{
   help_cache[name] = content;
   showHelpItem(name);
}

/**
 * Show the available scripts in the selectorbox
 *
 * @param result
 */
function showScriptSelect(result)
{
   var data = '';
   if (result[0])
   {
      data += '<option value="">Nieuw script</option>';
      for (var x in result)
      {
         if (result[x].title && result[x].type)
         {
            data += '<option value="' + result[x].id + '">' + result[x].title
                  + ' (' + result[x].type + ')'
                  + (result[x].current ? '*' : '') + '</option>';
         }
      }
   }
   else
   {
      data = '<option>Geen uitvraagscripts/enquêtes gevonden</option>';
   }
   $('[name=beheer_script_list]').html(data);
}

/**
 * Request script details from the server, or reset the script edit field
 * with default values if a new script is selected
 * @param int id The id of the requested script, or an empty string for a new
 *    script.
 */
function beheerGetScript(id)
{
   if (id)
   {
      basScript.getScript(id);
   }
   else
   {
      $('#beheer_4').find('[name=uitvraagscript_title]').val('');
      $('#beheer_4').find('[name=uitvraagscript_type]').val('CLIENT');
      $('#beheer_4').find('[name=edit_uitvraagscript]').val('');
      dateFieldSetValue('script_t_valid', '');
      dateFieldSetValue('script_t_invalid', '2037-12-31');
      document.getElementById('uitvraagscript_selected').innerHTML =
         '<label>Nieuw script</label>';
      $('#edit_uitvraagscript').attr('readonly', false);
      $('#beheer_4').find('a#beheer_editscript').show();
   }
}

/**
 * Display the details of the selected script
 *
 * @param result
 */
function showScriptDetails(result)
{
   $('#beheer_4').find('[name=uitvraagscript_title]').val((result.title
                                                              ? result.title : ''));
   $('#beheer_4').find('[name=uitvraagscript_type]').val((result.type
                                                             ? result.type : ''));
   $('#beheer_4').find('[name=edit_uitvraagscript]').val((result.script
                                                             ? result.script : ''));
   dateFieldSetValue('script_t_valid', result.t_valid);
   dateFieldSetValue('script_t_invalid', result.t_invalid);
   if (result.current)
   {
      document.getElementById('uitvraagscript_selected').innerHTML =
         '<label>Bekijk script</label>';
      $('#edit_uitvraagscript').attr('readonly', true);
      $('#beheer_4').find('a#beheer_editscript').hide();
   }
   else
   {
      document.getElementById('uitvraagscript_selected').innerHTML =
         '<label>Wijzig script</label>';
      $('#edit_uitvraagscript').attr('readonly', false);
      $('#beheer_4').find('a#beheer_editscript').show();
   }
}

/**
 * Store the new (or edited) script
 */
function beheerEditScript()
{
   var data = {
      id: $('#beheer_4 [name=beheer_script_list]').val(),
      title: $('#beheer_4 [name=uitvraagscript_title]').val(),
      role: $('#beheer_4 [name=uitvraagscript_type]').val(),
      script: $('#beheer_4 [name=edit_uitvraagscript]').val()
   };
   var err_fields = [];
   var err = [];

   if (!data.title)
   {
      err.push('Vul een titel in');
      err_fields.push("$('#beheer_4 [name=uitvraagscript_title]')");
   }
   if (!data.role)
   {
      // GV: cannot happen at the moment
      err.push('Selecteer een type');
      err_fields.push("$('#beheer_4 [name=uitvraagscript_type]')");
   }
   if (!data.script)
   {
      err.push('Uitvraagscript is leeg');
      err_fields.push("$('#beheer_4 [name=edit_uitvraagscript]')");
   }

   var t_valid = '';
   try
   {
      t_valid = dateFieldGetValue('script_t_valid');
   }
   catch (exc)
   {
      err.push(exc);
      err_fields.push("$('#script_t_valid').find('input')");
   }
   if (t_valid && !dateBefore('today', t_valid) && data.role != 'POLL')
   {
      err.push('De geldigheidsduur mag op zijn vroegst morgen ingaan');
      err_fields.push("$('#script_t_valid').find('input')");
   }

   if (err.length > 0)
   {
      handleError(err, err_fields);
   }
   else
   {
      data.t_valid = t_valid + ' 00:00:00';
      basScript.storeScript(data);
   }

   return false;
}

/**
 * Store a new (or edited) query
 */
function beheerEditQuery()
{
   var error = [];
   var msg = [];
   var query = {};

   query.id = $('#beheer_6 [name=beheer_query_list]').val();
   query.title = $('#beheer_6 [name=beheer_query_title]').val().trim();
   if (!query.title)
   {
      msg[0] = 'Vul een titel in';
      error[0] = "$('#beheer_6 [name=beheer_query_title]')";
   }
   query.type = $('#beheer_6 [name=beheer_query_type]').val();
   query.sql = $('#beheer_6 [name=beheer_query_sql]').val().trim();
   if (!query.sql)
   {
      msg[1] = 'Vul een query in';
      error[1] = "$('#beheer_6 [name=beheer_query_sql]')";
   }

   if(error.length > 0)
   {
      handleError(msg,error);
      return false;
   }

   basAdmin.storeQuery(query);
}

/**
 * Store a new period definition
 */
function beheerEditPeriods()
{
   var t_valid;

   try
   {
      t_valid = dateFieldGetValue('period_t_valid');
   }
   catch (exc)
   {
      handleError([exc], ["$('#period_t_valid').find('[class^=date_field]')"]);
      return false;
   }

   if (!dateBefore('today', t_valid))
   {
      handleError(['Datum ligt in het verleden'],["$('#period_t_valid').find('[class^=date_field]')"]);
      return;
   }

   var periods = [];
   for (var i = 0; i < NR_PERIOD_DEFS; i++)
   {
      var id_from = 'period_from_' + i;
      var id_upto = 'period_upto_' + i;
      if ($('#'+id_from).find('[name=hour]').val())
      {
         periods[i] = {};
         try
         {
            periods[i].from = timeFieldGetValue(id_from);
         }
         catch (exc)
         {
            handleError([exc], ["$('#"+id_from+"').find('[class^=time_field]')"]);
            return;
         }

         try
         {
            periods[i].upto = timeFieldGetValue(id_upto);
         }
         catch (exc)
         {
            handleError([exc], ["$('#"+id_upto+"').find('[class^=time_field]')"]);
            return;
         }
      }
   }

   basAdmin.storePeriodDefinition(t_valid, periods);
   return false;
}

/**
 * Show the available queries in the selectorbox
 *
 * @param result
 */
function beheerShowQuerySelect(result)
{
   var data = '';
   if(result.length > 0)
   {
      data += '<option value="">Nieuwe query</option>';
      for (var x in result)
      {
         if (result[x].id)
         {
            data += '<option value="' + result[x].id + '">' + result[x].title + '</option>';
         }
      }
   }
   else
   {
      data = '<option value="">Geen queries gevonden</option>';
   }
   $('[name=beheer_query_list]').html(data);
}

/**
 * Display the contents of the chosen Query
 *
 * @param result
 */
function beheerShowQuery(result)
{
   if (!result)
   {
      result = {};
   }
   $('#beheer_6').find('[name=beheer_query_id]').val((result.id ? result.id : ''));
   $('#beheer_6').find('[name=beheer_query_title]').val((result.title ? result.title : ''));
   $('#beheer_6').find('[name=beheer_query_type]').val((result.type ? result.type : ''));
   $('#beheer_6').find('[name=beheer_query_sql]').val((result.sql ? result.sql : ''));
}

/**
 * Show the available periods in the selectorbox
 *
 * @param result
 */
function beheerShowPeriodSelect(result)
{
   // Add date and time selectors if they don't exist
   if (!$('#period_def_list').attr('id'))
   {
      var rows   = '<div class="formrow">'
                 +    '<div class="desc">Bestaande periodes:</div>'
                 +    '<div class="input">'
                 +       '<select name="beheer_period_list" id="period_def_list" '
                 +          'onchange="basAdmin.getPeriodDefinition($(\'[name=beheer_period_list]\').val());">'
                 +          '<option value="">Nieuwe periode</option>'
                 +       '</select>'
                 +    '</div>'
                 + '</div>'
                 + '<div class="formrow">'
                 +    '<div class="desc">Geldig vanaf:</div>'
                 +    '<div class="input">'
                 +       createDateField('period_t_valid')
                 +    '</div>'
                 + '</div>';
      for (var i = 0; i < NR_PERIOD_DEFS; i++)
      {
         var desc = (i === 0 ? 'Tijden:' : '&nbsp;');
         var id_from = 'period_from_' + i;
         var id_upto = 'period_upto_' + i;
         rows   += '<div class="formrow">'
                 +    '<div class="desc">' + desc + '</div>'
                 +    '<div class="input">'
                 +       createTimeField(id_from, {style: "display:inline;"})
                 +       '&nbsp;-&nbsp;'
                 +       createTimeField(id_upto, {style: "display:inline;"})
                 +    '</div>'
                 + '</div>';
      }

      var link_attrs = {
         id: 'beheer_editperiods',
         onclick: 'beheerEditPeriods(); return false'
      };
      rows      += '<div class="formrow">'
                 +    '<div class="desc">&nbsp;</div>'
                 +    '<div class="input">'
                 +       createButton('Opslaan', '#', link_attrs)
                 +    '</div>'
                 + '</div>';

      $('#beheer_2').html(rows);
   }

   // Now add the period definitions stored
   var data = '';
   if (result[0])
   {
      data += '<option value="">Nieuwe periode</option>';
      for (var x in result)
      {
         if (result[x].t_valid)
         {
            var t = result[x].t_valid.substring(8,10) + '-'
                  + result[x].t_valid.substring(5,7) + '-'
                  + result[x].t_valid.substring(0,4);
            data += '<option value="' + result[x].t_valid + '">' + t + '</option>';
         }
      }
   }
   else
   {
      data = '<option>Geen periodes gevonden</option>';
   }
   $('[name=beheer_period_list]').html(data);
}

/**
 * Fill the periodlist in the admin section with the chosen periodDefinition
 *
 * @param result
 */
function beheerShowPeriod(result)
{
   var id_from, id_upto, i;

   if (result && result.t_valid)
   {
      dateFieldSetValue('period_t_valid', result.t_valid);
      $('#period_t_valid').find('input').attr('readonly', true);

      for (i = 0; i < NR_PERIOD_DEFS; i++)
      {
         id_from = '#period_from_' + i;
         id_upto = '#period_upto_' + i;
         $(id_from).find('input').val('');
         $(id_upto).find('input').val('');
      }

      if (result.periods.length > 0)
      {
         for (i in result.periods)
         {
            id_from = '#period_from_' + i;
            id_upto = '#period_upto_' + i;
            $(id_from).find('[name=hour]').val(result.periods[i].from.substring(0,2));
            $(id_from).find('[name=minute]').val(result.periods[i].from.substring(3,5));
            $(id_upto).find('[name=hour]').val(result.periods[i].upto.substring(0,2));
            $(id_upto).find('[name=minute]').val(result.periods[i].upto.substring(3,5));
         }
      }

      if (!dateBefore('today', result.t_valid))
      {
         $('a#beheer_editperiods').hide();
      }
      else
      {
         $('a#beheer_editperiods').show();
      }
   }
   else
   {
      dateFieldSetValue('period_t_valid', dateToString('tomorrow'));
      $('#period_t_valid').find('input').attr('readonly', false);

      $('#period_from_0').find('[name=hour]').val('00');
      $('#period_from_0').find('[name=minute]').val('00');
      $('#period_upto_0').find('[name=hour]').val('24');
      $('#period_upto_0').find('[name=minute]').val('00');
      for (i = 1; i < NR_PERIOD_DEFS; i++)
      {
         id_from = '#period_from_' + i;
         id_upto = '#period_upto_' + i;
         $(id_from).find('input').val('');
         $(id_upto).find('input').val('');
      }

      $('a#beheer_editperiods').show();
   }

   return false;
}

/**
 * Grab the system log for the given date
 */
function beheerGetSystemLog()
{
   var startdate, enddate;

   try
   {
      startdate = dateFieldGetValue('systemlog_startdate');
   }
   catch (exc)
   {
      handleError([exc], ["$('#systemlog_startdate').find('input')"]);
      return false;
   }
   try
   {
      enddate = dateFieldGetValue('systemlog_enddate');
   }
   catch (exc)
   {
      handleError([exc], ["$('#systemlog_enddate').find('input')"]);
      return false;
   }

   basAdmin.getSystemLog(startdate, enddate);
}

/**
 * Show the System log results
 *
 * @param result
 */
function beheerShowSystemLog(result)
{
   var text;
   if (result.length <= 0)
   {
      text = '<i>Geen log berichten gevonden</i>';
   }
   else
   {
      text = '<table>';
      for (var i = 0; i < result.length; i++)
      {
         var date = formatDBDate(result[i].cdate);
         var msg = result[i].msg.replace(/\n/g, '<br>');

         text += '<tr><td>' + date + '</td><td>' + msg + '</td></tr>';
      }
      text += '</table>';
   }

   $('#beheer_5').find('[name=messages]').html(text);
}

/**
 * Toggle the visibility of the password change popup
 */
function toggleChangePassword()
{
   $('#change_password').toggle();
   if ($.browser.msie && $.browser.version == '6.0')
   {
      $('#tlc_client_subdiv1').toggle();
   }
   if ($('#change_password').is(':visible'))
   {
      $('#old_password').focus();
   }
}

/**
 * Callback for the result of a request to change the password.
 * @param string type Either 'admin' when the password was changed by
 *    an employee in the administration tab, or 'client' (or anything else)
 *    when changed by a user.
 */
function handlePasswordChanged(type)
{
   alert('Het nieuwe wachtwoord is opgeslagen');
   if (type && type === 'admin')
   {
      $('#beheer_7').find('input').map(function(){
         $(this).val('');
      });
   }
   else
   {
      // Hide the password window, and clear the password lines
      toggleChangePassword();
      $('#change_password').find(':password').val('');
   }
}

/**
 * submit a password change from an admin or employee
 */
function beheerPasswordChange()
{
   var error = [];
   var msg = [];
   var oldpassword = $('#beheer_7 [name=beheer_old_password]').attr('name')
      ? $('#beheer_7 [name=beheer_old_password]').val()
      : null;
   var newpassword = $('#beheer_7 [name=beheer_new_password]').attr('name')
      ? $('#beheer_7 [name=beheer_new_password]').val()
      : null;
   var confirmpassword = $('#beheer_7 [name=beheer_new_password_again]').attr('name')
      ? $('#beheer_7 [name=beheer_new_password_again]').val()
      : null;

   if (!oldpassword || !newpassword || !confirmpassword)
   {
      msg.push('Wachtwoord mag niet leeg zijn');
      if (!oldpassword)
      {
         error.push("$('#beheer_7 [name=beheer_old_password]')");
      }
      if (!newpassword)
      {
         error.push("$('#beheer_7 [name=beheer_new_password]')");
      }
      if (!confirmpassword)
      {
         error.push("$('#beheer_7 [name=beheer_new_password_again]')");
      }
   }
   else if(newpassword != confirmpassword)
   {
      msg.push('Nieuwe wachtwoorden komen niet overeen');
      error.push("$('#beheer_7 [name=beheer_new_password],#beheer_7 [name=beheer_new_password_again]')");
   }
   if(error.length > 0)
   {
      handleError(msg,error);
      return false;
   }
   basClient.changePassword(oldpassword, newpassword, 'admin');
   return false;
}

/**
 * Retrieve a query, or clear the SQL fields when the new query option
 * is selected
 * @param int id If given, the ID of the query to retrieve
 */
function getQuery(id)
{
   if (id)
   {
      basAdmin.getQuery(id);
   }
   else
   {
      handleGetQueryResult({title: '', type: 'OTHER', sql: ''});
   }
}

/**
 * Set the fields of the SQL console
 */
function handleGetQueryResult(result)
{
   $('#beheer_6 [name=beheer_query_title]').val(result.title);
   $('#beheer_6 [name=beheer_query_type]').val(result.type);
   $('#beheer_6 [name=beheer_query_sql]').val(result.sql);

   // Remove the result of a previously executed query
   toggleTab('bl_admin_1');
   $('#bl_admin_2').hide();
   $('#beheer_8').hide();
}

/**
 * execute the given query
 */
function beheerExecuteQuery()
{
   if ($('#beheer_6 [name=beheer_query_sql]').val())
   {
      var sql = $('#beheer_6 [name=beheer_query_sql]').val();
      basAdmin.executeQuery(sql);
   }
   else
   {
      handleError(['Vul een query in'],
         ["$('#beheer_6 [name=beheer_query_sql]')"]);
      return false;
   }
}

/**
 * show the result of a executeQuery action
 *
 * @param result
 */
function beheerShowQueryResult(result)
{
   $('#bl_admin_1').attr('class','');
   $('#bl_admin_2').show().attr('class','current');
   $('div#beheer_6').hide();
   document.getElementById('beheer_8').innerHTML = result;
   $('div#beheer_8').show();
}

/**
 * Show the settings list from the ini file
 *
 * @param result
 */
function beheerShowSettings(result)
{
   var data = '';
   if (result)
   {
      var save_attrs = {
         onclick: 'beheerSaveSettings(); return false;'
      };

      for (var x in result)
      {
         data += '<div class="formrow">'
               +    '<div class="desc">'
               +       result[x].desc + ':'
               +    '</div>'
               +    '<div class="input">'
               +       '<input type="text" name="' + x + '"'
               +          ' id="beheer_setting_' + x + '"'
               +          ' value="' + result[x].value + '">'
               +    '</div>'
               + '</div>';
      }
      data    += '<div class="formrow">'
               +    '<div class="desc">&nbsp</div>'
               +    '<div class="input">'
               +       createButton('Opslaan', '#', save_attrs)
               +    '</div>'
               + '</div>';
   }

   $('#beheer_1').find('div.form').html(data);
}

/**
 * Save the settings in the ini file
 */
function beheerSaveSettings()
{
   var settings = {};
   $('#beheer_1').find('input').map(function(){
      settings[$(this).attr('name')] = $(this).val();
   });
   basAdmin.storeSettings(settings);
}

/**
 * Perform some basic checks on the password fields, and if these are passed,
 * submit a request to change the password to the server.
 */
function submitPasswordChange()
{
   var err = [];
   var err_fields = [];

   var oldpassword = $('[name=old_password]').val();
   var newpassword = $('[name=new_password]').val();
   var confirmpassword = $('[name=new_password_again]').val();

   if (!oldpassword || oldpassword.length === 0)
   {
      oldpassword = null;
   }
   if (!newpassword || newpassword.length === 0)
   {
      newpassword = null;
   }
   if (!confirmpassword || confirmpassword.length === 0)
   {
      confirmpassword = null;
   }

   if (!oldpassword || !newpassword || !confirmpassword)
   {
      if (!oldpassword)
      {
         err_fields.push("$('[name=old_password]')");
      }
      if (!newpassword)
      {
         err_fields.push("$('[name=new_password]')");
      }
      if (!confirmpassword)
      {
         err_fields.push("$('[name=new_password_again]')");
      }
      err.push('Wachtwoord mag niet leeg zijn');
   }
   else if(newpassword != confirmpassword)
   {
      err_fields.push("$('[name=new_password]')");
      err_fields.push("$('[name=new_password_again]')");
      err.push('Nieuwe wachtwoorden komen niet overeen');
   }

   if(err.length > 0)
   {
      handleError(err, err_fields);
      $('[name=new_password]').val('');
      $('[name=new_password_again]').val('');
      $('[name=new_password]').focus();
   }
   else
   {
      basClient.changePassword(oldpassword, newpassword, 'client');
   }

   return false;
}

/**
 * Click handler for the add/modify client button in the user details tab.
 */
function storeClientClick()
{
   user.dont_search_user = true;
   clientConfirm();
}

/**
 * Get the roles set in the selectorbox with ID id. If at least one backoffice
 * role is checked, a BACKOFFICE role is added to the result.
 * @param string id The ID of the selectorbox
 * @return array with the client roles
 */
function getRoles(id)
{
   var roles = selectorBoxGetValue(id);
   if (arrayContains(roles,'AAS')
       || arrayContains(roles,'LVNL')
       || arrayContains(roles,'RIJK'))
   {
      roles.push('BACKOFFICE');
   }
   return roles;
}

/**
 * Perform some basic checks on input fields, and if these pass, send a
 * request for changing user information to the server.
 */
function clientConfirm()
{
   document.getElementById("content_topleft").scrollTop = 0;

   var err_fields = [];
   var err = [];

   var surName = $('#tlc_client [name=surName]').val();
   var postalCode = $('#tlc_client [name=postalCode]').val();
   var number = $('#tlc_client [name=number]').val();
   var email = $('#tlc_client [name=email]').val();
   var phone1 = $('#tlc_client [name=phone1]').val();
   var phone2 = $('#tlc_client [name=phone2]').val();
   var prefFeedback = ($('#tlc_client [name=prefFeedback]').attr('id')
                       ? $('#tlc_client [name=prefFeedback]').val()
                       : null);
   var mailnews = $('#tlc_client [name=mailnews]').is(':checked');
   var mailactual = $('#tlc_client [name=mailactual]').is(':checked');
   var have_missing = !surName || !postalCode || !number;

   if (!surName)
   {
      err_fields.push("$('#tlc_client [name=surName]')");
   }
   if (!postalCode)
   {
      err_fields.push("$('#tlc_client [name=postalCode]')");
   }
   if (!number)
   {
      err_fields.push("$('#tlc_client [name=number]')");
   }
   if (prefFeedback == 'PHONE' && !phone1 && !phone2)
   {
      err_fields.push("$('#tlc_client [name=phone1]')");
      err_fields.push("$('#tlc_client [name=phone2]')");
      err.push('Manier van terugkoppeling is telefoon, maar er is geen telefoonnummer opgegeven');
   }
   if (user.type == 'BAS' || user.type == 'ADMIN')
   {
      if (prefFeedback == 'EMAIL' && !email)
      {
         err_fields.push("$('#tlc_client [name=email]')");
         err.push('Manier van terugkoppeling is email, maar er is geen email adres opgegeven');
      }
   }
   else if (!email)
   {
      err_fields.push("$('#tlc_client [name=email]')");
      have_missing = true;
   }
   if ((mailnews || mailactual) && !email)
   {
      err_fields.push("$('#tlc_client [name=email]')");
      err.push('Geabonneerd op nieuwsbrief of actueel nieuws, maar er is geen email adres opgegeven');
   }

   if (have_missing)
   {
      err.push('Vul alle verplichte velden in');
   }
   if (err.length > 0)
   {
      handleError(err, err_fields);
   }
   else if (!user.changing_details)
   {
      user.changing_details = true;
      var client = {
         id : $('#tlc_client').find('[name=id]').val(),
         firstName : $('#tlc_client').find('[name=firstName]').val(),
         surName : surName,
         surNamePrefix : $('#tlc_client').find('[name=surNamePrefix]').val(),
         street : $('#tlc_client').find('[name=street]').val(),
         number : number,
         postalCode : postalCode,
         city : $('#tlc_client').find('[name=city]').val(),
         phone1 : phone1,
         phone2 : phone2,
         email : email,
         oldpassword : null,
         newpassword : null,
         confirmpassword : null,
         sex : $('#tlc_client').find('[name=sex]').val(),
         prefFeedback : prefFeedback,
         mailnews: mailnews,
         mailactual: mailactual,
         basComments : ($('#tlc_client').find('[name=basComments]').attr('id')
                        ? $('#tlc_client').find('[name=basComments]').val()
                        : null)
      };
      if (user.type == 'ADMIN')
      {
         client.roles = getRoles('roles');
         client.active = $('#tlc_client [name=active]').is(':checked');
      }
      basClient.storeClient(client);
   }

   return false;
}

/**
 * Callback function called when client information is succesfully stored by
 * the server.
 * @param array result The resulting client details returned by the server
 */
function handleClientStored(result)
{
   if ($('#tl_login').is(':visible'))
   {
      alert('Bedankt voor het registreren, uw inloggegevens zijn naar het opgegeven e-mail adres verstuurd');
      toggleTab('tl_login');
   }
   else
   {
      alert('De gegevens zijn ingevoerd');
      if(result.id.length > 0)
      {
         showUserDetails(result);
      }
      activateResetPassword();
      user.changing_details = false;
      changeButtonText('client_confirm', 'Wijzigen');
   }
}

/**
 * Deactivate the reset password function for BAS employees or administrators.
 * This is done when the email-adress of a user is changed, to prevent sending
 * email to an address that is later not stored.
 */
function deactivateResetPassword()
{
   var handlers = {
      onmousedown: function() {alert("De wijzigingen moeten eerst opgeslagen worden voordat een nieuw wachtwoord kan worden ingevoerd"); return false;}
   };
   deactivateButton('resetpassword', handlers);
}

/**
 * Re-activate the reset password button after changes have been saved, or
 * the client form is reset.
 */
function activateResetPassword()
{
   var handlers = {
      onmousedown: function() {resetPasswordClick(); return false;}
   };
   activateButton('resetpassword', handlers);
}

/**
 * Click handler for the reset password button in the client details form
 * Sends a request to the server to replace a user's password with a new
 * (auto-generated) one and notify the user.
 */
function resetPasswordClick()
{
   user.dont_search_user = true;

   var email = null;
   if ($('#tlc_login:visible').find('input[name=username]').attr('name'))
   {
      email = $('#tlc_login:visible').find('input[name=username]').val();
   }
   else if ($('#tlc_client:visible').find('input[name=email]').attr('name'))
   {
      email = $('#tlc_client:visible').find('input[name=email]').val();
   }
   basClient.resetPassword(email);
}

/**
 * Send a request to the server to reset the password of the client
 * on the login tab.
 */
function loginResetPassword()
{
   var email = $('#tlc_login:visible').find('input[name=username]').val();
   if (!email)
   {
      handleError(['Vul een gebruikersnaam in aub'],
         ["$('#tlc_login input[name=username]')"]);
   }
   else
   {
      basClient.resetPassword(email);
   }
}

/**
 * Click handler for the reset client button in the user details form
 */
function resetClientClick()
{
   user.dont_search_user = true;
   resetClientForm();
}

/**
 * Clear the client input form and reset the default values
 */
function resetClientForm()
{
   $('#tlc_client').find('div.form,#tlc_client_subdiv2,div.subdiv').find('input:not([type=checkbox]),select,textarea').val('');
   $('#tlc_client').find('[name=prefFeedback]').val('PHONE');
   $('#tlc_client').find('[name=postalCode]').focus();
   changeButtonText('client_confirm', 'Toevoegen');
   listUsersFound({count: 0});
   activateResetPassword();

   if (user.type == 'BAS')
   {
      resetUitvraagScript();
      showIssueList({count: 0, issues: []}, true);
   }
   else if (user.type == 'ADMIN')
   {
      selectorBoxClearAll('roles');
      $('#tlc_client').find('[name=active]').attr('checked',true);
   }
}

/**
 * Send the login information to the server, and store them locally in a
 * cookie if desired.
 */
function loginClient()
{
   var username = $('#login').find('[name=username]').val();
   var password = $('#login').find('[name=password]').val();
   if (!username)
   {
      username = '';
   }
   if (!password)
   {
      password = '';
   }

   if ($('#tlc_login').find('[name=remember_me]').attr('checked'))
   {
      setCookie('email', username, 100);
      setCookie('password', password, 100);
   }
   else
   {
      deleteCookie('email');
      deleteCookie('password');
   }

   basClient.login(username, password);
}

/**
 * Submit the form to create a csv file
 * @param type
 */
function createExport(type)
{
   if (type == 'csv')
   {
      var errors = [];
      var err_fields = [];

      try
      {
         var startdate = dateFieldGetValue('export_startdate', true);
         $("form[name=export]").find('input[name=startdate]').val(startdate);
      }
      catch (exc)
      {
         errors.push(exc);
         err_fields.push("$('#export_startdate').find('input')");
      }

      try
      {
         var enddate = dateFieldGetValue('export_enddate', true);
         $("form[name=export]").find('input[name=enddate]').val(enddate);
      }
      catch (exc)
      {
         errors.push(exc);
         err_fields.push("$('#export_enddate').find('input')");
      }

      if (errors.length > 0)
      {
         handleError(errors, err_fields);
         return false;
      }

      $("form[name=export]").submit();
   }
   else if(type == 'sql')
   {
      $("form[name=export_sql]").submit();
   }
}

/**
 * Update the list of available overlays with the list returned by the
 * server
 * @param array list containing the ID and name of each overlay
 */
function handleOverlayList(list)
{
   var table = document.getElementById('overlay_list');
   if (!table)
   {
      return;
   }

   var have_vluchten = document.getElementById('overlay_vluchten') ? true : false;
   var checked = [];
   $('#overlay_list').find('input:checked').each(function(){
      checked[$(this).attr('id')] = true;
   });
   if (!have_vluchten)
   {
      checked.overlay_vluchten = true;
   }

   while (table.rows.length > 0)
   {
      table.deleteRow(0);
   }

   var chk = checked.overlay_vluchten ? " checked='checked'" : "";
   table.insertRow(-1).insertCell(-1).innerHTML =
      "<input type='checkbox' id='overlay_vluchten' "
      + "onclick='toggleTracksOverlay(this.checked)' " + chk + "/>"
      + 'Vluchten';
   for (var id in list)
   {
      var entry = list[id];
      var box_id = 'overlay_' + id;
      chk = checked[box_id] ? " checked='checked'" : "";
      table.insertRow(-1).insertCell(-1).innerHTML =
         "<input type='checkbox' id='" + box_id + "'"
         + " onclick='toggleOverlay(" + id + ",this.checked)'"
         + chk + "/>"
         + entry.name;
      setOverlay(id, entry.name, entry.children);
   }
}

/**
 * Toggle the visibility of the overlay runway and SID input elements,
 * based on whether the group checkbox is checked or not.
 * \param bool check If true, the box is selected and the elements are
 *    shown. Otherwise they are hidden.
 */
function toggleOverlayIsGroup(checked)
{
   if (checked)
   {
      document.getElementById('overlay_runway_row').style.display = 'block';
      document.getElementById('overlay_sid_row').style.display = 'block';
      document.getElementById('overlay_isgroup2').value = 'yes';
   }
   else
   {
      document.getElementById('overlay_runway_row').style.display = 'none';
      document.getElementById('overlay_sid_row').style.display = 'none';
      document.getElementById('overlay_isgroup2').value = '';
   }
}

/**
 * Ask the server for the list of mergeable records, starting with the row
 * stored in merger_search. If the optional parameter ret_total is true,
 * the total number of rows is also requested.
 * @param bool ret_total If true, also ask the server for the total number
 *   of similar client pairs.
 */
function listMergeableRecords(ret_total)
{
   var table = document.getElementById('merger_list');
   deleteContentRows(table,
      ['contentrow', 'contentrow_current', 'page_buttons']);
   var row = table.insertRow(-1);
   row.className = 'contentrow';
   var cell = row.insertCell(-1);
   cell.colSpan = 3;
   cell.innerHTML = '<i>Lijst van samenvoegbare klanten wordt opgevraagd</i>';

   basAdmin.listMergeableRecords(merger_search.page_size, merger_search.start,
      ret_total);
}

function showMergeableRecordsList(pairs)
{
   if (pairs.count)
   {
      showCounter(pairs.count, 'merger_pair_counter');
      merger_search.total = pairs.count;
   }

   var table = document.getElementById('merger_list');
   deleteContentRows(table,
      ['contentrow', 'contentrow_current', 'page_buttons']);

   for (var i = 0; i < pairs.records.length; i++)
   {
      var pair = pairs.records[i];

      var address = pair.postalCode + ' ' + pair.number;
      var id = 'merger_list_' + pair.client1.id + '_' + pair.client2.id;
      var name1 = pair.client1.surName;
      if (pair.client1.surNamePrefix)
      {
         name1 = pair.client1.surNamePrefix + ' ' + name1;
      }
      if (pair.client1.firstName)
      {
         name1 = pair.client1.firstName + ' ' + name1;
      }
      var name2 = pair.client2.surName;
      if (pair.client2.surNamePrefix)
      {
         name2 = pair.client2.surNamePrefix + ' ' + name2;
      }
      if (pair.client2.firstName)
      {
         name2 = pair.client2.firstName + ' ' + name2;
      }

      var row = table.insertRow(-1);
      row.className = 'contentrow';
      row.id = id;
      row.insertCell(-1).innerHTML = address;
      row.insertCell(-1).innerHTML = name1;
      row.insertCell(-1).innerHTML = name2;
      row.onclick = new Function(
         'getMergeDetails(' + pair.client1.id + ', ' + pair.client2.id + ');'
      );
   }

   var addprev = merger_search.start > 0;
   var addnext = merger_search.total > merger_search.start + pairs.records.length;
   if (addprev || addnext)
   {
      var which = addprev ? (addnext ? 'both' : 'prev') : 'next';
      addPageButtons(table, which, 2, 1, 'merger_list');
   }
}

function getMergeDetails(id1, id2)
{
   var id = 'merger_list_' + id1 + '_' + id2;
   $('#'+id).siblings('.contentrow_current').attr('class', 'contentrow');
   $('#'+id).attr('class', 'contentrow_current');
   basAdmin.getMergeDetails(id1, id2);
}

function showMergeDetails(details)
{
   var client1 = details.client1;
   var client2 = details.client2;
   var merge = details.merge;
   var i;

   document.getElementById('merger_c1_cdate').innerHTML
      = formatDBDate(client1.cdate, true);
   document.getElementById('merger_c2_cdate').innerHTML
      = formatDBDate(client2.cdate, true);
   document.getElementById('merger_m_cdate').innerHTML
      = formatDBDate(merge.cdate, true);

   document.getElementById('merger_c1_udate').innerHTML
      = formatDBDate(client1.udate, true);
   document.getElementById('merger_c2_udate').innerHTML
      = formatDBDate(client2.udate, true);
   document.getElementById('merger_m_udate').innerHTML
      = formatDBDate(merge.udate, true);

   document.getElementById('merger_c1_lastlogin').innerHTML
      = formatDBDate(client1.lastLogin, true);
   document.getElementById('merger_c2_lastlogin').innerHTML
      = formatDBDate(client2.lastLogin, true);
   document.getElementById('merger_m_lastlogin').innerHTML
      = formatDBDate(merge.lastLogin, true);

   document.getElementById('merger_c1_id').innerHTML = client1.id;
   document.getElementById('merger_c2_id').innerHTML = client2.id;
   document.getElementById('merger_m_id').innerHTML = merge.id;

   document.getElementById('merger_c1_firstname').innerHTML = client1.firstName;
   document.getElementById('merger_c2_firstname').innerHTML = client2.firstName;
   document.getElementById('merger_m_firstname').value = merge.firstName;

   document.getElementById('merger_c1_surnameprefix').innerHTML = client1.surNamePrefix;
   document.getElementById('merger_c2_surnameprefix').innerHTML = client2.surNamePrefix;
   document.getElementById('merger_m_surnameprefix').value = merge.surNamePrefix;

   document.getElementById('merger_c1_surname').innerHTML = client1.surName;
   document.getElementById('merger_c2_surname').innerHTML = client2.surName;
   document.getElementById('merger_m_surname').value = merge.surName;

   document.getElementById('merger_c1_sex').innerHTML = translateSex(client1.sex);
   document.getElementById('merger_c2_sex').innerHTML = translateSex(client2.sex);
   var sex_opts = document.getElementById('merger_m_sex').options;
   var merge_sex = merge.sex ? merge.sex : '';
   for (i = 0; i < sex_opts.length; i++)
   {
      if (sex_opts[i].value == merge_sex)
      {
         document.getElementById('merger_m_sex').selectedIndex = i;
         break;
      }
   }

   document.getElementById('merger_c1_postalcode').innerHTML = client1.postalCode;
   document.getElementById('merger_c2_postalcode').innerHTML = client2.postalCode;
   document.getElementById('merger_m_postalcode').innerHTML = merge.postalCode;

   document.getElementById('merger_c1_number').innerHTML = client1.number;
   document.getElementById('merger_c2_number').innerHTML = client2.number;
   document.getElementById('merger_m_number').innerHTML = merge.number;

   document.getElementById('merger_c1_street').innerHTML = client1.street;
   document.getElementById('merger_c2_street').innerHTML = client2.street;
   document.getElementById('merger_m_street').value = merge.street;

   document.getElementById('merger_c1_city').innerHTML = client1.city;
   document.getElementById('merger_c2_city').innerHTML = client2.city;
   document.getElementById('merger_m_city').value = merge.city;

   document.getElementById('merger_c1_phone1').innerHTML
      = client1.phone1 ? formatPhoneNumber(client1.phone1) : '';
   document.getElementById('merger_c2_phone1').innerHTML
      = client2.phone1 ? formatPhoneNumber(client2.phone1) : '';
   document.getElementById('merger_m_phone1').value
      = merge.phone1 ? formatPhoneNumber(merge.phone1) : '';

   document.getElementById('merger_c1_phone2').innerHTML
      = client1.phone2 ? formatPhoneNumber(client1.phone2) : '';
   document.getElementById('merger_c2_phone2').innerHTML
      = client2.phone2 ? formatPhoneNumber(client2.phone2) : '';
   document.getElementById('merger_m_phone2').value
      = merge.phone2 ? formatPhoneNumber(merge.phone2) : '';

   document.getElementById('merger_c1_preffeedback').innerHTML
      = translateFeedbackPreference(client1.prefFeedback);
   document.getElementById('merger_c2_preffeedback').innerHTML
      = translateFeedbackPreference(client2.prefFeedback);
   var fb_opts = document.getElementById('merger_m_preffeedback').options;
   for (i = 0; i < fb_opts.length; i++)
   {
      if (fb_opts[i].value == merge.prefFeedback)
      {
         document.getElementById('merger_m_preffeedback').selectedIndex = i;
         break;
      }
   }

   document.getElementById('merger_c1_email').innerHTML = client1.email;
   document.getElementById('merger_c2_email').innerHTML = client2.email;
   document.getElementById('merger_m_email').value = merge.email;

   document.getElementById('merger_c1_password').innerHTML
      = client1.password ? 'Ja' : 'Nee';
   document.getElementById('merger_c2_password').innerHTML
      = client2.password ? 'Ja' : 'Nee';
   var pwd_sel = document.getElementById('merger_m_password');
   for (i = pwd_sel.options.length - 1; i > 0; i--)
   {
      pwd_sel.remove(i);
   }

   var option;
   if (client1.password)
   {
      option = new Option('Van gebruiker 1', '1');
      try
      {
         pwd_sel.add(option, null);
      }
      catch (exc)
      {
         pwd_sel.add(option);
      }
   }
   if (client2.password)
   {
      option = new Option('Van gebruiker 2', '2');
      try
      {
         pwd_sel.add(option, null);
      }
      catch (exc)
      {
         pwd_sel.add(option);
      }
   }
   pwd_sel.selectedIndex = pwd_sel.options.length - 1;

   document.getElementById('merger_c1_mailnews').innerHTML
      = client1.mailnews ? 'Ja' : 'Nee';
   document.getElementById('merger_c2_mailnews').innerHTML
      = client2.mailnews ? 'Ja' : 'Nee';
   document.getElementById('merger_m_mailnews').checked = merge.mailnews;

   document.getElementById('merger_c1_mailactual').innerHTML
      = client1.mailactual ? 'Ja' : 'Nee';
   document.getElementById('merger_c2_mailactual').innerHTML
      = client2.mailactual ? 'Ja' : 'Nee';
   document.getElementById('merger_m_mailactual').checked = merge.mailactual;

   document.getElementById('merger_c1_roles').innerHTML = translateRoles(client1.roles);
   document.getElementById('merger_c2_roles').innerHTML = translateRoles(client2.roles);
   selectorBoxSetValue('merger_m_roles', merge.roles);

   document.getElementById('merger_c1_active').innerHTML
      = client1.active ? 'Ja' : 'Nee';
   document.getElementById('merger_c2_active').innerHTML
      = client2.active ? 'Ja' : 'Nee';
   document.getElementById('merger_m_active').checked = merge.active;

   document.getElementById('merger_c1_bascomments').innerHTML
      = client1.basComments ? client1.basComments.replace(/\n/g, '<br/>') : '';
   document.getElementById('merger_c2_bascomments').innerHTML
      = client2.basComments ? client2.basComments.replace(/\n/g, '<br/>') : '';
   document.getElementById('merger_m_bascomments').value
      = merge.basComments ? merge.basComments : '';

   document.getElementById('merger_c1_nrissues').innerHTML = client1.nrIssues;
   document.getElementById('merger_c2_nrissues').innerHTML = client2.nrIssues;
   document.getElementById('merger_m_nrissues').innerHTML = merge.nrIssues;

   document.getElementById('merger_button_ok').onclick = new Function(
      'mergeClients(' + client1.id + ', ' + client2.id + '); return false;'
   );
   document.getElementById('merger_button_never').onclick = new Function(
      'basAdmin.dontMergeClients(' + client1.id + ', ' + client2.id + '); return false;'
   );

   document.getElementById('nav_merger_records').style.display = 'block';
   toggleTab('nav_merger_records');
}

/**
 * Tell the server that the client records of the clients with IDs id1 and id2
 * should not be merged, and should not appear in the merge list again.
 * @param int id1 The ID of the first client
 * @param int id2 The ID of the second client
 */
function mergeClients(id1, id2)
{
   var merge = {
      firstName: document.getElementById('merger_m_firstname').value,
      surNamePrefix: document.getElementById('merger_m_surnameprefix').value,
      surName: document.getElementById('merger_m_surname').value,
      sex: document.getElementById('merger_m_sex').value,
      street: document.getElementById('merger_m_street').value,
      city: document.getElementById('merger_m_city').value,
      phone1: document.getElementById('merger_m_phone1').value,
      phone2: document.getElementById('merger_m_phone2').value,
      prefFeedback: document.getElementById('merger_m_preffeedback').value,
      email: document.getElementById('merger_m_email').value,
      password_idx: document.getElementById('merger_m_password').value,
      mailnews: document.getElementById('merger_m_mailnews').checked,
      mailactual: document.getElementById('merger_m_mailactual').checked,
      roles: getRoles('merger_m_roles'),
      active: document.getElementById('merger_m_active').checked,
      basComments: document.getElementById('merger_m_bascomments').value
   };

   var err = [];
   var err_fields = [];
   if (!merge.surName)
   {
      err.push('Achternaam is leeg');
      err_fields.push("$('#merger_m_surname')");
   }
   else if (!isValidSurName(merge.surName))
   {
      err.push('Achternaam bevat ongeldige tekens');
      err_fields.push("$('#merger_m_surname')");
   }

   if (merge.phone1 && !isValidPhoneNumber(merge.phone1))
   {
      err.push("'" + merge.phone1 + "' is geen geldig telefoonnummer");
      err_fields.push("$('#merger_m_phone1')");
   }
   if (merge.phone2 && !isValidPhoneNumber(merge.phone2))
   {
      err.push("'" + merge.phone2 + "' is geen geldig telefoonnummer");
      err_fields.push("$('#merger_m_phone2')");
   }

   if (merge.prefFeedback == 'EMAIL' && !merge.email)
   {
      err.push('Manier van terugkoppeling is email, maar er is geen email adres opgegeven');
      err_fields.push("$('#merger_m_email')");
   }
   else if (merge.prefFeedback == 'PHONE' && !merge.phone1 && !merge.phone2)
   {
      err.push('Manier van terugkoppeling is telefoon, maar er is geen telefoonnummer opgegeven');
      err_fields.push("$('#merger_m_email')");
   }

   if (merge.roles.length === 0)
   {
      err.push('Geen enkele klantrol is gezet');
      err_fields.push("$('#merger_m_roles')");
   }

   if (err.length > 0)
   {
      handleError(err, err_fields);
   }
   else
   {
      basAdmin.mergeClients(id1, id2, merge);
   }
}

/**
 * Clear all information in the record merger and close the tab.
 */
function clearMergerRecords()
{
   document.getElementById('merger_c1_cdate').innerHTML = '';
   document.getElementById('merger_c2_cdate').innerHTML = '';
   document.getElementById('merger_m_cdate').innerHTML = '';

   document.getElementById('merger_c1_udate').innerHTML = '';
   document.getElementById('merger_c2_udate').innerHTML = '';
   document.getElementById('merger_m_udate').innerHTML = '';

   document.getElementById('merger_c1_lastlogin').innerHTML = '';
   document.getElementById('merger_c2_lastlogin').innerHTML = '';
   document.getElementById('merger_m_lastlogin').innerHTML = '';

   document.getElementById('merger_c1_id').innerHTML = '';
   document.getElementById('merger_c2_id').innerHTML = '';
   document.getElementById('merger_m_id').innerHTML = '';

   document.getElementById('merger_c1_firstname').innerHTML = '';
   document.getElementById('merger_c2_firstname').innerHTML = '';
   document.getElementById('merger_m_firstname').value = '';

   document.getElementById('merger_c1_surnameprefix').innerHTML = '';
   document.getElementById('merger_c2_surnameprefix').innerHTML = '';
   document.getElementById('merger_m_surnameprefix').value = '';

   document.getElementById('merger_c1_surname').innerHTML = '';
   document.getElementById('merger_c2_surname').innerHTML = '';
   document.getElementById('merger_m_surname').value = '';

   document.getElementById('merger_c1_sex').innerHTML = '';
   document.getElementById('merger_c2_sex').innerHTML = '';
   document.getElementById('merger_m_sex').selectedIndex = 0;

   document.getElementById('merger_c1_postalcode').innerHTML = '';
   document.getElementById('merger_c2_postalcode').innerHTML = '';
   document.getElementById('merger_m_postalcode').innerHTML = '';

   document.getElementById('merger_c1_number').innerHTML = '';
   document.getElementById('merger_c2_number').innerHTML = '';
   document.getElementById('merger_m_number').innerHTML = '';

   document.getElementById('merger_c1_street').innerHTML = '';
   document.getElementById('merger_c2_street').innerHTML = '';
   document.getElementById('merger_m_street').value = '';

   document.getElementById('merger_c1_city').innerHTML = '';
   document.getElementById('merger_c2_city').innerHTML = '';
   document.getElementById('merger_m_city').value = '';

   document.getElementById('merger_c1_phone1').innerHTML = '';
   document.getElementById('merger_c2_phone1').innerHTML = '';
   document.getElementById('merger_m_phone1').value = '';

   document.getElementById('merger_c1_phone2').innerHTML = '';
   document.getElementById('merger_c2_phone2').innerHTML = '';
   document.getElementById('merger_m_phone2').value = '';

   document.getElementById('merger_c1_preffeedback').innerHTML = '';
   document.getElementById('merger_c2_preffeedback').innerHTML = '';
   document.getElementById('merger_m_preffeedback').selectedIndex = 0;

   document.getElementById('merger_c1_email').innerHTML = '';
   document.getElementById('merger_c2_email').innerHTML = '';
   document.getElementById('merger_m_email').value = '';

   document.getElementById('merger_c1_password').innerHTML = '';
   document.getElementById('merger_c2_password').innerHTML = '';
   var pwd_sel = document.getElementById('merger_m_password');
   for (var i = pwd_sel.options.length - 1; i > 0; i--)
   {
      pwd_sel.remove(i);
   }

   document.getElementById('merger_c1_mailnews').innerHTML = '';
   document.getElementById('merger_c2_mailnews').innerHTML = '';
   document.getElementById('merger_m_mailnews').checked = false;

   document.getElementById('merger_c1_mailactual').innerHTML = '';
   document.getElementById('merger_c2_mailactual').innerHTML = '';
   document.getElementById('merger_m_mailactual').checked = false;

   document.getElementById('merger_c1_roles').innerHTML = '';
   document.getElementById('merger_c2_roles').innerHTML = '';
   selectorBoxClearAll('merger_m_roles');

   document.getElementById('merger_c1_active').innerHTML = '';
   document.getElementById('merger_c2_active').innerHTML = '';
   document.getElementById('merger_m_active').checked = false;

   document.getElementById('merger_c1_bascomments').innerHTML = '';
   document.getElementById('merger_c2_bascomments').innerHTML = '';
   document.getElementById('merger_m_bascomments').value = '';

   document.getElementById('merger_c1_nrissues').innerHTML = '';
   document.getElementById('merger_c2_nrissues').innerHTML = '';
   document.getElementById('merger_m_nrissues').innerHTML = '';

   document.getElementById('merger_button_ok').onclick = null;
   document.getElementById('merger_button_never').onclick = null;

   document.getElementById('nav_merger_records').style.display = 'none';
   toggleTab('nav_merger_list');
}

/**
 * Send a request to the server to generate a script output table for the
 * period defined by the time fields in the GUI, using the name provided by
 * the client.
 */
function convertScriptOutput()
{
   var start_date, end_date, suffix,scriptid;
   var error = [];
   var err_field = [];

   try
   {
      start_date = dateFieldGetValue('convert_so_startdate');
   }
   catch (exc)
   {
      error.push(exc);
      err_field.push("$('#convert_so_startdate input')");
   }
   try
   {
      end_date = dateFieldGetValue('convert_so_enddate');
   }
   catch (exc)
   {
      error.push(exc);
      err_field.push("$('#convert_so_enddate input')");
   }
   suffix = document.getElementById('convert_so_suffix').value;
   if (!suffix)
   {
      error.push('De suffix voor de tabelnaam is leeg');
      err_field.push("$('#convert_so_suffix')");
   }

   scriptid = document.getElementById('convert_so_script_id').value;
   if (error.length > 0)
   {
      handleError(error, err_field);
      return;
   }

   basAdmin.convertScriptOutput(start_date, end_date, suffix, scriptid);
}

/**
 * Inform the user that the script output table is generated, and request
 * the new list of script output tables from the server.
 * @param string table the name of the newly created table
 */
function handleOutputConverted(table)
{
   basAdmin.listScriptOutputTables();
   alert("Tabel " + table + " is aangemaakt");
}

/**
 * Show the current list of script output tables.
 * @param array table_info The table names and row counts
 */
function showScriptOutputTables(table_info)
{
   var table = document.getElementById('script_output_tables');
   deleteContentRows(table, ['contentrow']);

   for (var i = 0; i < table_info.length; i++)
   {
      var row = table.insertRow(-1);
      row.className = 'contentrow';
      row.insertCell(-1).innerHTML = table_info[i].name;
      row.insertCell(-1).innerHTML = table_info[i].count;
      row.insertCell(-1).innerHTML =
           '<a href="#" onclick="basAdmin.dropScriptOutputTable(\'' + table_info[i].name + '\'); return false;" class="textlink">'
         +    'Verwijder'
         + '</a>';
   }
}

/**
 * Send a request to the server to classify a question, and optionally change
 * its state.
 * @param int id The issue ID of the question to classify
 * @param string state If set, the new state of the question
 */
function setQuestionAbout(id, state)
{
   var value = $('#question_about_buttons [name=question_about]:checked').val();
   if (!value)
   {
      handleError(['De vraag is niet geclassificeerd'], []);
   }
   else
   {
      changeScriptOutputAndState(id, 'vraag_over', value, state);
   }
}

/**
 * Send a request to the server to amend the script output for an issue,
 * and optionally change its state.
 * @param int id       The id of the issue of which to amend the script output
 * @param string key   The ID of the field to add
 * @param string value The value to store for this field
 * @param string state If set, the new state of the issue
 */
function changeScriptOutputAndState(id, key, value, state)
{
   var fields = {};
   fields[key] = value;
   basIssue.changeScriptOutputAndState(id, fields, state);
}

/**
 * Set the options for classifying questions from the server response
 * @param array options The possible options
 */
function setQuestionClasses(options)
{
   for (var i = 0; i < options.length; i++)
   {
      var option = options[i];
      question_about_options[option.value] = option.description;
   }
}

/**
 * Get the period for which the runway use should be displayed from the
 * input fields in the upper left tab.
 * @return array contianing the start and end dates of the period, or
 *    null when the input is invalid.
 */
function getRunwayUsePeriod()
{
   var errors = [];
   var err_fields = [];

   var period = {};

   try
   {
      period.from = dateFieldGetValue('runwayuse_from');
   }
   catch (exc)
   {
      errors.push(exc);
      err_fields.push("$('#runwayuse_from input')");
   }
   try
   {
      period.upto = dateFieldGetValue('runwayuse_upto');
   }
   catch (exc)
   {
      errors.push(exc);
      err_fields.push("$('#runwayuse_upto input')");
   }

   if (errors.length === 0 && dateBefore(period.upto, period.from))
   {
      errors.push('De begindatum moet voor de einddatum liggen');
      err_fields.push("$('#runwayuse_from input')");
      err_fields.push("$('#runwayuse_upto input')");
   }

   if (errors.length > 0)
   {
      handleError(errors, err_fields);
      return null;
   }
   else
   {
      return period;
   }
}

/**
 * Send a request to the server for the runway use in the specified period.
 */
function getRunwayUse()
{
   var period = getRunwayUsePeriod();
   if (period)
   {
      basAdmin.getRunwayUse(period.from, period.upto);
   }
}

/**
 * Create the runways use table from the data sent by the server, and
 * display it in the bottom left tab.
 * @param array result The runways use data returned by the server
 */
function showRunwayUse(result)
{
   var from_str = result.from.replace(/(\d{4})-(\d\d)-(\d\d)/, '$1/$2/$3');
   var upto_str = result.upto.replace(/(\d{4})-(\d\d)-(\d\d)/, '$1/$2/$3');
   var upto = new Date(upto_str);

   var date = new Date(from_str);
   var all_dates = [];
   while (date <= upto)
   {
      all_dates.push(dateToString(date));
      date.setDate(date.getDate()+1);
   }
   var all_dirs = ['LANDING', 'TAKEOFF'];

   var rows = '';
   var date_row = '<th></th>';
   var lt_row = '<th>tijd</th>';
   for (var i = 0; i < all_dates.length; i++)
   {
      date_row += '<th colspan="2">'
                +    all_dates[i].replace(/(\d{4})-(\d\d)-(\d\d)/, '$3-$2-$1')
                + '</th>';
      lt_row += '<th>L</th><th>T</lt>';
   }
   rows += '<tr>' + date_row + '</tr><tr>' + lt_row + '</tr>';

   var last_row = [];
   var idx;
   for (idx = 0; idx < all_dates.length*2; idx++)
   {
      last_row.push('');
   }

   for (var h = 0; h < 24; h++)
   {
      var hour = h + ':00';
      if (h < 10)
      {
         hour = '0' + hour;
      }
      var row = '<th>' + hour + '</th>';
      var dn = (h >= 23 || h < 6) ? 'night' : 'day';

      idx = 0;
      for (var idate = 0; idate < all_dates.length; idate++)
      {
         date = all_dates[idate];
         for (var idir = 0; idir < 2; idir++)
         {
            var dir = all_dirs[idir];

            var cell = '';
            var full_cell = '';
            if (!result.use[hour] || !result.use[hour][date]
               || !result.use[hour][date][dir])
            {
               full_cell = '---';
               cell = full_cell;
            }
            else
            {
               var rws = [];
               var count_rws = [];
               for (var rw in result.use[hour][date][dir])
               {
                  var count = result.use[hour][date][dir][rw];
                  count_rws.push(count + '*' + rw);
                  if (dn == 'night' || count >= 5)
                  {
                     rws.push(rw);
                  }
               }

               full_cell = count_rws.join('/');
               if (dn == 'night')
               {
                  cell = full_cell;
               }
               else
               {
                  cell = rws.join('/');
               }
            }

            if (cell != '---' && cell == last_row[idx] && dn != 'night')
            {
               cell = '';
            }
            else
            {
               last_row[idx] = cell;
            }

            row += '<td title="' + full_cell + '">' + cell + '</td>';
            idx++;
         }
      }
      rows += '<tr class="' + dn + '">' + row + '</tr>';
   }

   document.getElementById('blc_runwayuse').innerHTML
      = '<table class="runwayuse">' + rows + '</table>';

   // Unless a repaint is forced, IE6 does not show any content.
   if ($.browser.msie && $.browser.version == '6.0')
   {
      document.getElementById('bl_runwayuse').style.overflow = 'auto';
   }
}

/**
 * Export the runway use table for the specified period to Excel.
 */
function exportRunwayUse()
{
   var period = getRunwayUsePeriod();
   if (!period)
   {
      return;
   }

   var form = document.createElement('form');
   form.setAttribute('method', 'post');
   form.setAttribute('action', 'getrwuse.php');

   var from_field = document.createElement('input');
   from_field.setAttribute('type', 'hidden');
   from_field.setAttribute('name', 'from');
   from_field.setAttribute('value', period.from);
   form.appendChild(from_field);

   var upto_field = document.createElement('input');
   upto_field.setAttribute('type', 'hidden');
   upto_field.setAttribute('name', 'upto');
   upto_field.setAttribute('value', period.upto);
   form.appendChild(upto_field);

   document.body.appendChild(form);
   form.submit();
   document.body.removeChild(form);
}


