Object.extend(Prototype, {
	Browser: {
    IE:     !!(window.attachEvent && !window.opera),
    Opera:  !!window.opera,
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  }
});

var FormElement = Class.create();
FormElement.ERROR_MESSAGE = 101;
Object.extend(FormElement, {
	initialize: function(form, model, options) {
		new FormElement(form, model, options);
	}
});


Object.extend(FormElement.prototype, {
	initialize: function(form, model, options) {
		this.form  = $(form);
		this.model = model;
		this.options = Object.extend({
			url: 'get_form_errors',
			popup: true,
			popupMessage: 'Please correct the errors that are highlighted in red, then try again.',
			useBorders: true,
			errorBorderColor: 'red',
			correctedBorderColor: 'skyblue',
			submitButton: 'submit_btn',
			pageErrorId: "error_div"
		}, options || {});

		this.submitButton = $(this.options.submitButton);
		this.originalBorders = {};
		this.elementGroups = {};

		this.initializeForm();
		this.initializeOnSubmit();
	},
	initializeForm: function() {
		var elements = Form.getElements(this.form);
		for(var i = 0; elements[i]; i++) {

			if(elements[i].name && ["submit", "image", "file", "reset", "hidden"].indexOf(elements[i].type) == -1) {
				if(elements[i].style && elements[i].style.border) {
					this.originalBorders[elements[i].id] = elements[i].style.border;
				}

				var match = [];
				if(elements[i].id && elements[i].type && elements[i].type == "radio") {
					match = /(\w+\_\w+)_\w+/i.exec(elements[i].id);
				} else {
					match = /(\w+\[\w+\]+)\[\w+]/i.exec(elements[i].name);
				}

				if(match && match[0] && match[1]) {
					match[0] = match[0].replace(/\[/g,"_").replace(/\]/g,"");
					match[1] = match[1].replace(/\[/g,"_").replace(/\]/g,"");
					if (!this.elementGroups[match[1]]) {
						this.elementGroups[match[1]] = new Array();
					}
					this.elementGroups[match[1]].push(match[0]);
				}

				if (!this.canSetBorder(elements[i])) {
					this.wrap(elements[i], "span");
				}

				if (elements[i].maxLength && elements[i].maxLength > 0) {
					if (Prototype.Browser.Gecko) {
						elements[i].setAttribute("autocomplete", "off");
					}
					Event.observe($(elements[i].id), "keyup", function(event) {
						if([0, 8, 9, 16, 17, 18, 37, 38, 39, 40, 46].indexOf(event.keyCode) > -1) {
							return;
						}

						var element = Event.element(event);
						if(element.value.length >= element.maxLength) {
							//Removes all of the hidden inputs and elements that do not have a type attribute
							var elements = $A(element.form.elements).reject(function(e){ if(!(e.type && e.type != 'hidden')) { return e; }});
							var nextElement = elements[elements.indexOf(element) + 1];
							if (nextElement != undefined) {
								nextElement.focus();
								if (nextElement.select && (nextElement.tagName.toLowerCase() != 'input' ||
										!['button', 'reset', 'submit'].include(nextElement.type)))
									nextElement.select();
							}
						}
					});
				}
			}
		}
	},
	canSetBorder: function(element) {
		if(["text","textarea","password"].indexOf(element.type) > -1) {
			return true;
		}

		if(Prototype.Browser.IE) {
			return ["radio","checkbox"].indexOf(element.type) > -1 ? true : false;
		} else {
			return element.tagName.toLowerCase() == "select" ? true: false;
		}
	},
	wrap: function(element, tagName, id) {
		if(Prototype.Browser.IE) {
			var tmp = "<" + tagName;
			if (id) {
				tmp += " id=\"" + id +"\"";
			}
			tmp += ">" + element.outerHTML + "</" + tagName + ">";
			element.outerHTML = tmp;
		} else {
			var wrapper = document.createElement(tagName);
			if(id) wrapper.id = id;
			if(element.style.position == 'absolute') {
				wrapper.style.position = 'absolute';
				if (element.style.top) wrapper.style.top = element.style.top;
				if (element.style.left) wrapper.style.left = element.style.left;
			}

			wrapper.appendChild(element.cloneNode(true));
			element.parentNode.replaceChild(wrapper, element);
		}
	},
	initializeOnSubmit: function() {
		this.oldOnSubmit = (typeof this.form.onsubmit=='function')? this.form.onsubmit:function(){return true};
		Event.observe(this.form, "submit", this.onSubmit.bind(this));
	},
	onSubmit: function(event) {
		this.startProcessing();

		if(this.oldOnSubmit() == false) Event.stop(event);

		errors = this.getErrors();

		if (errors.elements.length == 0) {
      try {
        if (typeof this.options['onSuccess'] == 'function') {
					this.options['onSuccess'];
				} else {
					eval(this.options['onSuccess']);
				}
      } catch (e) {
        Prototype.emptyFunction(this, e);
      }
		} else {
			this.showErrors(errors);
      try {
        if (typeof this.options['onFailure'] == 'function') {
					this.options['onFailure'];
				} else {
					eval(this.options['onFailure']);
				}
      } catch (e) {
        Prototype.emptyFunction(this, e);
      }
			this.stopProcessing();
			Event.stop(event);
		}
	},
	startProcessing: function() {
		if(this.submitButton) {
			this.submitButton.disabled = true;
			if(this.submitButton.type.toLowerCase() == 'image' && this.options.processingImage) {
				this.savedProcessingImage = this.submitButton.src;
				this.submitButton.src = this.options.processingImage;
			}
		}
		this.reset();
	},
	stopProcessing: function() {
		if(this.submitButton) {
			this.submitButton.disabled = false;
			if(this.submitButton.type.toLowerCase() == 'image' && this.options.processingImage) {
				this.submitButton.src = this.savedProcessingImage;
			}
		}
	},
	getErrors: function() {

		var query = 'class_name=' + this.model + '&' + Form.serialize(this.form);
		this.transport = Ajax.getTransport();
		this.transport.open('post', this.options.url, false);
		this.transport.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		this.transport.setRequestHeader('Content-length', query.length);
		this.transport.setRequestHeader('Connection', 'close');
		this.transport.send(query);

		var response = eval(this.transport.responseText);
		var errors   = {
			elements: [],
			messages: []
		};

		for(var i = 0; response[i]; i++) {
			for(var j = 0; j < response[i].columns.length; j++) {
				if(response[i].columns[j]) {
					if(this.elementGroups[response[i].columns[j]]) {
						for(var k = 0; this.elementGroups[response[i].columns[j]][k]; k++) {
							errors.elements.push(this.elementGroups[response[i].columns[j]][k])
						}
					} else {
						errors.elements.push(response[i].columns[j]);
					}
				}
				if (errors.messages.indexOf(response[i].message)== -1) {
					errors.messages.push(response[i].message);
				}
			}
		}

		return errors;
	},
	showErrors: function(errors) {
		this._highlightBorders(errors.elements);

		if(this.options.pageErrorId) {
			this._display_errors_on_page(errors.messages);
		}

		if(this.options.popup) {
			this._popup(errors.messages);
		}
	},
	_highlightBorders: function (elementIds) {
		var element = null;
		for (var i = 0; i < elementIds.length; i++) {
			if(elementIds[i]) {
				var el = $(elementIds[i]);
				if (el) {
					element = this.canSetBorder(el) ? el : el.parentNode;
					element.style.border = "2px solid " + this.options.errorBorderColor;
				}
			}
		}
	},
	reset: function() {
		if(this.options.useBorders) {
			this._resetBorderStyles();
		}
		this._resetErrorMessages();
	},
	_resetBorderStyles: function() {
		var elements = Form.getElements(this.form);
		for (var i = 0; elements[i]; i++) {
			var el = elements[i];
			if (el.id && ["submit", "image", "file", "reset", "hidden"].indexOf(el.type) == -1) {
				if (this.canSetBorder(el)) {
					if (el.style.borderColor) {
						if (["radio", "checkbox"].indexOf(el.type) > -1) {
							el.style.border = "0px solid transparent";
						} else {
							el.style.border = (this.originalBorders[el.id]) ? this.originalBorders[el.id] : "1px solid "+this.options.correctedBorderColor;
						}
					}
				}
				else if (el.type && ["submit", "image", "file", "reset", "hidden"].indexOf(el.type) == -1) {
					el.parentNode.style.border= "0px solid transparent";
				}
			}
		}
	},
	_resetErrorMessages: function() {
		if($('error_div')) {
			$('error_div').innerHTML = "";
		}
	},
	_popup: function(errors) {
		if (this.options.popupMessage == FormElement.ERROR_MESSAGE) {
			alert(errors.join('\n'));
		} else {
			alert(this.options.popupMessage);
		}
	},
	_display_errors_on_page: function(messages) {
		if($(this.options.pageErrorId)) {
			$(this.options.pageErrorId).innerHTML = messages.join("<br />");
		}
	}
});
