/*
 * @copy Nitro (c) 2006
 *
 * @author niko
 * @desc HTML form validation with javascript
 * @since 2006/10/26
 * @version 0.11
 * @required javascript v1.7
 * @required prototype.js
 * @see bottom of this file for prototype comments
 *
 * @changes (date, desc, author)
   - 
   - 
 */

// ex. usage
//<input name="emailaddress{required|validEmail}" type="text" />
//<input name="idNumber{required|integer|length=5|ERROR=id number need to be 5 digits long}" type="text" />

/*
 * form: {c1|c2|...|cN}
 *  where c is <keyword>[=possibly params]
 *
 * all keywords accepts empty values as valid. to make 'em valid and non-empty, combile with keyword 'required'
 *
 * possible keywords
 *
 * required               not empty
 * validEmail            can be empty, to require valid email combile with keyword required as {validEmail|required}
 * integer                can be empty, integer value
 * ERROR=<Error message>  overrides default error message
 * length=<integer>       can be empty, length of the field
 * minlength=<integer>    can be empty, minimum length of the field
 * equals=<field>         field value must equal another
 *
 * maxlength should be monitored with html input-type element's attribute maxlength
 *
 * Custom reasons can be added by naming them custom*, ex:
 *   errorNotices['custom1'] = 'Username has been taken. Please choose another.';
 */


// data structure
var errorContainer  = new Array();  // keeps count of unique error fields
var errorMessage    = new Array();  // keeps count of unique error lines
var errorTypeCount  = new Object(); // keeps count of errors per field type

var roundCheck       = new Array();  // keeps count of error fields per check
var roundCheckFields = new Object(); // keeps count of error types per check

var requiredFields  = new Array();  // keeps count of required fields

var isCalled = false;

// error colors
var colorTheme = {

  errorColor: '#DFC88B',             // error field background color
  successColor: '#ffffff',           // normal field background color

  error: function (field, validateRules) {
    if (field.type == 'radio') {
      if (field.parentNode.nodeName == 'LABEL') {
	      field.parentNode.parentNode.style.background = colorTheme.errorColor;
      } else {
        field.parentNode.style.background = colorTheme.errorColor;
      }
    } else {
      field.style.backgroundColor = colorTheme.errorColor;
    }  
  },
  success: function(field) {
    if (field.type == 'radio') {
      if (field.parentNode.nodeName == 'LABEL') {
	      field.parentNode.parentNode.style.background = colorTheme.successColor;
      } else {
        field.parentNode.style.background = colorTheme.successColor;
      }
    } else {
      if (field.disabled == false) {
        field.style.backgroundColor = colorTheme.successColor;
      }  
    }
  }
}

// default error messages
var errorNotices = new Object();
errorNotices['required'] = 'Täytä kaikki vaadittavat kentät.';
errorNotices['validEmail'] = 'Sähköpostiosoite virheellinen.';
errorNotices['re_integer'] = 'Arvon täytyy olla kokonaisluku.';
errorNotices['re_length'] = 'Kentän vaadittu pituus on ';
errorNotices['re_minlength'] = 'Kentän vaadittu vähimmäispituus on ';
errorNotices['equals'] = 'Kentät eivät täsmää.';

// custom notices, see usage above
errorNotices['custom1'] = 'Käyttäjä tunnus varattu. Ole hyvä ja valitse toinen.';
errorNotices['custom2'] = 'Tämä sähköpostiosoite on käytössä. Oletko unohtanut salasanasi?';


/******************\
 * ERROR HANDLING *
\******************/

// keeps count of unique fields which have caused error
function addError(field, msgField, special) {
  if (field.disabled == true) return;
  
  roundCheckFields[msgField+special] = true;
  colorTheme.error(field);

  // this array is cleared per every check
  if (!roundCheck.inArray(field.name)) {
    roundCheck.push(field.name);
  }
  // prevent multible radio names etc being dublicated
  if (!errorContainer.inArray(field.name)) {
    errorContainer.push(field.name);
  }

  var customCall = msgField.substring(0, 6) == 'custom' ? 1 : 0;
  addErrorMsg(field, errorNotices[msgField] + special, customCall);

  errorTypeCount[field.name + msgField + special] = true;
}

// removes error field
function removeError(field, msgField, special)
{
  if (isRequiredField(field) && $F(field) == '' && field.disabled == false) return false;

  // error can be removed only if no errors were found on this round
  if (!roundCheck.inArray(field.name))
  {
    colorTheme.success(field);

    errorContainer.remove(field.name);

    // check that this have been error before and none was catched this round
    if (!roundCheckFields[msgField+special])
    {
      var customCall = msgField.substring(0, 6) == 'custom' ? 1 : 0;
      removeErrorMsg(field, errorNotices[msgField] + special, customCall);
    }

    errorTypeCount[field.name + msgField + special] = false;
  }

  return true;
}

// returns boolean value whetner error is already reported by fieldname
function isError(field)
{
  return (errorContainer.inArray(field.name) == true);
}

// add error message
function addErrorMsg(field, line, customCall)
{
  var pattern = new RegExp("{(.*\\|)*(ERROR=)(.+)(\\|.*)*}$");
  var test = field.name.match(pattern);
  if (test != null && test[3] != null) {
    if (customCall == 0)
      line = test[3];
  }

  if (errorMessage.inArray(line)) return;

  errorMessage.push(line);
}

// remove error mesage
function removeErrorMsg(field, line, customCall)
{
  var pattern = new RegExp("{(.*\\|)*(ERROR=)(.+)(\\|.*)*}$");
  var test = field.name.match(pattern);
  if (test != null && test[3] != null) {
    if (customCall == 0)
      line = test[3];
  }

  errorMessage.remove(line);
}

function isRequiredField(field)
{
  return (requiredFields.inArray(field.name) == true);
}

function isRequiredIntegerField(field)
{
  return (requiredInteger.inArray(field.name) == true);
}

/******************\
 * VALIDATION     *
\******************/

function validateForm(form_id) {

  if (isCalled) return false;
  isCalled = true;

  var formElement = $(form_id);
  if (formElement == null) return false;

  // clear errors
  roundCheck = roundCheck.clear();
  roundCheckFields = new Object();

  var required_fields = getRequiredElements(formElement, 'required', '*');

  for (var i = 0; i < required_fields.length; i++) {
    if (!requiredFields.inArray(required_fields[i].name)) {
      requiredFields.push(required_fields[i].name);
    }

    var t = required_fields[i].type;
    var errorFound = false;

    if (t == 'radio') {
	  	
      // if fields is already handled
      if (roundCheck.inArray(required_fields[i].name)) continue;

      var elements = document.getElementsByName(required_fields[i].name);

      var found = false;
      var j = 0;
      while (j < elements.length) {
        if (elements[j].checked == true) {
          j = elements.length;
          found = true;
        }
        j++;
      }
      if (found == false) {
        errorFound = true;      
        addError(required_fields[i], 'required', '');
      }
    }	

    else if (t ==  'select-one') {
      if (!$F(required_fields[i]) || $F(required_fields[i]) == 0 || required_fields[i].options[required_fields[i].selectedIndex].value == '') // equals empty
      {
        errorFound = true;
        addError(required_fields[i], 'required', '');
      }
    }

    else if (t == 'text' || 'password') {
      if ($F(required_fields[i]) == '') {
        errorFound = true;
        addError(required_fields[i], 'required', '');
      }
    }

    if (errorFound == false) {
      removeError(required_fields[i], 'required', '');
    }
  }

  var required_fields = getRequiredElements(formElement, 'validEmail', '*');

  for (var i = 0; i < required_fields.length; i++)
  {

    switch (required_fields[i].type)
    {
      case 'text':

        var re = new RegExp("^([a-z0-9_\\-.]+@([a-z0-9_\\-.]+\\.)+[a-z]{2,4})?$", 'i');

        if (!$F(required_fields[i]).match(re))
        {
          addError(required_fields[i], 'validEmail', '');
          break;
        }

      default:
        removeError(required_fields[i], 'validEmail', '');
        break;
    }
  }

  var required_fields = getRequiredElements(formElement, 'integer', '*');

  for (var i = 0; i < required_fields.length; i++)
  {
 
    switch (required_fields[i].type)
    {
      case 'text':
      case 'password':

        var re = new RegExp("^\\d*$");

        if (!$F(required_fields[i]).match(re))
        {
          addError(required_fields[i], 're_integer', '');
          break;
        }

      default:
        removeError(required_fields[i], 're_integer', '');
        break;
    }
  }

  var required_fields = getRequiredElements(formElement, 'length=\\d+', '*');

  for (var i = 0; i < required_fields.length; i++)
  {

    switch (required_fields[i].type)
    {
      case 'text':
      case 'password':

        if ($F(required_fields[i]).length == 0) break;

        var pattern = new RegExp("{(.*\\|)*(length=)(\\d+)(\\|.*)*}$");
        var test = required_fields[i].name.match(pattern);
        if (test != null && test[3] != null)
        {
          if ($F(required_fields[i]).length != test[3])
          {
            addError(required_fields[i], 're_length', test[3]);
          }
          else
          {
            removeError(required_fields[i], 're_length', test[3]);
          }
  
          break;
        }

      default:
        removeError(required_fields[i], 're_length', '');
        break;

    }
  }

  var required_fields = getRequiredElements(formElement, 'minlength=\\d+', '*');

  for (var i = 0; i < required_fields.length; i++)
  {

    switch (required_fields[i].type)
    {
      case 'text':
      case 'password':

        var pattern = new RegExp("{(.*\\|)*(minlength=)(\\d+)(\\|.*)*}$");
        var test = required_fields[i].name.match(pattern);
        if (test != null && test[3] != null)
        {
          if ($F(required_fields[i]).length < test[3] && $F(required_fields[i]).length != 0)
            addError(required_fields[i], 're_minlength', test[3]);
          else
            removeError(required_fields[i], 're_minlength', test[3]);
          break;
        }

      default:
        removeError(required_fields[i], 're_minlength', '');
        break;

    }
  }

  var required_fields = getRequiredElements(formElement, 'equals=.+', '*');

  for (var i = 0; i < required_fields.length; i++)
  {
    switch (required_fields[i].type)
    {
      case 'text':
      case 'password':

        var pattern = new RegExp("{(.+\\|)*(equals=)(.*?)(\\|.+)*}$");
        var test = required_fields[i].name.match(pattern);
        if (test != null && test[3] != null)
        {
          var second_element = document.getElementsByName(test[3]).item(0);
          if ($F(required_fields[i]) != $F(second_element))
          {
            addError(required_fields[i], 'equals', '');
            addError(second_element, 'equals', '');
          }
          else
          {
            removeError(required_fields[i], 'equals', '');
            removeError(second_element, 'equals', '');
          }
          break;
        }

      default:
        removeError(required_fields[i], 'equals', '');
        break;
    }
  }

  // EXTRA CHECK(S)
  extraChecks(formElement);

  // processForm temporaly moved to the takenUserNameResult
}

function processForm()
{

  formElement = getFormElement();
  if (formElement == null) return false;

  if (!isCalled) return false;

  // count error fields
  var errorCount = 0;
  for (var propName in errorTypeCount) {
    if (errorTypeCount[propName] == true) {
      errorCount += 1;
    }
  }

  var alertStr = '';
  for (var i = 0; i < errorMessage.length; i++) {
    alertStr += " - " + errorMessage[i] + "\n";
  }

  isCalled = false;

  if (alertStr != '') {
    alert("Löytyi "+errorCount+" virhettä: \n" + alertStr);
  } else {
    sendForm(formElement);
  }
}

var formElementGLOBAL = null;
function setFormElement(e) {
  formElementGLOBAL = e;
}
function getFormElement() {
  return formElementGLOBAL;
}

var ajaxCalls = 0; // prevents too much triggers

// place everything else that ain't covered in main function here
function extraChecks(formElement) {
  
  setFormElement(formElement);

  if (formElement.getAttribute('id') == 'regForm') {
  	if (ajaxCalls > 0) return;
    ajaxCalls += 1;

    // for some reason ie cannot detect object with id username
    var ajaxObj = new AjaxObject();
    ajaxObj.isNameTaken($F('usernameusername'), takenUsernameResult);
  }
  else {
    processForm();
  }  
}

function takenUsernameResult(res) {
  if (ajaxCalls > 1) return;
  ajaxCalls += 1;

  if (res.responseText == 1) {
    addError($('usernameusername'), 'custom1', '');
  } else {
    removeError($('usernameusername'), 'custom1', '');
  }

  var ajaxObj = new AjaxObject();
  ajaxObj.isEmailTaken($F('email'), takenEmailResult);
}

function takenEmailResult(res) {
  ajaxCalls = 0;

  if (res.responseText == 1) {
    addError($('email'), 'custom2', '');
  } else {
    removeError($('email'), 'custom2', '');
  }

  processForm();
}
// </extraChecks>

/*******************************************************************************************
 * getters
 */
function getRequiredElements(node, re, tag)
{
  var classElements = new Array();
  var els = node.getElementsByTagName(tag);
  var elsLen = els.length;

  var pattern = new RegExp("{(.+\\|)*"+re+"(\\|.+)*}$");
  
  for (i = 0, j = 0; i < elsLen; i++) {
    if ( pattern.test(els[i].name) ) {
      classElements[j] = els[i];
      j++;
    }
  }
  return classElements; 
}

/*******************\
 * ARRAY FUNCTIONS *
\*******************/

Array.prototype.inArray = function(search_term) {
  var i = this.length;
  if (i > 0) {
    do {
      if (this[i] === search_term) {
        return true;
      }
    } while (i--);
  }
  return false;
}

Array.prototype.remove = function(search_term) {
  var i = this.length;
  if (i > 0) {
    do {
      if (this[i] === search_term) {
        this.splice(i, 1);
        return true;
      }
    } while (i--);
  }
  return false;
}

/*
$()  returns to document.getElementById().
$F() returns the value of any field input control.
$A() converts the single argument it receives into an Array object.
*/
