/*
 * $Header: /EMC/sessionController.js   5   2005-12-14 12:47:22+01:00   eruis $
 * Created on:      13-April-2004
 * Original author: Eric Ruis
 *
 * Description:
 * Holds the methods and properties to (automatically) save and retrieve values using the session object
 * for the following HTML types:
 * 	SELECT
 * 	INPUT
 * 	TEXTAREA
 * 	TD
 * 	LI
 */
// Holds the array of onchange functions defined by the html. The onchange method of store fields
// will be replaced by sc_addField with sc_onFieldChange which in turn calls the HTML
// defined function.
var g_FunctionStore = new Array();

// Is called when the stored field content changes
function sc_onFieldChange(name, index) {
	debugWindow.writeln("sc_onFieldChange: name=" + name + ",index=" + index);
	var v = sc_getValue(name);

	if (v != null) {
		debugWindow.writeln("sc_saveState: name=" + name + ", value=" + v.val);
		calcSession.updateVariable(name, v.val);
	}

	if (index != null) {
		var f = g_FunctionStore[index];
		if (f != null)
			try { f.call(); } catch(E) { debugWindow.writeln("exception in '" + f + "': " + E.description); }
	}
}

// Automatically maintain the state of the element with the specified name in the session cookie.
// Gets the value of a 'from' element in the session cookie and creates a new value in the session cookie
// with the value of the 'from' element when a from element has been specified.
// If addCurrentValue is set true the current value of the element will be added to the session by the addField method.
function sc_addField(name, copyStatefromName, addCurrentValue) {
	debugWindow.writeln("sc_addField: name=" + name + ",copyState=" + copyStatefromName);

	var o = document.getElementById(name);
	if (o == null) {
		debugWindow.writeln('element ' + name + ' not found');
		return;
	}

	// do not add twice
	for (var i = 0; i < this.fields.length; i++) {
		if (this.fields[i] == name) return;
	}
	this.fields.push(name);

	// Copy the value from another element in the session state
	if (copyStatefromName != null) {
		var v = calcSession.getVariable(copyStatefromName);
		if (v!=null && v!="") calcSession.updateVariable(name, v);
	}

	// monitor changes automatically
	if (o.onchange == null) {
		o.onchange = new Function("sc_onFieldChange('" + name + "');");
	}
	else {
		// the current onchange function will be called by sc_onFieldChange
		var i = g_FunctionStore.push(o.onchange) - 1;
		o.onchange = new Function("sc_onFieldChange('" + name + "', " + i + ");");
	}

	if (addCurrentValue != null && addCurrentValue == true) {
		o.onchange();
	}
}

// Remove the specified HTML element from the automatic state maintenance
function sc_removeField(name) {
	var o = document.getElementById(name);
	if (o == null) {
		debugWindow.writeln('element ' + name + ' not found');
		return;
	}

	// remove the element
	for (var i = 0; i < this.fields.length; i++) {
		if (this.fields[i] == name) {
			// Restore the original onchange method if necessary
			var f = o.onchange.toString();
			var start = f.indexOf(",");
			if (f.indexOf(",") != -1) {
				// get the index of the original onchange method in the g_FunctionStore array
				var end = f.indexOf(")"); start += 1;
				var fi = parseInt(f.substring(start, end - start));
				if (isNaN(fi))
					o.onchange = null;
				else {
					// restore the method but do not remove it from the function array since this messes
					// up the indexes of all other onchange methods.
					o.onchange = g_FunctionStore[fi];
					g_FunctionStore[fi] = null;
				}
			}
			else
				o.onchange = null;

			// Remove the element from the store field array
			this.fields.splice(i, 1);
			break;
		}
	}
}

// Gets the list of elements in a group (e.g. the list of radio buttons with the same
// group name)
function sc_getGroup(name, type) {
	var list = new Array();
	var listOfInputElements = document.body.getElementsByTagName("INPUT");
	for (var i = 0; i < listOfInputElements.length; i++) {
		var inputObj = listOfInputElements.item(i);
		if (inputObj.type == type && inputObj.name == name) {
			list.push(inputObj);
		}
	}
	debugWindow.writeln("sc_getGroup: The group " + name + " has " + list.length + " members.");
	return list;
}

// Represents the value of an HTML element returned by the getValue method.
function ValueObject(val, type, disabled) {
	this.val = val;
	this.type = type;
	if (disabled != null)
		this.disabled = disabled;
	else
		this.disabled = false;
	return this;
}

// Gets the value (object) for the specified (by id) HTML element.
function sc_getValue(name) {
	var v = null;

	// locate the object
	var o = null;
	try { o = document.getElementById(name); } catch (E) {}
	if (o == null) {
		debugWindow.writeln("sc_getValue: object '" + name + "' not found!");
		return v;
	}

	// is this a selector?
	if (o.nodeName == "SELECT") {
		v = o.options[o.selectedIndex].text;
		debugWindow.writeln("sc_getValue: element '" + name + "' is a selector.");
		return new ValueObject(v, o.nodeName, o.disabled);
	}
	else if (o.nodeName == "INPUT" || o.nodeName == "TEXTAREA") {
		if (o.type == "radio" || o.type == "checkbox") {
			// getElementById always finds the first radio button or check box in a group
			// find the radio button or check box that is checked
			var list = sc_getGroup(name, o.type);
			if (o.type == "radio") debugWindow.writeln("sc_getValue: element '" + name + "' is a radio button.");
			else debugWindow.writeln("sc_getValue: element '" + name + "' is a check box.");

			if (list.length == 0) {
				// no group associated with the element
				if (o.checked) return new ValueObject(0, o.nodeName, o.disabled);
				else return new ValueObject(-1, o.nodeName, o.disabled);
			}
			else {
				// find the one that is checked
				for (var i = 0; i < list.length; i++) {
					o = list[i];
					if (o.checked) {
						debugWindow.writeln("sc_getValue: group element '" + name + "' is checked.");
						break;
					}
				}
			}

			// Check whether a checked item in the group has been found
			if (i == list.length)
				// none of the radio buttons or check boxes has been checked
				return new ValueObject(-1, o.nodeName, o.disabled);
			return new ValueObject(i, o.nodeName, o.disabled);
		}

		// determine the type of input
		v = o.value;
		return new ValueObject(v, o.nodeName, o.disabled);
	}
	else if (o.nodeName == "TD" || o.nodeName == "LI" || o.nodeName == "UL" || o.nodeName == "SPAN" || o.nodeName == "DIV") {
		switch (o.type)
		{
			case "TD": debugWindow.writeln("sc_getValue: element '" + name + "' is a table data element."); break;
			case "LI": debugWindow.writeln("sc_getValue: element '" + name + "' is a line item element."); break;
			case "UL": debugWindow.writeln("sc_getValue: element '" + name + "' is a unordered list element."); break;
			case "SPAN": debugWindow.writeln("sc_getValue: element '" + name + "' is a span element."); break;
			case "DIV": debugWindow.writeln("sc_getValue: element '" + name + "' is a div element."); break;
			default: debugWindow.writeln("sc_getValue: element '" + name + "' is an unsupported element."); break;
		}
		v = o.innerHTML;
		return new ValueObject(v, o.nodeName, o.disabled);
	}

	return null;
}

// Sets the value of the element with the specified name and disables it if appropriate
// The method fires the onchange method for all the supported elements but the radio button and check box
// For the latter two elements the onclick method is fired instead.
//
// The function allows you to only disable/enable a specific element by specifying a null value
function sc_setValue(name, val, disabled) {
	debugWindow.writeln("sc_setValue: name=" + name + ", value=" + val);

	// locate the object
	var o = null;
	try { o = document.getElementById(name); } catch (E) {}
	if (o == null) {
		debugWindow.writeln("object '" + name + "' not found!");
		return;
	}

	// nothing to do if both the new value and the disabled property are not set
	if (val == null && disabled == null) return;

	// monitor value changes and fire the onchange method when the value changes
	var valChanged = false;

	// is this a selector?
	if (o.nodeName == "SELECT") {
		// find the value in the list of options and select it
		for (var i = 0; val != null && i < o.length; i++) {
			if (val == "") {
			    // the empty value of a selector is a string of dashes
			    if (o.options[i].text.indexOf("---") == 0) {
						o.selectedIndex = i;
						if (curSelText != val) valChanged = true;
						break;
			    }
			}
		  else {
        if (o.options[i].text == val) {
					// Fire the onchange only if the selected changes
					var curSelText = o.options[o.selectedIndex].text;
					o.selectedIndex = i;
					if (curSelText != val) valChanged = true;
					break;
        }
	    }
		}
	}
	else if (o.nodeName == "INPUT" || o.nodeName == "TEXTAREA") {
		if (o.type == "radio" || o.type == "checkbox") {
			if (val != null) {
				// in case the check box wasn't checked at all, the index is -1.
				if (val == -1 && o.type == "checkbox")
					return true;

				if (val == "" || val < 0) {
					debugWindow.writeln("sc_setValue: radio button or checkbox index out of range!");
					return false;
				}

				// in case this a radio button or checkbox within a group, the value is the index of the button in the group that should be checked.
				var list = sc_getGroup(name, o.type);
				if (list.length != 0) {
					if (val >= list.length) {
						debugWindow.writeln("sc_setValue: radio button index out of range!");
						return false;
					}
					o = list[val];
				}
				o.checked = true;

				// execute the onclick method if defined
				if (o.onclick != null)
					try { o.onclick(); } catch(E) {debugWindow.writeln("exception in '" + o.onchange + "': " + E.description);}
			}
		}
		else {
			if (val != null) {
				var previousVal = o.value;
				o.value = val;
				if (previousVal != val)
					valChanged = true;
			}
		}
	}
	else if (o.nodeName == "TD" || o.nodeName == "LI" || o.nodeName == "UL" || o.nodeName == "SPAN" || o.nodeName == "DIV") {
		if (val != null) {
			var previousVal = o.innerHTML;
			o.innerHTML = val;
			if (previousVal != o.innerHTML)
				valChanged = true;
		}
	}
	else {
		// element not recognized/supported
		return false;
	}

	// Enable/Disable the element and update the session state
	if (disabled != null)  {
		if (String(disabled) == "true") {
			calcSession.updateVariable(name + "!disabled", disabled);
			o.disabled = true;
		}
		else {
			calcSession.removeVariable(name + "!disabled");
			o.disabled = false;
		}
	}

	// Fire the onchange method if the value has been changed
	if (valChanged && o.onchange != null)
		try { o.onchange(); } catch (E) {debugWindow.writeln("exception in '" + o.onchange + "': " + E.description);}

	return true;
}

// Restore the values from all field elements, registered using addField, from the session cookie
function sc_restore(allowEmptyValues) {
	if (allowEmptyValues == null)
		allowEmptyValues = false;

	debugWindow.writeln("sc_restore: #items to check " + this.fields.length);

	for (var i = 0; i < this.fields.length; i++) {
		var name = this.fields[i];
		var val = calcSession.getVariable(name);

		// check the disabled property
		var disabled = calcSession.getVariable(name + "!disabled");
		if (disabled == "")
			disabled == null; // property not defined in the session cookie for this variable

		debugWindow.writeln("sc_restore: retrieved name=" + name + ", value=" + val);

		if ((val == "" && allowEmptyValues) || val != "")
			if (!this.setValue(name, val, disabled))
				debugWindow.writeln("The value of '" + name + "' cannot be restored!");
	}
}

// save all field values registered by the controller to the session cookie
function sc_save() {
	debugWindow.writeln("sc_save: saving " + this.fields.length + " items to the session state");

	for (var i = 0; i < this.fields.length; i++) {
		var name = this.fields[i];
		var v = this.getValue(name);
		if (v != null) {
			debugWindow.writeln("sc_save: name=" + name + ", value=" + v.val);
			calcSession.updateVariable(name, v.val);

			// the default value for the disabled property is 'false'
			if (v.disabled)
				calcSession.updateVariable(name + "!disabled", v.disabled);
		}
	}
}

// Removes all stored values from the session cookie
function sc_clear() {
	debugWindow.writeln("sc_reset: removing " + this.fields.length + " items from the session state");

	for (var i = 0; i < this.fields.length; i++) {
		var name = this.fields[i];
		calcSession.removeVariable(name);
	}
}

function sc_sessionController() {
	// the array of fields maintained by this session controller
	this.fields = new Array();

	// methods
	this.addField = sc_addField;
	this.removeField = sc_removeField;
	this.getValue = sc_getValue;
	this.setValue = sc_setValue;

	this.clear = sc_clear;
	this.restore = sc_restore;
	this.save = sc_save;

	return this;
}

var sessionController = new Object();
sessionController = sc_sessionController();
