/**
 * Uses firebug to log messages if the firebug console is present.
 */
function log() {
	if (typeof console != "undefined") {
		console.info(arguments);
	}
}

/**
 * Another way to log messages if Firebug is present. 
 * This is solution is particular to jQuery and can be used when chaining jQuery objects
 * Example usage: $(element).find('li.source> input:checkbox').log("sources to uncheck").removeAttr("checked");
 */
jQuery.fn.log = function (msg) {
	if (typeof console != "undefined") {
		console.log("%s: %o", msg, this);
	}
	return this;
};


jQuery.getQueryParam = function(name) {
    name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
   
    var regexS = "[\\?&]" + name + "=([^&#]*)";
    var regex = new RegExp(regexS);
    var results = regex.exec(window.location.href);
   
    if (results === null) {
		return "";
	}
	else {
		return results[1];
	}
};

/**
 * Wrapper to invoke a JSON URL and follow up with a call to buildDropDown
 * @param {Object} url
 * @param {Object} value
 */
jQuery.fn.loadDropDown = function(url, value) {
	var obj = jQuery(this);
	return this.each(function() {
		if (obj.is("select")) {
			jQuery.getJSON(url, function(jsonResult) 
            {
				obj.buildDropDown(jsonResult.data, value);
			});
		}
	});
};

/**
 * Constructs dropdown elements from a list of objects.  Each object must contain a value member and a
 * text member ({value: 'xxx', text: 'yyy'}).  The value member translates to the <option> value and the 
 * text member translates to the text shown in the dropdown.
 * @param {Object} data
 * @param {Object} value
 */
jQuery.fn.buildDropDown = function(data, value) {
	var obj = jQuery(this);
	return this.each(function() {
		if (obj.is("select")) {
			var html = "<option value='-1'>Choose...</option>";
			
			for(var i=0; i < data.length; i++) {
				html += "<option value='" + data[i].value + "'>" + data[i].text + "</option>";
			}
			
			obj.html(html);
			
			if (value) {
				obj.val(value);
			}
		}
	});
};

jQuery.fn.setupFormHandlers = function(options){
	var obj = jQuery(this);
	return this.each(function() {
		obj.find("button[type='submit']").click(function(event) {
			event.preventDefault();
			obj.submit(options);
		});
	});
};

/**
 * Load data values for the selected object and bind them to the named children of the
 * object.  For example:
 * 
 * $("#mydiv").load({url: "foo"}); 
 * 
 * will call the url to get some JSON data.  If the JSON data returns a data.field.testfield = '123',
 * then any object in mydiv with a name='testfield' will be set to 123.  This works correctly 
 * with all input field types as well as div, span, and td tags.
 * 
 * This makes it very easy to use backend services to load different parts of the page, or the entire
 * page at once (using $('body').load(...))
 * 
 * This method takes the options available in jQuery.ajax() with the following exceptions:
 * 
 * 1.  options.datatype is always "json".
 * 2.  options.formatter is an additional function that can be specified to format the 
 * 		returned data prior to it being placed into the form.  function(field, data){}.
 * 3.  options.onComplete is an optional function that will be called when the form load
 * 		is complete.  function(result){} where result is the returned JSON object.
 * 
 * Note that this function expects returned values that it places into DOM objects, but
 * places no restriction on what the returned value is.  For example it's possible to return
 * a string and have it placed into a text field.  But it's also possible to return a full-blown
 * HTML construct and have it placed in a DIV.
 * 
 * @param {Object} options
 */
jQuery.fn.load = function(options) {
	if (!options) {
		options = {};
	}

	return this.each(function() {
		var jobj = jQuery(this);
		
		options.datatype = "json";
			
		options.success = function(json){
			if (json) {
				var response = eval("((" + json + "))");
				for (var field in response.data) {
					data = response.data[field];
					
					if (options.formatter) {
						result = options.formatter(field, response);
						data = result || data;
					}
					
					jobj.find(' [name=' + field + ']').val(data);
					jobj.find(' div[name=' + field + ']').html(data);
					jobj.find(' span[name=' + field + ']').html(data);
					jobj.find(' td[name=' + field + ']').html(data);
				}
				
				for (error in response.errors) {
					jobj.find('.' + error + '_error').val(response.errors[error]);
					jQuery('.' + error + '_error').html(response.errors[error]);
				}
			}
			
			log("LOAD RESPONSE: ", response);
			
			if(options.onComplete) {
				options.onComplete(response);
			}
		}
		
		log("LOAD REQUEST: ", options);
		jQuery.ajax(options);
	});
};

/**
 * Submit the form values and bind any response to the page. This method takes the options
 * available in jQuery.ajax() with the following exceptions:
 * 
 * 1.  options.datatype is always "json".
 * 2.  options.type is taken from the form's METHOD attribute.
 * 3.  options.url is taken from the form's ACTION attribute.
 * 4.  options.data is the serialized data of the form.
 * 5a. options.onPreSubmit is an option function called before anything else.  If it returns
 * 		true, form processing continues.  If it returns false, form submission stops.
 * 5.  options.validator is an optional function that will be called prior to form submission
 * 		in order to pre-validate the form.  function(){}.  Returning false will cause the 
 * 		form processing to stop prior to submission.  Return true or nothing at all will cause
 * 		the form to be submitted.
 * 6.  options.formatter is an additional function that can be specified to format the 
 * 		returned data prior to it being placed into the form.  function(field, data){}.
 * 7.  options.onComplete is an optional function that will be called when the form load
 * 		is complete.  function(result){} where result is the returned JSON object.
 * 
 * @param {Object} options
 */
jQuery.fn.submit = function(options) {
	if (!options) {
		options = {};
	}
	
	return this.each(function() {
		var jobj = jQuery(this);
		
		options.datatype = options.datatype || "json";
		options.type = options.method || jobj.attr("method");
		options.url = options.action || jobj.attr("action");
		options.data = jobj.serialize();
		
		jQuery('.error').html("");
		
		options.success = function(json){
			if (json) {
				var response = eval("((" + json + "))");
				
				for (var field in response.data) {
					data = response.data[field];
					
					if (options.formatter) {
						result = options.formatter(field, response);
						data = result || data;
					}
					
					jobj.find(' [name=' + field + ']').val(data);
					jobj.find(' div[name=' + field + ']').html(data);
					jobj.find(' span[name=' + field + ']').html(data);
					jobj.find(' td[name=' + field + ']').html(data);
				}
				
				for (error in response.errors) {
					jobj.find('.' + error + '_error').val(response.errors[error]);
					jQuery('.' + error + '_error').html(response.errors[error]);
				}
			}
			
			log("SUBMIT RESPONSE: ", response);
			
			if (options.onComplete) {
				options.onComplete(options, response);
			}
		}
		
		if (options.url == null) {
			log("SUBMIT REQUEST:  No action URL specified.", options);
			if (options.onComplete) {
				options.onComplete(options, response);
			}
			return;
		}
		
		//
		// Invoke a presubmit function before doing anything.  If the 
		// preSubmit returns false, stop form processing.
		//
		if (options.onPreSubmit) {
			if (options.onPreSubmit(options) == false) {
				return;
			}
		}
		
		//
		// Pretend validation worked, unless validation actually happened and failed.
		//
		var validated = true;
		
		if(options.validator) {
			var result = options.validator();
			if (result !== null) {
				validated = result;
			}
		}
		
		log("SUBMIT REQUEST to %s: ", options.url, options);
		
		if (validated && options.url) {
			jQuery.ajax(options);
		}
	});
};
