function validateForm() {
  // instantiate object
  var vObj = new formValidator();
  // collect elements
  var x = this.elements;
  // disable submit (to keep people from hitting it 2x)
  disableSubmit(x);
  for (var i=0;i<x.length;i++) {
    // required text fields
    if (x[i].getAttribute('required') && 
        (x[i].getAttribute('type')=='text') && 
        vObj.isEmpty(x[i].value)) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" is required.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // required radio buttons
    if (x[i].getAttribute('required') && 
        (x[i].getAttribute('type')=='radio') &&
        !vObj.isRadioSelected(x[i])) {
      alert('"'+getLegendFor(x[i])+'" is required.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Alphabetic Text
    if (x[i].getAttribute('validatefor')=='alpha' && !vObj.isAlphabetic(x[i].value)
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" should contain only letters.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Numeric Text
    if (x[i].getAttribute('validatefor')=='numeric' && !vObj.isNumber(x[i].value)
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" should contain only numbers.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Alphanumeric Text
    if (x[i].getAttribute('validatefor')=='alphanumeric' && !vObj.isAlphaNumeric(x[i].value)
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" should contain only alphanumeric characters.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Name
    if (x[i].getAttribute('validatefor')=='name' && !vObj.isName(x[i].value)
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" does not appear to be a valid name.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Title
    if (x[i].getAttribute('validatefor')=='title' && !vObj.isTitle(x[i].value)
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" does not appear to be a valid title.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Address
    if (x[i].getAttribute('validatefor')=='address' && !vObj.isAddress(x[i].value)
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" does not appear to be a valid address.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Post Code
    if (x[i].getAttribute('validatefor')=='postcode' && !vObj.isPostCode(x[i].value)
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" does not appear to be a valid post code.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Phone # (validates US or Int'l)
    if (x[i].getAttribute('validatefor')=='phone' && 
        (!vObj.isPhoneNumber(x[i].value) || !vObj.isIntlPhoneNumber(x[i].value))
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" does not appear to be a valid phone number.');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Email
    if (x[i].getAttribute('validatefor')=='email' && !vObj.isEmailAddress(x[i].value)
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      alert('"'+getLabelByFor(x[i].getAttribute('id'))+'" does not appear to be a valid email address."');
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // Range
    if (x[i].getAttribute('validatefor')=='range' &&
        x[i].getAttribute('range')
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      var range = x[i].getAttribute('range');
      range = range.split(',');
      if (!isWithinRange(x[i].value,range[0],range[1])) {
        alert('The value for "'+getLabelByFor(x[i].getAttribute('id'))+'" is not within the required range ('+range[0]+' to '+range[1]+')');
        x[i].focus();
        removeProgress(x);
        enableSubmit(x);
        return false;
      }
    }
    // Match
    if (x[i].getAttribute('match')
        && ((x[i].value!='') || x[i].getAttribute('required')) ) {
      var matchingID = x[i].getAttribute('match');
      var match = document.getElementById(matchingID);
      if (match.value != x[i].value) {
        alert('Your values for "'+getLabelByFor(x[i].getAttribute('id'))+'" and "'+getLabelByFor(matchingID)+'" do not match.');
        x[i].focus();
        removeProgress(x);
        enableSubmit(x);
        return false;
      }
    }
    // checkbox limit
    if (x[i].getAttribute('limit') &&
        vObj.limitReached(x[i],x[i].getAttribute('limit'))) {
      alert("Please limit your selection to "+x[i].getAttribute('limit'));
      x[i].focus();
      removeProgress(x);
      enableSubmit(x);
      return false;
    }
    // flip uploads to show action
    if (x[i].getAttribute('type')=='file') {
      if (x[i].value != '') {
        showProgress(x[i]);
      }
    }
  }
  enableSubmit(x); // need to enable for PHP
}

getLabelByFor = function (c) {
  var labels = document.getElementsByTagName("label");
  for(i = 0;i<labels.length;i++) {
    var labelFor = labels[i].getAttribute("for") ? 
      labels[i].getAttribute("for") : labels[i].getAttribute("htmlFor");
    if(labelFor == c) {
      var kids = labels[i].childNodes;
      for (j=0; j<kids.length; j++) {
        if (kids[j].nodeType == 3) { // node = text
          if (kids[j].nodeValue) {
            return kids[j].nodeValue;
          }
        }
      }
    } 
  }
}
getLegendFor = function (c) {
  var gp = c.parentNode.parentNode;
  var kids = gp.childNodes;
  for(i = 0;i<kids.length;i++) {
    if(kids[i].tagName == 'LEGEND') {
      var cnts = kids[i].childNodes;
      var content='';
      for (j=0; j<cnts.length; j++) {
        if (cnts[j].nodeType == 3) { // node = text
          if (cnts[j].nodeValue) {
            content += cnts[j].nodeValue;
          }
        }
      }
      return content;
    }
  }
  return 'A choice';
}
function setValidation() {
  // set the form to validate on submit
  var f = document.getElementsByTagName('form');
  for (var i=0;i<f.length;i++) {
    f[i].onsubmit = validateForm;
  }
  // set a watch on textareas for maxlength
  var t = document.getElementsByTagName('textarea');
  for (var i=0;i<t.length;i++) {
    if (t[i].getAttribute('maxlength'))
    t[i].onkeypress = checkLength;
  }
  // set up the progress meter for file inputs
  var fu = document.getElementsByTagName('input');
  for (var i=0;i<fu.length;i++){
    if (fu[i].getAttribute('type')=='file') {
      var progressBar = document.createElement('img');
      progressBar.src   = '/adchallenge/images/progress.gif';
      progressBar.alt   = 'A progress bar&#8230; your file is being uploaded';
      progressBar.title = 'Your '+getLabelByFor(fu[i].getAttribute('id'))+' is being uploaded';
      progressBar.id    = 'progress_'+fu[i].getAttribute('id');
      progressBar.style.display = 'none';
      fu[i].parentNode.insertBefore(progressBar,fu[i].nextSibling);
    }
  }
}
window.onload.actions.push(setValidation);

// disable submit
// x = array of form elements
function disableSubmit(x) {
  for (var i=0;i<x.length;i++) {
    if (x[i].getAttribute('type')=='submit') {
      x[i].disabled = true;
    }
  }
}
// enable submit
// x = array of form elements
function enableSubmit(x) {
  for (var i=0;i<x.length;i++) {
    if (x[i].getAttribute('type')=='submit') {
      x[i].disabled = false;
    }
  }
}

// show progress
// x = element to "replace"
function showProgress(x) {
  var f = document.getElementById(x.getAttribute('id'))
  var bar = 'progress_'+x.getAttribute('id');
  bar = document.getElementById(bar);
  f.style.display = 'none';
  bar.style.display = 'block';
}
// remove progress
// x = array of form elements
function removeProgress(x) {
  for (var i=0;i<x.length;i++) {
    if (x[i].getAttribute('type')=='file') {
      if (x[i].value != '') {
        x[i].style.display = 'block'; // removes any inline styles
        var progressBar = 'progress_'+x[i].getAttribute('id');
        progressBar = document.getElementById(progressBar);
        progressBar.style.display = 'none';
      }
    }
  }
}

// check length
function checkLength() {
  var maxL = this.getAttribute('maxlength');
  if (this.value.length > maxL) {
    alert("You have reached the maximum allowable length for this field");
    this.value.length = this.value.length-1;
    this.focus;
  }
}

// ---------------------------------------------------------------------
//                             VALIDATION OBJECT
// ---------------------------------------------------------------------
function formValidator() {
  // set up array to hold error messages
  this.errorList = new Array;
  
  // set up object methods
  this.isEmpty = isEmpty;
  this.isNumber = isNumber;
  this.isAlphabetic = isAlphabetic;
  this.isAlphaNumeric = isAlphaNumeric;
  this.isName = isName;
  this.isTitle = isTitle;
  this.isAddress = isAddress;
  this.isPostCode = isPostCode;
  this.isPhoneNumber = isPhoneNumber;
  this.isIntlPhoneNumber = isIntlPhoneNumber;
  this.isWithinRange = isWithinRange;
  this.isEmailAddress = isEmailAddress;
  this.isChecked = isChecked;
  this.isRadioSelected = isRadioSelected;
  this.radioValue = radioValue;
  this.totalChecked = totalChecked;
  this.limitReached = limitReached;
}
// ---------------------------------------------------------------------
//                             VALIDATION SCHEMA
// ---------------------------------------------------------------------
function isEmpty(val) { // check to see if input is whitespace only or empty
  if (val.match(/^\s+$/) || val == "") {
    return true;
  } else {
    return false;
  }
}
function isNumber(val) { // check to see if input is number
  if (isNaN(val)) {
    return false;
  } else {
    return true;
  }
}
function isAlphabetic(val) { // check to see if input is alphabetic
  if (val.match(/^[\w\s]+$/)) {
    return true;
  } else {
    return false;
  }
}
function isAlphaNumeric(val) { // check to see if input is alphanumeric
  if (val.match(/^[a-zA-Z0-9_]+$/)) {
    return true;
  } else {
    return false;
  }
}
function isName(val) { // check to see if input is words and numbers
  if (val.match(/^[a-zA-Z0-9\s.\-,&\/]+$/)) {
    return true;
  } else {
    return false;
  }
}
function isTitle(val) { // check to see if input is a valid title
  if (val.match(/^[a-zA-Z0-9\s.'\-,&:%$?#\/]+$/)) {
    return true;
  } else {
    return false;
  }
}
function isAddress(val) { // check to see if input is a valid address
  if (val.match(/^[a-zA-Z0-9\s.\-,&#\/]+$/)) {
    return true;
  } else {
    return false;
  }
}
function isPostCode(val) { // check to see if input is a valid post code
  if (val.match(/^[A-Z0-9\s\-]+$/)) {
    return true;
  } else {
    return false;
  }
}
function isPhoneNumber(val) {
  if (val.match(/^\(?\d{3}\)?\s|-\d{3}-\d{4}$/)) {
    return true;
  } else {
    return false;
  }
}
function isIntlPhoneNumber(val) {
  if (val.match(/^\d(\d|-){7,20}/)) {
    return true;
  } else {
    return false;
  }
}
function isWithinRange(val, min, max) { // check to see if value is within range
  if (val >= min && val <= max) {
    return true;
  } else {
    return false;
  }
}
function isEmailAddress(val) { // check to see if input is a valid email address
  if (val.match(/^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i)) {
    return true;
  } else { 
    return false;
  }
}
function isChecked(obj) { // check to see if form value is checked
  if (obj.checked) {
    return true;
  } else {
    return false;
  }
}
function isRadioSelected(obj) { // check to see if radio is checked
  var arr = document.getElementsByName(obj.name);
  for (var i=0; i<arr.length; i++) {
    if (arr[i].checked) {
      return true;
    }
  }
  // it has not returned true, so...
  return false;
}
function radioValue(obj) { // get radio value
  if (obj[0]) {
    for (var i=0; i<obj.length; i++) {
      if (obj[i].checked) {
        return obj[i].value;
      }
    }
  } else {
    if (obj.checked) {
      return obj.value;
    } else {
      return false;
    }
  }
}
function totalChecked(obj) { // limit checkboxes
  var total = 0;
  for (var i = 0; i < obj.length; i++)
  {
    if (obj[i].checked)
    {
      total++;
    }
  }
  return total;
}
function limitReached(obj,limit)
{
  var total = totalChecked(obj);
  if (total > limit)
  {
    return true;
  } else {
    return false;
  }
}
