/*
+-----------------------------------------------------------------------+
| Copyright (c) 2007 David Hauenstein			                        |
| All rights reserved.                                                  |
|                                                                       |
| Redistribution and use in source and binary forms, with or without    |
| modification, are permitted provided that the following conditions    |
| are met:                                                              |
|                                                                       |
| o Redistributions of source code must retain the above copyright      |
|   notice, this list of conditions and the following disclaimer.       |
| o Redistributions in binary form must reproduce the above copyright   |
|   notice, this list of conditions and the following disclaimer in the |
|   documentation and/or other materials provided with the distribution.|
|                                                                       |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
|                                                                       |
+-----------------------------------------------------------------------+
*/

/* $Id: jquery.inplace.js,v 0.9.9.2 2007/12/04 09:39:00 hauenstein Exp $ */

/**
  * jQuery inplace editor plugin.  
  *
  * Created by: David Hauenstein
  * http://www.davehauenstein.com/blog/
  *
  *
  * Nate Wiger (http://www.dangerrabbit.com) added callbacks, exposed 
  * more settings, added required values and "selected" options,
  * and enabled compatibility with jQuery.noConflict() mode.
  * Thanks to Joe and Vaska for helping me get this working on jQuery 1.1
  * Thanks to Pranav (http://www.startupdunia.com/) for finding a scope bug (0.9.4 release)
  * Thanks to Simon for finding some extraneous code (0.9.5 release)
  *
  *
  * @name  editInPlace
  * @type  jQuery
  * @param Hash    options						additional options 
  * @param String  options[url]					POST URL to send edited content
  * @param String  options[params]				paramters sent via the post request to the server; string; ex: name=dave&last_name=hauenstein
  * @param String  options[field_type]			can be: text, textarea, select; default: text
  * @param String  options[select_options]		this is a string seperated by commas for the dropdown options, if field_type is dropdown
  * @param String  options[textarea_cols]		number of columns textarea will have, if field_type is textarea; default: 25
  * @param String  options[textarea_rows]		number of rows textarea will have, if field_type is textarea; default: 10
  * @param String  options[bg_over]				background color of editable elements on HOVER
  * @param String  options[bg_out]				background color of editable elements on RESTORE from hover
  * @param String  options[saving_text]			text to be used when server is saving information; default: 'Saving...'
  * @param String  options[saving_image]		specify an image location instead of text while server is saving; default: uses saving text
  * @param String  options[value_required]		if set to true, the element will not be saved unless a value is entered
  * @param String  options[element_id]			name of parameter holding element_id; default: element_id
  * @param String  options[update_value]		name of parameter holding update_value; default: update_value
  * @param String  options[original_html]		name of parameter holding original_html; default: original_html
  * @param String  options[save_button]			image button tag to use as "Save" button
  * @param String  options[cancel_button]		image button tag to use as "Cancel" button
  * @param String  options[callback]			call function instead of submitting to url
  *             
  */

jQuery.fn.editInPlace = function(options) {

	//#######################################################################
	//DEFINE THE DEFAULT SETTINGS, SWITCH THEM WITH THE OPTIONS USER PROVIDES
	var settings = {
		url: "",
		params: "",
		field_type: "text",
		select_options: "",
		textarea_cols:  "25",
		textarea_rows:  "10",
		bg_over: "#ffc",
		bg_out:  "transparent",
		saving_text:   "Saving...",
		saving_image:  "",
		default_text:  "(Click here to add text)",
		select_text: "Choose new value",
		value_required: null,
		element_id:    "element_id",
		update_value:  "update_value",
		original_html: "original_html",
		save_button:   '<input type="submit" class="inplace_save" value="Save"/>',
		cancel_button: '<input type="submit" class="inplace_cancel" value="Cancel"/>',
		callback: null,
		success: null,
		error: function(request){
			alert("Failed to save value: " + request.responseText || 'Unspecified Error');
		}
	};

	if(options) {
		jQuery.extend(settings, options);
	}
	
	//#######################################################################
	//preload the loading icon if it exists
	if(settings.saving_image != ""){
		var loading_image = new Image();
		loading_image.src = settings.saving_image;
	}

	//#######################################################################
	//THIS FUNCTION WILL TRIM WHITESPACE FROM BEFORE/AFTER A STRING
	String.prototype.trim = function() {
		return this.replace(/^\s+/, '')
							 .replace(/\s+$/, '');
	};

	//#######################################################################
	//THIS FUNCTION WILL ESCAPE ANY HTML ENTITIES SO "Quoted Values" work
	String.prototype.escape_html = function() {
		return this.replace(/&/g, "&amp;")
							 .replace(/</g, "&lt;")
							 .replace(/>/g, "&gt;")
							 .replace(/"/g, "&quot;");
  };

	//#######################################################################
	//CREATE THE INPLACE EDITOR
	return this.each(function(){
		
		if(jQuery(this).html() == "") jQuery(this).html(settings.default_text);

		jQuery(this).data("editing",false);
		jQuery(this).data("hoverEditing",false);
		
		var original_element = jQuery(this);
		var click_count = 0;
		
		if(settings.field_type == "checkbox" || jQuery(this).attr('type') == "checkbox")
		{
			jQuery(this).click(function(e){
				
				var element = jQuery(e.target);
				
				//set saving message
				if(settings.saving_image != ""){
					var saving_message = '<img src="' + settings.saving_image + '" alt="Saving..." />';
				} else {
					var saving_message = settings.saving_text;
				}

				//place the saving text/image in the original element
				jQuery(this).html(saving_message);

				if(settings.params != ""){
					settings.params = "&" + settings.params;
				}

				var new_html = element.val();

				if(settings.callback) {
					
					html = settings.callback(element.attr("id"), new_html, element.data("original_html"), settings.params);
					if (html) {
						// put the newly updated info into the original element
						element.html(html || new_html);
					} else {
						// failure; put original back
						alert("Failed to save value: " + new_html);
						element.html(element.data("original_html"));
					}
				} else if (settings.value_required && new_html == "") {
					element.html(element.data("original_html"));
					alert("Error: You must enter a value to save this field");
				} else {
					jQuery.ajax({
						url: settings.url,
						type: "POST",
						data: settings.update_value + '=' + new_html + '&' + settings.element_id + '=' + 
								element.attr("id") + settings.params + 
								'&' + settings.original_html + '=' + element.data("original_html"),
						dataType: "html",
						success: function(html){
							// if the text returned by the server is empty, 
							// put a marker as text in the original element
							var new_text = html || settings.default_text;

							// put the newly updated info into the original element
							element.html(new_text);
							if (settings.success) settings.success(html, element);
						},
						error: function(request) {
							element.html(element.data("original_html"));
							if (settings.error) settings.error(request, element);
						}
					});
				}
				
				return true;
			});
			
			
			return;
		}
		
		if(settings.mode == "inline")
			jQuery(this).change(function() { saveInline(jQuery(this)); });
		
		var cancel = function(element) {
			element.data("editing",false);
			element.data("click_count",0);
			element.data("justEdited",false);
			element.css("background", settings.bg_out);
			element.html(element.data("original_html"));
		};
		
		var saveInline = function(element) {
			select = jQuery(element).children();
			var new_value = select.val();
			
			//set saving message
			if(settings.saving_image != ""){
				var saving_message = '<img src="' + settings.saving_image + '" alt="Saving..." />';
			} else {
				var saving_message = settings.saving_text;
			}

			element.append("<span class='saving'>" + saving_message + "</span>");
			select.css('display','none');
			
			if(settings.params != "")
				settings.params = "&" + settings.params;
			
			jQuery.ajax({
				url: settings.url,
				type: "POST",
				data: settings.update_value + '=' + new_value + '&' + settings.element_id + '=' + element.attr("id") + settings.params,
				dataType: "html",
				complete: function() {
					element.children('.saving').remove();
					select.css('display','');
				},
				success: function(html){
					if (settings.success) settings.success(html, element);
				},
				error: function(request) {					
					if (settings.error) settings.error(request, element);
				}
			
			});
		}
	
		var save = function(element) {
			if(element.data("editing"),true)
			{
				//put the original background color in
				element.css("background", settings.bg_out);
				element.data("editing",false);
				element.data("click_count",0);
			
				var new_html = element.children("form").children("input").val();
			
				//set saving message
				if(settings.saving_image != ""){
					var saving_message = '<img src="' + settings.saving_image + '" alt="Saving..." />';
				} else {
					var saving_message = settings.saving_text;
				}

				//place the saving text/image in the original element
				element.html(saving_message);

				if(settings.params != ""){
					settings.params = "&" + settings.params;
				}

				if(settings.callback) {
					html = settings.callback(element.attr("id"), new_html, element.data("original_html"), settings.params);
					if (html) {
						// put the newly updated info into the original element
						element.html(html || new_html);
					} else {
						// failure; put original back
						alert("Failed to save value: " + new_html);
						element.html(element.data("original_html"));
					}
				} else if (settings.value_required && new_html == "") {
					element.html(element.data("original_html"));
					alert("Error: You must enter a value to save this field");
				} else {
					jQuery.ajax({
						url: settings.url,
						type: "POST",
						data: settings.update_value + '=' + new_html + '&' + settings.element_id + '=' + 
								element.attr("id") + settings.params + 
								'&' + settings.original_html + '=' + element.data("original_html"),
						dataType: "html",
						success: function(html){
							// if the text returned by the server is empty, 
							// put a marker as text in the original element
							var new_text = html || settings.default_text;

							// put the newly updated info into the original element
							element.html(new_text);
							if (settings.success) settings.success(html, element);
						},
						error: function(request) {
							element.html(element.data("original_html"));
							if (settings.error) settings.error(request, element);
						}
					});
				}
					element.data("justEdited",true);
			}
		};
		
		var edit = function(element) {
			element.data("click_count",element.data("click_count") + 1);
			
			if(!element.data("editing"))
			{
				element.data("editing",true);

				//save original text - for cancellation functionality
				element.data("original_html",element.html());
				var buttons_code  = settings.save_button + ' ' + settings.cancel_button;

				//if html is our default text, clear it out to prevent saving accidentally
				if (element.data("original_html") == settings.default_text) element.html('');

				if (settings.field_type == "textarea")
				{
					var use_field_type = '<textarea name="inplace_value" class="inplace_field" rows="' + settings.textarea_rows +
										 '" cols="' + settings.textarea_cols + '">' + element.text().trim().escape_html() +
										 '</textarea>';
				}
				else if(settings.field_type == "text")
				{
					var use_field_type = '<input type="text" name="inplace_value" class="inplace_field" value="' +
											element.text().trim().escape_html() + '" />';
				}
				else if(settings.field_type == "select")
				{
					//var optionsArray = settings.select_options.split(',');
					'<select name="inplace_value" class="inplace_field">' + settings.select_options + '</select>';
					
					// var use_field_type = '<select name="inplace_value" class="inplace_field"><option value="">' + 
					// 															settings.select_text + '</option>';
					// 					for(var i=0; i<optionsArray.length; i++){
					// 						var optionsValuesArray = optionsArray[i].split(':');
					// 						var use_value = optionsValuesArray[1] || optionsValuesArray[0];
					// 						var selected = use_value == element.data("original_html") ? 'selected="selected" ' : '';
					// 						use_field_type += '<option ' + selected + 'value="' + use_value.trim().escape_html() + '">' + 
					// 											optionsValuesArray[0].trim().escape_html() + '</option>';
					// 	                    }
					// 					use_field_type += '</select>';
				}
				
				//insert the new in place form after the element they click, then empty out the original element
				element.html('<form class="inplace_form" style="display: inline; margin: 0; padding: 0;">' +
									use_field_type + ' ' + buttons_code + '</form>');
									
				

			}//END- if(!editing) -END

			//if(element.data("click_count") == 1)
			{
				//set the focus to the new input element
				element.children("form").children(".inplace_field").focus().select();

				jQuery(document).keyup(function(event){
				    if (event.keyCode == 27) {	//ESC
				        cancel(element);
								return false;
				    } 
				});
				
				//click elsewhere
				jQuery(document).click(function(){
					if(element.data("editing"))
						save(element);
					return false;
				});

				//CLICK CANCEL BUTTON functionality
				element.children("form").children(".inplace_cancel").click(function(){
					cancel(element);
					return false;
				});

				//CLICK SAVE BUTTON functionality
				element.children("form").children(".inplace_save").click(function(){
					save(element);

					return false;
				});
				
				element.children("form").submit(function(){
					save(element);
					return false;
				});

				
				
				
				
			}//END- if(click_count == 1) -END

			return false;

		}
		
		if(settings.mode != "inline")
			jQuery(this)
		
		//hovering works in FF, broken in safari, others unknown
		
		// .mouseover(function(){
		// 		
		// 		if(!original_element.data("editing") && !original_element.data("justEdited"))
		// 		{
		// 			original_element.data("hoverEditing",true);
		// 			edit(original_element);
		// 		}
		// 		
		// 		return false;
		// 	})
		// 	
		// 	.mouseout(function(){
		// 		
		// 		original_element.data("justEdited",false);
		// 		if(original_element.data("editing") && original_element.data("hoverEditing"))
		// 		{
		// 			cancel(original_element);
		// 		}
		// 		return false;
		// 	})
	
		
		
		.click(function(){
			original_element.data("hoverEditing",false);

			if(!original_element.data("editing"))
			{
				edit(original_element);
			}
			return false;
		});
		
		// jQuery(this).children("input.inplace_field").focus(function() {
		// 	alert('focus');
		// });

	});

};