﻿MV = MV || {};
/*
 * Inspired by
 * jQuery validation plug-in 1.5.5
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
 * http://docs.jquery.com/Plugins/Validation
 *
 * Copyright (c) 2006 - 2008 Jörn Zaefferer
*/

MV.Validation = (function() {

    var debug = false;

    var checkable = function(element) {
        return /radio|checkbox/i.test(element.type);
    };

    var getLength = function(value, element) {
        switch (element.nodeName.toLowerCase()) {
            case 'select':
                return $("option:selected", element).length;
            case 'input':
                if (checkable(element))
                    return this.findByName(element.name).filter(':checked').length;
        }
        return value.length;
    };

    var findByName = function(name) {
        // select by name and filter by form for performance over form.find("[name=...]")
        var form = this.currentForm;
        return $(document.getElementsByName(name)).map(function(index, element) {
            return element.form == form && element.name == name && element || null;
        });
    };

    var getValue = function(selector) {
        var value = $('#' + selector).val();
        var label = $('#' + selector + 'Label').text();

        return $.trim(value).length > 0 && label == value ? '' : value;
    };

    var getDependentValues = function(selectorArray) {
        var valueArray = [];
        for (var i = 0; i < selectorArray.length; i++) {
            var trueVal = getValue(selectorArray[i]);
            if (trueVal) {
                valueArray.push(trueVal);
            }
        }
        return valueArray;
    };

    var isNumber = function(value) {
        return /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
    }

    // http: //docs.jquery.com/Plugins/Validation/Methods
    var defaultValidators = {

        required: function(value, element, param) {
            // check if dependency is met
            switch (element.nodeName.toLowerCase()) {
                case 'select':
                    var options = $("option:selected", element);
                    return options.length > 0 && (element.type == "select-multiple" || ($.browser.msie && !(options[0].attributes['value'].specified) ? options[0].text : options[0].value).length > 0);
                case 'input':
                    if (checkable(element))
                        return getLength(value, element) > 0;
                default:
                    return $.trim(value).length > 0;
            }
        },

        number: function(value, element) {
            return value === '' || isNumber(value);
        },

        digits: function(value, element) {
            return /^\d+$/.test(value);
        },

        exclusiveRequired: function(value, element, additionalArgs) {
            var dependantValue = $('#' + additionalArgs[0]).val();
            var dependantLabel = $('#' + additionalArgs[0] + 'Label').text();
            var label = $('#' + element.id + 'Label').text();

            dependantValue = $.trim(dependantValue).length > 0 && dependantLabel == dependantValue ? '' : dependantValue;
            value = $.trim(value).length > 0 && label == value ? '' : value;

            return ($.trim(value).length > 0 || $.trim(dependantValue).length > 0) && !($.trim(value).length > 0 && $.trim(dependantValue).length > 0) && !($.trim(value).length == 0 && $.trim(dependantValue).length == 0);
        },

        dependent: function(value, element, additionalArgs) {
            var dependents = getDependentValues(additionalArgs);
            return $.trim(value).length > 0 || dependents.length > 0;
        },

        location: function(value, element, additionalArgs) {
            var dependents = getDependentValues(additionalArgs);
            return dependents.length > 0 || ($.trim(value).length > 0 && isNumber(value));
        },

        zipCode: function(value, element) {
            return getValue(element.id) === '' || /^\d{5}$|^\d{5}-\d{4}$/.test(value);
        }

    };

    var defaultRules = {};

    return {

        validators: {},
        rules: {},
        displayedMessages: {},

        init: function(opts) {
            this.leftOffestReferenceElement = $(opts.leftOffestReferenceSelector);
            this.validators = $.extend({}, defaultValidators);
            this.rules = $.extend({}, opts.validationRules);
            this.template = opts.template;
            this.errorMessageLeftPosition = '';

            return this;
        },

        validate: function() {
            var failedRules = this.runValidationRules('submit');
            if (failedRules) {
                this.displayValidationErrors(failedRules);
            }

            return failedRules.length == 0 ? true : false;
        },

        validateElement: function(element, testType, forceError) {
            var failedTests = this.runValidationRuleTests(element, this.rules[element].tests, testType, forceError);
            if (failedTests && failedTests.length > 0) {
                this.displayValidationError(element, failedTests);
            } else {
                this.clearValidationError(element);
            }
        },

        displayValidationErrors: function(failedRules) {
            this.clearAllValidationErrors();
            for (var j = 0; j < failedRules.length; j++) {
                var rule = failedRules[j];
                this.displayValidationError(rule.id, rule.failedTests);
            }
        },

        displayValidationError: function(referenceElementSelector, failedTests) {
            var errorExists = $('#' + referenceElementSelector + 'ErrorMessage').length > 0;
            var errorMessage = this.createValidationErrorMessage(failedTests);
            var error = this.createValidationError(referenceElementSelector, errorMessage);
            if (errorExists) {
                this.clearValidationError(referenceElementSelector);
            }
            this.showValidationError(error);
        },

        showValidationError: function(errorObj) {
            var eid = errorObj.id;
            if (this.displayedMessages[eid]) {
                var pos = eid.split('-');
                var t = parseInt(pos[0]) + 20;
                var l = parseInt(pos[1]);
                eid = t + '-' + l;
                errorObj.elm.css('top', t);
            }

            $('body').append(errorObj.elm);
            this.displayedMessages[eid] = errorObj;
        },

        forceValidationError: function(referenceElementSelector, errorMessage) {
            var error = this.createValidationError(referenceElementSelector, errorMessage);
            this.showValidationError(error);
        },

        createValidationError: function(referenceElementSelector, errorMessage) {
            var targetOffset = $('#' + referenceElementSelector).offset();
            var l = Math.round(this.getErrorLeftPosition());
            var t = Math.round(targetOffset.top);
            var error = this.template.clone().attr('id', referenceElementSelector + 'ErrorMessage').find('span').html(errorMessage).end().css({
                position: "absolute",
                marginLeft: 0, marginTop: 0,
                top: t, left: l
            });
            return { id: t + '-' + l, elm: error };
        },

        createValidationErrorMessage: function(failedTests) {
            var errorMessage = '';
            for (var i = 0; i < failedTests.length; i++) {
                var test = failedTests[i];
                if (test) {
                    errorMessage = i > 0 ? errorMessage + '<br/>' + test.message : test.message;
                }
            }
            return errorMessage;
        },

        clearAllValidationErrors: function() {
            $('.popup').remove();
            this.displayedMessages = {};
        },

        clearValidationError: function(referenceElementSelector) {
            var error = this.getErrorMessageObject(referenceElementSelector);
            if (error.id) {
                // ie does not like //error.elm.remove(); 
                var errorDom = error.elm.get(0);
                errorDom.parentNode.removeChild(errorDom);
                delete this.displayedMessages[error.id];
            }
        },

        runValidationRules: function(testType) {
            var failedRules = [];
            for (var ruleName in this.rules) {
                var failedTests = this.runValidationRuleTests(ruleName, this.rules[ruleName].tests, testType);
                if (failedTests && failedTests.length > 0) {
                    failedRules.push({ id: ruleName, failedTests: failedTests });
                }
            }
            return failedRules;
        },

        runValidationRuleTests: function(selector, tests, testType, forceError) {
            var $element = $('#' + selector);
            try {
                var element = $element.get(0);
                var _failedTests = [];
                for (var i = 0; i < tests.length; i++) {
                    var test = tests[i];
                    if (!testType || test.events.join(',').indexOf(testType) >= 0) {
                        var additionalArgs = test.additionalArgs && test.additionalArgs.length ? test.additionalArgs : [];
                        var p = forceError ? false : this.validators[test.name]($.trim(element.value), element, additionalArgs);
                        if (!p) {
                            _failedTests.push({ name: test.name, pass: p, message: test.message });
                        }
                    }
                }
                return _failedTests;
            } catch (e) {
                if (debug && window.console) {
                    console.log("exception occured when checking element %s, check the %s rule", selector);
                }
            };
        },

        reset: function() {
            this.clearAllValidationErrors();
        },

        addValidators: function(hash) {
            $.extend(this.validators, hash)
        },

        addValidator: function(key, fn) {
            this.validators[key] = fn;
        },

        removeValidator: function(key) {
            delete this.validators[key];
        },

        addRules: function(hash) {
            $.extend(this.rules, hash);
        },

        addRule: function(key, fn) {
            this.rules[key] = fn;
        },

        getErrorLeftPosition: function() {
            var left = (this.errorMessageLeftPosition) ? this.errorMessageLeftPosition : this.leftOffestReferenceElement.offset().left + this.leftOffestReferenceElement.width() + 7;
            this.errorMessageLeftPosition = left;
            return left;
        },

        getErrorMessageObject: function(referenceElementSelector) {
            var err = $('#' + referenceElementSelector + 'ErrorMessage');
            var eid = null;
            var eelm = null;
            if (err.length > 0) {
                eid = parseInt(err.css('top')) + '-' + parseInt(err.css('left'));
                eelm = err;
            }
            return { id: eid, elm: eelm };
        }

    }


})();
