// VALIDATOR.JS by frequency-decoder.com

// Array.push code by www.peterbailey.net
if(!Array.prototype.push)  {
        Array.prototype.push = function()
        {
                for ( var i = 0; i < arguments.length; i++ )
                {
                        this[this.length] = arguments[i];
                }
                return this.length;
        }
}

if(!String.prototype.trim) String.prototype.trim = function() { return this.replace(/^\s*/,'').replace(/\s*$/, ''); }

function formValidator(formid) {
        this.formid = formid;
        this.rules = new Array();
        this.styleLabelOnErr = true;
        this.invalidClass = "invalid";
        this.warningClass = "warn";
        this.validateAsErr = false;

        this.init = function() {
                var nodes = new Array();
                // getElementsByTagName returns an object collection which doesn't have a .concat() method
                var tempnodes = document.getElementById(self.formid).getElementsByTagName('input');
                for(var i = 0; i < tempnodes.length; i++) if(tempnodes.item(i).type.toUpperCase() == 'TEXT' || tempnodes.item(i).type.toUpperCase() == 'PASSWORD') nodes.push(tempnodes.item(i));
                tempnodes = document.getElementById(self.formid).getElementsByTagName('textarea');
                for(i = 0; i < tempnodes.length; i++) nodes.push(tempnodes.item(i));

                for(i = 0; i < nodes.length; i++) {
                        var current = nodes[i];
                        
                        // Only if the INPUT/TEXTAREA has an id;
                        if(typeof current.id != 'undefined' && current.id != "") {
                                while(current.previousSibling) {
                                        current = current.previousSibling;
                                        if(!(current.nodeName.toUpperCase().indexOf('TEXT') != -1 || current.nodeName.toUpperCase().indexOf('COMMENT') != -1)) { break; }

                                        if(current.nodeName.toUpperCase().indexOf('COMMENT') != -1 && current.nodeValue.trim().indexOf(nodes[i].id) == 0) {
                                                var argList = current.nodeValue.trim().split(" ");
                                                // Only if we can create a RegExp with the second argument
                                                if(argList.length > 3 && typeof new RegExp(argList[1]) != 'undefined') self.addRule(nodes[i], argList[1], (argList[2].trim() == "true" ? true : false), (argList[3].trim() == "true" ? true : false));
                                                //DEBUG: alert('Added RegExp ' + argList[1] + ' for element id: '+nodes[i].id)
                                        }
                                }
                        }
                }
                // Call the user defined initialisation
                self.extendedInitialisation();
        }

        this.styleFields = function(which) {

                var label = null;
                if(self.styleLabelOnErr) {
                        // Get the implicit label if any (f.y.i. depreciated as from HTML4)
                        if(self.rules[which].element.parentNode.tagName.toUpperCase() == 'LABEL') {
                                label = self.rules[which].element.parentNode;
                        // No implicit label then search explicit label list
                        } else {
                                var labelList = document.getElementById(self.formid).getElementsByTagName('label');
                                // loop through label array attempting to match each 'for' attribute to the id of the current element
                                for(var lbl = 0; lbl < labelList.length; lbl++) {
                                        // Internet Explorer requires the htmlFor test
                                        if(labelList[lbl]['htmlFor'] && labelList[lbl]['htmlFor'] == self.rules[which].element.id) {
                                                label = labelList[lbl];
                                        // All other compliant browsers
                                        } else if(labelList[lbl].getAttribute('for') == self.rules[which].element.id) {
                                                label = labelList[lbl];
                                        }
                                }
                        }
                }

                if(self.rules[which].valid == false) {
                        if(!self.validateAsErr || !self.rules[which].required) {
                                var classout = self.invalidClass;
                                var classin  = self.warningClass;
                        } else {
                                var classout = self.warningClass;
                                var classin  = self.invalidClass;
                        }
                        self.rules[which].element.className = self.rules[which].element.className.replace(classout, "");
                        if(self.rules[which].element.className.search(classin) == -1) { self.rules[which].element.className += " " + classin; }

                        if(self.styleLabelOnErr && label != null) {
                                label.className = label.className.replace(classout, "");
                                if(label.className.search(classin) == -1) label.className += " " + classin;
                        }
                } else {
                        self.rules[which].element.className = self.rules[which].element.className.replace(self.invalidClass, "");
                        self.rules[which].element.className = self.rules[which].element.className.replace(self.warningClass, "");
                        if(self.styleLabelOnErr && label != null) {
                                label.className = label.className.replace(self.invalidClass, "");
                                label.className = label.className.replace(self.warningClass, "");
                        }
                }
        }
        this.validateSingleRule = function(which) {
                self.rules[which].valid = !(self.rules[which].element.value.search(self.rules[which].regex) == -1);
                // Validate if empty and not a required field
                if(!self.rules[which].required && self.rules[which].element.value.trim() == '') self.rules[which].valid = true;
                
                // Call the user defined validation routine
                self.extendedValidateSingleRule(self.rules[which].element_id, which);
                // Style the input and label
                self.styleFields(which);
                // Carry out any post validation processing
                self.postSingleRuleValidation(self.rules[which].element_id, which);
        }
        // event handler for individual input fields (activated onblur/onchange)
        this.validateSingle = function(e) {
                var i = 0;
                while(i<self.rules.length) {
                        if(this.id == self.rules[i].element.id) {
                                self.validateSingleRule(i);
                        }
                        i++;
                }

                return true;
        }
        // event handler for the submit button
        this.validateAll = function(e) {
                self.validateAsErr = true;
                for(var i = 0; i < self.rules.length; i++) {
                        self.validateSingleRule(i);
                }
                self.postSubmitValidation();
                return (self.extendedValidate() && self.isValid());
        }
        // Are all (required) form elements valid
        this.isValid = function() {
                var valid = true;
                for(var i = 0; i < self.rules.length; i++) {
                        if(!self.rules[i].valid && self.rules[i].required) { valid = false; }
                }
                return valid;
        }
        
        // Create private variable 'self'
        var self = this;
        
        // Attach onsubmit event handler to the form
        document.getElementById(this.formid).onsubmit = self.validateAll;

        return this;
}
formValidator.prototype.addRule = function(element, regex, useblur, required) {
        var obj     = new Object();
        obj.element = element
        obj.element_id = element.id;
        
        // Use onchange or onblur, it's up to you..
        if(useblur) obj.element.onblur   = this.validateSingle;
        else        obj.element.onchange = this.validateSingle;
        
        // Is this a required form field?
        obj.required = required;

        obj.regex   = regex.trim();

        obj.valid   = false;
        this.rules.push(obj);
}

// Override the following prototypes on a page by page basis if needs be
formValidator.prototype.extendedInitialisation = function() { }
formValidator.prototype.extendedValidate = function() { return true; }
formValidator.prototype.extendedValidateSingleRule = function(id, rule) { }
formValidator.prototype.postSubmitValidation = function() { }
formValidator.prototype.postSingleRuleValidation = function() { }

formValidator.validatorCollection = new Array();

function initiateFormValidation() {
        // Bail out if the browser can't handle the script
        if(!document.getElementById  || !document.getElementsByTagName) return;

        var validator;
        var formCollection = document.getElementsByTagName('form');

        // Iterate over the forms
        for(i = 0; i < formCollection.length; i++) {
                // If the current form has an assigned id
                if(typeof formCollection[i].id != 'undefined' && formCollection[i].id != "") {
                        // Create new validator object
                        validator = new formValidator(formCollection[i].id);

                        // Initialise the rule parsing
                        validator.init();
                        formValidator.validatorCollection.push(validator);
                }
        }
}
// Helper function: Retuns the validator object corresponding to the form id
function getValidatorObject(formid) {
        for(i = 0; i < formValidator.validatorCollection.length; i++) {
                if(formValidator.validatorCollection[i].formid == formid) { return formValidator.validatorCollection[i]; }
        }
        return null;
}
/* Traditional event handler used to attach the onload event
   Check out brotherCakes generic onload should you need a more flexible mechanism

   http://www.brothercake.com/site/resources/scripts/onload/ */
onload = initiateFormValidation
