/*
* class:  MooForm
* version: 2.0
* by LEN nguyen van
* description: JavaScript Form Validation
*/

var MooForm = new Class({
	Implements: [Events, Options],	
	options: {		
		layer: {
			instance: null,
			offset: {
				x: 0,
				y: 0
			},
			css: {
				layer: 'mooFormLayer',
				popup: 'mooFormPopup'				
			},
			suffix: 'MooFormLayer',			
			zIndex: 9999,
			timeHide: 3000,
			timeWait: null
		},
		mooForm: {
			form: null,
			data: null,	
			error: null,
			ajax: false,	
			layer: 'layer',	
			language: 'en',
			overlay: null,
			delimeter: '|'
		},
		regExp: {
			alpha: /^[a-z ._-]+$/i,
			alphanumeric: /^[a-z0-9 ._-]+$/i,
			digit: /^[-+]?[0-9]+$/,
			number: /^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/,
			email: /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,
			phone: /^\+?1?([- ()\.]{0,3}\d){7,16}( *(x|ext)\.? *(\d{1,10}))?$/,
			url: /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i
		}		
	},
	initialize: function(form, data, options){	
		var that = this;			
		that.setOptions(options);
		var formInstance = $(form) || $(document.forms[form]);
		if (!formInstance) {
			that.log(form + ' Form does not exists.');
			return;
		}						
		that.options.mooForm.form = formInstance;
		that.options.mooForm.data = data;
		that.log(form + ' - instance of MooFrom is starting...');
		that.initLayer();
		that.initMooForm();		
		that.log(form + ' - instance of MooFrom is ready.');
	},
	appendData: function(data, position){	
		var that = this;		
		var newData = [];
		if(position >= that.options.mooForm.data.length){
			position = that.options.mooForm.data.length + 1;
		}
		newData = that.options.mooForm.data.filter(function(item, index){
			return index < position;
		});
		newData.push(data);
		for(var i = position; i < that.options.mooForm.data.length; i++){			
			newData.push(that.options.mooForm.data[i]);
		}
		that.options.mooForm.data = newData;
		that.initMooForm();		
	},
	initLayer: function(){
		var that = this;		
		var form = that.options.mooForm.form;
		var formId = form.get('id') || form.get('name');
		var layerContent = [];			
		switch(that.options.mooForm.layer){
			case 'layer':
				layerContent = [
					new Element('a', {
						'href': 'javascript:void(0);',
						'html': 'x',
						'class': 'close',
						'events': {
							'click': function(e){
								e.stop();
								that.hideLayer();
							}
						}
					}),
					new Element('p', {
						'class': 'message'
					})
				];
				break;
			case 'popup':
				layerContent = [
					new Element('table', {
						cellPadding: '0',
						cellSpacing: '0',
						border: '0'
					}).adopt([
						new Element('tbody').adopt([
							new Element('tr').adopt([
								new Element('td', {'class' : 'tl'}),
								new Element('td', {'class' : 't'}),
								new Element('td', {'class' : 'tr'})
							]),
							new Element('tr').adopt([
								new Element('td', {'class' : 'l'}),
								new Element('td', {'class' : 'c'}).adopt([
									new Element('p', {'class': 'message'}),
									new Element('a', {
										'class' : 'close',
										'events': {
											'click': function(e){
												e.stop();
												that.hideLayer();
											}
										} 	
									})									
								]),								
								new Element('td', {'class' : 'r'})
							]),
							new Element('tr').adopt([
								new Element('td', {'class' : 'bl'}),
								new Element('td', {'class' : 'b'}),
								new Element('td', {'class' : 'br'})
							])
						])
					])			
				];
				with(that.options.layer.offset){
					x = -7;
					y = -77;
				}
				break;			
		}		
		that.options.layer.instance = new Element('div', {
			'id': formId + that.options.layer.suffix,
			'class': that.options.layer.css[that.options.mooForm.layer]
		}).adopt(layerContent).inject($(document.body));			
		if(that.options.mooForm.layer == 'popup' && Browser.Engine.trident4){			
			that.fixIE6PNG();
		}
	},
	initMooForm: function(){
		var that = this;		
		var form = that.options.mooForm.form;
		var data = that.options.mooForm.data;		
		for (var i = 0; i < data.length; i++) {
			var element = $(form[data[i].name]);
			if(element){
				that.log(form.get('name') + ' - instance of MooFrom is initializing ' + element.get('name') + ' element');				
				that.initElement(element, data[i]);														
			}
	    }		
	    form.removeEvents().addEvents({
			'submit': function(e){				
				that.log(form.get('name') + ' - instance of MooFrom fired event : onSubmit');								
				if (that.isValidMooForm()) {					
					that.fireEvent('submit', this);							
					if(that.options.mooForm.ajax){
						e.stop();	
					}
				}
				else{
					e.stop();
				}
			},
			'reset': function(e){
				that.log(form.get('name') + ' - instance of MooFrom fired event : onReset');
				that.resetMooForm();					
			}			
	    });
	},
	isValidMooForm: function(){
		var that = this;
		var form = that.options.mooForm.form;
		var data = that.options.mooForm.data;			
		for (var i = 0; i < data.length; i++) {
			var element = $(form[data[i].name]);
			if(element){
				var validated = that.validElement(element);					
				element.store('element:validated', validated);
				that.log(form.get('name') + ' - instance of MooFrom is validating ' + element.get('name') + ' element : ' + validated);
				if (!validated) {
					that.showLayer(element);
					return false;
				}							
			}
	    }		
	    return true;
	},	
	resetMooForm: function(){
		var that = this;
		that.hideLayer();		
		var form = that.options.mooForm.form;		
		form.reset();				
	},
	initElement: function(element, data){
		var that = this;		
		element.store('element:data', data);
		element.store('element:validated', true);			    
	    if ($type(data.init) != false) {	    
			switch(element.type){
				case 'text':
				case 'textarea':
				case 'password':
					element.store('element:init', element.get('value').trim());
					if(element.get('value').trim() == ''){
						element.set('value', data.init);
					}
					element.addEvents({
						'focus': function(){							
			                if (element.get('value').trim() == data.init) {
			                    element.set('value', '');
			                }
						},
						'blur': function(){
			                if (element.get('value').trim() == '') {
			                    element.set('value', data.init);
			                }
			            }
					});
					break;
				case 'select-one':
				case 'select-multiple':
					element.store('element:init', element.selectedIndex);
					element.selectedIndex = Number(data.init);
					break;
				case 'radio':					
				case 'checkbox':
					element.store('element:init', element.checked);
					element.form[element.get('name')][Number(data.init)].checked = true;					
			}
	    }
		if ($type(data.maxLength) != false){
			switch(element.type){
				case 'text':
				case 'password':
					element.set('maxLength', data.maxLength);
					break;
				case 'textarea':
					element.addEvents({
						'keypress': function(e){
							if (element.get('value').trim().length >= data.maxLength && !/backspace|delete|left|right/.test(e.key)) { 
								element.set('value', element.get('value').trim().substring(0, data.maxLength));
								e.stop(); 
							}
						},
						'change': function(e){
							if (element.get('value').trim().length >= data.maxLength) { 
								element.set('value', element.get('value').trim().substring(0, data.maxLength));
							}
						}
					});					
			}
		}		    
		if ($type(data.restrict) != false){
			switch(element.type){
				case 'text':
				case 'password':					
				case 'textarea':
					element.addEvent('keypress', function(e){
						if (!/backspace|delete|left|right/.test(e.key) && !new RegExp(data.restrict).test(e.key)) { 
							e.stop(); 
						}
			        });					
			}
		}
		if ($type(data.events) != false) {			
			element.addEvents(data.events);
		}
		element.addEvents({
			'click': function(e){	
				that.fireEvent('change', element);				
				that.hideLayer(this);
			},
			'keyup': function(e){					
				that.fireEvent('change', element);				
				that.hideLayer(this);
			},
			'change': function(e){							
				that.fireEvent('change', element);				
				that.hideLayer(this);
			}
		});
	},
	validElement: function(element){
		var that = this;		
		var data = element.retrieve('element:data');
		switch ($type(data.valid)) {	        	
			case 'function':	
 				element.store('element:message', data.message[that.options.mooForm.language].trim());	
			    return data.valid(element);	            
			case 'boolean':				
				element.store('element:message', data.message[that.options.mooForm.language].trim());	
				return data.valid;				
	        case 'string':
				var validate = data.valid.trim();
				var validateArray = validate.split(that.options.mooForm.delimeter);
				var message = data.message[that.options.mooForm.language].trim();
				var messageArray = message.split(that.options.mooForm.delimeter);				
				for(var i = 0; i < validateArray.length; i++){					
					if(!that.isValidated(element, validateArray[i])){
						element.store('element:validated', false);
						element.store('element:message', messageArray[i]);	
						return false;
					}
				}											
		}	
		return true;
	},
	isValidated: function(element, valid){
		var that = this;
		var data = element.retrieve('element:data');		
		var value = $pick(element.get('value'), '').trim();		
		var defaultValidated = value == $pick(data.init, '');		
		switch(valid){			
			case 'range':
				switch(element.type){
					case 'text':
					case 'textarea':
						return defaultValidated || Number(value) >= $pick(data.minimum, 0) && Number(value) <= $pick(data.maximum, 0);					
				}
				break;
			case 'required':
				switch(element.type){
					case 'text':
					case 'textarea':
					case 'password':
					case 'file':
						return value.length > 0 && value != $pick(data.init, '');						
				}								
				break;
			case 'minimum':
				switch(element.type){
					case 'text':
					case 'textarea':
					case 'password':
					case 'file':
						return value.length >= $pick(data.minimum, 0) && value != $pick(data.init, '');						
				}								
				break;
			case 'alpha':
				switch(element.type){
					case 'text':
					case 'textarea':
					case 'password':					
						return defaultValidated || that.executeRegExp(that.options.regExp.alpha, value);							
				}								
				break;
			case 'alphanumeric':
				switch(element.type){
					case 'text':
					case 'textarea':
					case 'password':					
						return defaultValidated || that.executeRegExp(that.options.regExp.alphanumeric, value);							
				}								
				break;
			case 'digit':
				switch(element.type){
					case 'text':
					case 'textarea':
					case 'password':
						return defaultValidated || that.executeRegExp(that.options.regExp.digit, value);							
				}				
				break;
			case 'number':
				switch(element.type){
					case 'text':
					case 'textarea':
					case 'password':
						return defaultValidated || that.executeRegExp(that.options.regExp.number, value);							
				}				
				break;
			case 'email':
				switch(element.type){
					case 'text':
					case 'textarea':
						return defaultValidated || that.executeRegExp(that.options.regExp.email, value);							
				}				
				break;		
			case 'url':
				switch(element.type){
					case 'text':
					case 'textarea':
						return defaultValidated || that.executeRegExp(that.options.regExp.url, value);							
				}				
				break;		
			case 'phone':
				switch(element.type){
					case 'text':
					case 'textarea':
						return defaultValidated || that.executeRegExp(that.options.regExp.phone, value);							
				}				
				break;			
			case 'date':
				switch(element.type){
					case 'text':
					case 'textarea':
					case 'label':
						return defaultValidated || that.isDate($pick(data.format, 'dmy'), value);							
				}		
				break;
			case 'future':
				switch(element.type){
					case 'text':
					case 'textarea':
						var other = $(element.form[data.other]);
						var format = $pick(data.format, 'dmy');
						if(!other){
							var date = new Date();
							var _date = date.getDate();
							var _month = date.getMonth() + 1;
							var _year = date.getFullYear();
							switch(format){
								case 'dmy':
									other = new Array(_date, _month, _year).join('/');
									break;
								case 'mdy':
									other = new Array(_month, _date, _year).join('/');
									break;
								case 'ymd':
									other = new Array(_year, _month, _day).join('/');																	
							}
							
						}
						else{
							other = other.get('value').trim();
						}						
						return defaultValidated || that.isCompareDate(format, other, value) == -1;						
				}
				break;
			case 'compare':
				switch(element.type){
					case 'text':
					case 'textarea':
					case 'password':
						var other = $(element.form[data.other]);
						var format = $pick(data.format, 'dmy');
						if(!other){
							switch($pick(data.type, 'string')){
								case 'string':
									other = '';		
									break;
								case 'number':
									other = 0;	
									break;
								case 'date':										
									var date = new Date();
									var _date = date.getDate();
									var _month = date.getMonth() + 1;
									var _year = date.getFullYear();
									switch(format){
										case 'dmy':
											other = new Array(_date, _month, _year).join('/');
											break;
										case 'mdy':
											other = new Array(_month, _date, _year).join('/');
											break;
										case 'ymd':
											other = new Array(_year, _month, _day).join('/');																				
									}							
							}														
						}
						else{
							other = other.get('value').trim();
						}	
						switch($pick(data.operator, '=')){
							case '=':
								switch($pick(data.type, 'string')){
									case 'string':
										return defaultValidated || value == other;												
									case 'number':
										return defaultValidated || Number(value) == Number(other);											
									case 'date':										
										return defaultValidated || that.isCompareDate(format, other, value) == 0;										
								}								
								break;
							case '!=':
								switch($pick(data.type, 'string')){
									case 'string':
										return defaultValidated || value != other;												
									case 'number':
										return defaultValidated || Number(value) != Number(other);											
									case 'date':										
										return defaultValidated || that.isCompareDate(format, other, value) != 0;																			
								}							
								break;
							case '>':
								switch($pick(data.type, 'string')){
									case 'string':
										return defaultValidated || value > other;												
									case 'number':
										return defaultValidated || Number(value) > Number(other);											
									case 'date':										
										return defaultValidated || that.isCompareDate(format, other, value) == -1;								
								}								
								break;
							case '>=':
								switch($pick(data.type, 'string')){
									case 'string':
										return defaultValidated || value >= other;												
									case 'number':
										return defaultValidated || Number(value) >= Number(other);											
									case 'date':										
										return defaultValidated || value.isCompareDate(format, other) == -1 || that.isCompareDate(format, other, value) == 0;								
								}								
								break;
							case '<':
								switch($pick(data.type, 'string')){
									case 'string':
										return defaultValidated || value < other;											
									case 'number':
										return defaultValidated || Number(value) < Number(other);										
									case 'date':										
										return defaultValidated || that.isCompareDate(format, other, value) == 1;									
								}	
								break;
							case '<=':
								switch($pick(data.type, 'string')){
									case 'string':
										return defaultValidated || value <= other;											
									case 'number':
										return defaultValidated || Number(value) <= Number(other);										
									case 'date':										
										return defaultValidated || that.isCompareDate(format, other, value) == 1 || that.isCompareDate(format, other, value) == 0;									
								}									
						}												
				}
				break;					
			case 'checked':
				switch(element.type){
					case 'checkbox':						
					case 'radio':					
						var group = element.form[element.name];						
						if(!group.length){
							return group.checked;
						}
						else{									
							for(var i = 0; i < group.length; i++){								
								if(group[i].checked){
									return true;
								}
							}
						}			
						return false;						
				}		
				break;
			case 'selected':
				switch(element.type){
					case 'select-one':							
						return element.selectedIndex != $pick(data.init, (element.size == 0) ? 0 : -1);						
					case 'select-multiple':							
						return element.selectedIndex != $pick(data.init, -1);											
				}
				break;
			default:							
				if($type(eval('data.' + valid)) != false){						
					return eval('data.' + valid + '()');
				}				
		}
		return true;
	},
	executeRegExp: function(re, value){
		return re.test(value);
	},
	isDate: function(format, value){
		var re = /[.\/-]/;
	    var arr = value.split(re);
	    if (arr.length != 3) {
			return false;
		}
	    var y = parseInt(arr[format.indexOf('y')], 10);
	    var m = parseInt(arr[format.indexOf('m')], 10);
	    var d = parseInt(arr[format.indexOf('d')], 10);
	    var date = new Date(y, m - 1, d);
	    return (y == date.getFullYear() && m == date.getMonth() + 1 && d == date.getDate());
	},
	isCompareDate: function(format, other, value){		
		var re = /[.\/-]/;
	    var a1 = value.split(re);
	    var y1 = parseInt(a1[format.indexOf('y')]);
	    var m1 = parseInt(a1[format.indexOf('m')]);
	    var d1 = parseInt(a1[format.indexOf('d')]);	    
	    var a2 = other.split(re);
	    var y2 = parseInt(a2[format.indexOf('y')]);
	    var m2 = parseInt(a2[format.indexOf('m')]);
	    var d2 = parseInt(a2[format.indexOf('d')]);	    
	    if (y2 > y1) { 
			return 1; 
		}
	    else if (y2 < y1) { 
			return -1; 
		}
	    else {
	        if (m2 > m1) { 
				return 1; 
			}
	        else if (m2 < m1) { 
				return -1; 
			}
	        else {
	            if (d2 > d1) { 
					return 1; 
				}
	            else if (d2 < d1) { 
					return -1; 
				}
	            else { 
					return 0;
				}
	        }
	    }
	},
	showLayer: function(element, message){	    
		var that = this;	
		that.options.mooForm.error = element;	
		var data = element.retrieve('element:data');
		var layerInstance = that.options.layer.instance;
		if($type(data.events) != false){
			element.fireEvent('show', element);
		}
		that.fireEvent('show', element);				
		layerInstance.getElement('p.message').set('html', $pick(message, element.retrieve('element:message')));
		var layerOffset = data.offset || that.options.layer.offset;
		var elementCoords = element.getCoordinates();		
		new Fx.Scroll(window).start(0, elementCoords.top - 77).chain(function(){				
			layerInstance.set('styles', {
				'position': 'absolute',
				'top': elementCoords.top + elementCoords.height + layerOffset.y + 'px',
				'left': elementCoords.left + layerOffset.x + 'px',			
				'zIndex': that.options.layer.zIndex + 3
			});
			layerInstance.set('tween', {
				duration: 100,
				transition: Fx.Transitions.Quad.easeIn
			}).tween('opacity', 1);
			if(Browser.Engine.trident4){
				that.fixIE6Overlay(layerInstance, true);
			}
			switch(element.type){
				case 'file':
					break;
				case 'select-one':
				case 'select-multiple':
					element.focus();
					break;
				default:					
					element.select();
					element.focus();
					break;
			}
			$clear(that.options.layer.timeWait);
			that.options.layer.timeWait = that.hideLayer.periodical(that.options.layer.timeHide, that, element);	
		});
	},
	hideLayer: function(element){
		var that = this;		
		$clear(that.options.layer.timeWait);
		element = element || that.options.mooForm.error;		
		var layerInstance = that.options.layer.instance;
		var data = null;		
		if(element){
			data = element.retrieve('element:data');	
			if($type(data.events) != false){
				element.fireEvent('hide', element);
			}	
			that.fireEvent('hide', element);							
		}
		layerInstance.set('tween', {
			duration: 50,
			transition: Fx.Transitions.Quad.easeOut
		}).tween('opacity', 0);
		if(Browser.Engine.trident4){
			that.fixIE6Overlay(layerInstance, false);
		}		
	},
	fixIE6Overlay: function(layer, status){
		var that = this;
		var dim = layer.getCoordinates();
		if (!that.options.mooForm.overlay) {
			that.options.mooForm.overlay = new IFrame({
				'id': layer.id + 'Iframe',
				'src': 'javascript:false;',
				'frameBorder': 0,
				'scrolling': 'no',
				'styles': {
					'border': '0 none',
					'margin': 0,
					'padding': 0,
					'zIndex': that.options.layer.zIndex + 2,
					'visibility': 'hidden',
					'display': 'none',
					'position': 'absolute',
					'filter': 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
				}
			}).inject(layer, 'before');
		}
		that.options.mooForm.overlay.set('styles', {
			'visibility': status ? 'visible' : 'hidden',
			'display': status ? 'block' : 'none',
			'top': dim.top,
			'left': dim.left,
			'width': dim.width,
			'height': dim.height			
		});
	},
	fixIE6PNG: function(){
		var rpng = new RegExp('url\\(([\.a-zA-Z0-9_/:-]+\.png)\\)');
		var search = new RegExp('(.+)mooform\.css');
		for (var i = 0; i < document.styleSheets.length; i++){
			if (document.styleSheets[i].href.match(/mooform\.css$/)) {
				var root = document.styleSheets[i].href.replace(search, '$1');
				var count = document.styleSheets[i].rules.length;
				for (var j = 0; j < count; j++){
					var cssstyle = document.styleSheets[i].rules[j].style;
					var bgimage = root + cssstyle.backgroundImage.replace(rpng, '$1');
					if (bgimage && bgimage.match(/\.png/i)){
						var scale = (cssstyle.backgroundRepeat == 'no-repeat') ? 'crop' : 'scale';
						cssstyle.filter =  'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, src=\'' + bgimage + '\', sizingMethod=\''+ scale +'\')';
						cssstyle.backgroundImage = 'none';
					}
				}
			}
		}
	},
	show: function(element){	
		this.fireEvent('show', element);
	},	
	hide: function(element){
		this.fireEvent('hide', element);
	},	
	change: function(element){
		this.fireEvent('change', element);
	},				
	submit: function(element){		
		this.fireEvent('submit', element);
	},
	log: function(text, args){
		if (window.console){
			console.log(text.substitute(args || {}));
		}
	}	
});
