(function($) {
		function jForm(form, fn, settings) {
			this.form = form;
			this.fn = fn;
			this.has_error = false;
			this.errors = [];
			this.settings = settings;
		}

		jForm.prototype = {
			validate: function(e) {
				if(window.tinyMCE && tinyMCE['triggerSave']) tinyMCE.triggerSave();
				var bf = {};
				var that = this;
				$(":input").each(function() {
						var el = $(this), name = el.attr('name');
						if(!bf[name]) {
							bf[name] = new jField(this, that);
						} else {
							bf[name].add_value(this);
						}
					});
				this.fn.apply(this.form, [bf]);
				if(this.has_error) {
					if(this.settings.beforeAlert) {
						this.settings.beforeAlert.apply(this.form, e);
					}
					if(this.settings.autoFocus) {
						this.errors[0][1][0].focus();
					}
					if(this.settings['class']) {
						$(this.errors[0][1][0]).addClass(this.settings['class']);
					}
					alert(this.errors[0][0]);
					e.preventDefault();
					return false;
				}
				return true;
			},
			add_error: function(input, msg) {
				this.has_error = true;
				var name = this.title($(input).attr('name'));
				msg = msg.replace(/\{title\}/, name);
				this.errors.push([msg, input]);
			},
			title: function(name) {
				var words = name.split("_");
				var title = [];
				var i = 0;
				for(var j = 0; j < words.length; j++) {
					title[j] = words[j].substr(0,1).toUpperCase() + words[j].substr(1);
				}
				return title.join(" "); 
			}
		};

		function jField(input, form) {
			this.input = [];
			this.form = form;
			var nodeName = input.nodeName.toLowerCase();
			this.empty = true;
			this.value = null;
			this.values = [];
			if(nodeName == 'textarea') {
				this.type = 'textarea';
			} else if(nodeName == 'select') {
				this.type = 'select'; 
			} else {
				this.type = input.type.toLowerCase();
			}
			this.add_value(input)
		}

		jField.prototype = {
			add_value: function(input) {
				this.input.push(input);
				if(this.form.settings['class']) {
					$(input).removeClass(this.form.settings['class']);
				}
				
				var v = $(this.input).serializeArray();
				if($(this.input).attr('type').toLowerCase() == 'file') {
					this.value = $(this.input).val();
					this.empty = $.trim(this.value) == "";
				} else if(v.length == 0) {
					this.empty = true;
					this.value = null;
				} else {
					this.value = v[0].value;
					this.values = [];
					for(var i=0,l=v.length;i<l;i++) {
						this.values.push(v[i].value);
					}
					this.empty = $.trim(this.values.join()) == "";
				}
			},
			add_error: function(msg) {
				this.form.add_error(this.input, msg);
			},
			required: function(msg) { 
				if(this.empty) {
					if(msg) { this.add_error(msg); } 
					if(this.type == 'select' || this.type == 'radio') {
						this.add_error('Please choose a {title}');
					} else if(this.type == 'checkbox') {
						this.add_error('Please check: {title}');
					}
					this.add_error('{title} must be filled out');
				}
				return this;
			},
			email: function(msg) { 
				if(!this.empty && !(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,6})+$/).test(this.value)) {
					this.add_error(msg || '{title} must be a valid email address');
				}
				return this;
			},
			integer: function(msg) {
				if(!this.empty && !(/^[+-]?\d+$/.test(this.value))) {
					this.add_error(msg || '{title} must be an integer');
				}
				return this;
			},
			money: function(msg) {
				if(!this.empty) {
					var s = this.value.replace("$", "").replace(",", "");
					this.value = s;
					if(!(/^[+-]?\d+$/.test(s) || /^[+-]?\d+\.\d\d?$/.test(s))) {
						this.add_error(msg || '{title} must be a money value');
					}
				}
				return this;
			},
			greater_than: function(value, msg) {
				if(!this.empty && !((+this.value) > value)) {
					this.add_error(msg || '{title} must be greater than ' + value);
				} 
				return this;
			},
			less_than: function(value, msg) {
				if(!this.empty && !((+this.value) < value)) {
					this.add_error(msg || '{title} must be less than ' + value);
				} 
				return this;
			},
			min: function(size, msg) {
				if(!this.empty && this.value.length < size) {
					this.add_error(msg || '{title} must be at least ' + size + ' long');
				}
				return this;
			},
			max: function(size, msg) {
				if(!this.empty && this.value.length > size) {
					this.add_error(msg || '{title} must be at most ' + size + ' long');
				}
				return this;
			},
			swf_ext_check: function(msg) {
				if (!this.empty) {
					var img = this.value.toLowerCase();
					if (img.lastIndexOf(".swf") == -1) {
						this.add_error(msg || '{title} must be in SWF format');
					}
				}
				return this;
			},
			pdf_ext_check: function(msg) {
				if (!this.empty) {
					var img = this.value.toLowerCase();
					if (img.lastIndexOf(".pdf") == -1) {
						this.add_error(msg || '{title} must be in PDF format');
					}
				}
				return this;
			},
			img_ext_check: function(msg) {
				if (!this.empty) {
					var img = this.value.toLowerCase();
					if (img.lastIndexOf(".jpg") == -1 && img.lastIndexOf(".gif") == -1) {
						this.add_error(msg || '{title} must be either GIF or JPG format');
					}
				}
				return this;
			},
			img_swf_ext_check: function(msg) {
				if (!this.empty) {
					var img = this.value.toLowerCase();
					if (img.lastIndexOf(".jpg") == -1 && img.lastIndexOf(".gif") == -1 && img.lastIndexOf(".swf") == -1) {
						this.add_error(msg || '{title} must be either GIF, JPG or SWF format');
					}
				}
				return this;
			},
			swf_ext_check: function(msg) {
				if (!this.empty) {
					var img = this.value.toLowerCase();
					if (img.lastIndexOf(".swf") == -1) {
						this.add_error(msg || '{title} must be either GIF, JPG or SWF format');
					}
				}
				return this;
			}
		}

		$.fn.validate = function(fn, opts) {
			return this.each(function() { 
					var that = this, settings = $.extend({
							'mode': 'alert',
							'class': 'validationError',
							'autoFocus': true,
							'afterSubmit': null,
							'beforeAlert': null
						}, opts);
					$(this).submit(function(e) { 
							var f = new jForm(that, fn, settings);
							if(settings.afterSubmit) {
								settings.afterSubmit.apply(that, e);
							}
							return f.validate(e) 
						});
			});
		};
})(jQuery); 
