function trim(str)
{
 return (str ? '' + str : '').replace(/^\s*|\s*$/g, '');
}

function isDef(arg, argtype)
{
 try
 {
  if (argtype === null || typeof(argtype) == 'undefined') return (arg !== null && typeof(arg) != 'undefined');
  return (arg !== null && typeof(arg) == argtype);
 }
 catch (e) {}
 return false;
}

function el(id)
{
 try
 {
  if (!isDef(id, 'string') || id == '') return null;
  return document.getElementById(id);
 }
 catch(e) {}

 return null;
}

function findParent(elem, type, id)
{
 try
 {
  if (!isDef(elem) || !isDef(type, 'string') || type == '') return null;
  if (!isDef(id)) id = '';

  type = type.toUpperCase();

 var parent = (elem.parentNode ? elem.parentNode : (elem.parent ? elem.parent : NULL));
  while (isDef(parent) && (parent.nodeType != 1 || (parent.nodeName.toUpperCase() != type && (id == '' || id != parent.id))))
   parent = (parent.parentNode ? parent.parentNode : (parent.parent ? parent.parent : NULL));  // Select the parent of the specified type by iterating up the ancestor hiearchy
 
 if (isDef(parent)) return parent;
 }
 catch(e) {}

 return null;
}

function urlEncode (str)
{
 try
 {
  str = (str+'').toString();
  return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/~/g, '%7E');  // equivalent to PHP's rawurlencode
//  return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+').replace(/~/g, '%7E');    // equivalent to PHP's urlencode
 }
 catch (e) {}

 return str;
}

function urlDecode (str)
{
 try
 {
  return decodeURIComponent(str);  // equivalent to PHP's rawurldecode
//  return decodeURIComponent(str.replace(/\+/g, '%20'));  // equivalent to PHP's urldecode
 }
 catch (e) {}

 return str;
}

var xmlHttpActions = {action: 0};

var xmlhttp = null;

function showReceived(xml)  // do something with results
{

 switch(xmlHttpActions.action)
 {
  case 1:
   // Do something
  break

  case 2:
   // Do something else
  break

  case 3:
   // Do another thing
  break

  case 4:
   // etc, etc...
  break

  default:
   // Do default something
 }
   
}

function sendRequest(url, action, sync, get)  // send xml http request
{
 if (!isDef(action)) action = {action: 0};   // default is no action
 if (!isDef(sync, 'boolean')) sync = true;  // default is a synchronous request
 if (!isDef(get, 'boolean')) get = false;   // default is a POST request

 xmlHttpActions = action;
 xmlhttp = getDomXmlRequest();
 xmlhttp.onreadystatechange = xmlhttpChange;
 if (get)
 {
  xmlhttp.open("GET", url, !sync);
  xmlhttp.send(null);
 }
 else
 {
  var params = url.split('?');

  if (params.length == 1) params = '';
  else if (params.length == 2)
  {
   url = params[0];
   params = params[1];
  }
  else return false;

  xmlhttp.open("POST", url, !sync);

  xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");

  xmlhttp.send(params);
 }

 if (sync == true) return xmlhttp.responseText;
 return true;
}

function getDomXmlRequest()  // create xml http request
{
 if (typeof(XMLHttpRequest) != 'undefined') return new XMLHttpRequest();

 var ax=['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];
 for (var i=0; i<ax.length; i++)
 {
  try {return new ActiveXObject(ax[i]);}
  catch (e) {}
 }

 return null;
}

function xmlhttpChange()  // xml http request changed
{
 if (xmlhttp.readyState == 4)
 {
  if (xmlhttp.status==200) showReceived(getDomFromXml(xmlhttp.responseText));
  else alert("Problem retrieving response.");
 }
}

function getDomFromXml(xmlString)  // return xml doc object from xml string
{
 if (typeof ActiveXObject != 'undefined')
 {
  var dom = new ActiveXObject("Microsoft.XMLDOM");
  dom.async = false;
  dom.loadXML(xmlString);
 }
 else if (document.implementation && document.implementation.createDocument)
 {
  parser = new DOMParser();
  var dom = parser.parseFromString(xmlString, "text/xml");
 }
 return dom;
}

function getValidation(form)
{
 try
 {
  if (!isDef(form)) return null;
  
  var validateList = new Array();
  
  var tagList = document.getElementsByTagName('validate');
  for (var i=0; i<tagList.length; i++) validateList[i] = tagList[i];
  
  for (var i=0; i<validateList.length; i++)  
  {
   var parentForm = findParent(validateList[i], 'form');
   if (parentForm != form) validateList.splice(i--, 1);
  }
  
  return validateList;
 }
 catch (e) {}

 return null;
}

function getRegEx(validateList, element)
{
 try
 {
  if (!isDef(validateList) || !isDef(element) || validateList.length == 0) return '';
  
  var name = element.getAttribute('name');
  if (!isDef(name)) return '';
  
  for (var i=0; i<validateList.length; i++)  
  {
   var elems = validateList[i].getAttribute('elem');  // Optional user-defined validation regular expression
   if (isDef(elems))
   {
    elems = elems.split(' ');
    for (var j=0; j<elems.length; j++)
	{
	 if (trim(elems[j].toLowerCase()) == name.toLowerCase())
     {
	  var regEx = validateList[i].getAttribute('regex');
	  if (isDef(regEx)) return regEx;
	  return '';
	 } 
	}
   }
  }
 }
 catch (e) {}

 return '';
}

function getRequired(validateList, element)
{
 try
 {
  if (!isDef(validateList) || !isDef(element) || validateList.length == 0) return false;
  
  var name = element.getAttribute('name');
  if (!isDef(name)) return false;
  
  for (var i=0; i<validateList.length; i++)  
  {
   var elems = validateList[i].getAttribute('elem');  // Optional user-defined validation regular expression
   if (isDef(elems))
   {
    elems = elems.split(' ');
    for (var j=0; j<elems.length; j++)
	{
	 if (trim(elems[j].toLowerCase()) == name.toLowerCase())
     {
	  var required = validateList[i].getAttribute('required');
	  return isDef(required);
	 } 
	}
   }
  }
 }
 catch (e) {}

 return false;
}

function validateForm(form)
{
 try
 {
  validateForm.Params = '';
  validateForm.confirm = '';

  if (!isDef(form))
  {
   alert ('Form is undefined!');
   return false;
  } 
  
  var Validated = true;
  
  var validateList = getValidation(form);
   
  for (var i=0; i<form.elements.length; i++)  // Iterate through the form's input element array
  {
   var element = form.elements[i];
   var name = element.getAttribute('name');
  
   var validate = '';
   var userValidate = getRegEx(validateList, element);
   var required = getRequired(validateList, element);

   var elemType = element.nodeName.toUpperCase();             // Determine if form input element is of type "CHECKBOX" or "RADIO"
   var inputType = (element.type ? element.type.toUpperCase() : '');

   var isCheckbox = (elemType == 'INPUT' && inputType == 'CHECKBOX');
   var isRadioButton = (elemType == 'INPUT' && inputType == 'RADIO');
   var isButton = (elemType == 'INPUT' && (inputType == 'SUBMIT' || inputType == 'RESET' || inputType == 'BUTTON'));
   var isTextArea = (elemType == 'TEXTAREA');
   var isSelect = (elemType == 'SELECT' && inputType == 'SELECT-ONE');
   var isMultiSelect = (elemType == 'SELECT' && inputType == 'SELECT-MULTIPLE');
   
   if (isDef(name) && name != '' && !isButton)
   {
    validateForm.Params += ((validateForm.Params == '') ? '' : '&') + name + '=';
    
    if (isCheckbox || isRadioButton) validateForm.Params += (element.checked ? 'yes' : 'no');
    else if (isSelect)
    {
     if (element.options[element.selectedIndex].value != element.options[element.selectedIndex].text) // These are equal if an option has no value, (unfortunately this may also be true if the value is set to the same as the name)
      validateForm.Params += urlEncode(element.options[element.selectedIndex].value);
    } 
    else if (isMultiSelect)
    {
     var Selection = '[';
      
     for (var j=0; j<element.length; j++)
     {
      if (element.options[j].selected)
      {
       if (element.options[j].value != element.options[j].text) // These are equal if an option has no value, (unfortunately this may also be true if the value is set to the same as the name)
       {
        if (Selection == '[') Selection += element.options[j].value;
        else Selection += (', ' + element.options[j].value);
       } 
      }
     }
     Selection += ']';
      
     validateForm.Params += urlEncode(Selection);
    }
    else validateForm.Params += urlEncode(element.value);
   }     
  
   if (name == '__confirm') validateForm.confirm = element.value;  // Used for the optional confirmation alert dialog on success
   
   if ((isSelect || isMultiSelect) && userValidate == '') validate = '!^.+$!i';  // Multi-Select's need to validate each selected value

   if (required && validate == '') validate = '!^.+$!mi';  // This needs to be multi-line to support textareas
    
   if (validate == '') validate = userValidate;  // Apply any user-defined validation reg-ex's if available
   
   var elemValidated = true;
   
   if ((validate != '' || isCheckbox || isRadioButton) && name != '' && !isButton)        // Only apply validation test to form input elements that have a custom "validate" regex defined and have a name and are not buttons
   {
    elemValidated = false;

   if (!isCheckbox && !isRadioButton) var regExParam = validate.split('!');               // Extract the regular expression and any associated pattern flags by splitting the "validate" value on the '!' delimiter
    if (isCheckbox || isRadioButton || regExParam[0] == '' && regExParam.length == 3)  // Validation string must be in the form "!expression!flags" (flags are optional)
    {
     if (!isCheckbox && !isRadioButton) var regEx = new RegExp(regExParam[1], regExParam[2]);  // Create Regular Expression
     
     if (isCheckbox || isRadioButton || regEx)
     {
      if (isCheckbox) elemValidated = (required ? element.checked : true);              // Checkbox must be checked
      else if (isRadioButton)                                                       // Test that at least one of a set of radio buttons is checked
      {
       var j, radioButtons = eval('form.' + element.name);                          // Get the array holding all the radio button and all of its siblings
      
       for (j=0; j<radioButtons.length; j++) {if (radioButtons[j].checked) break;}  // Iterate through the array of radio buttons until one is found to be checked
       elemValidated = (j < radioButtons.length);                                       // Test if no radio buttons checked

       i += (radioButtons.length - 1);
      }
      else if (isSelect || isMultiSelect)
      {
       if (element.selectedIndex >= 0)
       {
        var selected = false;
        var selectValidated = true;
       
        for (var j=0; selectValidated && j<element.length; j++)
        {
         if (element.options[j].selected)
         {
          if (!selected && element.options[j].value == element.options[j].text) selectValidated = false;  // These are equal if an option has no value, (unfortunately this may also be true if the value is set to the same as the name)
          else
          {
           selected = true;
           selectValidated = regEx.test(element.options[j].value); 
          } 
         }
        }

        elemValidated = selectValidated;
       }    
      }
      else
      {
       if (element.value != 'value required') elemValidated = regEx.test(element.value);  // Value not equal to default error string and Passes Regular Expression Test
      }
      
      if (elemValidated)
      {
       if (element.parentNode) element.parentNode.style.color = '#000000';  // If elemValidated set the text colour of the input element and its parent cell to the default
       element.style.color = '#000000';
      }
      else
      {
       if (element.parentNode) element.parentNode.style.color = '#FF0000';      // Visually indicate error by setting text colour of the input element and its parent cell to red
       element.style.color = '#FF0000';
      
       if (element.value == '') element.value = 'value required';           // If input element's value is an empty string, set it to the error string, "value required"
      }
     } 
     else alert('Invalid Validation Expression');
    }
    else alert('Invalid Validation Expression');
   }  
   
   Validated = (Validated & elemValidated);
  }
 }
 catch (e) {Validated = false;}

 if (!Validated) alert('Form values shown in red are invalid!');  // Show alert dialog to indicate that form has failed to validate

 return Validated;
}
validateForm.Params = '';
validateForm.confirm = '';

function submitForm(elem)
{
 try
 {
  var Result = true;

  var form = findParent(elem, 'form');  // Select the form by iterating up the ancestor hiearchy
  if (!isDef(form)) return false;

  if (validateForm(form))
  {
   Result = sendRequest('./formmail.php?' + validateForm.Params);  // Returns an empty string on success or an error message on fail

   if (isDef(Result, 'boolean')) {/* Not currently implemented */}  // For asynchronous requests
   else if (isDef(Result, 'string'))   // For synchronous requests
   {
	if (Result == '') 
    {
	 if (validateForm.confirm != '') alert(validateForm.confirm);
     form.reset(); // Clear the form 
    }
    else alert(Result);

    return true;
   } 
  
   alert('Data not submitted correctly');  // Show alert dialog to indicate that form has failed to submit
   return false;
  }
 }
 catch (e) {}

 return false;
}

function resetForm(elem)
{
 try
 {
  var form = findParent(elem, 'form');  // Select the form by iterating up the ancestor hiearchy
  if (!isDef(form)) return false;

  form.reset();                        // Clear the form

  return true;
 }
 catch (e) {}

 return false;
}

function calcCalories(elem)
{
 try
 {
  var Result = true;

  var form = findParent(elem, 'form');  // Select the form by iterating up the ancestor hiearchy
  if (!isDef(form)) return false;
  
  if (validateForm(form))
  {
   var GoalWeight = Number(el('goal_weight').value);
   var Height = Number(el('height').value);
   var Age = Number(el('age').value);
   var Activity = Number(el('activity').value);
   
   var i, radioButtons = eval('form.sex');                          
   for (i=0; i<radioButtons.length; i++) {if (radioButtons[i].checked) break;}  // Iterate through the array of radio buttons until one is found to be checked
   var Male = (radioButtons[i].value == '1');
   
   var BMR;
   if (Male) BMR = Activity * (66 + 13.7 * GoalWeight + 5 * Height - 6.8 * Age);
   else BMR = Activity * (655 + 9.6 * GoalWeight + 1.8 * Height - 4.7 * Age);
   
   el('calresult').innerHTML = Math.round(BMR);  // Calories
   el('kjresult').innerHTML = Math.round(BMR * 4.184);  // Joules, (but we're calling them KiloJoules
  }
 }
 catch (e) {}

 return false;
}
