/*------------------------------------------------------------------------------
  Project: Exhibitor v3.1
  Script : exhibitor.js
  Version: 1.0
  Date   : 22 June 2010
  Author : Reuben Allott
 
  "Exhibitor v3.1 - the fastest and easiest way to configure, manage and
   operate an event, conference or access control system"

  Please visit the Exhibitor website at "www.allottware.co.za/exhibitor"
------------------------------------------------------------------------------*/

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
}

String.prototype.ltrim = function() {
	return this.replace(/^\s+/,"");
}

String.prototype.rtrim = function() {
	return this.replace(/\s+$/,"");
}

/*------------------------------------------------------------------------------
  AJAX
------------------------------------------------------------------------------*/

// Get HTML content asynchronously into element "elem"
// Call function onDone() when complete
// sPostParams is used when we want to POST a form submission
function GetHTMLAsync(elem, sUrl, onDone, onNotDone, sPostParams)
{
  var xmlHttp;
  if (sUrl.length == 0)
  {
    return 0;
  }
  try
  { // Firefox, Opera 8.0+, Safari, IE7
    xmlHttp = new XMLHttpRequest();
  }
  catch(e)
  {// Old IE
    try
    {
      xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    catch(e)
    {
      alert (EX_ERR_BROWSER);
      return 0;
    }
  }
  try
  {
    // Use a closure because this may be called multiple times in parallel
    xmlHttp.onreadystatechange = function()
    {
      if (xmlHttp.readyState == 4) // 4 = "loaded"
      {
        if (xmlHttp.status == 200) // 200 = "OK"
        {
          elem.innerHTML = xmlHttp.responseText;
          if (onDone != null)
          {
            onDone();
          }
        }
        else
        if (xmlHttp.status == 400) // 400 = "Failed" (e.g. payment did not go through)
        {
          elem.innerHTML = xmlHttp.responseText;
          if (onNotDone != null)
          {
            onNotDone();
          }
        }
        else
        {
          elem.innerHTML = "";
        }
      }
    };
    if (sPostParams != null)
    {
      xmlHttp.open('POST', sUrl, true);
      xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      xmlHttp.setRequestHeader("Content-length", sPostParams.length);
      xmlHttp.setRequestHeader("Connection", "close");
      xmlHttp.send(sPostParams);
    } else {
      xmlHttp.open("GET", sUrl, true);
      xmlHttp.send(null);
    }
  }
  catch (e)
  {
    elem.innerHTML = EX_ERR_AJAX;
  }
  return 1;
}

// Get XML document aynchronously
// Call function onDone(xmlDoc) when complete
function GetXMLAsync(sUrl, onDone, debug)
{
  var xmlHttp;
  if (sUrl.length == 0)
  {
    return 0;
  }
  if (onDone == null)
  {
    return 0;
  }
  try
  { // Firefox, Opera 8.0+, Safari, IE7
    xmlHttp = new XMLHttpRequest();
  }
  catch(e)
  {// Old IE
    try
    {
      xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    catch(e)
    {
      alert (EX_ERR_BROWSER);
      return 0;
    }
  }
  try
  {
    // Use a closure because this may be called multiple times in parallel
    xmlHttp.onreadystatechange = function()
    {
      if (xmlHttp.readyState == 4) // 4 = "loaded"
      {
        if (xmlHttp.status == 200) // 200 = "OK"
        {
		      if (debug == 1) alert(xmlHttp.responseText);
          onDone(xmlHttp.responseXML);
        }
      }
    };
    xmlHttp.open("GET", sUrl, true);
    xmlHttp.send(null);
  }
  catch (e)
  {
  }
  return 1;
}

// Reloads an element and sticks a progress image while loading
// The function onDone is called when the content has been loaded
// sPostParams should be null, except when called ala RealoadFormHTML
function ReloadHTML(sID, sUrl, onDone, onNotDone, sPostParams)
{
  var elem = document.getElementById(sID);
  if (elem)
  {
    elem.innerHTML = "<div class='Loader'>Loading...<br><img src='img/loading.gif'></div>";
    GetHTMLAsync(elem, sUrl, onDone, onNotDone, sPostParams);
  }
}

// Reloads an element with the results of doing a FORM POST submission.
// sFormID is the ID name of the <FORM> element.
function ReloadFormHTML(sID, sUrl, sFormID, onDone, onNotDone)
{
  ReloadHTML(sID, sUrl, onDone, onNotDone, GetFormParameters(sFormID));
}

/*------------------------------------------------------------------------------
  DOM
------------------------------------------------------------------------------*/

function getElementsByAttribute(elem, sTagName, sAttribute, sValue)
{ 
  var result = new Array();
	if (elem)
	{
    var elements = elem.getElementsByTagName(sTagName);
    var value = (typeof sValue != "undefined") ? new RegExp("(^|\\s)" + sValue + "(\\s|$)", "i") : null;
    var node;
    var attribute;
    for(var i=0; i<elements.length; i++)
    {
      node = elements[i];
      // avoid IE "invalid argument" error with getAttribute on non-existent
      // attributes with ":" in them (ex:xxx namepsace attributes)
      if ((node.attributes) && (node.attributes[sAttribute] != undefined))
      {
        attribute = node.getAttribute(sAttribute);
        if ((value == null) || value.test(attribute))
        {
          result.push(node);
        }
      }
    }
  }
  return result;
}

function getElementsByClassName(parent, classname, noRecurse) {
  var retnode = [];
  var myclass = new RegExp('\\b'+classname+'\\b', 'i');
  var elem
  if (noRecurse)
    elem = parent.childNodes;
  else
    elem = parent.getElementsByTagName('*');
  for (var i = 0; i < elem.length; i++)
  {
    var classes = elem[i].className;
    if (myclass.test(classes)) retnode.push(elem[i]);
  }
  return retnode;
};

// Get child nodes (no recursion)
function getNodesByTagName(parent, tagname) {
  var retnode = [];
  for (var i=0; i < parent.childNodes.length; i++)
  {
    if ((parent.childNodes[i].tagName) && (parent.childNodes[i].tagName.toUpperCase() == tagname.toUpperCase()))
      retnode.push(parent.childNodes[i]);
  }
  return retnode;
};

// Add a child element to a parent element
// NOTE: This only works for elements that accept text nodes inside them
function AddChild(DOC, PARENT, CHILD, CHILDTEXT, ATTRIBUTES)
{
  var ChildElement;

  if (typeof(CHILD) == "string")
  {
    ChildElement = DOC.createElement(CHILD);
  }
  else
  {
    ChildElement = CHILD;
  }
 
  if ((typeof CHILDTEXT != "undefined") && (CHILDTEXT != null))
  {
    var ChildText = DOC.createTextNode(CHILDTEXT);
    ChildElement.appendChild(ChildText);
  }

  if (typeof ATTRIBUTES != "undefined")
  {
    var Attributes = ATTRIBUTES;
    for (Att in Attributes)
    {
      ChildElement.setAttribute(Att,Attributes[Att]); 
    }
  }
 
  if (typeof(CHILD) == "string")
    PARENT.appendChild(ChildElement);

  return ChildElement;
}

// Removes passed-in element
function RemoveElement(ELEM)
{
  if (ELEM.parentNode.removeChild(ELEM))
    return true;
  else
    return false;
}

// Removes all of an element's children
function RemoveAllChildren(PARENT)
{	
  while (PARENT.hasChildNodes())
  {
    PARENT.removeChild(PARENT.childNodes[0]);
  }
}

function ClearElement(sID)
{
  var x = document.getElementById(sID);
  if (x)
  {
    RemoveAllChildren(x); 
  }
}

// Return query-string style parameter list of all input fields in a FORM.
// sFormID is the ID name of the <FORM> element.
function GetFormParameters(sFormID)
{
  var form = document.getElementById(sFormID);
  var sParams = ""
    
  function DoInputs(sTagName)
  {
    var elements = form.getElementsByTagName(sTagName);
    for (var i=0; i<elements.length; i++)
    {
      if (sParams.length>0) sParams = sParams + '&';
      sParams = sParams + elements[i].name + "=" + escape(encodeURI(elements[i].value));
    }
  }
  DoInputs('INPUT');
  DoInputs('SELECT');
  DoInputs('TEXTAREA');
  return sParams;
}

function GetParent(elem, tagname, classname)
{
  var myclass = new RegExp('\\b'+classname+'\\b', 'i');
  while ((elem != null) && ((elem.tagName != tagname) ||
    ((classname != null) && (!myclass.test(elem.className)))))
  {    
    elem = elem.parentNode;
  }
  return elem;
}

/*------------------------------------------------------------------------------
  Events
------------------------------------------------------------------------------*/

function StopEvents(e)
{
	if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
}

function AttachEvent(OBJ,EVT,FNCT)
{
  if (OBJ.addEventListener)
  {
    OBJ.addEventListener(EVT,FNCT,false);
    return true;
  } 
  else if (OBJ.attachEvent) 
  {
    return OBJ.attachEvent("on"+EVT,FNCT);
  }
}

function GetEventTarget(e)
{
  var elem = null;

  if (!e) 
    var e = window.event;

  if (e.nodeType == 1)
    elem = e
  else
    if (e.target) 
      elem = e.target; 
    else 
      if (e.srcElement) 
        elem = e.srcElement;
      
  // defeat Safari bug
  if ((elem != null) && (elem.nodeType == 3)) 
    elem = targ.parentNode;

  return elem;
}

/*------------------------------------------------------------------------------
  Cookies
------------------------------------------------------------------------------*/

function SetCookie(c_name, value, expiredays)
{
  var exdate = new Date();
  exdate.setDate(exdate.getDate() + expiredays);
  document.cookie = c_name + "=" + escape(value) +
    ((expiredays == null) ? "" : ";expires=" + exdate.toGMTString());
}

function GetCookie(c_name)
{
  if (document.cookie.length>0)
  {
    c_start = document.cookie.indexOf(c_name + "=");
    if (c_start != -1)
    {
      c_start = c_start + c_name.length + 1;
      c_end = document.cookie.indexOf(";",c_start);
      if (c_end == -1)
      {
        c_end = document.cookie.length;
      }
      return unescape(document.cookie.substring(c_start, c_end));
    }
  }
  return "";
}

/*------------------------------------------------------------------------------
   Navigation
------------------------------------------------------------------------------*/

function OpenURL(url, e)
{
  if ((url != "") && (url != undefined))
  {
    window.location.href = url;
  }
  StopEvents(e);
}

function PopupURL(url, e)
{
  if ((url != "") && (url != undefined))
  {
    OpenPopup(url, "_blank", 0, 0);
  }
  StopEvents(e);
}

function OpenPopup(url, target, w, h)
{
  var params = "location=0, scrollbars=1, resizable=1";
  if ((w > 0) && (h > 0)) params = params + ", width="+w+", height="+h;
  return window.open(url, target, params);
}

/*------------------------------------------------------------------------------
  Tables
  Build tables from XML data via AJAX.
  The retrieved XML is already a basic HTML table with following special 
  attributes:
  1) <table url="..."> gives the URL to use when we click on a row.
  2) <tr id="..."> gives the ID of the record when  we click on the row.
  We replace "ROWID" in the URL with the ID of the actual row clicked on.

  The "Fields" array lets us specify which fields to include in the table and
  to supply a custom header (for alternate languages maybe).

  Example:
  function GetOrdersTable(sID, sURL)
  {
    GetTable(sID, sURL, true,
    {
      RowNo:       "No",
      ProductCode: "Code",
      ProductName: "Product",
      ProductDesc: "Description",
      UnitPrice:   "Unit",
      Qty:         "Qty",
      Total:       "Total"
    }
    );
  }
------------------------------------------------------------------------------*/

function GetTable(sID, sURL, ShowHeaders, Fields)
{
  var x = document.getElementById(sID);
  
  if (x)
  {
    GetXMLAsync(sURL, function(xml) {
      BuildXmlTable(x, xml, ShowHeaders, Fields);
    }, 0);
  }
}

function BuildXmlTable(Table, Xml, ShowHeaders, Fields) 
{
  RemoveAllChildren(Table);

  var xData = Xml.documentElement;
  var xCols = Xml.getElementsByTagName("columns");
  Table.setAttribute("url", xData.getAttribute("url"));

  // Column definitions <col>
  if (xCols.length > 0)
  {
    xCols = xCols[0].getElementsByTagName("col");
    // Column header row <th>
    if ((ShowHeaders) || (ShowHeaders == undefined))
    {
      var tHead = AddChild(document, Table, "thead");
      AddTableHeadersFromXml(xCols,tHead);
    }
  }

  // Column data rows <td>
  var xRows = Xml.getElementsByTagName("rows");
  if (xRows.length > 0)
  {
    var tBody = AddChild(document, Table, "tbody");
    xRows = xRows[0].getElementsByTagName("r");
    AddTableRowsFromXml(xRows,xCols,tBody);
  }
}
    
function AddTableHeadersFromXml(xCols,tHead)
{
  var tr = AddChild(document, tHead, "tr");
  tr.className = "Header";
  for (var i=0; i<xCols.length; i++)
  {
    var text = (xCols[i].firstChild != null) ? xCols[i].firstChild.nodeValue : "";
    var th = AddChild(document, tr, "th");
    AddChild(document, th, th, text);
  }   
}

function AddTableRowsFromXml(xRows,xCols,tBody)
{
  for (var i=0; i<xRows.length; i++)
  {
    var tr = AddChild(document, tBody, "tr");
    var ID = xRows[i].getAttribute("id");
	
	  // NOTE: cannot use "id" in IE7
    tr.setAttribute("x", ID); 
    
    if (i%2==0)
      tr.className = "Even";
    else
      tr.className = "Odd";
	  
    for (j=0; j<xRows[i].childNodes.length; j++)
    {
      var text = (xRows[i].childNodes[j].firstChild != null) ? xRows[i].childNodes[j].firstChild.nodeValue : "";
      var td = AddChild(document, tr, "td");
      
      if (j < xCols.length) 
      {
        var cls = xCols[j].getAttribute("class");
        if (cls != null) 
        {
          td.className = cls;
        }
      }
      
      AddChild(document, td, td, text);
    }
    
    AttachEvent(tr, "click", TableClick);
  }
}

function TableClick(e)
{
  var elem = GetEventTarget(e);
  var tr = GetParent(elem, "TR");
  var table = GetParent(tr, "TABLE");
  var id = tr.getAttribute("x");
  var url = table.getAttribute("url");
  if ((url !== null) && (url != ""))
  {
    url = url.replace("=ROWID", "="+id);
    window.location.href = url;
  }
}

/*------------------------------------------------------------------------------
  Package Selection
------------------------------------------------------------------------------*/

function PackageChange()
{
  var input = document.getElementById("PackageID"); // <SELECT> input
  var pack = document.getElementById("Package"); // <DIV> containing the package
  if (pack)
  {
    var url = pack.getAttribute("ex:url");
    if ((url !== null) && (url != ""))
    {
      url = url.replace("=PACKID", "="+input.value).replace(/&amp;/g, "&");
      ReloadHTML("Package", url, CalculateTotals);
    }
  }
  // prevent inadvertantly changing selection with mouse scroll wheel
  input.blur();
}

function OptionClick(e)
{
  var input = GetEventTarget(e);
  var field = GetParent(input, "DIV", "Option");
  var options = getElementsByClassName(field, "Options", 1);
  var suboptions; 
  var i;

  // Make sub-options visible
  if (options.length > 0)
  {
    SetVisible(options[0], input.checked);   
  }

  // Enable the quantity selection
  var select = getElementsByClassName(field, "Details", 1);
  if (select.length > 0)
  {
    select = select[0].getElementsByTagName("SELECT");
    if (select.length > 0) 
      select[0].disabled = !input.checked;
  }

  // Hide sub-options of other items in the radio group
  if (input.getAttribute("type").toUpperCase() == "RADIO")
  {
    field = GetParent(field, "DIV", "Options");
    options = getElementsByClassName(field, "Option", 1);
    for (i=0; i<options.length; i++)
    {
      input = options[i].childNodes[0].childNodes[0]; // Option - Label - Input/Custom
      if (input.tagName) // Ignore custom captions
      {
        suboptions = getElementsByClassName(options[i], "Options", 1);
        if (suboptions.length > 0)
          SetVisible(suboptions[0], input.checked);
      }
    }
  }
  
  // Reclaculate main total
  CalculateTotals();
}

function ChangeQty(e)
{
  var select = GetEventTarget(e);
  var spans = getElementsByClassName(GetParent(select, "DIV", "Pricing"), "Val");
  var price = spans[0].innerHTML.replace(/,/g, "");
  var qty   = select.value;
  var total = price * qty; 
  spans[1].innerHTML = FormatMoney(total); 
  // Reclaculate main total
  CalculateTotals();
}

function CalculateTotals()
{
  var total = 0.0;
  var packages = document.getElementById("Packages");
  var input = document.getElementById("PackageID");
  var hide = getElementsByClassName(document, "HideIfTotalZero", 0);
  
  // Only if there is a package shown in the form
  if (packages && input)
  {
    var pack = getElementsByClassName(packages, "Package", 1);
    if (pack.length>0)
    {
      total += CalculateOptionTotal(pack[0]);
      document.getElementById("PriceTotal").innerHTML = FormatMoney(total);
      if (hide.length>0)
      {
        if (total > 0)
          hide[0].style.display = ""
        else
          hide[0].style.display = "none";
      }
      
    }
  }
}

function CalculateOptionTotal(Option)
{
  var label = getNodesByTagName(Option, "LABEL");
  var details = getElementsByClassName(Option, "Details", 1);
  var options = getElementsByClassName(Option, "Options", 1);
  
  // Note: there may not be an input checkbox/radio button present (eg: selection options)
  var input = (label.length == 1) ? getNodesByTagName(label[0], "INPUT") : null; 
  var checked = (label.length == 1) && (input.length > 0) ? input[0].checked : true;

  var total = 0.00;
  if (checked)
  {
    if (details.length == 1)
    {
      var Total = getElementsByClassName(details[0], "Total", 0);
      if (Total.length==1) total = parseFloat(Total[0].innerHTML.replace(/,/g, ""));
    }
    if (options.length == 1)
    {
      var suboptions = getElementsByClassName(options[0], "Option", 1);
      for (var i=0; i<suboptions.length; i++)
        total += CalculateOptionTotal(suboptions[i]); 
    }
    return total;
  } else {
    return 0;
  }  
}
 
function CheckPackageOptions()
{
  var fields = getElementsByAttribute(document, "*", "ex:mepo");
  for (var i=0; i<fields.length; i++) {
    if (((fields[i].checked) || (fields[i].tagName=="LABEL")) && IsElementVisible(fields[i]))
    {
      var fname = fields[i].getAttribute("ex:mepo");   
      if (!GetOptionValue(fname))
      {
        alert(EX_ERR_OPTIONS.replace("%s", fields[i].getAttribute("ex:caption")));
        ShowElement(fields[i], 1);
        return 0;
      }
    }
  }
  return 1;
}
 
function GetOptionValue(name)
{
  var radios = document.getElementsByName(name);
  for (var j=0; j<radios.length; j++)
    if (radios[j].checked) return radios[j].value;
  return null;  
}
  
/*------------------------------------------------------------------------------
   Form Validation
------------------------------------------------------------------------------*/

function ValidationAlert(caption) 
{
  alert(EX_ERR_COMPLETE.replace("%s", caption));
}

function CheckGTZ(item, caption)
{
  var x = document.getElementsByName(item);
  if ((x.length > 0) && ShouldElementBeValidated(x[0]))
  {
    if (!(x[0].value>0))
    {
      ValidationAlert(caption);
      ShowElement(x[0],1);
      return 0;
    }
  }
  return 1;
}

function CheckGTEZ(item, caption)
{
  var x = document.getElementsByName(item);
  if ((x.length > 0) && ShouldElementBeValidated(x[0]))
  {
    if (!(x[0].value>=0))
    {
      ValidationAlert(caption);
      ShowElement(x[0],1);
      return 0;
    }
  }
  return 1;
}

function CheckNonBlank(item, caption) 
{
  var x = document.getElementsByName(item);
  if ((x.length > 0) && ShouldElementBeValidated(x[0]))
  {
    if (x[0].value=="")
    {
      ValidationAlert(caption);
      ShowElement(x[0],1);
      return 0;
    }
  }
  return 1;
}

function CheckChecked(items, min, id, caption)
{
  var x = document.getElementsByName(items);
  var e = document.getElementById(id);
  if ((x.length > 0) && ShouldElementBeValidated(x[0]))
  {
    for(i=min; i<x.length; i++)
    {
      if (x[i].checked)
      {
        return 1;
      }
    }
    ValidationAlert(caption);
    // Ignore the hidden field before the actual visible checkboxes
    if (x.length>1)
    {
      e.scrollIntoView();
      ShowElement(x[1],1);
    }
    return 0;
  }
  return 1;
}

function CheckSelected(items, min, caption) 
{
  var x = document.getElementsByName(items);
  if ((x.length > 0) && ShouldElementBeValidated(x[0]))
  {
    for(i=min; i<x.length; i++)
    {
      if (x[i].selected)
      {
        return 1;
      }
    }
    ValidationAlert(caption);
    // Ignore the hidden field before the actual visible items
    if (x.length>1)
    {
      ShowElement(x[1],1);
    }
    return 0;
  }
  return 1;
}


// Check if any radio or drop-down item is selected
// "items" could be the name shared by a set of radio/checkbox <input>s
// or it could be a <select> tag with drop-down <options>.
// We assume that non-blank values are "not specified" and considered then unselected.
function CheckOptionSelected(items, caption) 
{
  var x = document.getElementsByName(items);
  if ((x.length > 0))
  {
    // Option is selected if the value of a <select> field is non-blank
    if (x[0].tagName == "SELECT" && x[0].value != "")
    {
      return 1;
    }
    // Option is selected if any radio buttons or check-boxes are checked
    for(i=0; i<x.length; i++)
    {
      //alert('count='+x.length+', tagName='+x[i].tagName+', type='+x[i].type+', checked='+x[i].checked+', selected='+x[i].selected+', value='+x[i].value)
      if (x[i].checked && x[i].value != "")
      {
        return 1;
      }
    }
    alert(caption);
    ShowElement(x[0],1);
    return 0;
  }
  return 1;
}

function CheckPassword() 
{
  if ((document.Registration.Pass1.value == "") || (document.Registration.Pass1.value != document.Registration.Pass2.value))
  {
    alert(EX_ERR_PASSWORD);
    ShowElement(document.Registration.Pass1,1);
    return 0;
  }
  return 1;
}

function CheckTermsAndConditions()
{
  if (!document.Registration.TC.checked)
  {
    alert(EX_ERR_TC);
    ShowElement(document.Registration.TC,1);
    return 0;
  }
  return 1;
}

function CheckEmailAddress(str) 
{
  var at = "@";
  var dot = ".";
  var atpos = str.indexOf(at);
  var dotpos = str.indexOf(dot);
  var strlen = str.length;
  var invalidChars = "\/\'\\ \";:?!()[]\{\}^|";
  var i;

  str = str.trim();

  // smallest possible email "a@b.c"
  if (strlen < 5)
  {
    return false;
  }

  // @ must be present
  if ((atpos <= 0) || (atpos >= strlen))
  {
    return false;
  }

  // only one @
  if (str.indexOf(at, atpos+1) > -1)
  {
    return false;
  }

  // . must be present after the domain
  if (str.indexOf(dot,atpos) == -1)
  {
    return false;
  }

  // Check common mistakes
  if ((str.indexOf("@.",0) >= 0) || (str.indexOf(".@",0) >= 0) || (str.indexOf("..",0) >= 0))
  {
    return false;
  }

  // Check invalid chars		
  for (i=0; i<invalidChars.length; i++) 
  {
    if (str.indexOf(invalidChars.charAt(i),0) > -1) 
    {
      return false;
    }
  }

  // Check non-ascii chars
  for (i=0; i<str.length; i++)
  {
    if (str.charCodeAt(i)>127)
    {
      return false;
    }
  }

  return true;
}

function CheckEmail(item, caption, CanBeBlank) 
{
  var email = document.getElementById(item);
  if ((email != null) && ShouldElementBeValidated(email))
  {
    if ((email.value == "") && (CanBeBlank == 1))
    {
      return 1;
    }
    if (!CheckEmailAddress(email.value))
    {
	    if (caption != null)
        alert(EX_ERR_EMAIL2.replace("%s", caption))
	    else
        alert(EX_ERR_EMAIL);
      ShowElement(email,1);
      return 0;
    }
  }
  return 1;
}

// Check if an input field has a number in it
// Supply max > min to check the number is within the range
function CheckNaN(item, min, max)
{
  var e = document.getElementById(item);
  if (isNaN(e.value))
  {
    alert(EX_ERR_NUMBER);
    ShowElement(e,1);
    return 0;
  }
  if ((max > min) && ((e.value < min) || (e.value > max)))
  {
    alert(EX_ERR_NUMBER2.replace("%min", min).replace("%max", max));
		ShowElement(e,1);
	  return 0;
  }
  return 1;
}

// Add "NoClear" to the class names of an input field to exclude it from the clear button
function ClearForm(FormID)
{
  var form = document.getElementById(FormID);
  var i;

  if (form == null)
  {
    form = document.getElementsByName(FormID);
    if (form.length>0)
      form = form[0]
    else
      form = null;
  }
  
  if (form != null)
  for (i=0; i<form.elements.length; i++)
  {
    var x = form[i];
    if ((x.tagName == "INPUT") && (x.className != "NoClear"))
    {
      if (x.type == "text") x.value = "";
	    if (x.type == "textarea") x.value = "";
	    if (x.type == "radio") x.checked = false;
	    if (x.type == "checkbox") x.checked = false;
      if (x.type == "select") x.selectedIndex = -1;
	    if (x.type == "select-one") x.selectedIndex = -1;
	    if (x.type == "select-multiple") x.selectedIndex = -1;
    }
  }
}

/*------------------------------------------------------------------------------
   Field Visibility
------------------------------------------------------------------------------*/

/*
 Determine if the element (question/group) should be validated.
 The element must be validated if it is a "visible" and required field.
 A field can be linked to another field such that it is only "visible" if
 a specific answer is selected for the other field (eg: please specify other).
 We can build forms with arbitrarily nested fieldsets, any of which could be
 collapsed, so a field that may not technically be visible on-screen may
 still have to be validated; hence we cannot simply check if the field element
 is visible. We have to trace upwards and check to see if there are any parent 
 elements that are not visible because of visibility/selection scripts. Our
 visibility scripts add a custom attribute "ex:hidden" to any element that
 should not be validated to simplify this bit of code here.
*/
function ShouldElementBeValidated(elem)
{
  while (elem != null) 
  {
    if (elem.style != null) 
    {
      if ((elem.attributes) && (elem.attributes["ex:hidden"] != undefined)) {
        if (elem.getAttribute("ex:hidden") == "1")
        {
          return 0;
        }
      }
    }
    elem = elem.parentNode 
  }
  return 1;
}

// Is the specified element fully in the viewport? 
function IsElementInViewport(el) 
{
  var top = el.offsetTop;  
  var left = el.offsetLeft;  
  var width = el.offsetWidth;  
  var height = el.offsetHeight;  

  while(el.offsetParent) {    
    el = el.offsetParent;    
    top += el.offsetTop;    
    left += el.offsetLeft;  
  }  

  return (    
    (top >= document.documentElement.scrollTop) &&    
    (top + height) <= (document.documentElement.scrollTop + document.documentElement.clientHeight) && 
    (left >= document.documentElement.scrollLeft) &&    
    (left + width) <= (document.documentElement.scrollLeft + document.documentElement.clientWidth)
  );
}

// Is element (and all parents) visible?
function IsElementVisible(elem)
{
  while (elem)
  {
    if (elem.style && (elem.style.display == "none"))
    {
      return false;
    }
    elem = elem.parentNode;
  }
  return true;
}
    
// Show the field on-screen and focus keyboard entry to it.
// Make sure we expand any collapsed fieldsets in the elements ancestry so it
// is visible to the user. A collapsed fieldset has the "Collapsed" class.
function ShowElement(elem,ShowInvalid)
{
  if (elem)
  {
    var x = elem.parentNode;
    while (x)
    {
      if ((x.tagName == "FIELDSET") && (x.className == "Collapsed"))
      {
  	    ToggleFieldset(x);
      }
      x = x.parentNode;
    }
    if (!IsElementInViewport(elem)) elem.scrollIntoView(true);
    elem.focus();
    // Set the element's parent Field div to the "Invalid" class for visual styling
    if (ShowInvalid==1)
      ShowInvalidField(elem);
  }
}

function ShowInvalidField(Field)
{
  var i;
  var fields;
  var parent;

  // Standard fields
  fields = getElementsByClassName(document, "Field");
  for (i=0; i<fields.length; i++)
    fields[i].className = fields[i].className.replace(" Invalid", "");

  // Package options
  fields = getElementsByClassName(document, "Option");
  for (i=0; i<fields.length; i++)
    fields[i].className = fields[i].className.replace(" Invalid", "");

  // Add "Invalid" to the class of the given field or option
  parent = GetParent(Field, "DIV", "Option");
  if (!parent)
    parent = GetParent(Field, "DIV", "Field");
  if (parent)
    parent.className = parent.className + " Invalid";
}

function SetVisible(Field, Visible) 
{
  if (Field != null)
  {
    if (Visible)
    {
      Field.style.display = "";
      Field.setAttribute("ex:hidden", "");
    } else {
      Field.style.display = "none";
      Field.setAttribute("ex:hidden", "1");
    }
  }
}

function SetVisibleID(FieldID, IsVisible)
{
  SetVisible(document.getElementById(FieldID), IsVisible);
}

function VisibleCheck(CheckboxID, FieldID, Inverted)
{
  var x = document.getElementById(CheckboxID);
  var Field = document.getElementById(FieldID);
  if (x != null)
  {
    v = (x.checked != Inverted) && (ShouldElementBeValidated(x));
    SetVisible(Field, v);
    return v;
  }
}

function VisibleListItem(ListID, Item, FieldID, Inverted) 
{
  var x = document.getElementById(ListID);
  var Field = document.getElementById(FieldID);
  if (x != null)
  {
    v = ((x.value==Item) != Inverted) && ShouldElementBeValidated(x);
    SetVisible(Field, v);
    return v;
  }
}

function VisibleListIndex(ListID, Index, FieldID, Inverted) 
{
  var x = document.getElementById(ListID);
  var Field = document.getElementById(FieldID);
  if (x != null)
  {
    v = ((x[Index].selected) != Inverted) && ShouldElementBeValidated(x);
    SetVisible(Field, v);
    return v;
  }
}

function VisibleText(TextID, Val, FieldID, Inverted)
{
  var x = document.getElementById(TextID);
  var Field = document.getElementById(FieldID);
  if (x != null)
  {
    v = ((x.value == Val) != Inverted) && ShouldElementBeValidated(x);
    SetVisible(Field, v);
    return v;
  }
}

/*------------------------------------------------------------------------------
   Toggle the collapsed/expanded state of tree nodes & fieldsets
------------------------------------------------------------------------------*/

function ToggleNode(item) 
{
  var next = item.nextSibling;
  while (next)
  {
    if (next.nodeName == "UL")
    {
      if (next.style.display == "")
      {
        next.style.display = "none";
        item.childNodes.item(0).src = "img/plus.gif";
      } else {
        next.style.display = "";
        item.childNodes.item(0).src = "img/minus.gif";
      }
      break;
    }
    next = next.nextSibling;
  }
}

/*
 ExWeb.DLL generates fieldsets in this format:
 <fieldset>
 <legend>
 <a onClick="ToggleFieldset(this)">Legend Caption</a>
 </legend>
 <div>
   // fieldset content (set of fields)
 </div>
 </fieldset>
 "item" is thus the <a> tag element
*/
function ToggleFieldset(item)
{
  var parent = item.parentNode; // <legend> tag
  var next = parent;
  var display = "";

  // Modifier so we can also call this function with "item" as the <fieldset> element
  if ((item) && (item.tagName == "FIELDSET"))
  {
    parent = item.firstChild;
    // Firefox handler for implicit #text nodes created for whitespace
    while (parent.nodeType != 1)
    {
      parent = parent.nextSibling;
    }
  }

  if (parent)
  {
    next = parent.nextSibling;  // <div> tag following the <legend>
    parent = parent.parentNode; // <fieldset> tag
    // This is a bit fiddly because implicit #text nodes in Mozilla do not have a style property.
    // We have to keep going until we find a real node to determine/toggle visibility status.
    while (next)
    {
      if (next.style) break;
      next = next.nextSibling;
    }
    if (next)
    {
      if (next.style.display == "")
      {
        parent.className = "Collapsed";
        display = "none";
      } else {
        parent.className = "Expanded";
        display = "";
      }
      while (next)
      {
        // ignore the implicit #text nodes
        if (next.style)
          next.style.display = display;
        next = next.nextSibling;
      }
    }
  }
}

// Toggle item visibility
function ToggleItem(ItemID)
{
  var x = document.getElementById(ItemID);
  if (x)
  {
    if (x.style.display == "")
    {
      x.style.display = "none";
    } else {
      x.style.display = "";
    }
  }
}

// Toggle item with plus/minus sign
function ToggleItem2(ItemID,ImgID)
{
  var x = document.getElementById(ItemID);
  var i = document.getElementById(ImgID);
  if (x)
  {
    if (x.style.display == "")
    {
      x.style.display = "none";
      i.src = "img/plus.gif";
    } else {
      x.style.display = "";
      i.src = "img/minus.gif";
    }
  }
}

// Toggle checkbox on/off
function ToggleInput(ItemID)
{
  var x = document.getElementById(ItemID);
  if (x)
  {
    if (!x.disabled) x.checked = !x.checked;
  }
}

/*------------------------------------------------------------------------------
   Limit characters entered into TEXTAREA fields
------------------------------------------------------------------------------*/

function LimitTextArea(Input, iMax, sRemainingID)
{
  var x = document.getElementById(sRemainingID);
  if (Input.value.length > iMax) {
    Input.value = Input.value.substring(0, iMax);
  }
  if (x)
    x.innerHTML = Input.value.length;
}

/*------------------------------------------------------------------------------
   Setup date/time pickers for form fields
   This requires the Dynarch Calendar script
------------------------------------------------------------------------------*/

function SetupDatePicker(sInputID, sButtonID)
{
  Calendar.setup({
    inputField: sInputID,
    ifFormat:   "%e %b %Y",
    button:     sInputID,
    align:      "Bl",
    electric:   false,
    singleClick:true
  });
  Calendar.setup({
    inputField: sInputID,
    ifFormat:   "%e %b %Y",
    button:     sButtonID,
    align:      "Bl",
    electric:   false,
    singleClick:true
  });
}

function SetupDateTimePicker(sInputID, sButtonID)
{
  Calendar.setup({
    inputField: sInputID,
    ifFormat:   "%e %b %Y %H:%M",
    button:     sInputID,
    align:      "Bl",
    electric:   false,
    showsTime:  true,
    timeFormat: 24,
    singleClick:true
  });
  Calendar.setup({
    inputField: sInputID,
    ifFormat:   "%e %b %Y %H:%M",
    button:     sButtonID,
    align:      "Bl",
    electric:   false,
    showsTime:  true,
    timeFormat: 24,
    singleClick:true
  });
}

/*------------------------------------------------------------------------------
   Form Initialization
------------------------------------------------------------------------------*/

function InitForm()
{
  // Add marker content to required fields (for IE7 which does not handle :after)
  var r = getElementsByClassName(document, "Required", 0);
  for (var i=0; i<r.length; i++)
  {
    var s = getElementsByClassName(r[i], "Caption", 0);
    for (var j=0; j<s.length; j++)
    {
      s[j].innerHTML = s[j].innerHTML + " <span class='Marker'>*</span>";
    }
  }

  /* Focus first visible field (what about SELECTs?)
  r = document.getElementsByTagName("INPUT");
  for (var i=0; i<r.length; i++)
    if ((r[i].type != "hidden") && (IsElementVisible(r[i]))) 
    {
      ShowElement(r[i]);
      break;
    }*/
  // Calculate package totals
  CalculateTotals();
}

/*------------------------------------------------------------------------------
   Registration
------------------------------------------------------------------------------*/

function ClickRegisterEmail() 
{
  if (CheckEmail("Email")) 
  {
    document.Registration.submit();
  }
}

function ClickRegisterAnother(url)
{
  if (confirm(EX_CONF_DUP))
  {
    window.location.href = url;
  }
}

function ClickRegisterSubmit()
{
  // "ValidateForm" must be defined in the host page
  if (ValidateForm())
  {
    document.Registration.submit();
  }
}

/*------------------------------------------------------------------------------
   Contact Login/Update
------------------------------------------------------------------------------*/

function ClickLoginEmail() 
{
  if (CheckEmail("Email")) 
  {
    document.Login.submit();
  }
}

function ShowPhotoUpload()
{
  var x = document.getElementById('PhotoUpload')
  var b = document.getElementById('PhotoCancel')
  if (x) {
    if (x.style.display == '') {
      x.style.display='none'
      if (b) b.value = 'Change...'
    } else {
      x.style.display=''
      if (b) b.value = 'Cancel'
    }
  }
}

/*------------------------------------------------------------------------------
   Language Selection
------------------------------------------------------------------------------*/

function SelectLanguage()
{
  var x = document.getElementById("Lang");
  var Lang = x.value;
  SetCookie("Lang", Lang);
  window.location.reload(true);
}

/*------------------------------------------------------------------------------
   Number/Money Formatting
------------------------------------------------------------------------------*/

function FormatNumber(num, dec, thou, pnt, curr1, curr2, n1, n2) 
{
  var x = Math.round(num * Math.pow(10,dec));
  if (x >= 0) n1=n2='';
  var y = (''+Math.abs(x)).split('');
  var z = y.length - dec; 
  if (z < 0) z--; 
  for(var i=z; i<0; i++) y.unshift('0'); 
  if (z < 0) z = 1; 
  y.splice(z, 0, pnt); 
  if(y[0] == pnt) y.unshift('0'); 
  while (z > 3) 
  {
    z-=3; 
    y.splice(z,0,thou);
  }
  var r = curr1+n1+y.join('')+n2+curr2;
  return r;
}

function FormatMoney(Amount)
{
  return FormatNumber(Amount, 2, ",", ".", "", "", "-", "");
}

/*------------------------------------------------------------------------------
   Main Menu System
------------------------------------------------------------------------------*/

function InitMenu(menuId)
{
	var m=document.getElementById(menuId);
  var a=m.getElementsByTagName("a");
  var i=0;
  var l=a.length;
  var z=50;
  var regCustom = new RegExp('\\bcustom\\b', "i");
  var regMenu = new RegExp('\\bmenu\\b', "i");
  var x;

	
  for(i;i<l;i++){

    x = a[i].parentNode;

    while (x != null)
    {
      if (regCustom.test(x.className)) { x = 0; break; }
      if (regMenu.test(x.className)) { x = 1; break; }
      x = x.parentNode;
    }

    if (x == 1) 
    {
      li = a[i].parentNode;
      ul = li.getElementsByTagName("ul");
    
      if (li.parentNode == m) {
        a[i].className += " Main";
      }
    
      if (ul.length>0) {
        ul[0].style.zIndex = z++;
        a[i].className += " Menu";     
           
        li.onmouseover = function(a,li) {
          return function() {
            li.style.display="block";  
            a.className += " Selected";
          }
        }(a[i],li.getElementsByTagName("ul")[0]);
      
        li.onmouseout = function(a,li) {
          return function() {
            li.style.display="none";
            a.className = a.className.replace(" Selected", "");
          }
        }(a[i],li.getElementsByTagName("ul")[0]);
      }
    }
	} 
}

