Ajax = {
	update_container_id: null,
	complete_callback: null,
	Request: function(request_type,options) {
		this.update_container_id = null;
		var proceed = true;
		if (options.url == undefined) {
			proceed = false;
		}
		if (options.beforeSend != undefined) {
			proceed = false;
		}
		if (request_type == 'update') {
			if (options.update_container_id == undefined || !options.update_container_id) {
				proceed = false;
			} else {
				this.update_container_id = '#'+options.update_container_id;
				options.update_container_id = null;		// We don't want to pass this value to the jQuery ajax request function
				options.complete = function(xhr,text_status) {
					jQuery(Ajax.update_container_id).html(xhr.responseText);
					if (Ajax.complete_callback != null) {
						Ajax.complete_callback();
					}
				}
			}
		}
		options.beforeSend = function(xhr) {
			Ajax.SetRequestHeaders(request_type,xhr);
		}
		if (proceed) {
			jQuery.ajax(options);
		}
	},
	SetRequestHeaders: function(request_type,xhr) {
		xhr.setRequestHeader('X-Ajax-Request', 'true');
		xhr.setRequestHeader('X-Request-Type', request_type);
	}
};

Ajax.FormValidator = function(form_id,options) {
	if (options === undefined) {
		var options = {}
	}
	if ($(form_id) == null) {
		alert('Cannot find form: '+form_id);
		return;
	}
	this._id = form_id;
	if (options.ajax_submit === undefined || options.ajax_submit === false) {
		this._submitter = Crumbs.Forms.Submit;
	}
	else {
		if (options.ajax_submit === true && (options.update_div === undefined && options.custom_ajax_submitter === undefined)) {
			alert('Cannot proceed with Ajax submit after validation without a div id or a custom submit callback!');
			return;
		}
		if (options.complete_callback !== undefined) {
			this.complete_callback = options.complete_callback;
		}
		if (options.custom_ajax_submitter !== undefined) {
			this._submitter = options.custom_ajax_submitter;
		}
		else {
			this.update_div = options.update_div;
			this._submitter = this.Submit;
		}
	}
	this.Validate();
};

Ajax.FormValidator.prototype = {
	Validate: function() {
		Crumbs.Forms.DisableSubmit(this._id);
		var params = jQuery('#'+this._id).serialize();
		// serialize doesn't include file fields, so add those on now:
		jQuery('#'+this._id+' input[type=file]').each(function() {
			params += "&"+escape(jQuery(this).attr('name'))+"="+escape(jQuery(this).val());
		});
		var url = jQuery('#'+this._id).attr('action');
		if (url == "") {
			url = top.location.href;
		}
		var Validator = this;		// Store a reference for the success function
		Ajax.Request('validation',{
			url: url,
			data: params,
			type: 'post',
			success: function(data,text_status,xhr) {
				jQuery('#'+Validator._id+' *').removeClass('error');
				Validator._submitter(Validator._id);
			},
			error: function(xhr,text_status,error_thrown) {
				var response = jQuery.parseJSON(xhr.responseText);
				var msg = response.message;
				var bad_fields = response.invalid_fields;
				jQuery('#'+Validator._id+' *').removeClass('error');
				Validator.HilightBadFields(bad_fields);
				if (msg != "") {
					alert(msg);
				}
				jQuery('input.error, select.error, textarea.error')[0].focus();	// Focus on the first erroneous field
				Crumbs.Forms.EnableSubmit(Validator._id);
			}
		});
	},
	HilightBadFields: function(bad_fields) {
		if (bad_fields.length > 0) {
			for (var i in bad_fields) {
				curr_field = bad_fields[i];
				if (typeof(curr_field) == "string") {
					var bad_field = jQuery('#attr_'+bad_fields[i]);
					if (bad_field) {
						bad_field.addClass("error");
						var hilight_field = bad_field.parent().find('label');
						if (hilight_field.length == 0) {
							hilight_field = bad_field
						}
						hilight_field.addClass("error");
					}
				}
			}
		}
	},
	Submit: function() {
		// Submit the form over AJAX
		var params = jQuery('#'+this._id).serialize();
		var url = jQuery('#'+this._id).attr('action');
		if (url == "") {
			url = top.location.href;
		}
		if (this.complete_callback !== null) {
			var my_callback = this.complete_callback;
		}
		else {
			var my_callback = function() {
				// Noop
			}
		}
		Ajax.Request('update',{
			url: url,
			data: params,
			type: 'post',
			complete: my_callback
		});
	}
};

Crumbs = {
	Forms: {
		btn_text: {},
		DisableSubmit: function(form_id) {
			// Cycle through all elements with the classname of "form-submit" and disable them
			window.top.jQuery('#'+form_id+' .form-submit').each(function() {
				if (Crumbs.Forms.btn_text[form_id] == undefined) {
					Crumbs.Forms.btn_text[form_id] = jQuery(this).val();
				}
				jQuery(this).attr('disabled','disabled');
				jQuery(this).val("Hang on...");
			});
		},
		EnableSubmit: function(form_id) {
			// Cycle through all elements with the classname of "form-submit" and enable them
			window.top.jQuery('#'+form_id+' .form-submit').each(function() {
				jQuery(this).attr('disabled','');
				jQuery(this).val(Crumbs.Forms.btn_text[form_id]);
			});
		},
		Submit: function(form_id) {
			jQuery('#'+form_id+'-is-valid').val('true');
			jQuery('#'+form_id).submit();
		}
	}
};
