﻿(function($) {

/*
 * BaseValidator
 */

var BaseValidator = function(element, options) {
	this.element = element;
	this.options = jQuery.extend({}, BaseValidator.defaults, options);
	// Determines event for validation
	switch ((element.attr('tagName') || '').toLowerCase()) {
		case 'select':
		case 'textarea':
			this._validateEvent = 'change';
			break;
		case 'input':
			switch (element.attr('type').toLowerCase()) {
				case 'radio':
				case 'checkbox':
					this._validateEvent = 'click';
					break;
				default:
					this._validateEvent = 'change';
					break;
			}
			break;
		default:
			break;
	}
	if (!element.data('validators'))
		element.data('validators', [this]);
	else
		element.data('validators').push(this);
	if (this._validateEvent) {
		var self = this;
		this._validateHandler = function(ev) { self._onValidate(ev); };
		this.element.bind(this._validateEvent, this._validateHandler);
	}
	// Register validator in page validators collection
	BaseValidator._validators.push(this);
}

BaseValidator.defaults = {
	validationGroup: '',
	errorMessage: '',
	enabled: true,
	labelElement: null
}

BaseValidator._validators = [];
BaseValidator._summaries = [];
BaseValidator._allowSubmit = true;

jQuery.extend(BaseValidator.prototype, {
	_validateEvent: null,
	_validateHandler: null,

	getData: function(key) { return this.options[key]; },
	setData: function(key, value) { this.options[key] = value; },

	getValidationGroup: function() { return this.getData('validationGroup'); },
	setValidationGroup: function(value) { this.setData('validationGroup', value); },

	getErrorMessage: function() { return this.getData('errorMessage'); },
	setErrorMessage: function(value) { this.setData('errorMessage', value); },

	getEnabled: function() { return this.getData('enabled'); },
	setEnabled: function(value) { this.setData('enabled', value); },

	getLabel: function() { return this.getData('label'); },
	setLabel: function(value) { this.setData('label', value); },

	getValue: function() { return this.getValueToValidate(this.element); },

	getIsValid: function() { return this.getData('isValid'); },
	setIsValid: function(value) {
		if (this.getData('isValid') !== value) {
			this.setData('isValid', value);
			var label = this.getLabel() ? $(this.getLabel()) : [];
			if (label.length) {
				var errorMessage = this.getErrorMessage();
				for (var validators = this.element.data('validators'), i = 0, il = validators.length; i < il; i++) {
					var validator = validators[i];
					if (!validator.getEnabled())
						continue;
					value &= validator.getIsValid();
					if (!value) {
						errorMessage = validator.getErrorMessage();
						break;
					}
				}
				if (value) {
					label.removeClass('notvalid');
					label.removeAttr('title');
				} else {
					label.addClass('notvalid');
					label.attr('title', errorMessage);
				}
			}
		}
	},

	validate: function(validationGroup) {
		var isValid = true;
		if (this.getEnabled()
			&& (this.getValidationGroup() == validationGroup
				|| !/^\^/.test(this.getValidationGroup())
					&& (typeof(validationGroup) == "undefined"
						|| validationGroup == null
						|| validationGroup.length == 0))) {
			isValid = this.evaluateIsValid();
		}
		this.setIsValid(isValid);
	},

	evaluateIsValid: function() { return false; },

	getValueToValidate: function(element) {
		return element.val();
	},

	_onValidate: function(ev) {
		this.validate(null);
	}
});

/*
 * RequiredFieldValidatorEx
 */

var RequiredFieldValidatorEx = function(element, options) {
	BaseValidator.apply(this, [element, jQuery.extend({}, RequiredFieldValidatorEx.defaults, options)]);
};

RequiredFieldValidatorEx.defaults = {
	initialValue: ''
};

jQuery.extend(RequiredFieldValidatorEx.prototype, BaseValidator.prototype, {
	getInitialValue: function() { return this.getData('initialValue'); },
	setInitialValue: function(value) { this.setData('initialValue', value); },

	evaluateIsValid: function() { return jQuery.trim(this.getValue()) != jQuery.trim(this.getInitialValue()); }
});

/*
 * ValidationDataType
 */

var ValidationDataType = {
	String: 0,
	Integer: 1,
	Double: 2,
	Date: 3,
	Currency: 4
};

/*
 * ValidationCompareOperator
 */

var ValidationCompareOperator = {
	Equal: 0,
	NotEqual: 1,
	GreaterThan: 2,
	GreaterThanEqual: 3,
	LessThan: 4,
	LessThanEqual: 5,
	DataTypeCheck: 6
};

/*
 * BaseCompareValidatorEx
 */

var BaseCompareValidatorEx = function(element, options) {
	BaseValidator.apply(this, [element, jQuery.extend({}, BaseCompareValidatorEx.defaults, options)]);
};

BaseCompareValidatorEx.defaults = {
	dataType: 'String',
	cultureInvariantValues: false
};

jQuery.extend(BaseCompareValidatorEx.prototype, BaseValidator.prototype, {
	getDataType: function() { return this.getData('dataType'); },
	setDataType: function(value) { this.setData('dataType', value); },

	getCultureInvariantValues: function() { return this.getData('cultureInvariantValues'); },
	setCultureInvariantValues: function(value) { this.getData('cultureInvariantValues', value); },

	convert: function(value) {
		var v, civ = this.getCultureInvariantValues();
		switch (this.getDataType()) {
			case ValidationDataType.Integer:
				return value.match(/^\s*[-\+]?\d+\s*$/) && !isNaN(v = parseInt(value, 10)) ? v : null;
			case ValidationDataType.Double:
				return isNaN(v = (civ ? parseInt(value) : parseInt(value))) ? null : v;
			case ValidationDataType.Date:
				return (v = (civ ? new Date(value) : new Date(value))) ? v.valueOf() : null;
			case ValidationDataType.Currency:
				// TODO: NotImplemented
				return null;
			default:
				return value.toString();
		}
	},

	compare: function(operand1, operand2, operator) {
		var dataType = this.getDataType();
		var op1 = this.convert(operand1);
		if (op1 == null)
			return false;
		if (operator == ValidationCompareOperator.DataTypeCheck)
			return true;
		var op2 = this.convert(operand2);
		switch (operator) {
			case ValidationCompareOperator.NotEqual:
				return op1 != op2;
			case ValidationCompareOperator.GreaterThan:
				return op1 > op2;
			case ValidationCompareOperator.GreaterThanEqual:
				return op1 >= op2;
			case ValidationCompareOperator.LessThan:
				return op1 < op2;
			case ValidationCompareOperator.LessThanEqual:
				return op1 <= op2;
			default:
				return op1 == op2;
		}
	}
});

/*
 * CompareValidatorEx
 */

var CompareValidatorEx = function(element, options) {
	BaseCompareValidatorEx.apply(this, [element, jQuery.extend({}, CompareValidatorEx.defaults, options)]);
};

CompareValidatorEx.defaults = {
	controlToCompare: null,
	operator: ValidationCompareOperator.Equal,
	valueToCompare: ''
};

jQuery.extend(CompareValidatorEx.prototype, BaseCompareValidatorEx.prototype, {
	getControlToCompare: function() { return this.getData('controlToCompare'); },
	setControlToCompare: function(value) { this.setData('controlToCompare', value); },

	getOperator: function() { return this.getData('operator'); },
	setOperator: function(value) { this.setData('operator', value); },

	getValueToCompare: function() { return this.getData('valueToCompare'); },
	setValueToCompare: function(value) { this.setData('valueToCompare', value); },

	evaluateIsValid: function() {
		var value = jQuery.trim(this.getValue());
		if (value.length == 0)
			return true;
		var controlToCompare = this.getControlToCompare() ? $(this.getControlToCompare()) : [];
		var valueToCompare = controlToCompare.length ? this.getValueToValidate(controlToCompare) : this.getValueToCompare();
		return this.compare(value, valueToCompare, this.getOperator());
	}
});

/*
 * RangeValidatorEx
 */

var RangeValidatorEx = function(element, options) {
	BaseCompareValidatorEx.apply(this, [element, jQuery.extend({}, RangeValidatorEx.defaults, options)]);
};

RangeValidatorEx.defaults = {
	minimumValue: '',
	maximumValue: ''
};

jQuery.extend(RangeValidatorEx.prototype, BaseCompareValidatorEx.prototype, {
	getMinimumValue: function() { return this.getData('minimumValue'); },
	setMinimumValue: function(value) { this.setData('minimumValue', 'value'); },

	getMaximumValue: function() { return this.getData('maximumValue'); },
	setMaximumValue: function(value) { this.setData('maximumValue', 'value'); },

	evaluateIsValid: function() {
		var value = jQuery.trim(this.getValue());
		if (value.length == 0)
			return true;
		return this.compare(value, this.getMinimumValue(), ValidationCompareOperator.GreaterThanEqual)
			&& this.compare(value, this.getMaximumValue(), ValidationCompareOperator.LessThanEqual);
	}
});

/*
 * RegExpValidatorEx
 */

var RegExpValidatorEx = function(element, options) {
	BaseValidator.apply(this, [element, jQuery.extend({}, RegExpValidatorEx.defaults, options)]);
};

RegExpValidatorEx.defaults = {
	validationExpression: null
};

jQuery.extend(RegExpValidatorEx.prototype, BaseValidator.prototype, {
	getValidationExpression: function() { return this.getData('validationExpression'); },
	setValidationExpression: function(value) { this.setData('validationExpression', value); },

	evaluateIsValid: function() {
		var value = jQuery.trim(this.getValue());
		if (value.length == 0)
			return true;
		var rx = new RegExp(this.getValidationExpression());
		var matches = rx.exec(value);
		return matches != null && value == matches[0];
	}
});

/*
 * EmailValidatorEx
 */

var EmailValidatorEx = function(element, options) {
	RegExpValidatorEx.apply(this, [element, jQuery.extend({}, EmailValidatorEx.defaults, options)]);
};

EmailValidatorEx.defaults = {
	validationExpression: '^\\w+((-\\w+)|(\\.\\w+))*\\@\\w+((\\.|-)\\w+)*\\.\\w+$'
};

jQuery.extend(EmailValidatorEx.prototype, RegExpValidatorEx.prototype, {
});

/*
 * Ingenuity.ValidateEventArgs
 */

var ValidateEventArgs = function(element, value, isValid) {
	this._element = element;
	this._value = value;
	this._isValid = isValid;
}

jQuery.extend(ValidateEventArgs.prototype, {
	getElement: function() { return this._element; },
	getValue: function() { return this._value; },
	getIsValid: function() { return this._isValid; },
	setIsValid: function(value) { this._isValid = value; }
});

/*
 * CustomValidatorEx
 */

var CustomValidatorEx = function(element, options) {
	BaseValidator.apply(this, [element, jQuery.extend({}, CustomValidatorEx.defaults, options)]);
};

CustomValidatorEx.defaults = {
	validateEmptyText: false,
	clientValidationFunction: null
};

jQuery.extend(CustomValidatorEx.prototype, BaseValidator.prototype, {
	getValidateEmptyText: function() { return this.getData('validateEmptyText'); },
	setValidateEmptyText: function(value) { this.setData('validateEmptyText', value); },

	getClientValidationFunction: function() { return this.getData('clientValidationFunction'); },
	setClientValidationFunction: function(value) { this.setData('clientValidationFunction', value); },

	raiseValidate: function(args) {
		if (this.getClientValidationFunction() != null)
			eval(this.getClientValidationFunction() + '(this, args)');
	},

	evaluateIsValid: function() {
		var value = jQuery.trim(this.getValue());
		if (value.length == 0 && !this.getValidateEmptyText())
			return true;
		var args = new ValidateEventArgs(this.element, value, true);
		this.raiseValidate(args);
		return args.getIsValid();
	}
});

/*
 * ValidationSummary
 */

$.widget('ui.validationSummary', {
	getValidationGroup: function() { return this._getData('validationGroup'); },
	setValidationGroup: function(value) { this._setData('validationGroup', value); },

	getHeaderHtml: function() { return this.children('p').html(); },
	setHeaderHtml: function(value) {
		if (this.getHeaderHtml() !== value) {
			var p = this.element.children('p');
			if (value) {
				if (p.length == 0)
					this.element.prepend(p = $('<p />'));
				p.html(value);
			} else
				p.remove();
		}
	},

	setErrors: function(errors) {
		if (errors && errors.length) {
			var ol = this.element.children('ol');
			if (ol.length == 0)
				ol = $('<ol />').appendTo(this.element);
			else
				ol.empty();
			for (var i = 0; i < errors.length; i++) {
				$('<li />').html(errors[i]).appendTo(ol);
			}
			this.element.show()
		} else
			this.element.hide();
	},

	_init: function() {
		BaseValidator._summaries.push(this);
	}
});

$.ui.validationSummary.defaults = {
	validationGroup: '',
	headerHtml: null,
	errors: []
}

jQuery.fn.validator = function(type, options) {
	return new (eval(type))(this, options);
}

jQuery.validateOnSubmit = function() {
	var result = BaseValidator._allowSubmit;
	BaseValidator._allowSubmit = true;
	return result;
}

jQuery.validate = function(validationGroup) {
	var validators = BaseValidator._validators;
	var isValid = true;
	var errors = new Object();
	for (var i = 0, il = validators.length; i < il; i++) {
		var v = validators[i];
		v.validate(validationGroup);
		isValid &= v.getIsValid();
		if (!v.getIsValid()) {
			if (!errors.hasOwnProperty(v.getValidationGroup()))
				errors[v.getValidationGroup()] = [];
			errors[v.getValidationGroup()].push(v.getErrorMessage());
		}
	}
	var summaries = BaseValidator._summaries;
	for (var i = 0; i < summaries.length; i++) {
		var vs = summaries[i];
		vs.setErrors(errors[vs.getValidationGroup()]);
	}
	return BaseValidator._allowSubmit = isValid;
}

})(jQuery);

function Page_ClientValidate(validationGroup) { return jQuery.validate(validationGroup); }
