/*
 * Edit In Place
 * http://josephscott.org/code/js/eip/
 * Version 0.3.3
 * License http://josephscott.org/code/js/eip/license.txt
 * 
 * Heavily modified by Mitchell Halligan
 */

// Fake a EditInPlace.* name space.
var EditInPlace = function() { };

// Default settings for editable ids.
EditInPlace.defaults = {
	id:						false,
	linked_id:				false,
	save_url:				null,
	type:					'text',
	select_text:			false,	// doesn't work in Safari
	select_width:			'',
	size:					false,  // will be calculated at run time
	max_size:				100,
	rows:					false,  // will be calculated at run time
	cols:					50,
	options:				false,
	escape_function:		encodeURIComponent,
	change_function:		false,
	presave_function:		false,
	on_blur:				'',
	editfield_class:		'eip_editfield',
	saving_text:			'Saving...',
	saving_class:			'eip_saving',
	empty_text:				'Click To Edit',
	edit_title:				'Click To Edit',
	click:					'click',  // or dblclick 
	parent_click:			false,	// allow click on element's parent?
	ajax_data:				false,
	validation:				'',
	minvalue:				'',
	maxvalue:				'',
	datasrc:				'',
	enabled:				true,
	required:				0
};

// Place to keep individual option sets.
EditInPlace.options = { };

// Make an id editable.
EditInPlace.makeEditable = function(args) {
	var id = args['id'];
	var el = $(id);
	
	// Create option array for an id.
	EditInPlace.options[id] = {
		// Basic default items needed to setup edit in place.
		enabled:				EditInPlace.defaults['enabled'],
		type:					EditInPlace.defaults['type'],
		empty_text:				EditInPlace.defaults['empty_text'],
		click:					EditInPlace.defaults['click'],
		parent_click:			EditInPlace.defaults['parent_click'],
		edit_title:				EditInPlace.defaults['edit_title'],
	
		// Private options that are managed for you.
		datafld:				'',
		recordnumber:			0,
		is_empty:				false,
		orig_text:				'',
		
		// Flag to indicate if all defaults have been applied.
		defaults_applied:		false
	};
	var id_opt = Object.extend(EditInPlace.options[id], args);
	
	// If it's a 'select' type, set the display value to a lookup in the options
	if (id_opt['type'] == 'select') {
		el.innerHTML = typeof id_opt['options'][el.innerHTML] != "undefined" ? id_opt['options'][el.innerHTML] : "";
	}

	// Override validation, if any, if it's a date type
	if(id_opt['type'] == 'date') {
		id_opt['validation'] = 'date';
	}
		
	// Check for empty values.
	if (el.innerHTML == '') {
		id_opt['is_empty'] = true;
		el.innerHTML = id_opt['empty_text'];
	}

	// Setup parent, if necessary.
	if (id_opt['parent_click']) {
		el.parentNode.setAttribute('eipchildid', id);
	}

	// Turn on event handling, if enabled.
	if (id_opt['enabled']) {
		el.title = id_opt['edit_title'];
		if (id_opt['parent_click']) {
			Event.observe(el.parentNode, id_opt['click'], EditInPlace.mouseclick_observer);
			el.parentNode.title = el.title;
		}
		else {
			Event.observe(id, id_opt['click'], EditInPlace.mouseclick_observer);
		}
	}
	else {
		el.setStyle({opacity: 0.25});
	}
};

// Enable/Disable the Edit In Place feature on an id.
EditInPlace.enable = function(id, bFlag) {
	var id_opt = EditInPlace.options[id];
	
	if (bFlag == null) {
		bFlag = id_opt['enabled'];
	}
	EditInPlace._toggleEvents(id, false);
	if (bFlag) {
		$(id).setStyle({opacity: 1});
		EditInPlace._toggleEvents(id, true);
	}
	else {
		$(id).setStyle({opacity: 0.25});
	}
	id_opt['enabled'] = bFlag;
};

// **************  Private Functions *****************

// Toggle click event watching on/off.
EditInPlace._toggleEvents = function(id, bFlag) {
	var el = $(id);
	var parentEl = el.parentNode;
	var id_opt = EditInPlace.options[id];

	if (bFlag) {
		el.title = id_opt['edit_title'];
		if (id_opt['parent_click']) {
			Event.observe(parentEl, id_opt['click'], EditInPlace.mouseclick_observer);
			parentEl.title = el.title;
		}
		else {
			Event.observe(id, id_opt['click'], EditInPlace.mouseclick_observer);
		}
	}
	else {
		el.title = "";
		if (id_opt['parent_click']) {
			Event.stopObserving(parentEl, id_opt['click'], EditInPlace.mouseclick_observer);
			parentEl.title = el.title;
		}
		else {
			Event.stopObserving(id, id_opt['click'], EditInPlace.mouseclick_observer);
		}
	}
}

// Process mouseclick event.
EditInPlace._mouseClick = function(event) {
	var id = Event.element(event).readAttribute('eipchildid');
	if (id == null) {
		var id = Event.element(event).id;
	}
	EditInPlace._showEditControl(id);
};

EditInPlace.mouseclick_observer = EditInPlace._mouseClick.bindAsEventListener();

// Show the appropriate edit control.
EditInPlace._showEditControl = function(id, bShowButtons) {
	EditInPlace._toggleEvents(id, false);
	if (bShowButtons == null) {
		bShowButtons = true;
	}
	// Make sure all defaults are applied before making this id editable.
	var id_opt = EditInPlace.options[id];
	if (!id_opt['defaults_applied']) {
		$H(EditInPlace.defaults).each(function(pair) {
			if (typeof id_opt[pair.key] == "undefined") {
				id_opt[pair.key] = pair.value;
			}
		});
		id_opt['defaults_applied'] = true;
	}
	// Store data-binding specifics.
	var el = $(id);
	if (el.dataFld != '') {
		id_opt['datafld'] = el.dataFld;
		id_opt['recordnumber'] = el.recordNumber;
		if (id_opt['datasrc'] == '') {
			if (el.dataSrc == '') {
				var datasrc = el.up('table').dataSrc.substr(1, el.up('table').dataSrc.length);			
			}
			else {
				var datasrc = el.dataSrc.substr(1, el.dataSrc.length);
			}
			id_opt['datasrc'] = datasrc;
		}
	}
	// Fade original content and show editing form
	var form = EditInPlace._formField(id);
	if (bShowButtons) {
		form += EditInPlace._formButtons(id);
	}
	new fx.Style(el, 'opacity', {duration: 300, transition: fx.Transitions.quadOut, onComplete: function() {
		Element.hide(id);
		new Insertion.After(id, form);
		if (bShowButtons) {
			Field.focus(id + '_edit');
			Event.observe(id + '_edit', 'keypress', EditInPlace._keypressEdit);
			Event.observe(id + '_edit', 'blur', EditInPlace._blurEdit);
			if(id_opt['type'] == 'date') {
				EditInPlace._datePicker(id + '_edit');
			}
		}
		else if (id_opt['validation'] != "") {
			Event.observe(id + '_edit', 'blur', checkValidation);
		}
		id_opt['orig_text'] = $F(id + '_edit');
		if (id_opt['linked_id']) {
			EditInPlace._showEditControl(id_opt['linked_id'], false);
		}
	}}).custom(1, 0);
};

// Process keypress in Edit control.
EditInPlace._keypressEdit = function(event) {
	if (event.keyCode == Event.KEY_RETURN) {
		var id = Event.element(event).id.replace(/_edit$/, '');
		var id_opt = EditInPlace.options[id];
		// Check validation, if any.
		if (id_opt['required'] == "1") {	
			if (!checkRequiredElement(Event.element(event))) {
				return;
			}
		}	
		if (id_opt['validation'] != "") {
			Event.stopObserving(id + '_edit', 'blur', EditInPlace._blurEdit);
			if (!checkValidation()) {
				Event.observe(id + '_edit', 'blur', EditInPlace._blurEdit);
				return;
			}
		}
		EditInPlace._saveClick(event, id);
		Event.stop(event);
	}
}

// Process blur of Edit control.
EditInPlace._blurEdit = function(event) {
	var id = Event.element(event).id.replace(/_edit$/, '');
	var toEl = document.elementFromPoint(event.clientX, event.clientY);
	if (toEl == null) {
		toEl = event.srcElement;
	}
	if ((toEl.id.include("_edit")) || (toEl.id == id + '_cancel')) {
		return;
	}
	var id_opt = EditInPlace.options[id];
	var mode = id_opt['on_blur'];
	if ((toEl.id != id + '_save') && (mode == "cancel")) {
		EditInPlace._cancelClick(event, id);
		return;
	}
	// Check validation, if any.
	if (id_opt['required'] == "1") {	
		if (!checkRequiredElement(Event.element(event))) {
			return;
		}
	}
	if (id_opt['validation'] != "") {
		if (!checkValidation()) {
			return;
		}
	}
	if (toEl.id != id + '_save') {
		EditInPlace._saveClick(event, id);
	}
};

// Process save button clicks.
EditInPlace._saveClick = function(event, id) {
	var id_opt = EditInPlace.options[id];
	
	// Check if there are linked items to save also.
	if (id_opt['linked_id']) {
		EditInPlace._saveClick(null, id_opt['linked_id']);
	}

	// Get new data and just cancel if it hasn't changed.
	var new_text = $F(id + '_edit');
	if (new_text == id_opt['orig_text']) {
		EditInPlace._cancelClick(event, id, false);
		return;
	}

	// Package data for the AJAX request.
	var params = '&id=' + id + '&content=' + id_opt['escape_function'](new_text);
	if (id_opt['type'] == 'select') {
		params += '&option_name=' + $(id + '_option_' + new_text).innerHTML;
	}

	// Build saving message.
	var saving = '<span class="' 
		+ id_opt['saving_class'] + '">' + id_opt['saving_text'] + '</span>\n';

	// Remove the form editor and display the saving message.
	Element.remove(id + '_editor');
	$(id).innerHTML = saving;

	// Make the saving message visible, and fade it in
	Element.show(id);
	new fx.Style($(id), 'opacity', {duration: 300, transition: fx.Transitions.quadOut}).custom(0, 1);

	// Send the changes via AJAX.
	if (id_opt['save_url'] != null) {
		// Call presave_function callback, if needed.
		if (id_opt['presave_function']) {
			id_opt['presave_function'](id_opt);
		}
		// If additional data was provided to be sent, add it on.
		if(id_opt['ajax_data']) {
			for(var i in id_opt['ajax_data']) {
				var url_data = id_opt['escape_function'](id_opt['ajax_data'][i]);
				params += '&' + i + '=' + url_data;
			}
		}
		getURL(id_opt['save_url'], "POST", params, true, EditInPlace._saveComplete);
	}
	else {
		window.setTimeout("EditInPlace._finishSave('" + $(id).id + "', '" + new_text + "')", 300);
	}
};

// Proces cancel button clicks.
EditInPlace._cancelClick = function(event, id, bProcessLinks) {
	var id_opt = EditInPlace.options[id];

	if (bProcessLinks == null) {
		bProcessLinks = true;
	}

	// Remove the edit form and class.
	Element.remove(id + '_editor');
	Element.removeClassName(id, id_opt['editfield_class']);

	// Display empty edit text if the id was empty.
	if($(id).innerHTML == '') {
		id_opt['id_empty'] = true;
		$(id).innerHTML = id_opt['empty_text'];
	}

	// Show the original content again, and fade it in
	Element.show(id);
	var maxOpacity = 1;
	if (!id_opt['enabled']) {
		maxOpacity = 0.25;
	}
	new fx.Style($(id), 'opacity', {duration: 300, transition: fx.Transitions.quadOut, onComplete: function() {
		EditInPlace.enable(id);
		if ((bProcessLinks) && (id_opt['linked_id'])) {
			EditInPlace._cancelClick(null, id_opt['linked_id']);
		}
	}}).custom(0, maxOpacity);
};

// Complete the successful AJAX request.
EditInPlace._saveComplete = function(strXML) {
	var domObj = new ActiveXObject("Microsoft.XMLDOM");
	if (domObj.loadXML(strXML)) {
		var objRoot = domObj.documentElement;
		var id = objRoot.selectSingleNode("//id").text;
		var content = objRoot.selectSingleNode("//content").text;
		var extraData = null;
		var extraDataNode = objRoot.selectSingleNode("//extradata");
		if (extraDataNode != null) {
			extraData = extraDataNode.xml;
		}
		EditInPlace._finishSave(id, content, extraData);
	}
};

// Finalize the save by updating display text and data source, if any. 
EditInPlace._finishSave = function(id, content, extraData) {
	var id_opt = EditInPlace.options[id];

	// Check to see if the user deleted all of the text.
	if(content == '') {
		id_opt['is_empty'] = true;
		var disp_value = id_opt['empty_text'];
	}
	else {
		id_opt['is_empty'] = false;
		// If it's a 'select' type, set the display value to a lookup in the options
		var disp_value = content;
		if (id_opt['type'] == 'select') {
			if ($H(id_opt['options']).keys().indexOf(content) != -1) {
				disp_value = id_opt['options'][content];
			}
		}
	}
	// Fade out the saving message.
	new fx.Style($(id), 'opacity', {duration: 300, transition: fx.Transitions.quadOut, onComplete: function() {
		// Update data source, if any.
		if(id_opt['datafld'] != '') {
			if(id_opt['recordnumber'] > 0) {
				$(id_opt['datasrc']).recordset.AbsolutePosition = id_opt['recordnumber'];
			}
			$(id_opt['datasrc']).recordset(id_opt['datafld']).Value = content;
		}
		// Set the new content from the Ajax request and fade it in.
		$(id).innerHTML = disp_value;
		var maxOpacity = 1;
		if (!id_opt['enabled']) {
			maxOpacity = 0.25;
		}
		new fx.Style($(id), 'opacity', {duration: 300, transition: fx.Transitions.quadOut, onComplete: function() {
			EditInPlace.enable(id);
			// Call change handler, if needed.
			if ((id_opt['change_function']) && (id_opt['orig_text'] != content)) {
				id_opt['change_function'](id_opt, extraData);
			}
		}}).custom(0, maxOpacity);
	}}).custom(1, 0);
}

// Build the form field to edit.
EditInPlace._formField = function(id) {
	var id_opt = EditInPlace.options[id];

	// If the original text value was empty then
	// make it empty again before we allow editing.
	if(id_opt['is_empty'] == true) {
		$(id).innerHTML = '';
	}

	// Every form is wrapped in a span.
	var field = '<span id="' + id + '_editor">\n';

	// Text/Date input edit.
	if((id_opt['type'] == 'text') || (id_opt['type'] == 'date')) {
		var size = $(id).innerHTML.length + 15;
		// Don't let the size get bigger than the maximum.
		if(size > id_opt['max_size']) {
			size = id_opt['max_size'];
		}

		// Use a specific size if one was provided.
		size = (id_opt['size'] ? id_opt['size'] : size);

		field += '<input id="' + id + '_edit" class="'
			+ id_opt['editfield_class'] + '" name="' + id
			+ '_edit" type="text" size="' + size + '" value="'
			+ $(id).innerHTML + '" validation="'
			+ id_opt['validation'] + '" minvalue="'
			+ id_opt['minvalue'] + '" maxvalue="'
			+ id_opt['maxvalue'] + '" required="'
			+ id_opt['required'] + '"';

		if(id_opt['select_text']) {
			field += ' onfocus="this.select();"';
		}
		
		if(id_opt['type'] == 'date') {
			field += ' onclick="EditInPlace._datePicker(this.id);"';
		}

		field += ' />\n';
	}
	// Textarea edit.
	else if(id_opt['type'] == 'textarea') {
		// Calculate the number of rows to use.
		var rows = ($(id).innerHTML.length / id_opt['cols']) + 4;

		// Use a specific rows is fone was provided.
		rows = (id_opt['rows'] ? id_opt['rows'] : rows);

		field += '<textarea id="' + id + '_edit" class="'
			+ id_opt['editfield_class'] + '" name="' + id + '_edit" rows="'
			+ rows + '"cols="' + id_opt['cols'] + '"'

		if(id_opt['select_text']) {
			field += 'onfocus="this.select();"';
		}

		field += '>' + $(id).innerHTML + '</textarea>\n';
	}
	// Select edit.
	else if(id_opt['type'] == 'select') {
		field += '<select id="' + id + '_edit" class="'
			+ id_opt['editfield_class'] + '" style="width: ' + id_opt['select_width'] + '" name="' + id + '_edit">\n';

		for(var i in id_opt['options']) {
			field += '<option id="' + id + '_option_' + i + '" name="' + id
				+ '_option_' + i + '" value="' + i + '"';

			if(id_opt['options'][i] == $(id).innerHTML) {
				field += ' selected="selected"';
			}

			field += '>' + id_opt['options'][i] + '</option>\n';
		}

		field += '</select>\n';
	}
	return(field);
};

// Build form buttons.
EditInPlace._formButtons = function(id) {
	return(
		'<span><label title="Save" id="' + id + '_save" style="color: green; font-family: wingdings; font-size: 14px;" onclick="EditInPlace._saveClick(null, \'' + id + '\')">'
		+ String.fromCharCode(254) + '</label>&nbsp;<label title="Cancel" id="' + id + '_cancel" style="color: red; font-family: wingdings; font-size: 14px;" onclick="EditInPlace._cancelClick(null, \'' + id + '\')">'
		+ String.fromCharCode(253) + '</label></span></span>'
	);
};

// Date Picker.
EditInPlace._datePicker = function(id) {
	var datePicker = $('myCaldiv' + id);
	if(datePicker == null) {
		datePicker = new Element('div', {'id': 'myCaldiv' + id}).setStyle({display: 'none', position: 'absolute', zIndex: 999});
		$(document.body).insert({'bottom': datePicker});
	}
	var datePickerTop = $(id).viewportOffset()['top'] + $(id).getHeight() + 2;
	var datePickerLeft = $(id).viewportOffset()['left'] + 2;
	datePicker.setStyle({top: datePickerTop, left: datePickerLeft});
	datePicker.update('<IFRAME style="Z-INDEX: 100; WIDTH: 150px; POSITION: absolute; HEIGHT: 140px" marginWidth=0 marginHeight=0 src="../common/dates/calendar_iframe.html?myfldname=##'+id+'&mymindate=&mymaxdate=&mydateflag=0" frameBorder=0 noResize scrolling=no></IFRAME>');
	datePicker.show();
};

