/**
 * The forms links initialization description.
 */
window.tx_icsfdfeformbuilder_links = [];
/**
 * The forms links value map definitions.
 */
window.tx_icsfdfeformbuilder_values = {};
/**
 * Initializes the forms links elements.
 * Takes the description and build the objects.
 */
function tx_icsfdfeformbuilder_link_load()
{
	for (var name in window.tx_icsfdfeformbuilder_links)
	{
		var link = new Link();
		link.init(window.tx_icsfdfeformbuilder_links[name]);
	}
}

/**
 * Represents a link between two controls.
 */
function Link()
{
}
/**
 * Initializes the link options.
 * @param options The link options. Properties:
 * - master: describes the master control.
 * - slave: describes the slave control.
 * Both properties have these sub-properties:
 * - element: contains the id of the control.
 * - type: references the object constructor of the link behaviour object.
 * - valueMap: contains the dictionary associating control values to link common keys. (Optional)
 * - parameters: contains specific settings for the link behaviour object.
 * For both controls, the link is registered to them.
 */
Link.prototype.init = function(options)
{
	var master = options.master.element;
	if ((typeof master != 'string') || (master == ''))
		throw "No master element defined. Could not build link.";
	var slave = options.slave.element;
	if ((typeof slave != 'string') || (slave == ''))
		throw "No slave element defined. Could not build link.";
	master = document.getElementById(master);
	if (master == null)
		throw "Could not find the link's master element " + options.master + ".";
	slave = document.getElementById(slave);
	if (slave == null)
		throw "Could not find the link's slave element " + options.slave + ".";
	this.initBaseEvents(master);
	this.initBaseEvents(slave);
	var masterType = options.master.type;
	if (masterType == null)
		masterType = LinkMaster;
	var slaveType = options.slave.type;
	if (slaveType == null)
		slaveType = LinkSlave;
	this.master = new masterType();
	this.slave = new slaveType();
	this.master.setValueMap(options.master.valueMap);
	this.slave.setValueMap(options.slave.valueMap);
	this.master.init(master, this.slave, options.master.parameters);
	this.slave.init(slave, this.master, options.slave.parameters);
	if (master.mlinks == null)
		master.mlinks = [];
	master.mlinks.push(this);
	if (slave.slinks == null)
		slave.slinks = [];
	slave.slinks.push(this);
};
/**
 * Initializes the control base events.
 * @remarks Overrides some events, keeping old functions.
 */
Link.prototype.initBaseEvents = function(element)
{
	if (element.link_evnt != null)
		return;
	element.link_evnt = {
	};
	for (var name in this.link_evnt)
	{
		element.link_evnt[name] = [];
		if (element[name] != null)
			element.link_evnt[name].push(element[name]);
		element[name] = this.link_evnt[name];
	}
};
/**
 * The event handlers for the control base events.
 * New event handlers can be added in this object.
 */
Link.prototype.link_evnt = {
	'onchange': function () {
		var functions = this.link_evnt.onchange;
		for (var name in functions)
			if (functions[name])
				functions[name].call(this);
	}
};

/**
 * Represents the base definition of all link behaviour object.
 */
function LinkBase()
{
}
/**
 * The reference to the control element.
 */
LinkBase.prototype.element = null;
/**
 * The reference to the associated link behaviour object of the other link side.
 */
LinkBase.prototype.peer = null;
/**
 * The dictionary associating control values to link common keys.
 * @remarks If this is null, the values may be passed "as is" to the other side.
 */
LinkBase.prototype.valueMap = null;
/**
 * Checks if the HTML element is of the specified HTML tag.
 * @param element The HTML element to check.
 * @param tag The HTML tag.
 * @return Result of the comparison between element's tag and tag.
 */
LinkBase.prototype.isTag = function(element, tag)
{
/**
 * Initializes this link behaviour object using the specified control and peer.
 * @param element The control element to link to.
 * @param peer The link's other side behaviour object.
 */
	return ((element.tagName != undefined) &&
			(element.tagName != null) &&
			(element.tagName.toLowerCase() == tag));
};
LinkBase.prototype.init = function(element, peer, settings)
{
	this.element = element;
	this.peer = peer;
};
/**
 * Obtains the current value of the control.
 * @return The current value or null if no current value.
 */
LinkBase.prototype.getValue = function()
{
/**
 * Obtains the current key of the control.
 * @return The current key if available, otherwise an empty string.
 */
	return null;
};
LinkBase.prototype.getKey = function()
{
/**
 * Defines the value to key association dictionary.
 * @param valueMap The dictionary.
 */
	return '';
};
LinkBase.prototype.setValueMap = function(valueMap)
{
/**
 * Sets the current key and updates the control accordingly.
 * @param key The new key to set.
 */
	this.valueMap = valueMap;
};
LinkBase.prototype.setKey = function(key)
{
/**
 * Handles the control's onchange event.
 */
};
LinkBase.prototype.valueChanged = function()
{
/**
 * Builds the list of control elements if the control have multiple elements.
 */
};
LinkBase.prototype.obtainElements = function()
{
	this.elements = [ this.element ];
	var i = 1;
	var id = this.element.id;
	var element = null;
	while ((element = document.getElementById(id + '_' + (i++))) != null)
		this.elements.push(element);
};

/**
 * Represents the base object for all master link behaviour objects.
 */
function LinkMaster()
{
}
// Setting the base object.
LinkMaster.prototype = new LinkBase();
/**
 * The kind of link behaviour object.
 */
LinkMaster.prototype.kind = 'master';

/**
 * Represents the base object for all slave link behaviour objects.
 */
function LinkSlave()
{
}
// Setting the base object.
LinkSlave.prototype = new LinkBase();
/**
 * The kind of link behaviour object.
 */
LinkSlave.prototype.kind = 'slave';

/**
 * Represents the master part of a one-way master to slave value relation.
 * Selection in the master updates the slave available values corresponding to the link key.
 */
function OneWayLinkMaster()
{
}
// Setting the base object.
OneWayLinkMaster.prototype = new LinkMaster();
// Saving the base object init method.
OneWayLinkMaster.prototype.LinkMaster_init = OneWayLinkMaster.prototype.init;
/**
 * The available custom getters depending of control type. After type resolution, 
 * it overrides the base class getters.
 */
OneWayLinkMaster.prototype.getters = {
	'getValue': {
		'select': function() {
			var options = this.element.options;
			var index = this.element.selectedIndex;
			if ((options != null) && (index >= 0))
				return options[index].value;
			return null;
		},
		'radio': function() {
			var value = null;
			for (var name in this.elements)
				if (this.elements[name].checked)
					value = this.elements[name].value;
			return value;
		}
	}
};
// Overriding base class init method.
OneWayLinkMaster.prototype.init = function(element, peer, settings)
{
	var master = this;
	this.LinkMaster_init(element, peer, settings);
	var kind = this.element.tagName.toLowerCase();
	if (kind != 'select')
		kind = this.element.type.toLowerCase();
	this.element.link_evnt.onchange.push(function() {
		master.valueChanged();
	});
	for (var name in this.getters)
		if (this.getters[name][kind] != null)
			this[name] = this.getters[name][kind];
};
// Overriding base class getKey method.
OneWayLinkMaster.prototype.getKey = function()
{
	var value = this.getValue();
	if (this.valueMap == null)
		return value;
	if (this.valueMap[value] != null)
		return this.valueMap[value];
	return '';
};
// Overriding base class valueChanged method.
OneWayLinkMaster.prototype.valueChanged = function()
{
	this.peer.setKey(this.getKey());
};

function OneWayLinkSlave()
{
}
// Setting the base object.
OneWayLinkSlave.prototype = new LinkSlave();
// Saving the base object init method.
OneWayLinkSlave.prototype.LinkSlave_init = OneWayLinkSlave.prototype.init;
/**
 * The available custom setters depending of control type. After type resolution, 
 * it overrides the base class setters.
 */
OneWayLinkSlave.prototype.setters = {
	'setKey': {
		'select': function(key) {
			if (this.elements == null)
			{
				this.elements = [];
				for (var i = 0; i < this.element.options.length; i++)
					this.elements.push(this.element.options[i]);
			}
			var selected = (this.element.selectedIndex >= 0) ? (this.element.options[this.element.selectedIndex]) : (null);
			var selectedInList = false;
			while (this.element.lastChild != null)
				this.element.removeChild(this.element.lastChild);
			if (key == '')
			{
				if (this.showAllIfNoParent == true)
				{
					for (var name in this.elements)
						this.element.appendChild(this.elements[name]);
					selectedInList = true;
				}
			}
			else
				for (var name in this.elements)
				{
					var el = this.elements[name];
					if ((el.value == '') && (this.alwaysShowEmpty == true))
					{
						this.element.appendChild(el);
						continue;
					}
					var elKey = (this.valueMap == null) ? this.elements[name].value : this.valueMap[this.elements[name].value];
					if (elKey != key)
					{
						el.selected = false;
					}
					else
					{
						if (el == selected)
							selectedInList = true;
						this.element.appendChild(el);
					}
				}
			if ((this.element.options.length <= ((this.alwaysShowEmpty == true) ? 1 : 0)) && (this.hideEmpty == true))
			{
				if (this.element.oldDisplay == null)
					this.element.oldDisplay = this.element.style.display;
				this.element.style.display = 'none';
			}
			else
			{
				if (this.element.oldDisplay != null)
					this.element.style.display = this.element.oldDisplay;
				this.element.oldDisplay = null;
				if (selectedInList)
					selected.selected = true;
				else
					this.element.selectedIndex = 0;
			}
			if (!this.inInit)
				this.element.onchange();
		},
		'radio': function(key) {
			for (var name in this.elements)
			{
				var el = this.elements[name];
				var elKey = (this.valueMap == null) ? this.elements[name].value : this.valueMap[this.elements[name].value];
				if (elKey != key)
				{
					el.checked = false;
					el.disabled = true;
					if (el.oldDisplay == null)
						el.oldDisplay = el.style.display;
					el.style.display = 'none';
				}
				else
				{
					el.disabled = false;
					if (el.oldDisplay != null)
						el.style.display = el.oldDisplay;
					el.style.oldDisplay = null;
				}
			}
		}
	}
};
// Overriding base class init method.
OneWayLinkSlave.prototype.init = function(element, peer, settings)
{
	this.LinkSlave_init(element, peer, settings);
	var kind = this.element.tagName.toLowerCase();
	if (kind != 'select')
		kind = this.element.type.toLowerCase();
	for (var name in this.setters)
		if (this.setters[name][kind] != null)
			this[name] = this.setters[name][kind];
	this.alwaysShowEmpty = settings.alwaysShowEmpty;
	this.hideEmpty = settings.hideEmpty;
	this.showAllIfNoParent = settings.showAllIfNoParent;
	this.inInit = true;
	this.peer.valueChanged();
	this.inInit = false;
};

