// LiveValidation 1.3 (prototype.js version)
// Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
// LiveValidation is licensed under the terms of the MIT License
var LiveValidation = Class.create();
Object.extend(LiveValidation, {
    VERSION: "1.3 prototype",
    TEXTAREA: 1,
    TEXT: 2,
    PASSWORD: 3,
    CHECKBOX: 4,
    SELECT: 5,
    FILE: 6,
    massValidate: function(C){
        var D = true;
        for (var B = 0, A = C.length; B < A; ++B) {
            var E = C[B].validate();
            if (D) {
                D = E;
            }
        }
        return D;
    }
});
LiveValidation.prototype = {
    validClass: "LV_valid",
    invalidClass: "LV_invalid",
    messageClass: "LV_validation_message",
    validFieldClass: "LV_valid_field",
    invalidFieldClass: "LV_invalid_field",
    initialize: function(B, A){
        if (!B) {
            throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");
        }
        this.element = $(B);
        if (!this.element) {
            throw new Error("LiveValidation::initialize - No element with reference or id of '" + B + "' exists!");
        }
        this.elementType = this.getElementType();
        this.validations = [];
        this.form = this.element.form;
        this.options = Object.extend({
            validMessage: "Thankyou!",
            onValid: function(){
                this.insertMessage(this.createMessageSpan());
                this.addFieldClass();
            },
            onInvalid: function(){
                this.insertMessage(this.createMessageSpan());
                this.addFieldClass();
            },
            insertAfterWhatNode: this.element,
            onlyOnBlur: false,
            wait: 0,
            onlyOnSubmit: false
        }, A ||
        {});
        var C = this.options.insertAfterWhatNode || this.element;
        this.options.insertAfterWhatNode = $(C);
        Object.extend(this, this.options);
        if (this.form) {
            this.formObj = LiveValidationForm.getInstance(this.form);
            this.formObj.addField(this);
        }
        this.boundFocus = this.doOnFocus.bindAsEventListener(this);
        Event.observe(this.element, "focus", this.boundFocus);
        if (!this.onlyOnSubmit) {
            switch (this.elementType) {
                case LiveValidation.CHECKBOX:
                    this.boundClick = this.validate.bindAsEventListener(this);
                    Event.observe(this.element, "click", this.boundClick);
                case LiveValidation.SELECT:
                case LiveValidation.FILE:
                    this.boundChange = this.validate.bindAsEventListener(this);
                    Event.observe(this.element, "change", this.boundChange);
                    break;
                default:
                    if (!this.onlyOnBlur) {
                        this.boundKeyup = this.deferValidation.bindAsEventListener(this);
                        Event.observe(this.element, "keyup", this.boundKeyup);
                    }
                    this.boundBlur = this.validate.bindAsEventListener(this);
                    Event.observe(this.element, "blur", this.boundBlur);
            }
        }
    },
    destroy: function(){
        if (this.formObj) {
            this.formObj.removeField(this);
            this.formObj.destroy();
        }
        Event.stopObserving(this.element, "focus", this.boundFocus);
        if (!this.onlyOnSubmit) {
            switch (this.elementType) {
                case LiveValidation.CHECKBOX:
                    Event.stopObserving(this.element, "click", this.boundClick);
                case LiveValidation.SELECT:
                case LiveValidation.FILE:
                    Event.stopObserving(this.element, "change", this.boundChange);
                    break;
                default:
                    if (!this.onlyOnBlur) {
                        Event.stopObserving(this.element, "keyup", this.boundKeyup);
                    }
                    Event.stopObserving(this.element, "blur", this.boundBlur);
            }
        }
        this.validations = [];
        this.removeMessageAndFieldClass();
    },
    add: function(A, B){
        this.validations.push({
            type: A,
            params: B ||
            {}
        });
        return this;
    },
    remove: function(A, B){
        this.validations = this.validations.reject(function(C){
            return (C.type == A && C.params == B);
        });
        return this;
    },
    deferValidation: function(A){
        if (this.wait >= 300) {
            this.removeMessageAndFieldClass();
        }
        if (this.timeout) {
            clearTimeout(this.timeout);
        }
        this.timeout = setTimeout(this.validate.bind(this), this.wait);
    },
    doOnBlur: function(){
        this.focused = false;
        this.validate();
    },
    doOnFocus: function(){
        this.focused = true;
        this.removeMessageAndFieldClass();
    },
    getElementType: function(){
        switch (true) {
            case (this.element.nodeName.toUpperCase() == "TEXTAREA"):
                return LiveValidation.TEXTAREA;
            case (this.element.nodeName.toUpperCase() == "INPUT" && this.element.type.toUpperCase() == "TEXT"):
                return LiveValidation.TEXT;
            case (this.element.nodeName.toUpperCase() == "INPUT" && this.element.type.toUpperCase() == "PASSWORD"):
                return LiveValidation.PASSWORD;
            case (this.element.nodeName.toUpperCase() == "INPUT" && this.element.type.toUpperCase() == "CHECKBOX"):
                return LiveValidation.CHECKBOX;
            case (this.element.nodeName.toUpperCase() == "INPUT" && this.element.type.toUpperCase() == "FILE"):
                return LiveValidation.FILE;
            case (this.element.nodeName.toUpperCase() == "SELECT"):
                return LiveValidation.SELECT;
            case (this.element.nodeName.toUpperCase() == "INPUT"):
                throw new Error("LiveValidation::getElementType - Cannot use LiveValidation on an " + this.element.type + " input!");
            default:
                throw new Error("LiveValidation::getElementType - Element must be an input, select, or textarea!");
        }
    },
    doValidations: function(){
        this.validationFailed = false;
        for (var C = 0, A = this.validations.length; C < A; ++C) {
            var B = this.validations[C];
            switch (B.type) {
                case Validate.Presence:
                case Validate.Confirmation:
                case Validate.Acceptance:
                    this.displayMessageWhenEmpty = true;
                    this.validationFailed = !this.validateElement(B.type, B.params);
                    break;
                default:
                    this.validationFailed = !this.validateElement(B.type, B.params);
                    break;
            }
            if (this.validationFailed) {
                return false;
            }
        }
        this.message = this.validMessage;
        return true;
    },
    validateElement: function(A, C){
        var D = (this.elementType == LiveValidation.SELECT) ? this.element.options[this.element.selectedIndex].value : this.element.value;
        if (A == Validate.Acceptance) {
            if (this.elementType != LiveValidation.CHECKBOX) {
                throw new Error("LiveValidation::validateElement - Element to validate acceptance must be a checkbox!");
            }
            D = this.element.checked;
        }
        var E = true;
        try {
            A(D, C);
        } 
        catch (B) {
            if (B instanceof Validate.Error) {
                if (D !== "" || (D === "" && this.displayMessageWhenEmpty)) {
                    this.validationFailed = true;
                    this.message = B.message;
                    E = false;
                }
            }
            else {
                throw B;
            }
        }
        finally {
            return E;
        }
    },
    validate: function(){
        if (!this.element.disabled) {
            var A = this.doValidations();
            if (A) {
                this.onValid();
                return true;
            }
            else {
                this.onInvalid();
                return false;
            }
        }
        else {
            return true;
        }
    },
    enable: function(){
        this.element.disabled = false;
        return this;
    },
    disable: function(){
        this.element.disabled = true;
        this.removeMessageAndFieldClass();
        return this;
    },
    createMessageSpan: function(){
        var A = document.createElement("span");
        var B = document.createTextNode(this.message);
        A.appendChild(B);
        return A;
    },
    insertMessage: function(B){
        this.removeMessage();
        var A = this.validationFailed ? this.invalidClass : this.validClass;
        if ((this.displayMessageWhenEmpty && (this.elementType == LiveValidation.CHECKBOX || this.element.value == "")) || this.element.value != "") {
            $(B).addClassName(this.messageClass + (" " + A));
            if (nxtSibling = this.insertAfterWhatNode.nextSibling) {
                this.insertAfterWhatNode.parentNode.insertBefore(B, nxtSibling);
            }
            else {
                this.insertAfterWhatNode.parentNode.appendChild(B);
            }
        }
    },
    addFieldClass: function(){
        this.removeFieldClass();
        if (!this.validationFailed) {
            if (this.displayMessageWhenEmpty || this.element.value != "") {
                if (!this.element.hasClassName(this.validFieldClass)) {
                    this.element.addClassName(this.validFieldClass);
                }
            }
        }
        else {
            if (!this.element.hasClassName(this.invalidFieldClass)) {
                this.element.addClassName(this.invalidFieldClass);
            }
        }
    },
    removeMessage: function(){
        if (nxtEl = this.insertAfterWhatNode.next("." + this.messageClass)) {
            nxtEl.remove();
        }
    },
    removeFieldClass: function(){
        this.element.removeClassName(this.invalidFieldClass);
        this.element.removeClassName(this.validFieldClass);
    },
    removeMessageAndFieldClass: function(){
        this.removeMessage();
        this.removeFieldClass();
    }
};
var LiveValidationForm = Class.create();
Object.extend(LiveValidationForm, {
    instances: {},
    getInstance: function(A){
        var B = Math.random() * Math.random();
        if (!A.id) {
            A.id = "formId_" + B.toString().replace(/\./, "") + new Date().valueOf();
        }
        if (!LiveValidationForm.instances[A.id]) {
            LiveValidationForm.instances[A.id] = new LiveValidationForm(A);
        }
        return LiveValidationForm.instances[A.id];
    }
});
LiveValidationForm.prototype = {
    initialize: function(A){
        this.element = $(A);
        this.fields = [];
        this.oldOnSubmit = this.element.onsubmit ||
        function(){
        };
        this.element.onsubmit = function(C){
            var B = (LiveValidation.massValidate(this.fields)) ? this.oldOnSubmit.call(this.element, C) !== false : false;
			
            if (!B) {
                Event.stop(C);
            }
        }
.bindAsEventListener(this);
    },
    addField: function(A){
        this.fields.push(A);
    },
    removeField: function(A){
        this.fields = this.fields.without(A);
    },
    destroy: function(A){
        if (this.fields.length != 0 && !A) {
            return false;
        }
        this.element.onsubmit = this.oldOnSubmit;
        LiveValidationForm.instances[this.element.id] = null;
        return true;
    }
};
var Validate = {
    Presence: function(A, B){
        var C = Object.extend({
            failureMessage: "Can't be empty!"
        }, B ||
        {});
        if (A === "" || A === null || A === undefined) {
            Validate.fail(C.failureMessage);
        }
        return true;
    },
    Numericality: function(B, C){
        var A = B;
        var B = Number(B);
        var C = C ||
        {};
        var D = {
            notANumberMessage: C.notANumberMessage || "Must be a number!",
            notAnIntegerMessage: C.notAnIntegerMessage || "Must be an integer!",
            wrongNumberMessage: C.wrongNumberMessage || "Must be " + C.is + "!",
            tooLowMessage: C.tooLowMessage || "Must not be less than " + C.minimum + "!",
            tooHighMessage: C.tooHighMessage || "Must not be more than " + C.maximum + "!",
            is: ((C.is) || (C.is == 0)) ? C.is : null,
            minimum: ((C.minimum) || (C.minimum == 0)) ? C.minimum : null,
            maximum: ((C.maximum) || (C.maximum == 0)) ? C.maximum : null,
            onlyInteger: C.onlyInteger || false
        };
        if (!isFinite(B)) {
            Validate.fail(D.notANumberMessage);
        }
        if (D.onlyInteger && ((/\.0+$|\.$/.test(String(A))) || (B != parseInt(B)))) {
            Validate.fail(D.notAnIntegerMessage);
        }
        switch (true) {
            case (D.is !== null):
                if (B != Number(D.is)) {
                    Validate.fail(D.wrongNumberMessage);
                }
                break;
            case (D.minimum !== null && D.maximum !== null):
                Validate.Numericality(B, {
                    tooLowMessage: D.tooLowMessage,
                    minimum: D.minimum
                });
                Validate.Numericality(B, {
                    tooHighMessage: D.tooHighMessage,
                    maximum: D.maximum
                });
                break;
            case (D.minimum !== null):
                if (B < Number(D.minimum)) {
                    Validate.fail(D.tooLowMessage);
                }
                break;
            case (D.maximum !== null):
                if (B > Number(D.maximum)) {
                    Validate.fail(D.tooHighMessage);
                }
                break;
        }
        return true;
    },
    Format: function(A, B){
        var A = String(A);
        var C = Object.extend({
            failureMessage: "Not valid!",
            pattern: /./,
            negate: false
        }, B ||
        {});
        if (!C.negate && !C.pattern.test(A)) {
            Validate.fail(C.failureMessage);
        }
        if (C.negate && C.pattern.test(A)) {
            Validate.fail(C.failureMessage);
        }
        return true;
    },
    Email: function(A, B){
        var C = Object.extend({
            failureMessage: "Must be a valid email address!"
        }, B ||
        {});
        Validate.Format(A, {
            failureMessage: C.failureMessage,
            pattern: /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
        });
        return true;
    },
    Length: function(A, B){
        var A = String(A);
        var B = B ||
        {};
        var C = {
            wrongLengthMessage: B.wrongLengthMessage || "Must be " + B.is + " characters long!",
            tooShortMessage: B.tooShortMessage || "Must not be less than " + B.minimum + " characters long!",
            tooLongMessage: B.tooLongMessage || "Must not be more than " + B.maximum + " characters long!",
            is: ((B.is) || (B.is == 0)) ? B.is : null,
            minimum: ((B.minimum) || (B.minimum == 0)) ? B.minimum : null,
            maximum: ((B.maximum) || (B.maximum == 0)) ? B.maximum : null
        };
        switch (true) {
            case (C.is !== null):
                if (A.length != Number(C.is)) {
                    Validate.fail(C.wrongLengthMessage);
                }
                break;
            case (C.minimum !== null && C.maximum !== null):
                Validate.Length(A, {
                    tooShortMessage: C.tooShortMessage,
                    minimum: C.minimum
                });
                Validate.Length(A, {
                    tooLongMessage: C.tooLongMessage,
                    maximum: C.maximum
                });
                break;
            case (C.minimum !== null):
                if (A.length < Number(C.minimum)) {
                    Validate.fail(C.tooShortMessage);
                }
                break;
            case (C.maximum !== null):
                if (A.length > Number(C.maximum)) {
                    Validate.fail(C.tooLongMessage);
                }
                break;
            default:
                throw new Error("Validate::Length - Length(s) to validate against must be provided!");
        }
        return true;
    },
    Inclusion: function(C, D){
        var E = Object.extend({
            failureMessage: "Must be included in the list!",
            within: [],
            allowNull: false,
            partialMatch: false,
            caseSensitive: true,
            negate: false
        }, D ||
        {});
        if (E.allowNull && C == null) {
            return true;
        }
        if (!E.allowNull && C == null) {
            Validate.fail(E.failureMessage);
        }
        if (!E.caseSensitive) {
            var A = [];
            E.within.each(function(F){
                if (typeof F == "string") {
                    F = F.toLowerCase();
                }
                A.push(F);
            });
            E.within = A;
            if (typeof C == "string") {
                C = C.toLowerCase();
            }
        }
        var B = (E.within.indexOf(C) == -1) ? false : true;
        if (E.partialMatch) {
            B = false;
            E.within.each(function(F){
                if (C.indexOf(F) != -1) {
                    B = true;
                }
            });
        }
        if ((!E.negate && !B) || (E.negate && B)) {
            Validate.fail(E.failureMessage);
        }
        return true;
    },
    Exclusion: function(A, B){
        var C = Object.extend({
            failureMessage: "Must not be included in the list!",
            within: [],
            allowNull: false,
            partialMatch: false,
            caseSensitive: true
        }, B ||
        {});
        C.negate = true;
        Validate.Inclusion(A, C);
        return true;
    },
    Confirmation: function(A, B){
        if (!B.match) {
            throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");
        }
        var C = Object.extend({
            failureMessage: "Does not match!",
            match: null
        }, B ||
        {});
        C.match = $(B.match);
        if (!C.match) {
            throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '" + C.match + "'!");
        }
        if (A != C.match.value) {
            Validate.fail(C.failureMessage);
        }
        return true;
    },
    Acceptance: function(A, B){
        var C = Object.extend({
            failureMessage: "Must be accepted!"
        }, B ||
        {});
        if (!A) {
            Validate.fail(C.failureMessage);
        }
        return true;
    },
    Custom: function(A, B){
        var C = Object.extend({
            against: function(){
                return true;
            },
            args: {},
            failureMessage: "Not valid!"
        }, B ||
        {});
        if (!C.against(A, C.args)) {
            Validate.fail(C.failureMessage);
        }
        return true;
    },
    now: function(A, D, C){
        if (!A) {
            throw new Error("Validate::now - Validation function must be provided!");
        }
        var E = true;
        try {
            A(D, C ||
            {});
        } 
        catch (B) {
            if (B instanceof Validate.Error) {
                E = false;
            }
            else {
                throw B;
            }
        }
        finally {
            return E;
        }
    },
    Error: function(A){
        this.message = A;
        this.name = "ValidationError";
    },
    fail: function(A){
        throw new Validate.Error(A);
    }
};
