/***************************************************************************************************
*
*-- Form validation script by Peter Bailey, Copyright (c) 2001-2002
* Version 5.00b
* Updated on May 28, 2002
* www.peterbailey.net
* me@peterbailey.net
*
* IF YOU USE THIS SCRIPT, GIVE ME CREDIT PLEASE =)
*
* Visit http://www.peterbailey.net/fValidate for more info
*
* Please contact me with any questions, comments, problems, or suggestions
*
* Note: This document most easily read with tab spacing set to 4
*
*******************************************************************************************************/

//  Main validation routine
function validateForm( f, bConfirm, bDisable, bDisableR, groupError, errorMode )
{
  //  Set defaults
  bConfirm = Boolean( bConfirm );
  bDisable = Boolean( bDisable );
  bDisableR = Boolean( bDisableR );
  groupError = Boolean( groupError );
  errorMode = ( errorMode ) ? parseInt( errorMode, 10 ) : 0;

  //  Init vars and fValidate object
  var params, fvCode, type;
  if ( typeof fv == 'undefined' )
  {
    var fv = new fValidate( f, errorMode, Boolean( groupError ) );
  }

  //  Loop through all form elements
  var elem, i = 0;
  while ( elem = f.elements[i++] )
  {
    //  Does element have validator attribute?
    if ( fvCode = elem.getAttribute( fv.config.code ) )
    {
      //  Set params, validation type, and validation state
      params         = fvCode.split( "|" );
      type           = params[0];
      elem.validated = true;

      //  Valid validator type?
      if ( typeof fv[type] == 'undefined' )
      {
        fv.devError( "The validator '" + type + "' was not found.\nRequested by: " + elem.name );
        return false;
      }

      //  Check for modifiers
      switch( params.last() )
      {
        case 'bok'  : //  bok requested
          params = params.reduce( 1, 1 );
          elem.bok = true;
          break;
        case 'if' : //  Conditional validation requested
          params = params.reduce( 1, 1 );
          elem._if_ = true;
          break;
        case 'then' : //  Conditional validation requested
          params = params.reduce( 1, 1 );
          elem._then_ = true;
          break;
        default   : //  No modifiers
          params = params.reduce( 1, 0 );

      }

      /*
      //  Bok requested?
      if ( params.last() == 'bok' )
      {
        //  Trim type and bok from params
        params = params.reduce( 1, 1 );
        elem.bok = true;
      }
      else
      {
        //  Trim bok from params
        params = params.reduce( 1, 0 );
      }
      */

      //  Is element an array?
      if ( /radio|checkbox/.test( elem.type ) )
      {
        //  Set group property
        elem.group = f.elements[elem.name];
      }

      //  Add events if not already added
      if ( typeof elem.fName == 'undefined' )
      {
        //  If element is an array
        if ( typeof elem.group != 'undefined' )
        {
          for ( var j = 0; j < elem.group.length; j++ )
          {
            //  Apply event-function to each child
            if ( fv.config.clearEvent != null )
            {
              addEvent( elem.group.item( j ), fv.config.clearEvent, fv, 'revertError', false );
            }
          }
        }
        else
        {
          //  Apply event-function to element
          if ( fv.config.clearEvent != null )
          {
            addEvent( elem, fv.config.clearEvent, fv, 'revertError', false );
          }
        }
      }

      //  Set formatted name, current element
      elem.fName    = elem.name.format();
      fv.elem       = elem;

      //  Create function to call the proper validator method of the fValidate class
      var func = new Function( "obj", "method", "obj[method]( " + params.toArgString() + " );" );
      func( fv, type );

      //  If element test failed AND group error is off, return false
      if ( elem.validated == false && groupError == false ) return false;

      //  Clear error if field okay
      if ( elem.validated == true ) fv.revertError();
    }
  } //  end of element loop

  //  If group error, show it
  if ( groupError ) fv.showGroupError();

  //  Return false if errors found
  if ( fv.errors.length > 0 ) return false;

  //  Show pre-submission confirmation
  if ( bConfirm && !confirm( fv.config.confirmMsg ) )
  {
    if ( fv.config.confirmAbortMsg != '' ) alert( fv.config.confirmAbortMsg );
    return false;
  }

  //  Disable reset and/or submit buttons if requested
  if ( bDisable )
  {
    if ( typeof fv.config.submitButton == 'object' )
    {
      for ( var sb, j = 0; ( sb = fv.config.submitButton[j] ); j++ )
      {
        if ( fv.elementExists( sb ) )
        {
          f.elements[sb].disabled = true;
        }
      }
    } else if ( fv.elementExists( fv.config.submitButton ) )
    {
      f.elements[fv.config.submitButton].disabled = true;
    }
  }
  if ( bDisableR && fv.elementExists( fv.config.resetButton ) )
  {
    f.elements[fv.config.resetButton].disabled = true;
  }

  //  Form validated and all options exercised - allow submission
  /*alert( 'Success!\n\nRemember, this is just a testing page!' );
  return false;*/
  return true;

  function addEvent( elem, evt, obj, method, capture )
  {
    var self = elem;
    if ( typeof elem.attachEvent != 'undefined' )
    {
      elem.attachEvent( "on" + evt, function() { obj[method]( self ) } );
    }
    else if ( typeof elem.addEventListener != 'undefined' )
    {
      elem.addEventListener( evt, function() { obj[method]( self ) }, capture );
    }
    else if ( fv.config.eventOverride )
    {
//      eleme['on' + evt] = function() { obj[method]( self ) };
      elem['on' + evt] = function() { obj[method]( self ) };
    }
  }

}

//  Constructor
function fValidate( f, errorMode, groupError )
{
  var self        = this;
  this.form       = f;
  this.errorMode  = errorMode;
  this.groupError = groupError;
  this.errors     = new Array();
  this.validated  = true;
  this.config     = new fValConfig();

  //  Add reset action to clear visual error cues
  f.onreset = function()
  {
    var elem, i = 0;
    while ( elem = this.elements[i++] )
    {
      self.revertError( elem );
    }
  }

  addLabelProperties();

  //  Parses form and adds label properties to elements that have one specified
  function addLabelProperties()
  {
    //  Collect all label elements in form, init vars
    if ( typeof f.getElementsByTagName == 'undefined' ) return;
    var labels = f.getElementsByTagName( "label" );
    var label, i = j = 0;
    var elem;

    //  Loop through labels retrieved
    while ( label = labels[i++] )
    {
      //  For Opera 6
      if ( typeof label.htmlFor == 'undefined' ) return;

      //  Retrieve element
      elem = f.elements[label.htmlFor];
      if ( typeof elem == 'undefined' )
      { //  No element found for label
        self.devError( "No element found for label: " + label.htmlFor );
      }
      else if ( typeof elem.label != 'undefined' )
      { //  label property already added
        continue;
      }
      else if ( typeof elem.length != 'undefined' && elem.length > 1 && elem.nodeName != 'SELECT' )
      { //  For arrayed elements
        for ( j = 0; j < elem.length; j++ )
        {
          elem.item( j ).label = label;
        }
      }
      //  Regular label
      elem.label = label;
    }
  }
}

//  Checks if element exists in form
fValidate.prototype.elementExists = function( elemName )
{
  return Boolean( typeof this.form.elements[elemName] != 'undefined' );
}

//  Receives error message and determines action
fValidate.prototype.throwError = function( error )
{
  var elem  = this.elem;

  //  Arrayed element?
  if ( typeof elem.name == 'undefined' )
  {
    elem = elem[0];
  }

  //  Bok requested AND element blank OR conditional validation?
  if ( elem.bok && this.isBlank() )
  { //  skip
    elem.validated = true;
    return;
  }

  //  Part of a conditional validation?
  if ( elem.cv )
  {
    return;
  }

  //  Retrieve error message, set failsafe to false
  var emsg = ( elem.getAttribute( this.config.emsg ) ) ? elem.getAttribute( this.config.emsg ) : error;
  elem.validated = false;

  //  Group error mode?
  if ( this.groupError )
  {
    //  Push error onto stack
/*  this.errors.push( {'elem':elem, 'msg': error} );*/
    this.errors.push( {'elem':elem, 'msg': emsg} );
  }
  else
  {
    //  Process error message
    this.showError( emsg );

    var focusElem = ( elem.type == 'hidden' )?
      elem.fields[0]:
      elem;

    //  Focus and select elements, if possible
    this.selectFocus( focusElem );
  }
}

//  Shows error message to user
fValidate.prototype.showError = function( emsg, last, elem )
{
  //  Set variables
  var self     = this,
    elem     = this.setArg( elem, this.elem ),
    isHidden = Boolean( elem.type == 'hidden' ),
/*  label    = ( isHidden ) ? null : elem.label || null;*/
    label    = ( isHidden ) ? null : elem.label;

  if ( typeof this.showErrors == 'undefined' ) this.showErrors = new Array();

  //  Determine which error modes to use
  switch( this.errorMode )
  { //  This represents all possible combinations
    case 0  : alertError(); break;
    case 1  : inputError(); break;
    case 2  : labelError(); break;
    case 3  : appendError(); break;
    case 4  : boxError(); break;
    case 5  : inputError(); labelError(); break;
    case 6  : inputError(); appendError(); break;
    case 7  : inputError(); boxError(); break;
    case 8  : inputError(); alertError(); break;
    case 9  : labelError(); appendError(); break;
    case 10 : labelError(); boxError(); break;
    case 11 : labelError(); alertError(); break;
    case 12 : appendError(); boxError(); break;
    case 13 : appendError(); alertError(); break;
    case 14 : boxError(); alertError(); break;
    case 15 : inputError(); labelError(); appendError(); break;
    case 16 : inputError(); labelError(); boxError(); break;
    case 17 : inputError(); labelError(); alertError(); break;
    case 18 : inputError(); appendError(); boxError(); break;
    case 19 : inputError(); appendError(); alertError(); break;
    case 20 : inputError(); boxError(); alertError(); break;
    case 21 : labelError(); appendError(); boxError(); break;
    case 22 : labelError(); appendError(); alertError(); break;
    case 23 : appendError(); boxError(); alertError(); break;
    case 24 : inputError(); labelError(); appendError(); boxError(); break;
    case 25 : inputError(); labelError(); appendError(); alertError(); break;
    case 26 : inputError(); appendError(); boxError(); alertError(); break;
    case 27 : labelError(); appendError(); boxError(); alertError(); break;
    case 28 : inputError(); labelError(); appendError(); boxError(); alertError(); break;
  }

  //  Regular alert error
  function alertError()
  {
    if ( self.groupError ) self.showErrors.push( emsg );
    else alert( emsg );
    if ( last ) alert( "The following fields had errors\n\n" + self.showErrors.join( "\n\n" ) );
  }

  //  Applies class to element's label
  function labelError()
  {
    if ( label == null ) return;
    label.className = self.config.errorClass;
  }

  //  Applies class to form element
  function inputError()
  {
    if ( ( typeof elem.length != 'undefined' && elem.length > 1 && elem.nodeName != 'SELECT' ) || isHidden )
    {
      var subelem, i = 0;
      while( subelem = ( isHidden ) ? elem.fields[i++] : elem.item( i++ ) )
      {
        if ( subelem.className != '' )
        {
          subelem.revertClass = subelem.className;
        }
        subelem.className = self.config.errorClass;
      }
    }
    else
    {
      elem.className = self.config.errorClass;
    }
  }

  //  Appends error message to element's label
  function appendError()
  {
    if ( label == null || typeof label.innerHTML == 'undefined' ) return;
    if ( typeof label.original == 'undefined' )
      label.original = label.innerHTML;
    label.innerHTML = label.original + " - " + emsg.toHTML();
  }

  //  Appends Error message to pre-defined element
  function boxError()
  {
    if ( typeof self.boxError == 'undefined' ) self.boxError = document.getElementById( self.config.boxError );
    if ( typeof self.boxError == 'undefined' )
    {
      this.devError( "A element with the requested id, " + self.config.boxError + ", was not found." );
    }
    var errorId = self.config.boxErrorPrefix + self.elem.name,

      errorElem;

    if ( errorElem = document.getElementById( errorId ) )
    {
      errorElem.firstChild.nodeValue = emsg.toHTML();
    }
    else
    {
      errorElem = document.createHTMLElement( 'li', { id: errorId, 'innerHTML': emsg.toHTML(), title: 'Click to: ' + self.elem.name } );
      errorElem.style.cursor = 'pointer';
      errorElem.onclick = function()
      {
        var elem = self.form.elements[this.id.replace( self.config.boxErrorPrefix, "" )];

        if ( typeof elem.fields != 'undefined' ) elem = elem.fields[0];

        if ( typeof elem.select != 'undefined' ) elem.select();
        if ( typeof elem.focus != 'undefined' ) elem.focus();
      }
      self.boxError.appendChild( errorElem );
    }
    self.boxError.style.display = "block";
  }

}

//  Processes errors in stack for group error mode
fValidate.prototype.showGroupError = function()
{
  for ( var error, firstElem, i = 0; ( error = this.errors[i] ); i++ )
  {
    if ( i == 0 ) firstElem = error.elem;
    this.elem = error.elem;
    this.showError( error.msg, Boolean( i == ( this.errors.length - 1 ) ) );
  }
  //this.selectFocus( firstElem );
}

//  Reverts any visible error notification upon event
fValidate.prototype.revertError = function( elem )
{
  var errorElem, subelem, i = 0;
  elem = this.setArg( elem, this.elem );
  var isHidden = Boolean( elem.type == 'hidden' );

  if ( ( typeof elem.length != 'undefined' && elem.length > 1 && elem.nodeName != 'SELECT' ) || isHidden )
  {
    if ( isHidden && typeof elem.fields != 'undefined' )
    {
      while( subelem = ( isHidden ) ? elem.fields[i++] : elem.item( i++ ) )
      {
        subelem.className = subelem.revertClass || '';
      }
    }
  }
  else
  {
    elem.className = elem.revertClass || '';
  }
  if ( typeof elem.label != 'undefined' )
  {
    elem.label.className = '';
    elem.label.innerHTML = ( elem.label.original || elem.label.innerHTML );
  }
  if ( typeof this.boxError != 'undefined' )
  {
    if ( typeof this.boxError.normalize != 'undefined' ) this.boxError.normalize();
    if ( errorElem = document.getElementById( this.config.boxErrorPrefix + elem.name ) )
    {
      this.boxError.removeChild( errorElem );
    }
    if ( this.boxError.childNodes.length == 1 ) this.boxError.style.display = "none";
  }
}

//  Adds error to error stack - group error mode
fValidate.prototype.addError = function( error )
{
  this.errors.push( {'elem':this.elem, 'emsg': error} );
}

//  Focus and select elements, if possible
fValidate.prototype.selectFocus = function( elem )
{
  if ( typeof elem.select != 'undefined' ) elem.select();
  if ( typeof elem.focus != 'undefined' )  elem.focus();
}

//  Developer assistance method - shows error if validator/element-type mismatch
fValidate.prototype.typeMismatch = function( type )
{
  var pats = {
    'text': 'text|password|textarea',
    'ta': 'textarea',
    'hidden': 'hidden',
    's1': 'select-one',
    'sm': 'select-multiple',
    'select': 'select-one|select-multiple',
    'rg': 'radio',
    'cb': 'checkbox',
    'file': 'file'
    };
  if ( ! new RegExp( pats[type] ).test( this.elem.type ) )
  {
    this.devError( "Validator/Element type mismatch.\n\nElement: " + this.elem.fName + "\nElement type: " + this.elem.type + "\nType required by validator: " + pats[type].replace( /\|/g, " or " ) );
    this.elem.validated = false;
    return true;
  }
  return false;
}

//  Generic argument setting method
fValidate.prototype.setArg = function( arg, def )
{
  return ( typeof arg == 'undefined' || arg == '' || arg == null ) ? def : arg;
}

//  Blank checker.  Optional string argument for evaluating element other than current
fValidate.prototype.isBlank = function( el )
{
  elem = this.form.elements[el] || this.elem;
  return Boolean( /^\s*$/.test( elem.value ) );
}

//  Throws developer errors
fValidate.prototype.devError = function( msg )
{
  var a = [
    '! WARNING ! -- fValidate developer-assist error\n',
    '----------------------------------------------------------------------------------------------',
    msg,
    '----------------------------------------------------------------------------------------------',
    '\nIf you are not the developer, please contact the website administrator regarding this error.'
    ];
  alert( a.join( "\n" ) );
}
/* Non-fValidate methods *****************************************/

/***************************************************************************/
function stripChars (formObj, bag)
{
  var i;
  var returnString = "";

  // Search through string's characters one by one.
  // If character is not in bag, append to returnString.

  for (i = 0; i < formObj.length; i++) {
    // Check that current character isn't whitespace.
    var c = formObj.charAt(i);

    if (bag.indexOf(c) == -1) {
      returnString += c;
    }
  }

  return returnString;
}

//  For easy creation of DOM nodes
document.createHTMLElement = function( elemName, attribs )
{
  if ( typeof document.createElement == 'undefined' ) return;

  var elem = document.createElement( elemName );

  if ( typeof attribs != 'undefined' )
  {
    for ( var i in attribs )
    {
      switch ( true )
      {
        case ( i == 'text' )  : elem.appendChild( document.createTextNode( attribs[i] ) ); break;
        case ( i == 'class' ) : elem.className = attribs[i]; break;
        default : elem.setAttribute( i, '' ); elem[i] = attribs[i];
      }
    }
  }
  return elem;
}

//  Trims b items from the beginning of the array, e items from the end
Array.prototype.reduce = function( b, e )
{
  var a = new Array();
  var count = 0;
  for ( var i = b; i < this.length - e; i++ )
  {
    a[count++] = this[i];
  }
  return a;
}

//  Returns array as argument-compatible string
Array.prototype.toArgString = function()
{
  var a = new Array();
  for ( var i = 0; ( arg = this[i] ); i++ )
  {
    a.push( "'" + arg + "'" );
  }
  return a.toString();
}

//  Prototype push if missing
//if ( typeof Array.push == 'undefined' )
Array.prototype.push = function()
{
  var arg, i = 0;
  while( arg = arguments[i++] )
  {
    this[this.length] = arg;
  }
  return this.length;
}

//  Returns last item of the array
Array.prototype.last = function()
{
  return this[this.length-1];
}

//  Removes the follow charaters _[] from an elements name for human-reading
String.prototype.format = function()
{
  return this.replace( /\_/g, " ").replace( /\[|\]/g, "" );
}

//  Replaces newline characters with XHTML BR tags
String.prototype.toHTML = function()
{
  return this.replace( /\n/g, "<br />" ).replace( /\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;" );
}

//  Escapes necessary characters for string-generated regular expressions
String.prototype.toPattern = function()
{
  //return this.replace( /([\.\*\+\{\}\(\)\<\>\^\$\\])/g, "\\$1" );
  return this.replace(/([\!\@\#\$\%\^\&\*\(\)\_\+\-\=\{\}\|\[\]\\\:\"\;\'\<\>\?\,\.\/])/g,"\\$1");
}

function puncStr(str) {
  str = str.replace("pipe", "|");
//  return str.replace(/([\\\|\(\)\[\{\^\$\*\+\?\.])/g,"\\$1");
  return str.replace(/([\!\@\#\$\%\^\&\*\(\)\_\+\-\=\{\}\|\[\]\\\:\"\;\'\<\>\?\,\.\/])/g,"\\$1");
  }
//  EOF