/**
 * Support function for client side processing.
 *
 * @author     Herman Kuiper
 * @copyright  Frontier Information Technologies
 * @version    v1
 *
 * @package    fritz
 **/

/**
 * Variables to hold the current window size and body size
 * (ie viewport versus document size)
 */
var wWidth, wHeight, bWidth, bHeight;

/**
 * Determine the correct window size and the content size.
 * Store these values in some global variables.
 */
function getSizes()
{
   if(self.innerHeight) // all except Explorer
   {
      wWidth = self.innerWidth;
      wHeight= self.innerHeight;
   }
   else if(document.documentElement && document.documentElement.clientHeight) // Explorer 6 Strict Mode
   {
      wWidth = document.documentElement.clientWidth;
      wHeight= document.documentElement.clientHeight;
   }
   else if(document.body) // other Explorers
   {
      wWidth = document.body.clientWidth;
      wHeight= document.body.clientHeight;
   }

   var test1 = document.body.scrollHeight;
   var test2 = document.body.offsetHeight;
   if(test1 > test2) // All but Explorer Mac
   {
      bWidth = document.body.scrollWidth;
      bHeight= document.body.scrollHeight;
   }
   else // Explorer Mac; would also work in Explorer 6 Strict, Mozilla and Safari
   {
      bWidth = document.body.offsetWidth;
      bHeight= document.body.offsetHeight;
   }
}

/**
 * Resize a given iframe element to its optimal size.
 * This done by getting the scoll size and adding a bit
 * for possible scrollbars.
 *
 * @param object iframeWindow The window property of the iframe to resize
 * @param int reqWidth If we are a top window, resize the window itself to the given size
 * @param int reqHeight If we are a top window, resize the window itself to the given size
 */
function resize(iframeWindow, reqWidth, reqHeight)
{
   if(top == iframeWindow)
      resizeMain(reqWidth, reqHeight);
   else if(iframeWindow.document.height)
   {
      var iframeElement = document.getElementById(iframeWindow.name);
      iframeElement.style.height = (iframeWindow.document.height + 27) + 'px';
      iframeElement.style.width = (iframeWindow.document.width + 20) + 'px';
   }
   else if(document.all)
   {
      var width, height;

      if(iframeWindow.document.compatMode && iframeWindow.document.compatMode != 'BackCompat')
      {
         width = iframeWindow.document.documentElement.scrollWidth + 12 + 'px';
         height = iframeWindow.document.documentElement.scrollHeight + 12 + 'px';
      }
      else
      {
         width = iframeWindow.document.body.scrollWidth + 12 + 'px';
         height = iframeWindow.document.body.scrollHeight + 12 + 'px';
      }

      var iframeElement = document.all[iframeWindow.name];
      iframeElement.style.height = height;
      if(iframeElement.style.width.indexOf("%") == -1)
         iframeElement.style.width = width;
   }
}


/**
 * Resize a given iframe element containing an SVG to its optimal size.
 *
 * @param object iframeWindow The window property of the iframe to resize
 * @param object svg          The svg itself
 */
function svgResize(iframeWindow, svg)
{
  var width = svg.documentElement.getAttribute("width");
  var height = svg.documentElement.getAttribute("height");
  var iframeElement = document.all[iframeWindow.name];
  iframeElement.style.height = height;
  if(iframeElement.style.width.indexOf("%") == -1)
     iframeElement.style.width = width;
}

/**
 * Flag to indicate we are resizing the window ourselves, so do not store updated size
 */
var inJSResize;

/**
 * Resize a browser window top its optimal size, with a given
 * maximum. For browsers that support it, use sizeToContent,
 * else do a guestimate.
 */
function resizeMain(reqWidth, reqHeight)
{
   inJSResize = true;

   if(window.sizeToContent)
      window.sizeToContent();
   else
   {
      if(reqWidth > 0 && reqHeight > 0)
         window.resizeTo(reqWidth, reqHeight);
      else
      {
         getSizes();
         reqWidth = Math.min(800, bWidth + 16);
         reqHeight = Math.min(600, bHeight + 16);
         window.resizeTo(reqWidth, reqHeight);
      }

      // Now check to see how much window space is occupied by scrollbars etc,
      // and accomodate for those

      // Create the checkpoint element
      var cp = document.createElement("div");
      cp.style.position = "absolute";
      cp.style.width = "0px";
      cp.style.height = "0px";
      cp.style.right = "0px";
      cp.style.bottom = "0px";

      // We can only read it's position after we insert it into the document
      document.body.appendChild(cp);

      // Here we get the actual client size
      var current_width = cp.offsetLeft;
      var current_height = cp.offsetTop;
      // Here we find out how much more we need in order to get to the
      // needed w x h size (or in other words, we compute the size of
      // window decorations: border, scroll bars, title)
      var dw = reqWidth - current_width;
      var dh = reqHeight - current_height;
      // And _finally_ we get what we need
      window.resizeBy(dw + document.body.scrollLeft, dh + document.body.scrollTop);

      // we can safely delete the checkpoint now
      document.body.removeChild(cp);
   }

   inJSResize = false;
}

/**
 * Store the current size of the window in two form variables, so that
 * the template engine can store those sizes later in a cookie.
 */
function storeResize()
{
   if(!inJSResize && checkSeparateWindow())
   {
      document.getElementById("wWidth").value = document.body.offsetWidth;
      document.getElementById("wHeight").value = document.body.offsetHeight;
   }
}

/**
 * Let the user goto another child object in a master-detail
 * relationship (represented by a tabsheet).
 *
 * @param string id   Name of object for which this is a jump.
 * @param int    next ID of object to jump to, or -1 if a new object needs to be created
 */
function jumpObject(id, next)
{
   var i = document.getElementById("next_" + id);
   i.value = next;
   // Make sure the page reloads with the tabsheet in view
   document.forms['epaForm'].action += "#m_" + id;
   uploadForm();
}


/**
 * Global flag to indicate a form validation process is going on.
 */
var uploadFormValidate = false;
/**
 * Number of invalid form fields.
 */
var uploadFormInvalidCount = 0;

/**
 * Validate the current form.
 *
 * This done by calling any onchange handlers defined for each form element.
 * When there are multiple invalid fields, an alert is presented to
 * the user, else the onchange handler of the field is asked to display
 * an error.
 */
function validateForm()
{
   isControlledExit = true;

   uploadFormValidate = true;
   uploadFormInvalidCount = 0;
   var firstInvalid = null;

   if(!document.getElementsByTagName)
      return true;

   var inputs = document.getElementsByTagName("INPUT");
   for(var i = 0; i < inputs.length; i++)
      if(inputs[i].onchange)
      {
         inputs[i].onchange();
         if(uploadFormInvalidCount > 0 && firstInvalid == null)
            firstInvalid = inputs[i];
      }

   if(uploadFormInvalidCount > 1)
   {
      alert("There are invalid values");
      firstInvalid.focus();
   }
   else if(uploadFormInvalidCount == 1)
   {
      uploadFormValidate = false;
      firstInvalid.onchange();
   }
   return (uploadFormInvalidCount == 0);
}

/**
 * Upload the current form to the server, first checking
 * if all values are valid.
 */
function uploadForm()
{
   if(typeof checkSeparateWindow != 'undefined' && checkSeparateWindow())
      document.forms['epaForm'].refer.value = 0;

   var valid = validateForm();
   if(valid)
   {
      if(uploadForm.arguments.length > 0)
         document.getElementById("refer").value = uploadForm.arguments[0];
      document.forms['epaForm'].submit();
   }

   return (valid && uploadForm.arguments.length == 0);
}

/**
 * Upload the current form, but also set the URL of the
 * page which should process the posted contents.
 *
 * @param string where URL of page which should receive the posted data
 */
function uploadElsewhere(where)
{
   document.getElementById("url").value = where;
   return uploadForm();
}

/**
 * Re-load the form and add an additional input field for the given
 * attribute.
 *
 * @param string obj    Name of object for which an attribute input field should be added
 * @param string attr   Name of attribute to add input field for
 */
function addMultiple(obj,attr)
{
   document.getElementById("mO").value = obj;
   document.getElementById("mA").value = attr;
   document.forms['epaForm'].submit();
}

/**
 * Our parent form. When using a search for to add values to
 * another form, this field indicates which form to add the selected
 * values to.
 */
var parentForm;

/**
 * Load a form in a new window.
 *
 * @param string name   Name of target (frame/window) to load form in
 * @param string url    The &lt;a&gt; element which contains the URL to load into the frame, or
 *                      a string which contains the URL
 * @param string decoration Optional string for window decoration
 */
function loadAsSeparate(name, url)
{
   var l = url.href ? url.href : url;

   if(document.getElementById(name))
      document.getElementById(name).src = l;
   else
   {
      var d;
      if(loadAsSeparate.arguments.length > 2)
         d = loadAsSeparate.arguments[2];
      else
         d = (name[0] != '_' ? 'width=700,height=500,status=no,menubar=no,resizable=yes,scrollbars=yes' : '');

      var win = window.open(l, name, d);
      if(win.focus)
         win.focus();
      parentForm = url.href ? url.parentNode : null;    // The div containing all found values
   }
   return false;
}

/**
 * Insert a selected value into the search form for which this was
 * a find.
 *
 * @param object div HTML element containing input element to store selected values in
 * @param string value Value to store
 * @param string label Label to show to user for the selected value
 */
function insertObject(div, value, label)
{
   // Get all input elements
   var inputs = div.getElementsByTagName("INPUT");

   // If there is just a single input field or type text, this is a "free text" search
   // and we need only to copy the label
   if(inputs.length == 1 && inputs[0].type == "text")
      inputs[0].value = label;
   else
   {
      // Multiple checkboxes; first check the items already in the list,
      // and set a checkmark if the current value is in the list already.
      for(var i = 0; i < inputs.length; i++)
         if(inputs[i].value == value)
         {
            inputs[i].checked = true;
            return;
         }

      // Are multiple values allowed? If so, copy a checkbox and append
      // to the parent.
      var newDiv;
      if(div.getAttribute("MULTIPLE") == "yes")
      {
         if(div.childNodes[1].style.visibility == "hidden")
            newDiv = div.childNodes[1];
         else
         {
            newDiv = div.lastChild.cloneNode(true);
            div.appendChild(newDiv);
         }
      }
      else
         newDiv = div;

      // Check the input element for the current value
      var input = newDiv.getElementsByTagName("INPUT")[0];
      input.value = value;
      input.checked = true;

      // If necessary, show the container with checkboxes
      newDiv.style.visibility = 'visible';
      if(div.getAttribute("MULTIPLE") == "yes")
         div.style.height = Math.max(100, (16 * div.childNodes.length)) + "px";

      // Set label for value
      var span = newDiv.getElementsByTagName("SPAN")[0];
      span.innerHTML = label;
   }
}

/**
 * Copy one or more values from a search result to another
 * input field.
 *
 * @param string name Name of object (not used)
 * @param string mode Specific mode for operation (not used)
 * @see _deleteObjects
 * @see _exportObjects
 */
function _copyObjects(name, mode)
{
   // Copy ignores mode (and the @what attribute of <action> as well)
   if(!opener.parentForm)
   {
      alert("The window for this search form as changed.\nPlease re-open another search form.");
      window.close();
      return;
   }

   var isMultiple = true;
   var inputs = document.getElementsByTagName("INPUT");
   for(var i = 0; i < inputs.length; i++)
      if(inputs[i].name == 'id[]' && inputs[i].checked)
      {
         // If only a single selection is allowed, close window after copy
         isMultiple = (inputs[i].name == "radio");
         // See if there is a label we should copy as well
         var hasCopy = inputs[i].parentNode.nextSibling.getElementsByTagName("COPY");
         if(hasCopy.length)
         {
            var val = hasCopy[0].parentNode.innerHTML;
            var p1 = val.toUpperCase().indexOf("<COPY>");
            var p2 = val.toUpperCase().indexOf("</COPY>");
            insertObject(opener.parentForm, inputs[i].value, val.substring(p1 + 6, p2));
         }
         else
            insertObject(opener.parentForm, inputs[i].value, inputs[i].parentNode.nextSibling.innerHTML);
      }

   if(!isMultiple)
      window.close();
}

/**
 * Export selected objects to Excel
 *
 * @param string name Name of object
 * @param string mode Specific mode for operation. If set to "auto"
 *                    supress object selector in export-objects.php
 * @see _copyObjects
 * @see _deleteObjects
 */
function _exportObjects(name, mode)
{
   var prevUrl = document.getElementById("url").value;
   document.getElementById("url").value = "export-objects.php";

   document.getElementById("action1").value = name;
   if(mode == "auto")
      document.getElementById("action2").value = 1;
   document.forms['epaForm'].submit();

   document.getElementById("url").value = prevUrl;
}

/**
 * Allow a custom PHP callback on an action
 *
 * @param string name Name of object
 * @param string mode Name of PHP function which should handle the IDs
 * @see _copyObjects
 * @see _exportObjects
 */
function _customObjects(name, mode)
{
   document.getElementById("action1").value = name;
   document.getElementById("action2").value = mode;
   document.forms['epaForm'].submit();
}

/**
 * Clear all visible input fields in the form.
 */
function clearSearchForm()
{
   var inputs = document.getElementsByTagName("INPUT");
   for(var i = 0; i < inputs.length; i++)
   {
      if(inputs[i].type != "hidden")
      {
         inputs[i].value = "";
         inputs[i].checked = false;;
      }
   }
}

/**
 * Toggle all checkboxes named 'id[]'
 */
function selectToggle()
{
   var inputs = document.getElementsByTagName("INPUT");
   for(var i = 0; i < inputs.length; i++)
      if(inputs[i].name == 'id[]')
         inputs[i].checked = !inputs[i].checked;
   return false;
}

/**
 * Validate a time input field, to see if it is in
 * hh:mm or hhmm format.
 *
 * @param string val    Value to check
 */
function validTime(val)
{
   if(val.length == 0)
      return true;
   if(val.length < 4 || val.length > 5)
      return false;
   var h = parseInt(val.substring(0, 2));
   var m = parseInt(val.substring(val.length == 4 ? 2 : 3));
   return (h != "NaN" && m != "NaN" && h >= 0 && h < 24 && m >= 0 && m < 60);
}

/**
 * Netscape 7.02 seemed to ignore the return false when enter was pressed in an input
 * element. So record any enters, and have the delete button check for enter (so that
 * an enter does not trigger a delete).
 */
var enterPressed;

/**
 * Confirm if the user really wants to delete an object.
 * Also ignores any enter keys pressed on the delete button.
 *
 * @param string str    Message to display to the user.
 */
function confirmButton(str)
{
   if(enterPressed)
      return false;
   else
   return confirm(str);
}

/**
 * Let the enter key not submit the form, but advance to the
 * next input field (only when in a regular input field)
 *
 * @param object field  The field for which this function was called
 * @param object event  Event object
 */
function handleEnter(field, event)
{
   var keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;
   if(keyCode == 13)
   {
      var i;
      for(i = 0; i < field.form.elements.length; i++)
         if(field == field.form.elements[i])
            break;

      i = (i + 1) % field.form.elements.length;
      field.form.elements[i].focus();
      enterPressed = true;
      return false;
   }
   else
   {
      enterPressed = false;
      return true;
   }
}

/**
 * Open a small window to show a description for an
 * attribute.
 *
 * @param string content String to display.
 */
function help(content)
{
   var win = window.open('', 'fritzHelp', 'width=250,height=250,status=no,menubar=no,resizable=yes,scrollbars=yes');
   win.document.open();
   win.document.write("<html><head><title>Help</title>");
   if(document.getElementById("css"))
   {
      var link = document.getElementById("css");
      win.document.write("<link rel='stylesheet' type='text/css' href='" + link.getAttribute("href") + "' />");
      win.document.write("<link rel='stylesheet' type='text/css' href='" + link.nextSibling.getAttribute("href") + "' />");
   }
   win.document.write("</head><body class='help'>");
   win.document.write("<h1>Help</h1>");
   win.document.write(content);
   win.document.write("<div style='margin-top: 1em; text-align: right; font-size: smaller'><a href='javascript:window.close()'>close this window</a></div>");
   win.document.write("</body></html>");
   win.document.close();

   win.focus();

   return false;
}

/**
 * Number of currently selected tab (e.g. for export-objects.php)
 */
var currentTab = 0;

/**
 * Show a specific tab from a tabsheet (e.g. from export-objects.php).
 * Expect <td id='t1_num'>[</td><td>label</td><td>]</td> for each tab
 * and a <div id='t2_num'> with the content for a tab. This function
 * will change the class of the table cell to indicate the new current
 * tab, and will hide/display the previous/current tab content.
 *
 * @param int num Number of tab to show
 */
function showTab(num)
{
   var tab = document.getElementById("t1_" + currentTab);
   tab.className = 'tab-left';
   tab.nextSibling.className = 'tab tablabel';
   tab.nextSibling.nextSibling.className = 'tab-right';
   document.getElementById("t2_" + currentTab).style.display = 'none';

   currentTab = num;
   tab = document.getElementById("t1_" + num);
   tab.className = 'tabselected-left';
   tab.nextSibling.className = 'tabselected tablabel';
   tab.nextSibling.nextSibling.className = 'tabselected-right';
   document.getElementById("t2_" + num).style.display = 'block';

   return false;
}

/**
 * Initialize variables, etc. Called on onload.
 */
function init()
{
   // If we don't have activeElement (i.e. non-IE), fake it.
   if((!document.activeElement || typeof document.activeElement == "undefined") &&
      (document.body && typeof document.body != "undefined"))
   {
      document.activeElement = document.body;
      var all = document.body.getElementsByTagName('*');
      for(var i = 0; i < all.length; i++)
      {
         var functionBody = 'document.activeElement = this;';
         var oldOnFocus = all[i].getAttribute('onfocus');
         if(oldOnFocus)
            functionBody += oldOnFocus;
         all[i].onfocus = new Function('event', functionBody);
      }
   }

   if(document.getElementById)
   {
      if(document.getElementById("focusme"))
         document.getElementById("focusme").focus();
   }

   // If a subform, signal our loading is done, so cursor can return to normal
   if(parent.resize)
      parent.setNormal();

   // Kick-start keep alive script
   setTimeout('keepalive()', 1000 * 60 * 20);
}


/**
 * This variable keeps track if the user pressed one of our buttons to leave a page, or not.
 */
var isControlledExit = false;

/**
 * A event handler which can be attached to onbeforeunload when using version control,
 * to remind the user that leaving a page without using OK or Cancel will result in
 * a scartch version remaining.
 */
function controlledExit()
{
   if(!isControlledExit)
      event.returnValue = "You should leave this page using the buttons or links on the form,\notherwise the form will remain locked for modification.";
}

/**
 * This function will keep the connection to the webserver open such that
 * the session data will not expire while the user is out for coffee.
 */
function keepalive()
{
   document.forms['alive'].submit();
   setTimeout('keepalive()', 1000 * 60 * 20);      // re-submit every 20 minutes
}

/**
 * This function will set the input name of a hidden input variable
 * to 'username', such that when submitting the form (which will be
 * the next step), the user is effectively logged out.
 */
function logout()
{
   document.getElementById('username').name = 'username';
}

/**
 * Number of child windows still loading
 */
var childCount = 0;

/**
 * Set the wait cursor for a page, with a count of child pages which should
 * have loaded before the cursor is allowed to return to its regular state.
 */
function setWait(count)
{
   childCount = count;
   document.body.style.cursor = 'wait';
}

/**
 * Try to return cursor to regular state. If childCount > 0,
 * nothing happens.
 */
function setNormal()
{
   if(childCount > 0)
   {
      childCount--;
      if(childCount == 0)
         document.body.style.cursor = 'default';
   }
}
