/**
 * The validanguage library was written by DrLongGhost in 2008. See attached MIT_License.js
 * and readme.txt for licensing and documentation.  Visit http://www.drlongghost.com/ for updates.
 * 
 * 
 * @namespace  Global validanguage object
 * @author     DrLongGhost
 * @version    0.9.9
 */
var validanguage = {
/**
 * Valid values are 'none', 'dojo', 'jquery', 'prototype', and 'scriptaculous'.
 * @public
 * @default 'none'
 */
useLibrary: 'none',

/**
 * @private
 */
version: '0.9.9',

/**
 * @namespace  validanguage.settings object
 */
settings: {
	/**
	 * Should be set if Error Message is required to be placed in user defined element.
	 * @default false
	 */
	 showErrorCustomArea : false,
	 /**
	 * Should be set if Error Message is required to be placed in user defined element. Array of ids of all elements which are
	 * user-defined for error messages.
	 * @default []
	 */
	 showErrorCustomAreaFields: [],
	/**
     *  Should be set Error Message on new Line or Besides Right side of Form element
     *  By default, validanguage.showError() displays error message on perticular form.
     *  error msg.
     *  @default true
     */
	 isNewLineError : true,
	 /**
     *  Should be set if Error Message is displayed on new Line means above variable is set to true on validanguage.showError()
     *  By default, validanguage.showError() displays error message with setting left margin style on perticular form.
     *  error msg.
     *  @default true
     */
	 marginLeftValue : '0px',
    /**
     *  Should an alert() be shown when a validation fails?
     *  By default, validanguage.showError() and validanguage.hideError() instead place the
     *  error msg underneath the failed field.
     *  @default false
     */
    showAlert: false,
    
    /**
    * Should the target element of a failed validation receive focus when a validation fails?
    * IMPORTANT note regarding showAlert and focusOnError. Do NOT set both of these to true if using onblur validations. Pick either one or the other.
    * When you use both, it is possible to create infinite loops in which a validation failure generates an alert, triggering an onblur,
    * which triggers another validation failure and subsequent alert.   
    * If you aren't using onblur validations at all, you can safely use both.
    * @default false
    */
    focusOnerror: false,
    
    /**
    * When a form is submitted, are all form fields validated, or do we stop once one fails?
    * @default true
    */
    validateAllFieldsOnsubmit: true,
    
    /**
    * Override this to set a global success handlers for all validation results
    * If you want to use only alert messages via showAlert, set this to {} to turn off inline error msgs
    * @default 'validanguage.hideError' 
    */
    onsuccess: 'validanguage.hideError',
    
    /**
    * Override this to set a global error handler for all validation results
    * If you want to use only alert messages via showAlert, set this to {} to turn off inline error msgs
    * @default 'validanguage.showError' 
    */
    onerror: 'validanguage.showError',
    
    /** 
    * Default generic error message
    * @default  'You have entered an invalid entry in the form'
    */
    errorMsg: 'You have entered an invalid entry in the form',
     /** 
    * Default generic error flash message
	* For that you have to include tinybox.js file first
    * @default  'You have entered an invalid entry in the form'
    */
	flashMsg: 'You have some error on your form, please check it out..!',
	/** 
    * Default showFlashMsg is true if you don't need to display it then make it false.
	* For that you have to include tinybox.js file first
    * @default  true
    */
	showFlashMsg: true,
    /** 
    * Default error event flag for detecting event
    * @default  'You have skipped a required field'
    */
	flag : true,
	
    requiredErrorMsg: 'You have skipped a required field',
    
    /** 
    * Default error message for the validateMinlength validation
    * @default  'The indicated field must be at least {!minlength} characters long'
    */
    minlengthErrorMsg: 'The indicated field must be at least {!minlength} characters long',
    
    /** 
    * Default error message for the validateMaxlength validation
    * @default  'The indicated field may not be longer than {!maxlength} characters'
    */
    maxlengthErrorMsg: 'The indicated field may not be longer than {!maxlength} characters',
    
    /** 
    * Default error message for the validateCharacters function
    * @default  'You have entered invalid characters'
    */
    characterValidationErrorMsg: 'You have entered invalid characters',
    
    /**
    * Class name used in showError() to assign to the DIVs
    * which are created to show the inline error msgs.
    * @default  'vdError'
    */
    onErrorClassName: 'vdError',
    
    /**
    * Class name used in hideError() to assign to a DIV
    * which was created to show an inline error msgs which is then removed.
    * @default  'vdNoError'
    */
    noErrorClassName: 'vdNoError',
    
    /**
     * Class name used in hideError() to assign to a form field which passes validation
     * @default 'passedField'
     */
    passedFieldClassName: 'passedField',
    
    /**
     * Class name used in showError() to assign to a form field which fails validation
     * @default 'failedField'
     */
    failedFieldClassName: 'failedField',
    
    /**
    * Used to make the ID used in hideError() to assign to the SPAN element inside the vdError
    * DIV.  The errorMsgSpanSuffix is appended to the end of the form field's ID to make the SPAN ID.
    * If a SPAN with this ID already exists in the DOM, it will be used. If it doesn't exist, one will
    * be created dynamically.
    * @default  '_errorMsg'
    */
    errorMsgSpanSuffix: '_errorMsg',
	
	hintMsgSpanSuffix: '_hintMsg',
    
    /** 
    * To display a combined list of all fields which failed validation in addition to the
    * inline error msgs, set showFailedFields to true.  The fields will be listed using the
    * "field" attribute (or ID if field is not available).
    * @default  false
    */
    showFailedFields: false,
    
    /** 
    * The text specified in errorListText will be placed at the top of the errorDiv generated
    * by the showFailedFields option in showError().
    * @default  '<strong>Please correct the following fields:</strong>'
    */
    errorListText: '<strong>Please correct the following fields:</strong>',
    
    /** 
    * Specifies the ID to be assigned to the DIV used for the showFailedFields option in showError().
    * If a DIV with this ID exists in the DOM, it will be used. If it doesn't exist, one will
    * be created dynamically.
    * @default  'vdErrorDiv'
    */
    errorDivId: 'vdErrorDiv',
    
    /** 
    * Specifies the ID to be assigned to the UL used for the showFailedFields option in showError().
    * @default  'vdErrorList'
    */
    errorListId: 'vdErrorList',
    
    /** 
    * Used to make the ID used for the showFailedFields option in showError().
    * The errorListItemSuffix is appended to the end of the form field's ID to make the ID for the LI item.
    * @default  '_vd_li'
    */
    errorListItemSuffix: '_vd_li',
    
    /**
    * Determines the ID of the DIV created in the showSubmitMessage() function used to
    * replace a form's submit button once the form has been submitted.
    * @default  'vdSubmitMessage'
    */
    showSubmitMessageId: 'vdSubmitMessage',
    
    /**
    * Determines the text used by the showSubmitMessage() function  which is used
    * replace a form's submit button once the form has been submitted.  If desired, you can include HTML
    * or IMG tags instead of the default text.
    * @default  'Loading'
    */
    showSubmitMessageMessage: 'Loading',
    
    /**
    * This array is used in the validateRequired function to determine whether a select box
    * has been left on the default, "empty" option.  Add/Remove from this array as needed.
    * @default  ['&nbsp;','0',' ','']
    */
    emptyOptionElements: ['&nbsp;','0',' ',''],
    
    /**
    * If a validation is supplied without any event handlers, how should it be treated in loadElAPI()?
    * This setting also affects the behavior of the required=true and maxlength/minlength shortcuts.
    * @default  ['submit']
    */
    defaultValidationHandlers: ['blur','submit'],
    
    /**
    * If a transformation is supplied without any event handlers, how should it be treated in loadElAPI()?
    * @default  ['blur']
    */
    defaultTransformationHandlers: ['blur'],
    
    /**
     * Should any validanguage.toggle() transformations which are defined for form fields on the
     * page be automatically called when the page has finished loading.
     * @default true
     */
    callToggleTransformationsOnload: true,
    
    /**
     * Should the toggle visibility API in validanguage.toggle() default to "hidden" if a given target
     * does not satisfy any provided "visible" conditions?  If you set this to false, you will need to
     * explicitly provide the desired "hidden" conditions.
     * @default true
     */
    toggleVisibilityDefaultsToHidden: true,
    
    /**
     * How long are ajax requests allowed to run before they are timed out?
     * Especially useful for ajax requests used as part of a form submission.
     * @default 30
     */
    ajaxTimeout: 30,
    
    /**
     * When an ajax request is sent as part of a form submit validation routine 
     * and it times out, should the form submit by default?
     * This defaults to true to help guard against programming errors blocking
     * users from being able to submit a form due to a broken ajax validation.
     * @default true
     */
    submitFormOnExpiredAjax: true,
    
    /**
     * Should ajax lookups be cached?  If set to true, any ajax calls for the same value
     * on the same form field will use the cached results from the earlier lookup.
     * @default true
     */
    cacheAjaxLookups: true,
    
    /**
    * Should the HTML document be scanned for validanguage comment tags?
    * Set this to false if you arent using the comment API for better performance.
    * @default  true
    */
    loadCommentAPI: true,
    
    /**
    * Determines the delimeter used in the loadCommentAPI() function to split up each
    * comment into multiple validanguage tags.
    * You probably want to keep this as "\n" to be safe, but if you want to be allowed
    * to use carriage returns inside validanguage comment tags, you can set this to
    * "/>" if you are careful to always close your validanguage tags
    * @default  "\n"
    */
    commentDelimiter: "\n",
    
    /**
    * Color for the textbox to flash when invalid input is entered. The default is light red.
    * Set this to empty to turn flashing off.
    * @default  '#FF6666'
    */
    validationErrorColor : '#FF6666',
    
    /**
    * Normal color of the textbox. The default is empty. Used in conjunction with validationErrorColor
    * to make the textboxes flash.
    * @default  ''
    */
    normalTextboxColor : '',
    
    /**
    * Amount of time the text box flashes the validationErrorColor. The default is 100ms
    * @default  100
    */
    timeDelay : 100,
    
    /**
    * Typing delay for the ontyping event. This is the amount of time between keystrokes
    * that must elapse before the event fires.  The default is just over 1 second.
    * @default  1100
    */
    typingDelay: 25,
    
    /**
    * Should the validateRequiredAlternatives function be assigned onclick to radio buttons
    * and checkboxes named as "requiredAlternatives"?  Setting this to true ensures that
    * checking/unchecking a radio button or checkbox will correctly call showError/hideError.
    * @default  true
    */
    validateRequiredAlternativesOnclick: true,
    
    /**
    * Defines the default behavior of the validateRegex function.
    * Is a match against the regex an error or a success?
    * @default  false
    */
    errorOnMatch: false,
    
    /**
    * Override this to setup a function to run after all validanguage form fields have
    * been intialized inside the populate() function.  The default is an empty function.
    * @default  function() { }
    */
    onload: function() { },
    
    //dummy field I put here so the onload above will have a comma after it
    foo: ''
},

//PRIVATE PROGRAM VARIABLES
ajaxLookup:             {},   //hash table to store details on dispatched ajax requests
alertCounter:           true, //this counter prevents infinite loops from being created between alerts() and onblur handlers
debug:                  false, //enable debugging msgs in Ajax code?
el:                     {},
fields:                 {},
forcedSubmission:       false, //enables a form submission to bypass all validation
forms:                  {},
formLookup:             {},  //hash table to map form element IDs to the ID of the parent form.
ignoreTheseKeyCodes:    [8,37,38,39,40,46], //keycodes that are always permitted during keypress suppression
requiredAlternatives:   [],  //hash table used to store requiredAlternatives associations
supportedEvents:        ['blur','change','keypress','keyup','keydown','submit','click','typing','focus'],
supportedEventHandlers: ['onblur','onchange','onkeypress','onkeyup','onkeydown','onsubmit','onclick','ontyping','onfocus'],
typingDelay:            [],  //hash table to store ontyping timeouts
vdLoaded:               false, //changed to true after populate() has completed

/**
* Generic cross-browser addEvent() function.
* 
* @param {Object} Object to receive the event
* @param {Object} Event type
* @param {Object} Function to be called
*/
addEvent: function(obj, event, func){
    if (obj.addEventListener) {
        obj.addEventListener(event, func, false);
        return true;
    } else if (obj.attachEvent){
        var newEvent = obj.attachEvent("on"+event, func);
        return newEvent;
    }
}, //close addEvent

/**
* Reassigns the validanguage.addEvent function, if an external library is being used.
*/
addEventInit: function() {
    // overwrite validanguage.addEvent()
    switch ( this.useLibrary ) {
        case 'prototype':
        case 'scriptaculous':
            this.addEvent = function(obj, evtHandler, func) {
                Event.observe(obj, evtHandler, func);
            }
            break;
        case 'dojo':
            this.addEvent = function(obj, evtHandler, func) {
                dojo.connect(obj, 'on'+evtHandler, func);
            }
            break;
        case 'jquery':
            this.addEvent = function(obj, evtHandler, func) {
                if (obj == window) {
                    jQuery(document).ready(func);
                } else {
                    var selector = '#' + obj.id;
                    jQuery(selector).bind(evtHandler, func);
                }
            }
            break;      
    }
}, //close addEventInit

/**
* This function wraps multiple validanguage.el.elemId.validations event handlers
* and transformations within a single wrapper to call all loaded validations/transformations
* and exit as soon as a validation returns false.
* 
* @param {Object} Form element object
* @param {string} eventType, such as "blur" or "keydown"
* @param {integer} validationsCounter, denotes the array index of this item in 
*                       validanguage.el.elemId.validations
*/
addOrCreateValidationWrapper: function( Obj, eventType, validationsCounter ) {
    var id = Obj.id;
    if (eventType == 'submit') {
        if (this.empty(validationsCounter)) return; // exit early for onsubmit transformations
        var formId = validanguage.formLookup[id];
        if (typeof formId == 'number') {
            var form = document.forms[formId];
        } else {
            var form = document.getElementById(formId);
        }
        if (typeof validanguage.forms[formId].validations == 'undefined') {
            validanguage.forms[formId].validations = [];
            this.addEvent(form, eventType, function(e) {
                var evt = e || window.evt;
                var result = validanguage.validationWrapper(e);
                if (result == false) {
                    evt.returnValue = false; //IE
                    if (evt.preventDefault) evt.preventDefault(); //Everyone else
                    return false;
                } else {
                    return true;
                }
            });
        }
        //add the element and validationsCounter to the list of onsubmit validations for the parent form
        validanguage.forms[formId].validations[validanguage.forms[formId].validations.length] = { element: Obj, validationsCounter: validationsCounter };
    } else {

        if( typeof validanguage.el[id].handlers == 'undefined' ) validanguage.el[id].handlers = {};
        if( typeof validanguage.el[id].handlers[eventType] == 'undefined' ) {
            validanguage.el[id].handlers[eventType] = [];
            if( eventType == 'typing') {
                this.addEvent(Obj, 'keyup', function(e){ validanguage.validationWrapper(e, 'typingTimeout'); });               
            } else {
                this.addEvent(Obj, eventType, function(e){ validanguage.validationWrapper(e); });               
            }
        }
        //add validationsCounter to the list of validations for this object/eventType combo
        validanguage.el[id].handlers[eventType][validanguage.el[id].handlers[eventType].length] = validationsCounter;
    }
},

/**
* This function is used to either load a new validation for a form field, or to
* reactivate a validation previously removed with the removeValidation() method.
* 
* NOTE: When adding a new validation, you will need to have previously inserted
* all the relevant details about the validation in the validanguage.el.formField
* object.
* 
* @param {String} elemId
* @param {String/Array} eventTypes
* @param {String/Array/Function} validationNames
*/
addValidation: function ( elemId, eventTypes, validationNames ) {
    if( typeof validationNames[0]=='undefined' ) validationNames = [ validationNames ];
    if( typeof eventTypes=='string' ) eventTypes = [ eventTypes ];

    var vals = this.el[elemId].validations;
    for (var i = vals.length - 1; i > -1; i--) {
        if ( validationNames[0] == '*' || this.inArray(vals[i].name, validationNames) ) {
            for( var j=eventTypes.length-1; j>-1; j--) {
                this.addOrCreateValidationWrapper(document.getElementById(elemId), eventTypes[j]);
            }
        }
    }
},

/**
* Very simple AJAX function
* @param {String} url
* @param {Function} callback
*/
ajax: function( url, callback ) {
    if(window.ActiveXObject){      
        var ajaxObj = new ActiveXObject("Microsoft.XMLHTTP");      
    } else if(window.XMLHttpRequest){      
        var ajaxObj = new XMLHttpRequest();      
    }
    
    ajaxObj.open("POST", url, true);
    ajaxObj.onreadystatechange = function() {
        if(ajaxObj.readyState==4) {
            callback(ajaxObj.responseText);
        }
    };
    ajaxObj.send(null);
}, //close ajax

/**
* Initializes validanguage.ajax as browser-specific
*/
ajaxInit: function() {
    switch ( this.useLibrary ) {
        //reassign validanguage.ajax
        case 'prototype':
        case 'scriptaculous':
            this.ajax = function(url, callback, options) {
                if (validanguage.empty(options)) options = {};
                options.onSuccess = callback;
                new Ajax.Request(url, options);
            }
            break;
        case 'dojo':
            this.ajax = function(url, callback, options) {
                if (validanguage.empty(options)) options = {};
                options.url = url;
                options.handle = callback;
                dojo.xhrGet(options);
            }
            break;
        case 'jquery':
            this.ajax = function(url, callback, options) {
                if (validanguage.empty(options)) options = {};
                options.url = url;
                options.success = callback;
                jQuery.ajax(options);
            }
            break;
    }
},

/**
 * This function is called by setInterval and is used to check
 * whether or not all ajax callbacks for a form have returned.
 * @param {String} ID or index of the form
 * @param {String} Event Type
 */
ajaxValidationWrapper: function( form, eventType ) {
    var nodeType = (typeof this.formLookup[form]=='undefined') ? 'forms' : 'fields';

    if (this.empty(validanguage[nodeType][form][eventType].dispatchedAjax)) {
        window.clearInterval(validanguage[nodeType][form][eventType].ajaxInterval);
    } else {
        for (var id in validanguage[nodeType][form][eventType].dispatchedAjax) {
            if (typeof time == 'function') continue;
            if (validanguage[nodeType][form][eventType].dispatchedAjax[id] + (validanguage.settings.ajaxTimeout*1000) < new Date().getTime()) {
                //abort requests older than X seconds old
                if (this.debug) console.log('Aborting request...');
                delete this[nodeType][form][eventType].failedValidations[id];
                if (this.empty(this[nodeType][form][eventType].failedValidations)) this[nodeType][form][eventType].failedValidations = 'callManually';
                delete this[nodeType][form][eventType].dispatchedAjax;
                if (nodeType=='forms' && this.validateForm(form).result === true) {
                    if (this.debug) console.log('Request Aborted.');
                    if (this.settings.submitFormOnExpiredAjax) {
                        this.forcedSubmission = true;
                        document.getElementById(form).submit();
                    }
                }
                // If a non-"Form Submit" ajax request is aborted, this is currently handled by doing nothing...
                return;
            }
        }
    }
},

/**
* This function loads all the validanguage.toggle() rules which
* are defined for a form following document.onload()
*/
callToggleTransformationsOnload: function() {
    if (this.settings.callToggleTransformationsOnload) {
        for (var id in this.el) {
            if (typeof this.el[id].transformations != 'undefined') {
                for (var i = this.el[id].transformations.length - 1; i > -1; i--) {
                    if (typeof this.el[id].transformations[i].name == 'undefined') 
                        continue;
                    var funcString = this.el[id].transformations[i].name;
                    if (typeof funcString == 'string' && funcString.indexOf('validanguage.toggle') > -1) {
                        var transformations = this.resolveArray(funcString, 'function');
                        var j = transformations.length;
                        for (var k = 0; k < j; k++) {
                            transformations[k].call(document.getElementById(id));
                        }
                    }
                }
            }
        }
    }    
},

/**
* Combines 2 node lists into 1
* @param {Object} obj1
* @param {Object} obj2
*/
concatCollection: function(obj1,obj2) {
    var i;
    var arr = new Array();
    var len1 = obj1.length;
    var len2 = obj2.length;
    for (i=0; i<len1; i++) {
        arr.push(obj1[i]);
    }
    for (i=0; i<len2; i++) {
        arr.push(obj2[i]);
    }
    return arr;
},

/**
* Emulates PHP's empty() function. For convenience, you can specify whether
* boolean false is considered empty. Defaults to false is NOT empty.
* Ignores functions.
* 
* @param {Object} testVar
* @param {bool} falseIsEmpty
*/
empty: function ( testVar, falseIsEmpty ) {
    if( testVar == null || testVar == undefined || testVar == NaN || testVar === 'null' || (testVar =='' && typeof testVar == 'string') ) return true;
    if( falseIsEmpty==true && testVar==false) {
         return true;
    }
    if(typeof testVar == 'object') {
        for (var i in testVar) {
            if( typeof testVar[i] == 'function' ) continue;
            
            // Prevent infinite recursion in Safari 4
            var recurse = true;
            for (var j in testVar[i]) {
                if (testVar[i][j] === testVar) recurse = false;
            }
            
            if(recurse && validanguage.empty(testVar[i], falseIsEmpty)==false ) {
                return false; 
            }
        }
        return true;
    } else {
       return false;
    }
},

/**
 * This function iterates thru the ajaxLookup array and returns the
 * index number corresponding to the passed counter value
 * @param {String} element ID
 * @param {Object} counter
 */
getAjaxLookupIndex: function(id, counter) {
    for( var i=this.ajaxLookup[id].length-1; i>-1; i--) {
        if (this.ajaxLookup[id][i].counter==counter) return i;
    }
    return 0;
}, //close getAjaxLookupIndex

/**
* Fetches all comment nodes in the passed form node and returns them in a node list
* Doesnt work in konqueror, since konqueror strips all comments from the DOM
* 
* @param {Containing Node} el
*/
getComments: function(el) {
    if (!el) el = document.documentElement;
    var comments = new Array();
    var length = el.childNodes.length;
    for (var c = 0; c < length; c++) {
        if (el.childNodes[c].nodeType == 8) {
            comments[comments.length] = el.childNodes[c];
        } else if (el.childNodes[c].nodeType == 1) {
            comments = comments.concat(this.getComments(el.childNodes[c]));
        }
    }
    return comments;
}, //close getComments

/**
* Helper function used by validateDate() and validateTimestamp().
* @param {Object} options object provided by the user to validateDate() or validateTimestamp().
* @param {Object} defaults which should be used.  Used to allow validateDate() and validateTimestamp()
* to have different default dateOrder values.
*/
getDateTimeDefaultOptions: function ( options, defaults ) {
    if( options==null ) options = {};
    
    // Date options
    if( typeof options.dateOrder=='undefined' ) options.dateOrder=defaults.dateOrder;
    options.dateOrder = options.dateOrder.toLowerCase();
    if( typeof options.allowedDelimiters=='undefined' || typeof options.allowedDelimiters!='string' ) options['allowedDelimiters'] = './-';
    if( typeof options.twoDigitYearsAllowed=='undefined' ) options.twoDigitYearsAllowed = false;
    if( typeof options.oneDigitDaysAndMonthsAllowed=='undefined' ) options.oneDigitDaysAndMonthsAllowed = true;
    if( typeof options.maxYear=='undefined' ) options.maxYear = new Date().getFullYear() + 15;
    if( typeof options.minYear=='undefined' ) options.minYear = 1900;
    if( typeof options.rejectDatesInTheFuture=='undefined' ) options.rejectDatesInTheFuture = false;
    if( typeof options.rejectDatesInThePast=='undefined' ) options.rejectDatesInThePast = false;
    
    // Time options
    if( typeof options.timeIsRequired=='undefined' ) options.timeIsRequired = false;
    if( typeof options.timeUnits=='undefined' ) options.timeUnits = 'hms';
    if( typeof options.microsecondPrecision=='undefined' ) options.microsecondPrecision = 6;
    return options;
}, //close getDateTimeDefaultOptions

/**
* This function checks for a given setting in increasing specificity
* within the validanguage.forms[formId].settings object, and within the passed
* validanguage.el objects
* 
* @param {string} Name of the setting to be retrieved
* @param {string} ID of the form field object being validated
* @param {Object} validanguage.el.objId.validations[index] object
*/
getElSetting: function( setting, id, validationObj ) {
    var formSetting = this.getFormSettings(id);
    var retVal = formSetting[setting]; //global setting
    if( typeof validationObj!='undefined' && typeof validationObj[setting] != 'undefined' ) {
        retVal = validationObj[setting];   
    } else if( typeof this.el[id][setting] != 'undefined' ) {
        retVal = this.el[id][setting];
    }
    return retVal;
},

/**
* This function returns the validanguage.form[formId].setting object for the passed element ID
* @param  {string or Node}  id of the input field or input node
* @return {Object}  settings object
*/
getFormSettings: function(id) {	
    var formName = ( document.getElementById(id).nodeName.toLowerCase()=='form' ) ? 
        id : this.formLookup[id];	
    return this.forms[formName].settings;
},

/**
* This function parses the passed comment to retrieve the indicated setting
* 
* @param  {String}  Name of the setting to retrieve / needle
* @param  {String}  Full text of the HTML comment   / haystack
* @return {String}  The value of the requested setting
*/
getSettingFromComment: function( setting, comment ) {  
    var needle = ' '+setting+'='; 
    var startPos = comment.indexOf(needle);
    if( startPos == -1) return null;
    var delimiterPos = (startPos*1) + (needle.length*1);
    var delimeter = '\\' + comment.charAt(delimiterPos);
    var Regex = needle+delimeter+'(.+?)'+delimeter;
    var myreg = new RegExp(Regex);
    var thisMatch = myreg.exec(comment, 'gi');
    if (thisMatch == null) {
        return null; //no match
    } else if (thisMatch[1]) {
        //Convert booleans. I hope this doesnt screw anyone later....
        if(thisMatch[1]=='true') thisMatch[1]=true;
        if(thisMatch[1]=='false') thisMatch[1]=false;
        return thisMatch[1];
    }
}, //close getSettingFromComment

/**
* This function hides the div containing the validanguage error messages for
* failed validations
*/
hideError: function() {
	var settings = validanguage.getFormSettings(this.id);
    var errorDisplay = document.getElementById(this.id + settings.errorMsgSpanSuffix);
    if (errorDisplay != null) {
        errorDisplay.innerHTML = '';
        var errorDiv = errorDisplay.parentNode;
    
        errorDiv.style.display = 'none';
        errorDiv.className = settings.noErrorClassName;
    }
    if (! this.className.match(validanguage.settings.passedFieldClassName)) this.className += ' '+validanguage.settings.passedFieldClassName;
    if (this.className.match(validanguage.settings.failedFieldClassName)) this.className = this.className.replace(validanguage.settings.failedFieldClassName,'');
    
    //Do we need to remove any vd_li items?
    if( !settings.showFailedFields ) return;
    if( document.getElementById(this.id + settings.errorListItemSuffix) != null ) {
        var errorList = document.getElementById(settings.errorListId);
        errorList.removeChild( document.getElementById(this.id + settings.errorListItemSuffix) );
        if( errorList.getElementsByTagName('LI').length==0 )
            document.getElementById(settings.errorDivId).style.display='none';
    }
}, //close hideError

/**
* Determines whether the passed item is present in the array or object.
* 
* @param {Object} needle
* @param {Object} haystack
*/
inArray: function( needle, haystack ) {
    for( var i=haystack.length-1; i>-1; i-- ){
        if( haystack[i]===needle ) return true;
    }
    return false;
},

/**
* This function searches settingsHaystack for all variables defined in the settingsNeedles
* array, and if they are located, they are copied over to the settingsTarget
* 
* @param {Object} settingsHaystack -- Object location to be searched for settings
* @param {Array}  settingsNeedles  -- Array of settings to be checked
* @param {Object} settingsTarget   -- Object location where any defined settings should be copied to
* @param {String} constrainType    -- Optional type constraint
*/
inheritIfDefined: function ( settingsHaystack, settingsNeedles, settingsTarget, constrainType ) {
    if( typeof settingsNeedles.length == 'undefined' ) return false;
    for( var i=settingsNeedles.length-1;i>-1;i--) {
        if ( typeof settingsHaystack[settingsNeedles[i]]!='undefined' &&
           ( this.empty(constrainType) || typeof settingsHaystack[settingsNeedles[i]]==constrainType )
        ) {
            settingsTarget[settingsNeedles[i]] = settingsHaystack[settingsNeedles[i]];
        }
    }
},

/**
* Initialization function for validanguage. Adds the onload hook
* which fires off the populate() method to add all the other event
* handlers
*/
init: function() {
    if (typeof validanguageLibrary!='undefined') this.useLibrary = validanguageLibrary;
    this.addEventInit();
    this.ajaxInit();
    this.addEvent(window, 'load', function() {
        validanguage.populate.call(validanguage);
    });
},

/**
* Function to insert 1 Node after another in the DOM. If the referenceNode
* is a label, this function will use the nextSibling instead
* 
* @param {Node} nodeToAdd
* @param {Node} referenceNode
*/
insertAfter: function (nodeToAdd, referenceNode ) {
    if (referenceNode.nextSibling) {
        if (referenceNode.nextSibling.nodeName.toLowerCase() == 'label') {
            referenceNode.parentNode.insertBefore(nodeToAdd, referenceNode.nextSibling.nextSibling);
        } else {
           referenceNode.parentNode.insertBefore(nodeToAdd, referenceNode.nextSibling);
        }
    } else {
        referenceNode.parentNode.appendChild(nodeToAdd);
    }
}, //close insertAfter

/**
 * This function examines the ajaxLookup array to determine whether or not the
 * specified ajaxCounter pertains to the most recent ajax call for that form field.
 * @param {String} formFieldId
 * @param {Integer} ajaxCounter
 */
isExpiredAjax: function (formFieldId, ajaxCounter) {
    if (this.empty(formFieldId) || this.empty(ajaxCounter)) return false;
    var h     = this.getAjaxLookupIndex(formFieldId, ajaxCounter);
    var arr   = this.ajaxLookup[formFieldId];
    var event = arr[h].eventType;
    
    for (var i = arr.length-1; i > 0; i--) {
        if (event == arr[i].eventType) {
            if (arr[i].counter == ajaxCounter) {
                return false;
            } else {
                return true;
            }
        }
    }
    return false;
},

/**
* This function parses all comments in the current document, looking for
* the comment-based API and converts any validanguage statements it
* finds into the element/json-based API for further processing.
* 
* @param  {Array}  For konqueror, we pass this function an Array with all
*                  the comments (retrieved via AJAX)
*                  For all other browsers, konquerorComments is undefined and
*                  we retrieve the comments normally via the DOM
*/
loadCommentAPI: function( konquerorComments ) {
    var supportedSettings = ['mode','expression','suppress','onsubmit','onblur','onchange',
        'onkeypress','onkeyup','onkeydown','onclick', 'ontyping','onfocus',
        'errorMsg','onerror','onsuccess','focusOnError',
        'showAlert','required','requiredAlternatives',
        'maxlength','minlength','regex','field',
        'errorOnMatch','modifiers','transformations','validations'];

    var allComments = (this.empty(konquerorComments)) ? this.getComments() : konquerorComments;
    var length = allComments.length;
    for (var j=0; j<length; j++) {

    var singleComment = (this.empty(konquerorComments)) ? allComments[j].nodeValue : allComments[j];
    var tagArray = singleComment.split(validanguage.settings.commentDelimiter); 
    var tagArrayLength = tagArray.length;

    for (var a=0; a<tagArrayLength; a++) {
        var commentText = tagArray[a];
        commentText = commentText.replace(/\n/g,' ');
        commentText = commentText.replace(/\r/g,' ');
        var isValidanguageRegEx = /<validanguage/i;
        if (isValidanguageRegEx.test(commentText)) {
            //get the targets
            var targets = this.getSettingFromComment('target', commentText);
            var settings = []; //reset settings
            if (this.empty(targets, true)) 
                continue;
            targets = this.resolveArray(targets, 'string');
            for (var k = supportedSettings.length - 1; k > -1; k--) {
                var tempSetting = this.getSettingFromComment(supportedSettings[k], commentText);
                if (!(tempSetting == null || (typeof tempSetting == 'string' && tempSetting == '') ))
                    settings[supportedSettings[k]] = tempSetting;
            }

            //iterate thru our targets and assign the settings
            k = targets.length;
            for (var l = 0; l < k; l++) {
                var id = targets[l];
                var obj = document.getElementById(id);
                if (typeof this.el[id] == 'undefined' || obj == null) 
                    this.el[id] = {};

                /** CHARACTER VALIDATION **/
                //start keypressValidation
                if (typeof settings.expression != 'undefined') {
                    this.el[id].characters = {};
                    this.inheritIfDefined(settings, ['expression','errorMsg','mode','suppress','onerror','onsuccess'], this.el[id].characters);
                    this.inheritIfDefined(settings, this.supportedEventHandlers, this.el[id].characters);
                }
                //close keypressValidation

                /** REGEX **/
                if (typeof settings.regex != 'undefined') {
                    this.el[id].regex = { expression: settings.regex };
                    this.inheritIfDefined(settings, ['errorOnMatch','modifiers'], this.el[id].regex);
                    this.inheritIfDefined(settings, this.supportedEventHandlers, this.el[id].regex);
                }

                /** MISC SETTINGS **/
                // Only inherit event handlers that are non-boolean transformations
                this.inheritIfDefined(settings, this.supportedEventHandlers, this.el[id], 'string');
                this.inheritIfDefined(settings, ['minlength','maxlength','requiredAlternatives','required','focusOnError','showAlert',
                    'onsuccess','onerror','errorMsg'], this.el[id]);                  
                if (typeof settings.minlength != 'undefined') {
                    this.el[id].minlengthEvents = {};
                    this.inheritIfDefined(settings, this.supportedEventHandlers, this.el[id].minlengthEvents);
                }
                if (typeof settings.maxlength != 'undefined') {
                    this.el[id].maxlengthEvents = {};
                    this.inheritIfDefined(settings, this.supportedEventHandlers, this.el[id].maxlengthEvents);
                }
                if (typeof settings.required != 'undefined') {
                    this.el[id].requiredEvents = {};
                    this.inheritIfDefined(settings, this.supportedEventHandlers, this.el[id].requiredEvents);
                }

                /** VALIDATIONS AND TRANSFORMATIONS **/
                if (typeof this.el[id].validations == 'undefined') this.el[id].validations = [];
                if (typeof this.el[id].transformations == 'undefined') this.el[id].transformations = [];
                var functionModifiers = ['focusOnError','showAlert','onsuccess','onerror','errorMsg','isAjax'];

                //Load validations
                if( typeof settings.validations != 'undefined' && !this.empty(settings.validations) ) {
                    this.el[id].validations[this.el[id].validations.length] = {};
                    this.el[id].validations[this.el[id].validations.length-1].name = settings.validations;
                    this.inheritIfDefined(settings, this.supportedEventHandlers, this.el[id].validations[this.el[id].validations.length-1]);
                    this.inheritIfDefined(settings, functionModifiers, this.el[id].validations[this.el[id].validations.length-1]);
                }
                //Load transformations
                if( typeof settings.transformations != 'undefined' && !this.empty(settings.transformations) ) {
                    this.el[id].transformations[this.el[id].transformations.length] = {};
                    this.el[id].transformations[this.el[id].transformations.length-1].name = settings.transformations;
                    this.inheritIfDefined(settings, this.supportedEventHandlers, this.el[id].transformations[this.el[id].transformations.length-1]);
                }

                } // foreach (targets) 
            } // close if(validanguage_comment)
        } // close tagArray loop
    } // close allComments loop         
}, //close loadCommentAPI

/**
 * This function parses the validanguage.el object to load all the
 * form-element-specific validation settings which the end user has defined
 * via the Object-based API
 */
loadElAPI: function() {
    for( var elem in this.el ) {  //for each element....
        //skip to the next if it's not an element ID
        try { if( typeof document.getElementById(elem) == undefined || this.empty(document.getElementById(elem)) ) continue; } catch(e) { continue; }
        
        var Obj = document.getElementById(elem);
        var settings = validanguage.getFormSettings(elem);
        if (typeof this.el[elem].validations == 'undefined') this.el[elem].validations = [];
        if (typeof this.el[elem].field == 'undefined') this.el[elem].field = elem;

        /** REQUIRED **/         
        if (typeof this.el[elem].required != 'undefined' && this.el[elem].required==true) {
            this.el[elem].validations[this.el[elem].validations.length] = {};
            this.el[elem].validations[this.el[elem].validations.length-1].name = 'validanguage.validateRequired';
            this.el[elem].validations[this.el[elem].validations.length-1].errorMsg = (typeof this.el[elem].errorMsg=='undefined') ? settings.requiredErrorMsg : this.el[elem].errorMsg;
            this.inheritIfDefined( this.el[elem], this.supportedEventHandlers, this.el[elem].validations[this.el[elem].validations.length-1] );
    
            //If specific requiredEvents are provided, use those instead of the element level event handlers
            if( typeof this.el[elem]['requiredEvents']!='undefined') this.inheritIfDefined( this.el[elem]['requiredEvents'], this.supportedEventHandlers, this.el[elem].validations[this.el[elem].validations.length-1] );
    
            //We need to call the validateRequiredAlternatives function when a requiredAlternative is clicked
            if(settings.validateRequiredAlternativesOnclick==true && typeof this.el[elem].requiredAlternatives != 'undefined' ) {
                var onsuccessFuncs = (typeof this.el[elem].onsuccess!='undefined') ? this.el[elem].onsuccess : settings.onsuccess;
                var onerrorFuncs = (typeof this.el[elem].onerror!='undefined') ? this.el[elem].onerror : settings.onerror;
                var alts = this.resolveArray(this.el[elem].requiredAlternatives,'string');
                for( var y=alts.length-1; y>-1; y--) {
                    this.requiredAlternatives[alts[y]] = {};
                    if( !((typeof document.getElementById(alts[y]).type != 'undefined') && (document.getElementById(alts[y]).type=='checkbox'||document.getElementById(alts[y]).type=='radio')) ) continue;
                    this.requiredAlternatives[alts[y]].onsuccess = onsuccessFuncs;
                    this.requiredAlternatives[alts[y]].onerror = onerrorFuncs;
                    this.requiredAlternatives[alts[y]].errorMsg = (typeof this.el[elem].errorMsg=='undefined') ? settings.requiredErrorMsg : this.el[elem].errorMsg;
                    this.requiredAlternatives[alts[y]].parentId = elem;
                    this.addEvent( document.getElementById(alts[y]), 'click', function(e) { validanguage.validateRequiredAlternatives(e); } );
                }
            }
        }

        /** REGEX **/
        if (typeof this.el[elem].regex != 'undefined') {
            this.el[elem].validations[this.el[elem].validations.length] = {};
            this.el[elem].validations[this.el[elem].validations.length - 1].name = 'validanguage.validateRegex';
            var errorMsg = (typeof this.el[elem].errorMsg == 'undefined') ? settings.errorMsg : this.el[elem].errorMsg;
            if(typeof this.el[elem].regex.errorMsg != 'undefined') errorMsg = this.el[elem].regex.errorMsg
               this.el[elem].validations[this.el[elem].validations.length - 1].errorMsg = errorMsg;
            this.inheritIfDefined(this.el[elem], this.supportedEventHandlers, this.el[elem].validations[this.el[elem].validations.length - 1]);
            this.inheritIfDefined(this.el[elem].regex, this.supportedEventHandlers, this.el[elem].validations[this.el[elem].validations.length - 1]);
            if(typeof this.el[elem].regex.errorOnMatch=='undefined') this.el[elem].regex.errorOnMatch=settings.errorOnMatch;
        }

        /** MAXLENGTH **/
        if (typeof this.el[elem].maxlength != 'undefined') {
            this.el[elem].validations[this.el[elem].validations.length] = {};
            this.el[elem].validations[this.el[elem].validations.length-1].name = 'validanguage.validateMaxlength';
            this.el[elem].validations[this.el[elem].validations.length-1].errorMsg = settings.maxlengthErrorMsg.replace('{!maxlength}',this.el[elem].maxlength);
            //If specific maxlengthEvents are provided, use those instead of the element level event handlers
            if( typeof this.el[elem]['maxlengthEvents']!='undefined') this.inheritIfDefined( this.el[elem]['maxlengthEvents'], this.supportedEventHandlers, this.el[elem].validations[this.el[elem].validations.length-1] );
        }

        /** MINLENGTH **/
        if (typeof this.el[elem].minlength != 'undefined') {
            this.el[elem].validations[this.el[elem].validations.length] = {};
            this.el[elem].validations[this.el[elem].validations.length-1].name = 'validanguage.validateMinlength';
            this.el[elem].validations[this.el[elem].validations.length-1].errorMsg = settings.minlengthErrorMsg.replace('{!minlength}',this.el[elem].minlength);
            //If specific minlengthEvents are provided, use those instead of the element level event handlers
            if( typeof this.el[elem]['minlengthEvents']!='undefined') this.inheritIfDefined( this.el[elem]['minlengthEvents'], this.supportedEventHandlers, this.el[elem].validations[this.el[elem].validations.length-1] );
        }

        //start keypressValidation
        /** CHARACTERS **/
        if (typeof this.el[elem].characters != 'undefined' &&
            typeof this.el[elem].characters.mode != 'undefined' &&
            typeof this.el[elem].characters.expression != 'undefined'
        ) {

            //supported shortcuts
            var expression = this.el[elem].characters.expression;
            expression = expression.replace('alphaUpper','ABCDEFGHIJKLMNOPQRSTUVWXYZ');
            expression = expression.replace('alphaLower','abcdefghijklmnopqrstuvwxyz');
            expression = expression.replace('alpha','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
            expression = expression.replace('numeric','0123456789');
            this.el[elem].characters.characterExpression = expression;
            
            var validanguageExpr = ';';
            for (var j=expression.length-1;j>-1;j--){
                validanguageExpr += expression.charCodeAt(j) + ';';
            }
            this.el[elem].characters.expression = validanguageExpr;         
            if(typeof this.el[elem].characters.suppress=='undefined' || this.el[elem].characters.suppress==true) this.addEvent(Obj, "keypress", validanguage.validateKeypress );
            
            this.el[elem].validations[this.el[elem].validations.length] = {};
            this.el[elem].validations[this.el[elem].validations.length-1].name = 'validanguage.validateCharacters';
            this.el[elem].validations[this.el[elem].validations.length-1].errorMsg = true;
        
            for( var z=this.supportedEventHandlers.length-1; z>-1; z--) {
                if(typeof this.el[elem].characters[this.supportedEventHandlers[z]] != 'undefined' && this.el[elem].characters[this.supportedEventHandlers[z]]==true) this.el[elem].validations[this.el[elem].validations.length-1][this.supportedEventHandlers[z]] = true;
            }
            
            //assign onerror
            if(typeof this.el[elem].characters.errorMsg != 'undefined') {
                this.el[elem].validations[this.el[elem].validations.length-1].errorMsg = this.el[elem].characters.errorMsg;
            } else {
                this.el[elem].validations[this.el[elem].validations.length-1].errorMsg = settings.characterValidationErrorMsg;
            }
        }
        //close keypressValidation
        if (typeof this.el[elem].transformations == 'undefined') this.el[elem].transformations = [];

        /** TRANSFORMATIONS **/
        //First, check for transformations listed by event type, such as onblur="foo"
        var j = this.supportedEventHandlers.length;
        for (var k = 0; k < j; k++) {
            var handler = this.supportedEventHandlers[k];
            if( typeof this.el[elem][handler] != 'undefined' && typeof this.el[elem][handler] != 'boolean' ) {
                //Add the defined transformation to the transformations array
                this.el[elem].transformations[this.el[elem].transformations.length] = {};
                var n = this.el[elem].transformations.length - 1;
                this.el[elem].transformations[n].name = this.el[elem][handler];
                //store the event handler
                this.el[elem].transformations[n][handler] = true;
            }
        }
        
        var h = this.el[elem].transformations.length;
        for (var i = 0; i < h; i++) {
            //for each transformation, load the appropriate function in the element's transformations array
            var eventLoaded = false;
            var j = this.supportedEvents.length;
            for (var k = 0; k < j; k++) {
                if(this.supportedEvents[k]=='submit') continue;
                if (typeof this.el[elem].transformations[i]['on' + this.supportedEvents[k]] != 'undefined' && this.el[elem].transformations[i]['on' + this.supportedEvents[k]] == true) {
                    eventLoaded = true;
                    this.addOrCreateValidationWrapper(Obj, this.supportedEvents[k]);
                }
            }
        
            //if they didnt supply any events, we default to the defined defaultTransformationHandlers (usually only "blur")
            if (eventLoaded == false) {
                if (Obj.nodeName.toLowerCase() == 'form') {
                    this.addOrCreateValidationWrapper(Obj, 'submit');
                } else {
                    for (var l = settings.defaultTransformationHandlers.length - 1; l > -1; l--) {
                        this.addOrCreateValidationWrapper(Obj, settings.defaultTransformationHandlers[l], 999);
                    }
                }
            }
        }

        /** VALIDATIONS **/
        if (typeof this.el[elem].validations != 'undefined') {

            // Sort the validations array to ensure that any isAjax function appears last        
            var h = this.el[elem].validations.length-1;
            ajaxLoop:
            for (var i = 0; i < h; i++) {
                if (this.el[elem].validations[i].isAjax) {
                    var copy = this.el[elem].validations[i];
                    this.el[elem].validations.splice(i,1);
                    this.el[elem].validations.push(copy);
                    break ajaxLoop;
                }
            }
            
            var h = this.el[elem].validations.length;
            for (var i = 0; i < h; i++) {
                //for each validation, load the appropriate function in the element's validations array
                var eventLoaded = false;
                var j = this.supportedEvents.length;
                for (var k = 0; k < j; k++) {
                    if (typeof this.el[elem].validations[i]['on' + this.supportedEvents[k]] != 'undefined' && this.el[elem].validations[i]['on' + this.supportedEvents[k]] === true) {
                        eventLoaded = true;
                        this.addOrCreateValidationWrapper(Obj, this.supportedEvents[k], i);
                    }
                }
        
                //if they didnt supply any events, we default to the defined defaultValidationHandlers (usually only "submit")
                if (eventLoaded == false) {
                    for( var l=settings.defaultValidationHandlers.length-1; l>-1;l--) {
                        this.addOrCreateValidationWrapper(Obj, settings.defaultValidationHandlers[l], i);                     
                    }
                }
            }
        }          
    } //close elem loop
},

/**
* This function searches the passed subject and returns an Array of strings 
* which are delimited by the characters passed to the function in the 
* first 2 arguments.  Used to pull comments from the document source
* @param  {String} startChar
* @param  {String} endChar
* @param  {String} subject
* @return {Array}
*/
parseSubstring: function( startChar, endChar, subject ) {
    var matches = [];
    var parts = subject.split(startChar);
    for( var i=0; i<parts.length; i++) {
        var endPos = parts[i].indexOf(endChar);
        if( endPos != -1) matches.push( parts[i].substring(0, endPos) );
    }
    return matches;
}, //close parseSubstring

/**
 * Main function to be called onload to load all the validations
 */
populate: function(){
    this.sniffBrowser();
    if( this.browser=='ie5' ) return; //There's no way I'm supporting IE5, so it's safest to just not run validanguage at all
    
    if (typeof console == 'undefined') this.debug=false;
    
    /** 
    *  Iterate thru all the form elements on the page to populate 
    *  the formLookup hash table and load the default settings
    **/
    var forms = document.getElementsByTagName('form');
    for (var i=0, j=forms.length; i<j; i++) {
        var formName = (this.empty(forms[i].id)) ? i : forms[i].id;
        this.forms[formName] = { settings: this.settings };
        
        var allInputs = forms[i].getElementsByTagName('input');
        var allTextareas = forms[i].getElementsByTagName('textarea');
        var allSelect = forms[i].getElementsByTagName('select');
        var allObjects = this.concatCollection(allInputs, allTextareas);
        var allObjects = this.concatCollection(allObjects, allSelect);
        var radios = {}; // hash lookup to store radio buttons we've seen
        
        for (var k=allObjects.length-1; k>-1; k--) {
            //iterate thru the array and store the id in our formLookup hash table
            if (typeof(allObjects[k].id) != 'undefined' && !this.empty(allObjects[k].id)) {
                this.formLookup[allObjects[k].id] = formName;
            }
        }
    }
    
    if (this.browser == 'konqueror' && this.settings.loadCommentAPI == true) {
        this.ajax(document.location.href, function(docText) {
            //prototype
            if (docText.responseText) docText = docText.responseText;
            var comments = validanguage.parseSubstring( '<!--', '-->', docText );
            validanguage.loadCommentAPI( comments );
            if (validanguage.overloadFormSettings) validanguage.overloadFormSettings();
            if (validanguage.el && !validanguage.empty(validanguage.el)) {
                validanguage.loadElAPI();
                if (validanguage.callToggleTransformationsOnload) validanguage.callToggleTransformationsOnload();
                //Call any onload handler defined by the user
                validanguage.settings.onload.call(validanguage);                
                validanguage.vdLoaded = true;
            }
        });
    } else {      
        //Load comment API
        if (this.settings.loadCommentAPI == true) this.loadCommentAPI( );
    
        //Load Form-Specific Settings      
        if (this.overloadFormSettings) this.overloadFormSettings();
    
        //Load the validanguage.el API
        if (this.el && !this.empty(this.el)) this.loadElAPI();
        
        // Call any defined validanguage.toggle() functions to true up the UI
        if (this.callToggleTransformationsOnload) this.callToggleTransformationsOnload();
        
        //Call any onload handler defined by the user
        this.settings.onload.call(this);
        
        this.vdLoaded = true;
    }
    //Garbage collection
    this.addEvent(window, 'unload', function() { delete validanguage; });
            
}, //close populate

/**
* This function is used to deactivate a previously loaded validation.
* Provide the element ID of the field and a list of event types and validation
* names to deactivate. You can use the * character as the eventType and/or
* the validationName arguments to include ALL eventTypes/validationNames.
* 
* NOTE: if you are tempted to use removeValidation('id','*','*'), you may be
* better off using validanguage.el.id.disabled=true, as this is much easier to
* undo later.
* 
* NOTE: It is up to you to make sure no error msgs are displaying before disabling
* a validation.  If one is showing and all validations are disabled, the onsuccess
* handlers used to clear the error msgs will never be called.
* 
* @param {String} elemId
* @param {String/Array} eventTypes
* @param {String/Array/Function} validationNames
*/
removeValidation: function ( elemId, eventTypes, validationNames ) {
    //prep our arguments
    if( eventTypes == '*' ) {
        eventTypes = this.supportedEvents;
    } else if( typeof eventTypes[0]=='undefined') {
        eventTypes = [ eventTypes ];
    }
    if( typeof validationNames=='string' ) validationNames = [ validationNames ];
    for (var j = eventTypes.length - 1; j > -1; j--) {
        if (eventTypes[j] == 'submit') {
            //Remove form.onsubmit validations
            var vals = this.forms[this.formLookup[elemId]].validations;
            formValLoop:
            for (var i = vals.length - 1; i > -1; i--) {
                if( vals[i]==undefined || vals[i].element.id != elemId ) continue formValLoop;
                if ( validationNames[0] == '*' || this.inArray( this.el[elemId].validations[vals[i].validationsCounter].name, validationNames ) ) {
                    try { delete vals[i]; } catch(e) {}
                }
            }
        } else {
            //Remove field-specific validations
            var vals = this.el[elemId].validations;
            for (var i = vals.length - 1; i > -1; i--) {
                if ( validationNames[0] == '*' || this.inArray(vals[i].name, validationNames) ) {
                    try { delete this.el[elemId].handlers[eventTypes[j]][i]; } catch(e) {}
                }
            }            
        }
    }
}, //close removeValidation

/**
* This function accepts as input a function, a string, an array of
* functions, an array of strings, or a comma-delimited list of functon
* names as its argument and returns an Array of functions or strings 
* comprising the passed arguments. Example:  transforms 'foo, bar'
* to [foo, bar]
* 
* @param {Function or String or Array} args
* @param {String}  returnType should be either 'string' or 'function'
* @return {Array of Functions}
*/
resolveArray: function (args, returnType, ignoreCommas) {
    var returnArray = [];
    if( typeof args == 'object' ) {
        var i=args.length;
        for (var j=0; j<i; j++) {
            returnArray[returnArray.length] = this.resolveArray(args[j],returnType)[0];
        }
        return returnArray;
    }
    if( typeof args == 'function' ) {
        returnArray[0] = args;
        return returnArray;
    }
    if ( typeof args == 'string' ) {
        if(returnType=='string') args = args.replace(' ',''); //dont remove spaces when returning a function
    
        if( args.indexOf(',') == -1 || ignoreCommas==true ) {
            //function name as a string
            if( returnType=='function' ) {
                if( args.indexOf('(') != -1 && args.indexOf('function')==-1 ) {
                    //In order to preserve scope for functions with parameters attached,
                    //we must transform "func1(text,foo)" into "function(text) { return func1.call(this,text,foo) }"
                    var splitAt = args.indexOf('(');
                    var funcName = args.substring(0,splitAt);
                    var params = args.substring(++splitAt,args.length);
                    var args = 'function(text) { return '+funcName+'.call(this,'+params+'}';
                }
                eval("var argsHandle="+args); //easiest way to handle dot notation and framesets
                returnArray[returnArray.length] = argsHandle;
            } else {
                returnArray[returnArray.length] = args;
            }
        } else {
            //comma-delimited list of function names
            var tempArray = this.smartCommaSplit(args);
            var i=tempArray.length;
            if(i==1) {
                //The only commas in the string appear within braces or parens
                returnArray = this.resolveArray(tempArray[0], returnType, true);
            } else {
                for (var j=0; j<i; j++) {
                    returnArray[returnArray.length] = this.resolveArray(tempArray[j],returnType)[0];
                }
            }
        }
        return returnArray;
    }
    return false;      
},

/**
 * This function is called from an ajax callback to report back
 * to validanguage the status of the ajax validation.
 * @param {Object} id Id of the form field associated with this validation
 * @param {Boolean} returnStatus Whether or not the field is valid
 * @param {String|Integer} type Event Type or (alternately) the ajaxCounter that can be used
 *     to check validanguage.ajaxLookup to determine the eventType
 * @param {String} errorMsg Error message to show for the failed field
 */
setValidationStatus: function( id, returnStatus, type, errorMsg ) {
    if (type == undefined) {
        type = 'submit';
    } else if (!this.inArray(type, this.supportedEvents)) {
        var i = this.getAjaxLookupIndex(id, type);
        type = (this.ajaxLookup[id][i].eventType) ? this.ajaxLookup[id][i].eventType : 'submit';
    }
    var nodeType = (type=='submit') ? 'forms' : 'fields';
    var form = (type=='submit') ? validanguage.formLookup[id] : id;
    
    if (this.debug) {
        console.log('setValidationStatus for '+id+'. Pending requests before deleting:');
        console.dir(this[nodeType][form][type].dispatchedAjax);
        console.dir(this[nodeType][form][type].failedValidations);
    }
    
    //exit early if the request has been aborted. TO DO: Better way to detect aborted request
    if (typeof this[nodeType][form][type].failedValidations[id] == 'undefined') {
        if (this.debug) console.log('Exiting setValidationStatus for aborted request');
        //return;
    }
    
    if (returnStatus === false) {
        if (!this.empty(errorMsg)) this[nodeType][form][type].failedValidations[id].errorMsg = errorMsg;
    } else {
        delete this[nodeType][form][type].failedValidations[id];
        if (this.empty(this[nodeType][form][type].failedValidations)) this[nodeType][form][type].failedValidations = 'callManually';
    }
    delete this[nodeType][form][type].dispatchedAjax[id];
    
    // Store the details on this request in ajaxLookup if the user supplied an ajaxCounter
    if (typeof i != 'undefined') {
        this.ajaxLookup[id][i].result = returnStatus;
        if (!this.empty(errorMsg)) this.ajaxLookup[id][i].errorMsg = errorMsg;
    }
    
    if (this.debug) {
        console.log('Pending requests after deleting:');
        console.dir(this[nodeType][form][type].dispatchedAjax);
        console.log('Failed validations:');
        console.dir(this[nodeType][form][type].failedValidations);
    }
    
    // We only validate the form if there are no other pending ajax requests we're waiting on to come back
    if (nodeType=='forms' && this.empty(this[nodeType][form][type].dispatchedAjax) && this.validateForm(form).result === true) {
        if (this.debug) console.log('Form Submitted');
        validanguage.forcedSubmission = true;
        document.getElementById(form).submit();
    } else if (nodeType=='fields' && this.empty(this[nodeType][form][type].dispatchedAjax)) {
        // Trigger another blur/typing/etc event
        if (this.debug) console.log('Throwing Event');
        this.validationWrapper(id, type);
    }
},

/**
* This function shows the error messages for failed validations by dynamically
* creating a div
* @param {string}  Text of the error message to be displayed
*/
showError: function( errorMsg ) {
    var settings = validanguage.getFormSettings(this.id);
	
	
	
	if(validanguage.flag==false && validanguage.showFlashMsg==true)
    			TINY.box.show(settings.flashMsg,0,0,0,0,3);
				
	var errorDisplay = document.getElementById(this.id + settings.errorMsgSpanSuffix);
    if( errorDisplay == null ) {
         var formField = document.getElementById(this.id);
		 // Changes By Keyur.
		 // Purpose: Made Changes On Js Statements because we need out message on right side of input field so chages as below.
		 if(!settings.isNewLineError)
	         var errorDiv = document.createElement('SPAN'); // * changed
		 
	  	 else{
			 var errorDiv = document.createElement('DIV');
			errorDiv.style.marginLeft = settings.marginLeftValue;
		 }
		 
		 //
		 var incustomFields = false;
		 var l = settings.showErrorCustomAreaFields.length;
		 for(var i=0;i<l;i++) {
			 if(formField.id==settings.showErrorCustomAreaFields[i]){
				 incustomFields = true;
			 }
		 }
		 
		 if(settings.showErrorCustomArea && incustomFields) {						
			errorDiv = document.getElementById('custom_'+formField.id);					
		 } else {
		 	validanguage.insertAfter( errorDiv, formField );
		 }
		 //
		 var innerHTML = '&nbsp;<span id="'+ this.id + settings.errorMsgSpanSuffix+'">&nbsp;</span>';
		 
		 errorDiv.innerHTML = innerHTML;
         errorDiv.className = settings.onErrorClassName;   
		
		 /* Apply Class For Tiny MCE Editor */
		 var tags=document.getElementsByTagName('textarea');
		 for (var _i = 0; _i < tags.length; _i++){
			if (tags[_i].id==this.id){
					if(document.getElementById('mce_editor_'+_i)!= null)
							 errorDiv.className='js-err-message-editor';				
			}
		 }
         var errorDisplay = document.getElementById(this.id + settings.errorMsgSpanSuffix);            
    } else {         
         var errorDiv = errorDisplay.parentNode;
		  if(!settings.isNewLineError)
	         errorDiv.style.display = '';// * changed
		  else{
			errorDiv.style.marginLeft = settings.marginLeftValue;
			var tags=document.getElementsByTagName('textarea');
			 
			 errorDiv.style.display = 'block';
			 
		  }
		  
         errorDiv.className = settings.onErrorClassName;
		 /* Apply Class For Tiny MCE Editor */
		 var tags=document.getElementsByTagName('textarea');
		 for (var _i = 0; _i < tags.length; _i++){
			if (tags[_i].id==this.id){
					if(document.getElementById('mce_editor_'+_i)!= null)
							 errorDiv.className='js-err-message-editor';				
			}
		 }
    }
    if(validanguage.useLibrary=='scriptaculous') new Effect.Highlight(errorDiv, { startcolor: '#A85F5F', endcolor: '#C0A6A6', restorecolor: '#ddd' });
    errorDisplay.innerHTML = errorMsg;
    if (! this.className.match(validanguage.settings.failedFieldClassName)) this.className += ' '+validanguage.settings.failedFieldClassName;
    if (this.className.match(validanguage.settings.passedFieldClassName)) this.className = this.className.replace(validanguage.settings.passedFieldClassName,'');
    
    //if(this.nodeName.toLowerCase()=='input' && this.type.toLowerCase()=='text') this.style.backgroundColor = '#FF6666';
    
    //Do we need to add any vd_li items?
    if( !settings.showFailedFields ) return;
    if( document.getElementById(settings.errorDivId) == null ) {
        var errorDiv = document.createElement('DIV');
        errorDiv.id = settings.errorDivId;
        document.body.appendChild(errorDiv);
    } else {
        var errorDiv = document.getElementById(settings.errorDivId);
    }
    if (document.getElementById(settings.errorListId) == null) {
        errorDiv.innerHTML = settings.errorListText + '<br/><ul id="'+settings.errorListId+'"></ul>';
    }
    var errorDivInner = errorDiv.innerHTML.toLowerCase();
    errorDivInner = errorDivInner.replace(/"/g,''); //remove quotes for IE weirdness
    
    var errorList = document.getElementById(settings.errorListId);
    var listItem = '<li id="'+this.id+settings.errorListItemSuffix+'">'+validanguage.el[this.id].field+'</li>';
    var listItemExists = listItem.toLowerCase();
    listItemExists = listItemExists.replace(/"/g,''); //remove quotes for IE weirdness
    
    if(errorDivInner.indexOf(listItemExists)==-1) errorList.innerHTML += listItem;
    document.getElementById(settings.errorDivId).style.display='block';
}, //close showError

/**
* This function is intended for us as an onsubmit transformation. It replaces the form's
* submit button with the text specified in settings.showSubmitMessageMessage.
* 
* @param {Object} validationResult
* @param {Object} failedValidations
*/
showSubmitMessage: function( validationResult, failedValidations ) {
    if( validationResult==false ) return;
    var settings = validanguage.forms[this.id].settings;
    
    //first, we need to find the submit button and hide it
    var inputs = this.getElementsByTagName('INPUT');
    for( var i=inputs.length-1; i>-1; i-- ) {
        if( typeof inputs[i].type!='undefined' && inputs[i].type=='submit' ) {
            validanguage.forms.submitButton = submitButton = inputs[i];
            break;
        }
    }
    submitButton.style.display='none';
    var loadingDiv = document.createElement('DIV');
    loadingDiv.id = settings.showSubmitMessageId;
    loadingDiv.innerHTML = settings.showSubmitMessageMessage; 
    validanguage.insertAfter( loadingDiv, inputs[i] );
    
    //set a timeout to unhide the submit button after 60 seconds
    //in case the request times out and the user needs to resubmit the form
    setTimeout( function( ){ validanguage.forms.submitButton.style.display='inline'; }, 60000);
}, //close showSubmitMessage

/**
* This function splits a string into an array using commas as the delimiter,
* while being smart enough to ignore commas appearing inside parenthesis and
* braces.
* 
* @param {string} Comma-delimited string
* @return {Array}
*/
smartCommaSplit: function ( str ) {
    var openParens = 0;
    var openBraces = 0;
    var lastSplit = 0;
    var returnArray = [];
    var len = str.length;
    for( var i=0; i<len; i++ ) {
        switch (str.charAt(i)) {
            case '(': openParens++; break;
            case ')': openParens--; break;
            case '{': openBraces++; break;
            case '}': openBraces--; break;
            case ',':
            if( openParens==0 && openBraces==0 ){
                returnArray[returnArray.length] = str.substring(lastSplit,i);
                lastSplit = ++i;               
            }
            break;
        }
    }
    returnArray[returnArray.length] = str.substring(lastSplit,i);
    return returnArray;
},

/**
* Determines roughly which browser they're using. Defaults to FF for anything
* that isnt IE, Opera, Konqueror or Safari, which assuming the browser is standards-compliant,
* should be good enough for what I'm using it for.  Yea, yea, I know....
*/
sniffBrowser: function() {
    //yo...   dont hate.
    var isIE/*@cc_on=1@*/;      
    if (isIE) {
        this.browser = 'ie';
        var version = parseFloat(navigator.appVersion.split('MSIE')[1]);
        if( version < 6 ) this.browser = 'ie5';
    } else if(navigator.appName.indexOf('Opera')!=-1) {
        this.browser = 'opera';
    } else if(navigator.vendor.indexOf('Apple')!=-1) {
        this.browser = 'safari';
    } else if (navigator.vendor.indexOf('KDE')!=-1) {
        this.browser = 'konqueror';
    } else {
        this.browser = 'ff';
    }
},

/**
 * Transformation supporting 3 major features: toggling visibility,
 * changing the values of form fields and adding options to select
 * elements.
 * @param {Array} toggleArgs Array of objects
 */
toggle: function( toggleArgs ) {
    var j = toggleArgs.length;
    var settings = validanguage.getFormSettings(this.id);
    var formName = validanguage.formLookup[this.id];
    for (var i=0; i < j; i++) {
        var obj = toggleArgs[i];
        var targets = validanguage.resolveArray(obj.target, 'string');
        var radioExceptionApplies = false;
        // If we are running toggle() for a radio button find out if all the other radio buttons in the same group are unchecked,
        if (this.nodeName.toLowerCase()=='input' && this.type.toLowerCase() == 'radio') {
            var radioButtonChecked = false;
            var radios = false;
            for (var k = document.forms.length - 1; k > -1; k--) {
                if (document.forms[k].id == formName) {
                    radios = document.forms[k][this.name];
                    break;
                }
            }
            for (var k = radios.length - 1; k > -1; k--) if (radios[k].checked) radioButtonChecked = true;
            if (!radioButtonChecked) radioExceptionApplies=true;
        }
        
        // toggle visibility
        if (obj.toggle) {
            var visibleMet = (obj.toggle.visible) ? validanguage.toggleCriteriaMet(this, obj.toggle.visible, settings) : false;
            var hiddenMet = (obj.toggle.hidden) ? validanguage.toggleCriteriaMet(this, obj.toggle.hidden, settings) : false;
            for (var k = targets.length - 1; k > -1; k--) {
                if (visibleMet && !radioExceptionApplies) validanguage.toggleDisplay(targets[k], '');
                if (hiddenMet || (settings.toggleVisibilityDefaultsToHidden && !visibleMet)) validanguage.toggleDisplay(targets[k], 'none');
            }
        } // close toggle visibility
        
        // toggleAttribute
        if (typeof obj.toggleAttribute != 'undefined') {
            for (var k = targets.length - 1; k > -1; k--) {
                var attribute = obj.toggleAttribute.attribute;
                var value     = obj.toggleAttribute.value;
                if (obj.toggleAttribute.condition == 'checked' && this.checked == true)
                    document.getElementById(targets[k])[attribute] = value;
                else  if (obj.toggleAttribute.condition == 'unchecked' && this.checked == false) 
                    document.getElementById(targets[k])[attribute] = value;
                else if ((this.value) && (obj.toggleAttribute.condition == this.value)) 
                    document.getElementById(targets[k])[attribute] = value ;
            }
        } // close toggleAttribute
        
        // value control
        if (typeof obj.values != 'undefined') {
            for (var k = targets.length - 1; k > -1; k--) {
                if (typeof obj.values.checked != 'undefined' && this.checked==true) document.getElementById(targets[k]).value=obj.values.checked;
                else if (typeof obj.values.unchecked != 'undefined' && this.checked==false) document.getElementById(targets[k]).value=obj.values.unchecked;
                else if (typeof obj.values[this.value] != 'undefined') document.getElementById(targets[k]).value=obj.values[this.value];
            }
        } // close toggle value
        
        // dynamic selects
        if (typeof obj.dynamicSelect != 'undefined') {
            // store the value
            var newValue = this.value;
            if (typeof validanguage.el[this.id]['value']!= 'undefined' && validanguage.el[this.id]['value']==newValue) return;
            var sel2 = document.getElementById(targets[0]); // dynamicSelect only supports 1 target
            for (var sel1Key in obj.dynamicSelect) {
                if (typeof obj.dynamicSelect[sel1Key] == 'object' ) {
                    var sel1Val = obj.dynamicSelect[sel1Key];
                    if (sel1Key == this.value) {
                        // remove the existing options
                        while (sel2.options.length > 0) { sel2.remove(0); }
                        for (var sel2Key in sel1Val) {
                            if (sel2Key == '_default') continue;
                            var opt = document.createElement('option');
                            opt.value = sel2Key;
                            opt.text  = sel1Val[sel2Key];
                            sel2.options.add(opt);
                        }
                        if (typeof sel1Val['_default'] != 'undefined') sel2.value = sel1Val['_default'];
                    }
                }
            }
            validanguage.el[this.id]['value'] = newValue;
        } // close dynamicSelect
    }
}, //close toggle

/**
 * Determines whether the criteria used by the toggle function
 * has been met
 * @param {Object} field
 * @param {string} criteria
 * @param {Object} settings object
 */
toggleCriteriaMet: function( field, criteria, settings ) {
    if (criteria == 'checked') {
        return !!(field.checked);
    } else if (criteria == 'unchecked') {
        return !(field.checked);
    } else if (criteria == 'empty') {
        return !!(validanguage.inArray(field.value, settings.emptyOptionElements));
    } else if (criteria == 'notEmpty') {
        return !(validanguage.inArray(field.value, settings.emptyOptionElements));
    } else {
        return !!(field.value == criteria);
    }    
}, //close toggleCriteriaMet

/**
 * Function used to hide or show a node. This function will also automatically disable or enable
 * any form fields contained within the passed node.
 * @param {Object} nodeId ID of the node to hide/show
 * @param {Object} visibility (optional) Pass either 'none' or '' to hide/show. Or leave blank to toggle
 */
toggleDisplay: function( nodeId, visibility ) {
    var node = document.getElementById(nodeId);
    var nodeName = node.nodeName.toLowerCase();
    if (visibility==null||visibility==undefined) visibility = (node.style.display=='none') ? '' : 'none';
    disabledBool = (visibility=='none') ? true : false;

    // show/hide the passed node
    node.style.display = visibility;
    
    // disable/enable any form fields contained within the node
    if (nodeName == 'input' || nodeName == 'textarea' || nodeName == 'select') {
        node.disabled = disabledBool;
        return;
    }
    var allInputs = node.getElementsByTagName('input');
    var allTextareas = node.getElementsByTagName('textarea');
    var allSelect = node.getElementsByTagName('select');
    var allObjects = this.concatCollection(allInputs, allTextareas);
    var allObjects = this.concatCollection(allObjects, allSelect);
    for (var i = allObjects.length - 1; i > -1; i--) {
        allObjects[i].disabled = disabledBool;
    }
}, //close toggleDisplay

/**
* Validates that a field does not contain any of the invalid characters
* listed in the characters validation rules.
* 
* @param {string} text
*/
validateCharacters: function( text ) {
    var id = this.id;
    var mode = validanguage.el[id].characters.mode;
    var expression = validanguage.el[id].characters.characterExpression;
    switch(mode) {
        case 'allow':
            outerLoop:        
            for( var i=text.length-1;i>-1;i--) {
                innerLoop: 
                for (var j=expression.length-1; j>-1; j--) {
                    if(expression.charAt(j)==text.charAt(i)) continue outerLoop;
                }
                //if we got here, they entered a disallowed character
                return false;
            }
            break;
        case 'deny':
            outerLoop:        
            for( var i=text.length-1;i>-1;i--) {
                innerLoop: 
                for (var j=expression.length-1; j>-1; j--) {
                    if(expression.charAt(j)==text.charAt(i)) return false;
                }
            }
            break;
    }
    return true;
}, //close validateCharacters

/**
 * Validates that a valid credit card number has been supplied
 * @param {string}  text
 * @param {array}   cardTypes
 * @param {boolean} testChecksum  Pass false to skip the luhn checksum test 
 */
validateCreditCard: function (text, cardTypes, testChecksum) {
    if (validanguage.empty(cardTypes)) cardTypes = ['amex','disc','mc','visa'];
    // Strip any non-digits
    var text=text.replace(/\D/g,'');

    var cards = {
        'amex'     : '^3[4|7]\\d{13}$',
        'bankcard' : '^56(10\\d\\d|022[1-5])\\d{10}$',
        'diners'   : '^(?:3(0[0-5]|[68]\\d)\\d{11})|(?:5[1-5]\\d{14})$',
        'disc'     : '^(?:6011|650\\d)\\d{12}$',
        'electron' : '^(?:417500|4917\\d{2}|4913\\d{2})\\d{10}$',
        'enroute'  : '^2(?:014|149)\\d{11}$',
        'jcb'      : '^(3\\d{4}|2100|1800)\\d{11}$',
        'maestro'  : '^(?:5020|6\\d{3})\\d{12}$',
        'mc'       : '^5[1-5]\\d{14}$',
        'solo'     : '^(6334[5-9][0-9]|6767[0-9]{2})\\d{10}(\\d{2,3})?$',
        'switch'   : '^(?:49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])|36[0-9]{2})\\d{10}(\\d{2,3})?)|(?:564182\\d{10}(\\d{2,3})?)|(6(3(33[0-4][0-9])|759[0-9]{2})\\d{10}(\\d{2,3})?)$',
        'visa'     : '^4\\d{12}(\\d{3})?$',
        'voyager'  : '^8699[0-9]{11}$'
    };
    var validCard = false;
    for (var i = cardTypes.length; i--; i > -1) {
        validCard = validanguage.validateRegex(text, { expression: cards[cardTypes[i]] });
        if (validCard) break;
    }
    if (!validCard) return false;
    if (testChecksum===false) return true;
    
    /** Run the luhn checksum test
      * Luhn algorithm number checker - (c) 2005-2008 shaman - www.planzero.org
    */

    // Set the string length and parity
    var number_length=text.length;
    var parity=number_length % 2;
    
    // Loop through each digit and do the maths
    var total=0;
    for (var i=0; i < number_length; i++) {
        var digit=text.charAt(i);
        // Multiply alternate digits by two
        if (i % 2 == parity) {
            digit=digit * 2;
            // If the sum is two digits, add them together (in effect)
            if (digit > 9) {
              digit=digit - 9;
            }
        }
        // Total up the digits
        total = total + parseInt(digit);
    }
    
    // If the total mod 10 equals 0, the number is valid
    if (total % 10 == 0) {
        return true;
    } else {
        return false;
    }
}, //close validateCreditCard

/**
 * Validates that a valid date is supplied and is entered in the correct format.
 * The format is specified by the following arguments<br/>
 * @param {String} text to be validated<br/>
 * @param {Object} Options object containing any of the following options<br/>
 *   dateOrder: {String} This must be either 'ymd','mdy','dmy','myd','ydm', or 'dym<br/>
 *   allowedDelimiters: {String}. A string containing the list of delimiters which are allowed. 
 *                       Example: './-'<br/>
 *   twoDigitYearsAllowed: {Boolean}. Is a 2-digit year valid<br/>
 *   oneDigitDaysAndMonthsAllowed: {Boolean}. Is a 1-digit month or a 1-digit day valid<br/>
 *   maxYear: {Integer}.  Years greater than maxYear will be treated as invalid. Defaults to 15 years from today<br/>
 *   minYear: {Integer}.  Years less than minYear will be treated as invalid.  Defaults to 1900<br/>
 *   rejectDatesInTheFuture: {Boolean}. Are dates in the future valid?  rejectDatesInTheFuture defaults to false<br/>
 *   rejectDatesInThePast: {Boolean}. Are dates in the past valid?  rejectDatesInThePast defaults to false<br/>
 */
validateDate: function( text, options ) {
    //Set default values
    options = validanguage.getDateTimeDefaultOptions(options, {dateOrder: 'mdy'} );
    
    //Loop thru the allowedDelimiters to start building our regex and figure out which one was actually used
    var delimiterUsed;
    var delimiterRegex = '(';
    for( var i=options.allowedDelimiters.length-1;i>-1;i--) {
        delimiterRegex += '\\' + options.allowedDelimiters.charAt(i);
        if (i>0) delimiterRegex += '|';
        if (text.indexOf(options.allowedDelimiters.charAt(i)) > -1) {
            delimiterUsed = options.allowedDelimiters.charAt(i);
        }
    }
    delimiterRegex += ')';
    if( delimiterUsed==null ) return false; //no delimiter was used
    var parts = text.split(delimiterUsed);
    if( parts.length!=3 ) return false;     //they used more than one delimiter or didnt give us a valid date
    
    //Next we need to build the regex to validate the date comprises only integers and delimiters
    var regex = '^';
    for(var j=0; j<3; j++) {
        switch( options.dateOrder.charAt(j) ) {
            case 'y':
                var num = (options.twoDigitYearsAllowed) ? '{2,4}' : '{4}';
                regex += '\\d' + num;
                break;
            case 'm':
            case 'd':
                var num = (options.oneDigitDaysAndMonthsAllowed) ? '{1,2}' : '{2}';
                regex += '\\d' + num;
                break;
        }
        if(j<2) regex += delimiterRegex;
    }
    regex += '$';
    //Run the regex
    var reg = new RegExp( regex );
    var thisMatch = reg.exec(text);
    if (thisMatch == null) return false;
    
    //grab our dates
    var year = parts[options.dateOrder.indexOf('y')];
    var month = parts[options.dateOrder.indexOf('m')];
    var day = parts[options.dateOrder.indexOf('d')];
    
    // Verify the year isnt 3-digits long to account for me being lazy in the regex check above
    if( year.length==3 ) return false;
    
    //Make sure the year is in bounds
    if( (year < options.minYear && year.length==4) || (year > options.maxYear) ) return false;
    
    //Next we check that the date actually exists, to rule out stuff like "12/32/1976"
    if( !validanguage.validateDateExists(year,month,day) ) return false;
    
    if( options.rejectDatesInTheFuture || options.rejectDatesInThePast ){
        var now = new Date();
        var then = new Date();
        then.setDate(day);
        then.setMonth(--month); // January = 0
        then.setFullYear(year);
        if( (options.rejectDatesInTheFuture && then > now) || (options.rejectDatesInThePast && then < now) ) return false;
    }
    return true;
}, //close validateDate

/**
* Helper function to verify a date actually exists. Used to reject values
* such as "12/35/2009"
* @param {integer} year, preferably 4-digit
* @param {integer} month
* @param {integer} day
* @return {Boolean}
*/
validateDateExists: function(year, month, day) {
    if(year.length==2) {
        var prefix = (year > 20 ) ? '19' : '20';
        year = prefix+year.toString();
    }
    if( month.charAt(0)=='0' ) month = month.substr(1,1);
    if( day.charAt(0)=='0' ) day = day.substr(1,1);
    if( month < 0 || month > 12 ) return false;
    switch( month.toString() ) {
        case '4':
        case '6':
        case '9':
        case '11':
            var maxDay = 30;
            break;
        case '2':
            var maxDay = ((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0)) ) ? 29 : 28;
            break;
        default:
            var maxDay = 31;         
    }
    if( day < 0 || day > maxDay ) return false;
    return true;
}, //close validateDateExists

/**
 * Validates an email address
 */
validateEmail: function( text ) {
    if(! text.match(/^([a-zA-Z0-9]+[a-zA-Z0-9._%-]*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,4})$/) )
       return false;
    else return true;
},
/*
* Validate boolean method for of cake : Added By-> Minesh R. Shah
*/
validateBoolean: function (text){
var bool_values=Array(0, 1, '0', '1', true, false,'true','false');
	for(i=0;i<bool_values.length;i++)
		if(bool_values[i]==text)
			return true;
	
	return false;
},
/*
* Validate decimal method for of cake : Added By-> Minesh R. Shah
*/
validateDecimal: function (text,places,regexp){
	//var reg =new RegExp(/^-?\d+(\.\d+)?$/);
	
//	var reg =new RegExp(/^\d+\.{1}\d{0,2}$/);
	var reg =new RegExp(/^\d+\.{1}\d+$/);
	
	
	places = places || null;
	if(places!=null)
	{
		//alert(/^-?\d+(\.\d{'0,2'}+)?$/.test(text));
		x="^[-+]?[0-9]*\\.{1}[0-9]{'"+ places +"'}";	
		///alert(^-?\d+(\.\d+)?$/.test('15.22'));
		var reg=new RegExp(x);
	}
	reg = regexp || reg;
	
 	//(reg.exec(text)== null) ?  x=false : x= true; 	return x;
	(!text.match(reg)) ?  x=false : x= true; 	return x;
},

/*
* Validate image extension method for of cake : Added By-> Minesh R. Shah
*/
validateExtension: function (text,ext){

	var org_ext=text.split(".");
	var available_ext=ext.split(",");
	
	for(i=0;i<available_ext.length;i++)
		if(org_ext[1]==available_ext[i])
			return true;
	return false;
	
	
},

/*
* Validate inList  method for of cake : Added By-> Minesh R. Shah
*/
validateInlist: function (text,list){

	//var org_ext=text.split(".");
	var available_list=list.split(",");
	
	for(i=0;i<available_list.length;i++)
		if(text==available_list[i])
			return true;
	return false;
	
	
},
/*
* Validate Comparison method for of cake : Added By-> Minesh R. Shah
*/
validateComparison: function (text,expr,check){
	
	/*alert(text);
	alert(expr);
	alert(check);*/
	
			switch (expr) {
			case '>':
				if (text > parseInt(check)) 
					return true;
				break;
			case '<':
				if (text < parseInt(check)) 
					return true;
				break;
			case '>=':
				//alert("yes");
				if (text >= parseInt(check))
					return true;
				break;
			case '<=':
				if (text <= parseInt(check)) 
					return true;
				break;
			case '==':
				if (text == parseInt(check)) 
					return true;
				break;
			case '!=':
				if (text != parseInt(check)) 
					return true;
				break;
		}
		return false;
},

/*
* validatePositive  method for checking valid positive value of field cake : 
*Params : text for input text and 
		: allowZero boolean based on that it will allow 0 or not
		: By default it will be allowed zero
Added By-> Keyur R. Vaghani
*/
validatePositive: function (text,allowZero){
	  
	  if(allowZero==true || allowZero==1)
		{
			if(parseInt(text)>=0){
					return true;
			}
			return false;
		}
		else
		{
			if(parseInt(text)>0){
					return true;
			}
			return false;
		}
},


/*
* Validate Money  method for of cake : Added By-> Minesh R. Shah
*/
validateMoneycustom: function (text,symbolPos){


	var symbolPos = symbolPos || 'left';
	
	
	if(symbolPos=='right')
		var reg =new RegExp(/^(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{2})?$/);
	else
		var reg =new RegExp(/^(?!\x{00a2})\p{Sc}?(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{2})?$/);
		
	(!text.match(reg)) ?  x=false : x= true; 	return x;	

	
	
},

/*
* Validate custom method for Matching New Password and Confirm Password Type by keyur
*/
validMatchPass: function(text,usernewpass){
		var newpass = document.getElementById(usernewpass).value;		
		if(newpass === text){
				return true;
		}
	return false;
},

/*
* Validate custom method for Matching New Password and Confirm Password Type by keyur
*/
validAlternative: function(text,usernewpass){
		//alert(usernewpass);
		//alert($(usernewpass).value);		
		
		var newpass = document.getElementById(usernewpass).value;
		//alert(newpass);
		if(newpass === text){
				return false;
		}
		if(text != '')		{

			if(!text.match("^http:\/\/www\.[a-z]+\.(com)|(org)|(edu)|(net)|(co\.[a-z]{2})$"))
				return false;
			 else return true;
			 /*var urlregex = new RegExp("^(http:\/\/www.|https:\/\/www.|ftp:\/\/www.|www.){1}([0-9A-Za-z]+\.)");
			  if(urlregex.test(text))
			  {
				return(true);
			  }
			  return(false);*/
			 
			 
		}
	return true;
},

/*
* Validate EqualTo  method for of cake : Added By-> Minesh R. Shah
*/
validateEqualTo: function (text,args)
{
	return (text === args);
	
},

/*
* Validate Range  method for of cake : Added By-> Minesh R. Shah
*/
validateRange: function (text,lower, upper){
	
	
	return (parseInt(text) > parseInt(lower) && parseInt(text) < parseInt(upper));

},

/*
* Validate time method for of cake : Added By-> Minesh R. Shah
*/
validateTime: function (text){
var reg =new RegExp(/^((0?[1-9]|1[012])(:[0-5]\d){0,2}([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$/);
 	(reg.exec(text)== null) ?  x=false : x= true; 	return x;
},

/*
* Validate custom method for Trim All Text validation by keyur
*/
validateTrim: function (text){
 if(text != null) {
	 while (text.substring(0,1) == ' '){
	  text = text.substring(1, text.length);
	 }
	 while (text.substring(text.length-1, text.length) == ' '){
	  text = text.substring(0,text.length-1);
	 }
	 return text;
 }
},

/*
* Validate custom method for showing/hiding on focus Hint Messages validation by keyur
*/
validateShowHint: function(){
	//alert('in'+this.id);
	var settings = validanguage.getFormSettings(this.id);
	var divhintid=this.id+settings.hintMsgSpanSuffix;
	if(document.getElementById(divhintid)!=null){
		document.getElementById(divhintid).style.display='';
	}
},
validateHideHint: function(){
	var settings = validanguage.getFormSettings(this.id);
	var divhintid=this.id+settings.hintMsgSpanSuffix;
	if(document.getElementById(divhintid)!=null){
		document.getElementById(divhintid).style.display='none'; 
	}
},

/*
* Validate custom method for custom validation by keyur
*/
validateCustom: function (text,expr){
		
		var reg =new RegExp(/^[A-Z\_]*$/);
		expr=expr || reg;
	
		if(!text.match(expr))
			return false;
		else
			return true;
},
/*
* Validate custom method for custom validation by keyur
* which check that first letter is capital 'A' or not.
*/
validateChkFirst: function(text,expr){
		var reg =new RegExp(/^A/);
		expr=expr || reg;
		
		if(!text.match(expr))
			return false;				
		else
			return true;
			
},

/**
* Calls the validationWrapper function on the passed form ID
* @param {String or Form Node} Id of the form
* @return {Object}  Object containing 2 elements:
*    Object.result is a boolean to indicate whether any fields in the form failed validation
*    Object.failedValidations will contain info detailing any form fields with failed validations
*/
validateForm: function( formId ) {
    var form;
    if( formId==undefined) form = document.forms[0];
    else if (typeof formId=='string') form = document.getElementById(formId);
    else form = formId;
    return this.validationWrapper( form, 'validateForm' );
},

/**
 * Validates that a field contains a valid IPv4 address
 */
validateIP: function( text ) {
	var bytes = text.split('.');
	if (bytes.length == 4) {
        for (var i=bytes.length-1; i> -1; i--) {            
			if (!(validanguage.validateNumeric(bytes[i]) && bytes[i] >= 0 && bytes[i] <= 255)) {
				return false;
			}
        }
		return true;
	}
	return false;    
}, //close validateIP

/**
* Function to suppress the keys specified in validanguage.el.characters
* from being entered into a textarea or text box.
* 
* This function must be forked to fetch the keyCode
* and differentiate between control and non-control characters.
* For example: in Mozilla both the delete key and . equate to 46.  
* We dont fork based on supported functions because IE and Moz event models
* for keyCodes/charCodes are different and confusing, so we're
* better off sniffing the browser.
* 
* @param {Event Object} e
*/
validateKeypress: function(e) {
    var evt = e || window.evt;
    var $this = evt.currentTarget || evt.srcElement;
    var id = $this.id;
    var formName = validanguage.formLookup[id];
    var settings = validanguage.getFormSettings(id);
    
    if (validanguage.browser == 'ie' || validanguage.browser == 'opera') {
        //branch for IE and opera
        //we dont have to worry about noncontrol keys since they dont fire keypress events in IE
        charCode = evt.keyCode;
        if (((charCode == 16) && (evt.shiftKey)) || (evt.ctrlKey)) 
            return true; //prevents firing on ctrl key in opera
            
        if (evt.which == 0) return true;
            
    } else {
        //branch for Mozilla
        if ((evt.charCode == 0) || (evt.ctrlKey)) 
            return true;
        //return true for all control keys and if control is held down
        
        charCode = evt.which; //capture charCode of non-control keys
    }
    
    charCode += ';';
    searchString = new String(validanguage.el[id].characters.expression);
    var mode = validanguage.el[id].characters.mode;
    
    if ( ( (searchString.search(charCode) != -1 ) && (mode == 'allow') ) ||
          ( (searchString.search(charCode) == -1 ) && (mode == 'deny') ) ){
        return true;
    } else {
        $this.style.backgroundColor = settings.validationErrorColor;
        setTimeout("document.getElementById('" + id + "').style.backgroundColor = validanguage.forms['"+formName+"'].settings.normalTextboxColor",validanguage.forms[formName].settings.timeDelay);
        evt.returnValue = false; //IE
        if(evt.preventDefault) evt.preventDefault();    //Everyone else
        return false;
    }
}, //close validateKeypress

/**
* Validates that a field is less than maxlength characters long
* @param {string} text
*/
validateMaxlength: function( text, max ) {
    var id = this.id;
    var maxlength = (validanguage.empty(max)) ? validanguage.el[id].maxlength : max;
    if(text.length > maxlength)
        return false;
    else return true;
},

/**
* Validates that a field is greater than minlength characters long
* @param {string} text
*/
validateMinlength: function( text, min ) {
    var id = this.id;
    var minlength = (validanguage.empty(min)) ? validanguage.el[id].minlength : min;
    if(text.length < minlength)
       return false;
    else return true;
},

/**
* Validates that a field is numeric
* @param {string} text
*/
validateNumeric: function( text ) {
    if(! text.match(/^\d+$/) )
        return false;
    else return true;
},

/**
* Validates the element against a user-defined regex stored in
* validanguage.el[id].regex
* 
* @param {string} text
* @param {object} optional object containing regex settings
*/
validateRegex: function( text, obj ) {
    var id = this.id;
    var regexObj = (validanguage.empty(obj)) ? validanguage.el[id].regex : obj;
    if(typeof regexObj.modifiers=='undefined') regexObj.modifiers='';
    if(typeof regexObj.errorOnMatch=='undefined') regexObj.errorOnMatch=false;
    var myreg = (typeof regexObj.expression=='string') ? new RegExp(regexObj.expression) : regexObj.expression;
    var thisMatch = myreg.exec(text, regexObj.modifiers);
    if (thisMatch == null) {  //no match
        var returnStatus = (regexObj.errorOnMatch==false||regexObj.errorOnMatch=='false') ? false : true;
    } else {                  //match
        var returnStatus = (regexObj.errorOnMatch==false||regexObj.errorOnMatch=='false') ? true : false;         
    }
    return returnStatus;
},

/***
* Validates whether or not an element has been filled out,
* selected or checked.  This function is a wrapper which
* calls validateRequiredChild()
* 
* @param {string} text
*/
validateRequired: function( unused ) {
    var id = this.id; 
    if(typeof validanguage.el[id].requiredAlternatives == 'undefined') {
        var alternatives = [ id ];
    } else {         
        var alternatives = validanguage.resolveArray(validanguage.el[id].requiredAlternatives,'string');
        alternatives[alternatives.length] = id;
    }
    for( var i=alternatives.length-1; i>-1; i--) {
        id = alternatives[i];
        var elem = document.getElementById(id);
        var text = elem.value;
		//Added By Keyur: For Trim all String From Js
		text = validanguage.validateTrim(text);
		if(document.getElementById(id).type != 'file')
			document.getElementById(id).value = text;
        var notEmpty = validanguage.validateRequiredChild.call(elem, text);
        if(notEmpty==true) return true; //if this element or one of its alternatives is not empty, it validates
    }
    return false;
},

/**
* This function calls the validateRequired method on the "master/required"
* form field when the "alternative" form field is clicked and then calls
* the appropriate onerorr/onsuccess function.
*/
validateRequiredAlternatives: function(e) {
    var evt = e || window.evt;
    var $this = evt.currentTarget || evt.srcElement;
    var id = $this.id;
    var parentId = validanguage.requiredAlternatives[id].parentId;
    var onsuccess = validanguage.requiredAlternatives[id].onsuccess;
    var onerror = validanguage.requiredAlternatives[id].onerror;
    var parent = document.getElementById(parentId);
    if (validanguage.validateRequired.call(parent) == true) {
        successHandlers = validanguage.resolveArray(onsuccess, 'function');
        for (var m = successHandlers.length - 1; m > -1; m--) {
            successHandlers[m].call(parent);
        }
    } else {      
        errorHandlers = validanguage.resolveArray(onerror, 'function');
        for (var m = errorHandlers.length - 1; m > -1; m--) {
            errorHandlers[m].call(parent, validanguage.requiredAlternatives[id].errorMsg);
        }
    }
},

/***
* Child function called by validateRequired to validates whether or not an element has been filled out,
* selected or checked.  validateRequiredChild is required to add support for the requiredAlternatives
* array.
* 
* @param {string} text
*/
validateRequiredChild: function( text ) {
    var type = ( typeof this.type != 'undefined' ) ? this.type : null;
    if( this.nodeName.toLowerCase() == 'textarea' ) type = 'text';
    if( this.nodeName.toLowerCase() == 'select' ) type = 'select';
    
    switch( type ) {
        case 'checkbox':
            if( this.checked == false ){
                return false;
            }
            break;
        
        case 'radio':
            var formId = validanguage.formLookup[this.id];
            var radios = (typeof formId == 'number') ? document.forms[formId][this.name] : document.getElementById(formId)[this.name];
            for( var i=radios.length-1; i>-1;i--) {
                if (radios[i].checked == true) return true;
            }
            return false;
            break;
        
        case 'text':
        case 'password':
        case 'file':
            if(validanguage.empty(text)) {
                return false;
            }
            break;
        
        case 'select':
            if (validanguage.empty(text)) {
                return false;
            }
            settings = validanguage.getFormSettings(this.id);
            for( var i=settings.emptyOptionElements.length-1; i>-1; i-- ) {
                //see if they have selected any of the "empty" option elements
                if( text==settings.emptyOptionElements[i]) return false;
            }
            break;
    } //close switch
    return true;
},

/**
* Validates that the entered text is a valid timestamp.  The options object supports all the options listed
* under the validateDate function as well as the following additional onces<br/>
* @param {String} text to be validated<br/>
* @param {Object} Options object containing any of the following options:<br/>
*   timeIsRequired: {Boolean}  Is a date which is provided without an accompanying time considered a valid timestamp?
*     timeIsRequired defaults to false.<br/>
*   timeUnits: {String} A string containing a list of all the time units which are allowed to be entered in the timestamp.
*     These may include any of the following: h for hours, m for minutes, s for seconds, u for microseconds,
*     and t for timezone.  Example:  "hms" for hours, minutes and seconds or "hmsut" for all 5 units.
*     Defaults to "hms"<br/>
*   microsecondPrecision {Integer} Indicates the supported number of decimal places for the microseconds. Defaults to 6.
*   
*/
validateTimestamp: function( text, options ) {
    // Set default options
    options = validanguage.getDateTimeDefaultOptions(options, {dateOrder: 'ymd'}  );
        
    // Check the date portion of the timestamp
    var pos = text.indexOf(' ');
    var date = ( pos == -1 ) ? text : text.substr(0,pos);      
    if( !validanguage.validateDate(date, options) ) return false;

    // Check whether they provided a time
    if( pos != -1 ) {
        var time = text.substring(++pos);
    } else {
        if( !options.timeIsRequired ) return true;
        if( options.timeIsRequired ) return false;
    }
        
    // Build the regex to validate the time
    var regex = '^\\d{1,2}:\\d{1,2}';
    if( options.timeUnits.indexOf('s')!=-1 ) regex += '(:\\d{1,2}';
    if( options.timeUnits.indexOf('u')!=-1 ) regex += '(\\.\\d{1,'+options.microsecondPrecision+'})?';
    if( options.timeUnits.indexOf('s')!=-1 ) regex += ')?';
    if( options.timeUnits.indexOf('t')!=-1 ) {
        regex += '( ?[\\+|\\-]{1,1}(\\d|0\\d|10|11|12|13)(\\:(00|30))?)?';
    }
    regex += '$';

    //Run the regex
    var reg = new RegExp( regex );
    var thisMatch = reg.exec(time);
    if (thisMatch == null) return false;

    //Finally we need to make sure that the hours, minutes and seconds which were entered are valid
    var timeparts = time.split(':');
    if( timeparts[0] > 23) return false;
    if( timeparts[1] > 59) return false;
    if( timeparts.length > 2){
        var seconds = timeparts[2].substr(0,2);
        if(seconds > 59) return false;
    }
    return true;
}, //close validateTimestamp

/**
* Validates a URL
* 
* @param {string} text
*/
validateURL: function( text ) {
	
	//if(!text.match("^[A-Za-z]+://[A-Za-z0-9-_]+\\.[A-Za-z0-9-_%&\?\/.=]+$"))
	//if(!text.match("\b(http?|ftp|file)://[A-Z0-9+&@#.]*[A-Z0-9]"))
	//if(objValue.value.search(/^http:\/\/www\.[a-z]+\.(com)|(org)|(edu)|(net)$/)==-1) {
	//if(objValue.value.search(/^http:\/\/www\.[a-z]+\.(com)|(org)|(edu)|(net)|(co\.[a-z]{2})$/)==-1) {
	//if(!text.match("^(http://|https://)(([a-z0-9]([-a-z0-9]*[a-z0-9]+)?){1,63}\[.]{1})+[a-z]{2,6}"))
	if(!text.match("^http:\/\/www\.[a-z]+\.(com)|(org)|(edu)|(net)|(co\.[a-z]{2})$"))
		return false;
    else 
		return true;
		
},

/**
* DEPRECATED - Please use validateDate() instead as validateUSDate
* will be removed in the future. validateDate() defaults to MM/DD/YYYY
* when called without any additional arguments.
* @param {string} text
*/
validateUSDate: function( text ) {      
    return validanguage.validateDate(text);
},

/**
 * Validates that a US Phone number is entered
 * Custom Changes In Parameters    - By Minesh
 * @param {string} text
 */
validateUSPhoneNumber: function( text,expr,country ) {
	
	var expr = expr || null;
	var country= country || 'US'; 
	//alert(expr);
	
	//var reg = new RegExp( expr );
	
	if(expr!='null')
	{
			if(! text.match(expr) )
        	return false;
		    else return true;
	}
	else
	{
		  if(! text.match(/^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$/) )
        	return false;
		    else return true;
	}
},
/**
 * Validates that a US Social Security Number is entered
 * 
 * @param {string} text
 */
validateUSSSN: function( text ) {
    if(! text.match(/^\d{3}( |-|.){0,1}\d{2}( |-|.){0,1}\d{4}$/) )
        return false;
    else return true;
},

/**
 * Validates that a US zip code was entered
 * 
 * @param {string} text
 */
validateUSZipCode: function( text ) {
    if(! text.match(/^\d{5}( |-|.){0,1}(\d{4})?$/) )
        return false;
    else return true;
},

/*
* Validate custom method for checking the total of primary and secondary charity should be equal to 100
*/
validCheckTotal: function(text,compareid,allowedid){
		var assisted = document.getElementById(compareid).value;
		var total_allowed = document.getElementById(allowedid).value;
		//var total = parseFloat(primarycharity) + parseFloat(text);
		if(parseInt(assisted) <= parseInt(total_allowed)){
				return true;
		}
	return false;
},

// Allow only letter or string
validateString: function(text,id){
	var alphaExp = /^[a-zA-Z]+$/;
	if($(id).value.match(alphaExp)){
		return true;
	}else{
		return false;
	}
},

/**
* This is a wrapper for all the validation event handlers assigned to both
* form field element and to forms themselves. This function also calls any
* transformations before running the validations.
* 
* We use a wrapper for a number of reasons, including exiting validation
* early as soon as a single validation function fails.
* 
* @param {Event|String} Event object or Node ID
* @param {String} Custom Event
* @param {Number} ajaxLookupIndex
* 
*/
validationWrapper: function(e, customEvent) {
    var calledManually = false;
    if (validanguage.inArray(customEvent, validanguage.supportedEvents)) {
        // check for manually thrown events
        var $this = document.getElementById(e);
        var type = customEvent;
        var id = e;
        var calledManually = true;
    } else if (customEvent=='validateForm') {
        // validate form event
        var $this = e;
        var form = validanguage.whichFormAmI($this);
        var type = 'submit';
    } else {
        // standard event handlers
        var evt = e || window.evt;
        var $this = evt.currentTarget || evt.srcElement;
        var type = evt.type;
        if (type == 'submit') {
           var form = validanguage.whichFormAmI($this);
           var id = form;
        } else {
           var id = $this.id;
           var form = validanguage.formLookup[id];
        }
        if (customEvent=='typingTimeout') {    
           if( validanguage.typingDelay[id] ) window.clearTimeout(validanguage.typingDelay[id]);
           eval("validanguage.typingDelay[id] = window.setTimeout(\"validanguage.validationWrapper('"+id+"', 'typing')\", validanguage.settings.typingDelay );");
           return true;
        } 
    }
	//For Showing Flash messages on submit only
	if(validanguage.showFlashMsg==true)
	{
	    if(type=='submit')
 			validanguage.flag=false;
		else
		  validanguage.flag=true;
	}
	// Bail out early if the parent form is marked as disabled
    if( typeof validanguage.el[form] != 'undefined' && typeof validanguage.el[form].disabled != 'undefined' && validanguage.el[form].disabled == true ) return true;
    if (validanguage.forcedSubmission) return true;
    var validations = (type=='submit') ? validanguage.forms[form].validations : validanguage.el[id].handlers[type];
    var i=validations.length;
    var failedValidations = {};  //key value pair of id => validation
        
    // Prep the dispatchedAjax and failedValidations vars
    if (!validanguage.ajaxLookup[id]) validanguage.ajaxLookup[id] = [ 1 ];
    var ajaxLocation = (type=='submit') ? 'forms' : 'fields';
    
    if (!validanguage[ajaxLocation][id]) validanguage[ajaxLocation][id] = {};
    if (!validanguage[ajaxLocation][id][type]) validanguage[ajaxLocation][id][type] = {};
    validanguage[ajaxLocation][id][type].dispatchedAjax = {};
    if (typeof validanguage[ajaxLocation][id][type].failedValidations == 'undefined') 
        validanguage[ajaxLocation][id][type].failedValidations = {};
    
    // Handle re-validating a field when a prior ajax validation still has not returned
    // by resetting failedValidations and aborting the pending ajax requests
    if (!calledManually && !validanguage.empty(validanguage[ajaxLocation][id][type].failedValidations)) {
        validanguage[ajaxLocation][id][type].failedValidations = {};
    }        
    
    if(type=='submit') {

        if (validanguage.empty(validanguage[ajaxLocation][form][type].failedValidations)) {
        
            outerLoop:
            for(var j=0; j<i; j++) {
                if( typeof validations[j]=='undefined' || validations[j]==999) continue outerLoop; //skip deativated validations and transformations
                id = validations[j].element.id; //reassign $this and id to the form field in question
                var $this = validations[j].element;
                if( (typeof failedValidations[id] != 'undefined') || 
                       (typeof $this.disabled != 'undefined' && $this.disabled==true) ||
                       (typeof validanguage.el[id].disabled != 'undefined' && validanguage.el[id].disabled==true)
                 ) {
                    //continue outerLoop; //skip disabled fields or fields that already flunked // Commented by Chintan to display validation for disabled field
                }
                if( typeof validanguage.el[id].failed != 'undefined' && validanguage.el[id].failed==true) {
                    failedValidations[id] = { failed: true, field: validanguage.el[id].field };
                    continue outerLoop; //handle fields manually marked as invalid
                }
                var validOptionalField = !!(typeof validanguage.el[id].required != 'undefined' && (validanguage.el[id].required==false||validanguage.el[id].required=='false') &&
                    !$this.value.match(/[^\s]/));
                    
                var validationsCounter = validations[j].validationsCounter;
                var validation = validanguage.el[id].validations[validationsCounter];
                var funcs = validanguage.resolveArray(validation.name, 'function');
                innerLoop:
                for (var m=funcs.length-1; m>-1; m--) {
                    if (typeof failedValidations[id] != 'undefined') continue innerLoop; //this field already flunked
                    
                    //handle ajax validations
                    if (validation.isAjax && !validOptionalField) {
                        
                        if (!validanguage.ajaxLookup[id]) validanguage.ajaxLookup[id] = [ 1 ];                                
                        var dispatchAjax = true;
                        
                        // handle cached ajax lookups
                        if (validanguage.settings.cacheAjaxLookups && validanguage.ajaxLookup[id].length > 1) {
                            ajaxLookupLoop:
                            for (var ajaxLookupIndex = validanguage.ajaxLookup[id].length - 1; ajaxLookupIndex > -1; ajaxLookupIndex--) {
                                var lookupToCheck = validanguage.ajaxLookup[id][ajaxLookupIndex];
                                if (lookupToCheck.value == $this.value && typeof lookupToCheck.result=='boolean') {
                                    dispatchAjax = false;
                                    var result = lookupToCheck.result;
                                    if (result == false) {
                                        failedValidations[id]          = validation; //store this function in failedValidations. We remove it later if it's not applicable.
                                        failedValidations[id].field    = validanguage.el[id].field;
                                        failedValidations[id].errorMsg = lookupToCheck.errorMsg;
                                    }
                                    break ajaxLookupLoop;
                                }
                            }
                        }
                        
                        if (dispatchAjax) {
                            validanguage[ajaxLocation][form][type].dispatchedAjax[$this.id] = new Date().getTime();
                            failedValidations[id] = validation; //store this function in failedValidations. We remove it later if it's not applicable.
                            failedValidations[id].field = validanguage.el[id].field;
                            funcs[m].call($this, $this.value, validanguage.ajaxLookup[id][0]);  //fire off the ajax call
                            
                            // Store the details in ajaxLookup
                            var ajaxCounter = validanguage.ajaxLookup[id][0]++;
                            validanguage.ajaxLookup[id].push({
                                counter: ajaxCounter,
                                eventType: type,
                                value: $this.value,
                                result: 'pending'
                            });
                            var result = 'pending';
                        }                        
                    } else {
                        var result = (validOptionalField || funcs[m].call($this, $this.value));
                    }
                    
                    // Run the handlers on this item
                    if (result == false) {
                        failedValidations[id] = validation; //defer onerror handlers till later
                        failedValidations[id].field = validanguage.el[id].field;
                        if(! validanguage.forms[form].settings.validateAllFieldsOnsubmit) break outerLoop;
                    } else {
                        var onsuccess = validanguage.getElSetting('onsuccess',id,validation);
                        successHandlers = validanguage.resolveArray(onsuccess, 'function');
                        for (var n=successHandlers.length-1; n>-1; n--) {
                            successHandlers[n].call($this);
                        }
                    }
                } //close innerLoop
            } //close outerLoop
            validanguage.forms[form][type].failedValidations = failedValidations;
        }
        
        if (!validanguage.empty(validanguage.forms[form][type].dispatchedAjax)) {
            //If one or more ajax calls are pending, cancel the form submit until
            //the ajax call comes back
            validanguage.forms[form][type].ajaxInterval = window.setInterval( function() {
                validanguage.ajaxValidationWrapper(form, type);
            }, 500);
            return false;
        }

        //swap failedValidations into local variable
        failedValidations = (validanguage.forms[form][type].failedValidations==='callManually') ? {} : validanguage.forms[form][type].failedValidations;
        validanguage.forms[form][type].failedValidations = {};
        
        if( validanguage.empty(failedValidations) ) {
           var submitStatus = true;
        } else { //call all appropriate onerror handlers
           for (var o in failedValidations) {
               if( typeof failedValidations[o] == 'function' ) continue;
               var id = o;
               $this = document.getElementById(o);
               validation = failedValidations[o];
               var focusOnerror = validanguage.getElSetting('focusOnerror',id,validation);
               var errorMsg = validanguage.getElSetting('errorMsg',id,validation);
               var onerror = validanguage.getElSetting('onerror',id,validation);
               errorHandlers = validanguage.resolveArray(onerror,'function');
               for (var m=errorHandlers.length-1; m>-1; m--) {
                   errorHandlers[m].call($this, errorMsg);
               }

               var focusOnerror = validanguage.getElSetting('focusOnerror',id,validation);
               if( focusOnerror==true ) $this.focus();
               var showAlert = validanguage.getElSetting('showAlert',id,validation);
               if( showAlert ) alert(errorMsg);
           }
           var submitStatus = false;
        }

        //Call onsubmit transformations
        var transformation = (validanguage.el[form] &&  validanguage.el[form].onsubmit) ? validanguage.el[form].onsubmit : [];
        if (typeof transformation == 'string') transformation = [ transformation ];
        for (var n=transformation.length-1; n>-1; n--) {
           var transformations = validanguage.resolveArray(transformation[n],'function');
           for (var o=transformations.length-1; o>-1; o--) {
               var returnStatus = transformations[o].call(document.getElementById(form), submitStatus, failedValidations);
               if(typeof returnStatus == 'boolean') submitStatus = returnStatus;
           }
        }
        if( customEvent=='validateForm' ) {
           return { result: submitStatus, failedValidations: failedValidations };
        }
        return submitStatus;

    } else {

        var validation;
        
        //Skip disabled fields
        if( (typeof validanguage.el[id].disabled=='boolean' && validanguage.el[id].disabled==true)
               || (typeof $this.disabled!='undefined' && $this.disabled==true) ) {
           return;
        }
        var transformations = validanguage.el[id].transformations;
        var p = transformations.length;
        for( var q=0; q<p; q++) {
            if( typeof transformations[q]['on'+type]=='undefined' || transformations[q]['on'+type]!=true ) continue;
            var transformation = validanguage.resolveArray(transformations[q].name, 'function');
            var trLength = transformation.length;
            for (var m=0; m<trLength; m++) {
                transformation[m].call($this);
            }         
        }

        var validOptionalField = !!(typeof validanguage.el[id].required != 'undefined' && (validanguage.el[id].required==false||validanguage.el[id].required=='false') &&
            !$this.value.match(/[^\s]/));
                           
        if( typeof validanguage.el[id].failed=='boolean' && validanguage.el[id].failed==true ) {
           //Allow a user to manually flunk a field
           result = false;
        } else {
            
           if (validanguage.empty(validanguage.fields[id][type].failedValidations)) {
               //see if the field is valid
                var validationCounter;
                outerLoop: for (var j = 0; j < i; j++) {
                    if (typeof validations[j] == 'undefined' || validations[j] == 999) {
                        continue outerLoop; //skip deactivated validations and transformations
                    }
                    else {
                        validationCounter = validations[j];
                    }
                    //we run thru all the validations until one fails
                    validation = validanguage.el[id].validations[validationCounter];
                    var funcs = validanguage.resolveArray(validation.name, 'function');
                    
                    for (var m = funcs.length - 1; m > -1; m--) {
                        //handle ajax validations
                        if (typeof validation.isAjax != 'undefined') {

                            var dispatchAjax = true;
                            
                            // handle cached ajax lookups
                            if (validanguage.settings.cacheAjaxLookups && validanguage.ajaxLookup[id].length > 1) {
                                ajaxLookupLoop:
                                for (var ajaxLookupIndex = validanguage.ajaxLookup[id].length - 1; ajaxLookupIndex > -1; ajaxLookupIndex--) {
                                    var lookupToCheck = validanguage.ajaxLookup[id][ajaxLookupIndex];
                                    if (lookupToCheck.value == $this.value && typeof lookupToCheck.result=='boolean') {
                                        dispatchAjax = false;
                                        var result = lookupToCheck.result;
                                        if (result == false) {
                                            failedValidations[id]          = validation; //store this function in failedValidations. We remove it later if it's not applicable.
                                            failedValidations[id].field    = validanguage.el[id].field;
                                            failedValidations[id].errorMsg = lookupToCheck.errorMsg;
                                        }
                                        break ajaxLookupLoop;
                                    }
                                }
                            }
                            
                            if (dispatchAjax && !validOptionalField) {
                                validanguage.fields[id][type].dispatchedAjax[id] = new Date().getTime();
                                failedValidations[id] = validation; //store this function in failedValidations. We remove it later if it's not applicable.
                                failedValidations[id].field = validanguage.el[id].field;
                                funcs[m].call($this, $this.value, validanguage.ajaxLookup[id][0]); //fire off the ajax call
                                
                                // Store the details in ajaxLookup
                                var ajaxCounter = validanguage.ajaxLookup[id][0]++;
                                validanguage.ajaxLookup[id].push({
                                    counter: ajaxCounter,
                                    eventType: type,
                                    value: $this.value,
                                    result: 'pending'
                                });
                            }
                        } else {
                            var result = (validOptionalField || funcs[m].call($this, $this.value));
                        }
                        
                        if (result == false) 
                            break outerLoop;
                    }
                }
                if (validationCounter == undefined) return true; //exit early if all validations have been removed
                validanguage.fields[id][type].failedValidations = failedValidations;
            }
        }        
        
        if (!validanguage.empty(validanguage.fields[id][type].dispatchedAjax)) {
            //If one or more ajax calls are pending, exit early until the ajax call comes back
            validanguage.fields[id][type].ajaxInterval = window.setInterval( function() {
                validanguage.ajaxValidationWrapper(id, type);
            }, 500);
            return false;
        }

        //swap failedValidations into local variable
        if (typeof result == 'undefined') {
            failedValidations = (validanguage.fields[id][type].failedValidations === 'callManually') ? {} : validanguage.fields[id][type].failedValidations;
            if (failedValidations[id] && failedValidations[id].name) validation = failedValidations[id].name;
            var result = validanguage.empty(failedValidations) ? true : false;
        }
        validanguage.fields[id][type].failedValidations = {};
        
        //handle the result
        if( result == true ) {
           var onsuccess = validanguage.getElSetting('onsuccess',id,validation);
           successHandlers = validanguage.resolveArray(onsuccess,'function');
           for (var m=successHandlers.length-1; m>-1; m--) {
               successHandlers[m].call($this);
           }   
           return true;
        } else {
           var focusOnerror = validanguage.getElSetting('focusOnerror',id,validation);
           var errorMsg = (failedValidations[id] && failedValidations[id].errorMsg) ? failedValidations[id].errorMsg : validanguage.getElSetting('errorMsg',id,validation);
           var onerror = (failedValidations[id] && failedValidations[id].onerror) ? failedValidations[id].onerror : validanguage.getElSetting('onerror',id,validation);
           errorHandlers = validanguage.resolveArray(onerror,'function');
           for (var m=errorHandlers.length-1; m>-1; m--) {
               errorHandlers[m].call($this, errorMsg);
           }

           var focusOnerror = validanguage.getElSetting('focusOnerror',id,validation);
           if( focusOnerror==true ) $this.focus();

           var showAlert = validanguage.getElSetting('showAlert',id,validation);
           if( showAlert ) alert(errorMsg);

           return false;
        }      
    }
},

/**
* This function returns the ID of a form, if it exists. Otherwise, it
* returns the form's index position in the document.forms array.
* By using this function, we need not require all forms to have
* distinct IDs.
* 
* @param {DOM Node} Form Node
* @return {String} Form ID or Index position
*/
whichFormAmI: function ( obj ) {
    if (typeof obj.id != 'undefined' && (!validanguage.empty(obj.id))) return obj.id;
    var forms = document.forms;
    for (var i = 0, j = forms.length; i < j; i++) {
        if (forms[i] == obj) return i;
    }
}

} //close validanguage

validanguage.init();

