/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
/* Business cascading type handlers */

/* Super-class of all BusinessCascadingTypeHandler: sub-classes should implement a 'getValue' function */
var BusinessCascadingTypeHandler = Class.create(AbstractTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix, maxNbOfPickers)
	{
		$super(ifdKey, idPrefix);
		this.maxNbOfPickers = maxNbOfPickers;
	},
	getElementValue: function(elementIndex)
	{
		var controllers = this.getElementControllers();
		if (controllers.length <= elementIndex)
			return null;
		return controllers[elementIndex].getValue();
	},
	disable: function()
	{
		var controllers = this.getElementControllers();
		for (var i = 0; i < controllers.length; i++)
			controllers[i].disable();
	},
	enable: function()
	{
		var controllers = this.getElementControllers();
		for (var i = 0; i < controllers.length; i++)
			controllers[i].enable();
	},
	setOnChange: function(/*Function*/onChangeHandler)
	{
		this.onChangeHandler = onChangeHandler;
		/* store the current value, in order to avoid to trigger the onChange if there is no actual change.
			See triggerOnChangeIfNeeded */
		if (this.getValue)
		{
			this.currentValue = this.getValue();
		}
	},
	/** PRIVATE: return the array of the element picker controllers */
	getElementControllers:function()
	{
		var controllers = new Array();
		var nbOfPickers = this.getNbOfElements();
		for (var i = 0; i < nbOfPickers; i++)
		{
			var controller = window[this.baseId +"_"+i.toString() + 'Controller'];
			if (controller)
				controllers[i] = controller;
			else
				break;
		}
		return controllers;
	},
	/** PRIVATE: Attaches onChange handler on every drawn picker
		in order to redraw the picker when an element has changed.
		Even the last one is attached to the onCHange handler in order to allow to trigger
		the "onChange" on the picker. The function is called at the initialization of
		the picker and every time it is redrawn after an element change. */
	setupOnChangeHandlers: function()
	{
		var controllers = this.getElementControllers();
		for (var i = 0; i < controllers.length; i++)
		{
			if (controllers[i].setOnChange)
			{
				controllers[i].setOnChange(BusinessCascadingTypeHandler.elementPickerChangeHandler.bindAsEventListener(this, i));
			}
			else {
				/* make the hypothesis that the element pickers are one-field pickers */
				var picker = $(this.baseId + "_" + i.toString());
				if (picker == null) break;
				picker.observe('change', BusinessCascadingTypeHandler.elementPickerChangeHandler.bindAsEventListener(this, i));
			}
		}
	},
	/** PRIVATE: call the onChange handler if needed */
	triggerOnChangeIfNeeded: function()
	{
		if (this.onChangeHandler)
		{
			var valueChanged = true;
			if (this.getValue)
			{
				var value = this.getValue();
				if (value == this.currentValue)
					valueChanged = false;
				else
					this.currentValue = value;
			}
			if (valueChanged)
				this.onChangeHandler(this.lastChangeEvent);
		}
	},
	/** PRIVATE: return the number of elements in the cascade at a given moment */
	getNbOfElements : function()
	{
		return $F(this.baseId + "_nbElements");
	}
});

BusinessCascadingTypeHandler.elementPickerChangeHandler = function(event, pickerIndex)
{
	if (pickerIndex < (this.maxNbOfPickers - 1))
	{
		var requestParameters = { storageId: $F(this.baseId + 'StorageId') };
		var nbPickers = parseInt($F(this.baseId + '_nbElements'));
		requestParameters[this.ifdKey + '_presentationType'] = $F(this.baseId + '_presentationType');
		requestParameters[this.ifdKey + '_elementChanged'] = 'true';
		requestParameters[this.ifdKey + '_changedElementIndex'] = pickerIndex.toString();
		requestParameters[this.ifdKey + '_nbElements'] = nbPickers.toString();
		/*var form = $(this.baseId + '_presentationType').up('form'); 'up' does not seem to work with HtmlUnit */
		var form = $($(this.baseId + '_presentationType').form);
		var formElements = form.getElements();
		for (var i = 0; i < nbPickers; i++)
		{
			/* transfer all the form element values of the picker i*/
			for (var j = 0; j < formElements.length; ++j)
			{
				if (formElements[j].id)
				{
					if (formElements[j].name && formElements[j].id.startsWith(this.baseId + "_" + i.toString()))
					{
						var value = $F(formElements[j].id);
						if (value != null && value != '')
							requestParameters[formElements[j].name] = value;
					}
				}
			}
		}
		this.lastChangeEvent = event;	// for elementPickerChangedCallback, which will be called when redrawing the picker
		/*
		 *	onChangeHandler is called after the Ajax update, in the callback method elementPickerChangedCallback
		 */
		new CitobiAjax.Updater(this.baseId + '_picker', getAjaxServletUrl('DrawBusinessCascadingPicker'), requestParameters);
	}
	else
	{
		this.lastChangeEvent = event;
		this.triggerOnChangeIfNeeded();
	}
};

var BusinessFixedCascadingTypeHandler = Class.create(BusinessCascadingTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix, nbOfPickers)
	{
		$super(ifdKey, idPrefix, nbOfPickers);
		this.nbOfPickers = nbOfPickers;
	},
	getValue: function()
	{
		return this.getElementValue(this.nbOfPickers - 1);
	}
});

var CascadingObjectModuleTypeHandler = Class.create(BusinessCascadingTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix)
	{
		$super(ifdKey, idPrefix, 2);
	},
	getValue: function()
	{
		var module = this.getElementValue(0);
		if (module && module != '')
		{
			var paramName = this.getParameterName();
			if (paramName != null)
			{
				var paramValue = this.getElementValue(1);
				if (paramValue != null && paramValue != '')
					return module +"#" + paramName +"#" + paramValue + "#"+this.getParameterValueClassName();
			}
			return module;
		}
		else
			return null;
	},

	/* PRIVATE: get the possible parameter name */
	getParameterName: function()
	{
		return $(this.baseId + "_paramName") ? $F(this.baseId + "_paramName") : null;
	},
	/* PRIVATE: get the possible parameter name */
	getParameterValueClassName: function()
	{
		return $(this.baseId + "_paramValueClass") ? $F(this.baseId + "_paramValueClass") : null;
	}
});

var CascadingRelationTargetTypeHandler = Class.create(BusinessCascadingTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix)
	{
		$super(ifdKey, idPrefix, 2);
	},
	getValue: function()
	{
		var relation = this.getElementValue(0);
		if (relation && relation != '')
		{
			var paramName = this.getParameterName();
			if (paramName != null)
			{
				var paramValue = this.getElementValue(1);
				if (paramValue != null && paramValue != '')
					return relation +"#" + paramName +"#" + paramValue + "#"+this.getParameterValueClassName();
			}
			return relation;
		}
		else
			return null;
	},

	/* PRIVATE: get the possible parameter name */
	getParameterName: function()
	{
		return $(this.baseId + "_paramName") ? $F(this.baseId + "_paramName") : null;
	},
	/* PRIVATE: get the possible parameter name */
	getParameterValueClassName: function()
	{
		return $(this.baseId + "_paramValueClass") ? $F(this.baseId + "_paramValueClass") : null;
	}
});

var CascadingObjectPropertyTypeHandler = Class.create(BusinessFixedCascadingTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix)
	{
		$super(ifdKey, idPrefix, 2);
	},
	getValue: function()
	{
		var module = this.getElementValue(0);
		var property = this.getElementValue(1);
		if (property == null || property == "")
			return null;
		else
			return module+"#"+property;
	}
});

var CascadingMTSCriteriumTypeHandler = Class.create(BusinessCascadingTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix)
	{
		$super(ifdKey, idPrefix, 3);
	},
	getValue: function()
	{
		var objectProperty = this.getElementValue(0);
		if (objectProperty == null || objectProperty == "")
			return null;
		var operator = this.getElementValue(1);
		if (operator == null || operator == "")
			return null;
		

		/* Take the property, without the ObjectModule */
		var index = objectProperty.lastIndexOf("#");
		var property = objectProperty.substring(index + 1);
		if (this.getNbOfElements() == 3)
		{
			var value = this.getElementValue(2);
			if (value && value != '')
			{
				return property+"#"+operator+"#"+value;
			}
			else
				return null;
		}
		else
			return property+"#"+operator;
	}
});

/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
/* Actito Paragraph type handler */

var ActitoParagraphTypeHandler = Class.create(ParagraphTypeHandler,
{
	paragraphStylesheet : window.form2StaticResourcePath+"/style/actitoParagraphContent.css",
	theCustomImgSrc : window.form2StaticResourcePath+"/images/actitoparagraph/actitoCustom.gif",
	isActitoCustomEnabled : true,
	initialize: function($super, ifdKey, idPrefix)
	{
		$super(ifdKey, idPrefix);
		/* Items to appear in toolbar. */
		this.paragraphToolbarItems.push("actitoCustom");
	},
	setActitoCustomEnabled : function(_isActitoCustomEnabled)
	{
		this.isActitoCustomEnabled = _isActitoCustomEnabled;
	},
	/** OVERRIDE*/
	getToolbar : function(editor,editorId,width)
	{
		return new ActitoParagraphToolbar(editor,editorId,width);
	},
	/** OVERRIDE : Action taken when toolbar item activated */
	paragraphToolbarAction : function($super)
	{
		/*WARNING !!! : in this method 'this' is not the type handler, but the editor button img*/
		$super();

		var theToolbar = this.parentNode.parentNode.paragraphToolbarObject;
		var theParagraphEditor = theToolbar.paragraphEditorObject;
		var theIframe = theParagraphEditor.theIframe;

		switch (this.action)
		{
			case "actitoCustom":
				var theAlt = "";
					var theSelection = null;
					var theRange = null;

					/* IE selections */
					if (theIframe.contentWindow.document.selection)
					{
						/* Escape quotes in alt text */
						theAlt = theAlt.replace(/"/g, "'");

						theSelection = theIframe.contentWindow.document.selection;
						theRange = theSelection.createRange();
						theRange.collapse(false);
						theRange.pasteHTML("<img alt=\"" + theAlt + "\" src=\"" + theParagraphEditor.theCustomImgSrc + "\"/>");

						break;
					}
					/* Mozilla selections */
					else
					{
						try
						{
							theSelection = theIframe.contentWindow.getSelection();
						}
						catch (e)
						{
							return false;
						}

						theRange = theSelection.getRangeAt(0);
						theRange.collapse(false);

						var theImageNode = theIframe.contentWindow.document.createElement("img");
						theImageNode.src = theParagraphEditor.theCustomImgSrc;
						theImageNode.alt = theAlt;

						theRange.insertNode(theImageNode);

						break;
				}
			default:
				break;
		}

		if (theParagraphEditor.wysiwyg == true)
		{
			theIframe.contentWindow.focus();
		}
		else
		{
			theParagraphEditor.theTextarea.focus();
		}

		return false;
	},
	/** OVERRIDE : Changes the toolbar status*/
	changeState:function($super,theParagraphEditor,theParentNode,tempParentNode,theLevel)
	{
		$super(theParagraphEditor,theParentNode,tempParentNode,theLevel);

		theParentNode = tempParentNode;

		while (theParentNode.nodeName.toLowerCase() != "body")
		{
			var tagName = theParentNode.nodeName.toLowerCase();
			if (tagName == "img")
			{
				theParagraphEditor.theToolbar.setState("ActitoCustom", "on");
			}
			else
				theParagraphEditor.theToolbar.setState("SelectBlock", "<" + theParentNode.nodeName.toLowerCase() + ">");

			theParentNode = theParentNode.parentNode;
			theLevel++;
		}
	},
	/** OVERRIDE : Checkes if a string is the nodeName of an accepted element */
	isAcceptedElementName : function($super,theString)
	{
		var isAcceptedElementNameForBasicAction = $super(theString);

		if (!isAcceptedElementNameForBasicAction)
		{
			var elementList = new Array("img");
			for (var i = 0; i < elementList.length; i++)
			{
				if (theString == elementList[i])
				{
					return true;
				}
			}
		}
		else
			return true;

		return false;
	}
});

/** Paragraph Toolbar */
var ActitoParagraphToolbar = Class.create(ParagraphToolbar,
{
	initialize:function($super,theEditor,theEditorId, width)
	{
		$super(theEditor,theEditorId,width);

		/* Create toolbar items */
		for (var i = 0; i < theEditor.paragraphToolbarItems.length; i++)
		{
			switch (theEditor.paragraphToolbarItems[i])
			{
				case "actitoCustom":
					if (theEditor.isActitoCustomEnabled)
					this.addButton(this.theList.id + "ButtonActitoCustom", "paragraphButtonActitoCustom", "Actito Custom", "actitoCustom",theEditor);

						break;
					}
				}
	}
});


/* -----------------------------------------------------------------------------------------------
 QuestionAnswer handlers: they have the following functions in common: disable, enable,
 	getNormalValue and getOtherValue, isKnown, isNormalValueSelected, isOtherValueSelected,
 	isValueKnown, isValueEqualTo, isValueIn, setOnChange.

 	Each handler must implementent a setupOnChangeListener that aims to bind
 	the function 'AbstractQuestionAnswerTypeHandler.onChangeHandler'
 	to an event 'change'.
*/
var AbstractQuestionAnswerTypeHandler = Class.create(AbstractTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix)
	{
		$super(ifdKey, idPrefix);
		this.onChangeListenersEnabled = false;
		this.displayConditionEvaluators = new Array();
		this.onChangeListeners = new Array();
	},
	setOnChange : function(onChangeListener)
	{
		this.onChangeListeners.push(onChangeListener);
		if(!this.onChangeListenersEnabled)
		{
			this.setupOnChangeListener();
			this.onChangeListenersEnabled = true;
		}
	},
	addDisplayConditionEvaluator : function(displayConditionEvaluator)
	{
		this.displayConditionEvaluators[this.displayConditionEvaluators.length] = displayConditionEvaluator;
		if(!this.onChangeListenersEnabled)
		{
			this.setupOnChangeListener();
			this.onChangeListenersEnabled = true;
		}
	}
});
AbstractQuestionAnswerTypeHandler.onChangeHandler = function(event)
{
	if (this.onChangeListeners)
	{
		this.onChangeListeners.each(function(f){f(event);});
	}
	this.displayConditionEvaluators.each(function(f){f(event);});
};


/*
	Abstract type handler for the single-value: all the 'sub-classes' must implement
	the following methods: disable, enable, getNormalValue and getOtherValue, isOtherValueSelected.

 	Each handler must implementent a setupOnChangeListener that aims to bind
 	the function 'AbstractQuestionAnswerTypeHandler.onChangeHandler'
 	to an event 'change'.
*/
var AbstractSingleValueQuestionAnswerTypeHandler = Class.create(AbstractQuestionAnswerTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix)
	{
		$super(ifdKey, idPrefix);
	},
	isKnown: function()
	{
		return this.isNormalValueSelected() || this.isOtherValueSelected();
	},
	isValueKnown: function()
	{
		return this.isNormalValueSelected();
	},
	isValueEqualTo: function(value)
	{
		return this.getNormalValue() == value;
	},
	isValueIn: function(/* Array */values)
	{
		var thisValue = this.getNormalValue();
		return values.indexOf(thisValue) >= 0;
	},
	isNormalValueSelected : function()
	{
		var value = this.getNormalValue();
		return value != null && value != "";
	}
});

/*
	Type handler for a single-value question with presentation type different from 'hidden'
	and 'radio': there is no other value picker. The question answer picker is fully
	delegated to the normal value picker.
*/
var DelegatedSingleValueQuestionAnswerTypeHandler = Class.create(AbstractSingleValueQuestionAnswerTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix, normalValuePickerController)
	{
		$super(ifdKey, idPrefix);
		this.normalValuePickerController = normalValuePickerController;
	},
	getNormalValue: function()
	{
		return this.normalValuePickerController.getValue();
	},
	getOtherValue: function()
	{
		return null;
	},
	isOtherValueSelected: function()
	{
		return false;
	},
	isOtherValueAllowed: function()
	{
		return false;
	},
	disable: function()
	{
		this.normalValuePickerController.disable();
	},
	enable: function()
	{
		this.normalValuePickerController.enable();
	},
	setError: function(/*boolean*/isInError, /*String*/errorMessage)
	{
		this.normalValuePickerController.setError(isInError, errorMessage, this.baseId +"_picker");
	},
	setupOnChangeListener : function()
	{
		if (this.normalValuePickerController.setOnChange)
		{
			this.normalValuePickerController.setOnChange(AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
		}
		else
		{
			/* make the hypothesis that the other-value picker is a one-field picker */
			var picker = $(this.baseId);
			picker.observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
		}
	}
});


/*
	Type handler for a single-value question with presentation type is 'radio'.
*/
var SingleValueQuestionAnswerRadioTypeHandler = Class.create(AbstractSingleValueQuestionAnswerTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix, otherValuePickerController)
	{
		$super(ifdKey, idPrefix);
		this.otherValuePickerController = otherValuePickerController;
		if (otherValuePickerController)
		{
			var picker = $(this.baseId +'_OtherValue');
			picker.observe('focus', this.onOtherValueFocus.bindAsEventListener(this));
			this.getOtherValueRadionBtn().observe('click', this.setFocusOnOtherValuePicker.bindAsEventListener(this));
		}
	},
	/** PRIVATE: return the id of the other value picker. PRE: otherValueAllowed = true */
	getOtherStringValue: function()
	{
		return $(this.baseId + '_otherStringValue').value;
	},

	isOtherValueAllowed: function()
	{
		return this.otherValuePickerController != null;
	},

	/* PRIVATE Gets all the picker input elements whatever the presentation type is.*/
	getPickerInputs: function()
	{
		var allInputsByName = $A(document.getElementsByName(this.ifdKey)); /* Get input elements by name.*/
		var pickerInputs = allInputsByName.findAll(function(input) { return input.id.startsWith(this.baseId); }, this); /* Remove the ones who belongs to other pickers.*/
		return pickerInputs.map(Element.extend); /* Extend the DOM elements.*/
	},
	getNormalValue: function()
	{
		if (this.isOtherValueSelected())
		{
			return null;
		}
		else
			return this.getSelectedRadioButtonValue();
	},
	isOtherValueSelected: function()
	{
		if (this.isOtherValueAllowed())
		{
			var value = this.getSelectedRadioButtonValue();
			return (value == this.getOtherStringValue());
		}
		else
			return false;
	},
	getOtherValue: function()
	{
		if (this.isOtherValueSelected())
		{
			var value = this.otherValuePickerController.getValue();
			return value==null?"":value;	/* if other value selected, other value cannot be null */
		}
		else
			return null;
	},
	/** PRIVATE: get the selected radio button value */
	getSelectedRadioButtonValue: function()
	{
		var elements = this.getPickerInputs();
		var selectedElement = elements.find(function(element) { return element.checked; });
		if (selectedElement != null)
			return $F(selectedElement);
		else
			return null;
	},
	disable: function()
	{
		this.getPickerInputs().invoke('disable');
		if (this.isOtherValueAllowed())
			this.otherValuePickerController.disable();
	},
	enable: function()
	{
		this.getPickerInputs().invoke('enable');
		if (this.isOtherValueAllowed())
		{
			this.otherValuePickerController.enable();
		}
	},
	setupOnChangeListener : function()
	{
		var elements = this.getPickerInputs();
		var thisPicker = this;
		elements.each(function(e){e.observe('click', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(thisPicker));});

		if (this.otherValuePickerController)
		{
			if (this.otherValuePickerController.setOnChange)
			{
				this.otherValuePickerController.setOnChange(AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
			}
			else
			{
				/* make the hypothesis that the delegated picker is a one-field picker */
				var picker = $(this.baseId +'_OtherValue');
				picker.observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
			}
		}
	},
	getOtherValueRadionBtn : function()
	{
		var otherValueString = this.getOtherStringValue();
		if (otherValueString)
			return this.getPickerInputs().find(function(element) { return (element.value == otherValueString); } );
		else
			return null;
	},
	settingOnFocusOnOtherValuePicker : false,
	onOtherValueFocus : function(event)
	{
		if (this.settingOnFocusOnOtherValuePicker)
			return;
		var elementToCheck = this.getOtherValueRadionBtn();
		if (!elementToCheck.checked)
		{
			elementToCheck.checked = true;
			var fct = AbstractQuestionAnswerTypeHandler.onChangeHandler.bind(this);
			fct(event);
		}
	},
	setFocusOnOtherValuePicker : function(event)
	{
		this.settingOnFocusOnOtherValuePicker = true;
		var picker = $(this.baseId +'_OtherValue').focus();
		this.settingOnFocusOnOtherValuePicker = false;
	}
});


/*
	Type handler for a single-value question with presentation type is 'hidden'.
*/
var SingleValueQuestionAnswerHiddenTypeHandler = Class.create(AbstractSingleValueQuestionAnswerTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix, otherValuePickerController)
	{
		$super(ifdKey, idPrefix);
		this.otherValuePickerController = otherValuePickerController;
	},
	isOtherValueAllowed: function()
	{
		return this.otherValuePickerController != null;
	},
	isOtherValueSelected: function()
	{
		if (!this.isOtherValueAllowed())
			return false;
		return $(this.baseId +"_HasOther").value == 'true';
	},
	getNormalValue: function()
	{
		if (this.isOtherValueAllowed())
		{
			if (!this.isOtherValueSelected())
			{
				var value = $(this.baseId).value;
				return value==""?null:value;
			}
			else
				return null;
		}
		else
		{
			var value = $(this.baseId).value;
			return value==""?null:value;
		}
	},
	getOtherValue: function()
	{
		if (this.isOtherValueAllowed())
		{
			if (this.isOtherValueSelected())
			{
				var value = this.otherValuePickerController.getValue();
				return value==null?"":value;	/* if other value selected, other value cannot be null */
			}
			else
				return null;
		}
		else
			return null;
	},
	disable: function()
	{
		$(this.baseId).disable();
		if (this.isOtherValueAllowed())
		{
			$(this.baseId+"_HasOther").disable();
			this.otherValuePickerController.disable();
		}
	},
	enable: function()
	{
		$(this.baseId).enable();
		if (this.isOtherValueAllowed())
		{
			$(this.baseId+"_HasOther").enable();
			this.otherValuePickerController.enable();
		}
	},
	setupOnChangeListener : function()
	{
		$(this.baseId).observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));

		if (this.otherValuePickerController)
		{
			$(this.baseId +'_HasOther').observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));

			if (this.otherValuePickerController.setOnChange)
			{
				this.otherValuePickerController.setOnChange(AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
			}
			else
			{
				/* make the hypothesis that the delegated picker is a one-field picker */
				var picker = $(this.baseId +'_OtherValue');
				picker.observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
			}
		}
	}
});


/*
	Abstract type handler for the multiple-value: all the 'sub-classes' must implement
	the following methods: disable, enable, getNormalValues and getOtherValue, isOtherValueSelected.

 	Each handler must implementent a setupOnChangeListener that aims to bind
 	the function 'AbstractQuestionAnswerTypeHandler.onChangeHandler'
 	to an event 'change'.
*/
var AbstractMultipleValueQuestionAnswerTypeHandler = Class.create(AbstractQuestionAnswerTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix)
	{
		$super(ifdKey, idPrefix);
	},
	isKnown: function()
	{
		return this.isNormalValueSelected() || this.isOtherValueSelected();
	},
	isValueKnown: function()
	{
		return this.isNormalValueSelected();
	},
	areValuesEqualTo: function(/* Array */ values)
	{
		if (values == null)
			values = new Array();
		var selectedValues = this.getNormalValues();
		if (selectedValues == null)
			selectedValues = new Array();
		values = values.without("").uniq();
		if (selectedValues.length != values.length)
			return false;
		for (var i = 0; i < selectedValues.length; ++i)
		{
			if (selectedValues.indexOf(values[i]) < 0)
				return false;
		}
		return true;
	},
	contains: function(/* Array */values)
	{
		var selectedValues = this.getNormalValues();
		for (var i = 0; i < values.length; ++i)
		{
			if (selectedValues.indexOf(values[i]) < 0)
				return false;
		}
		return true;
	},
	isNormalValueSelected : function()
	{
		var values = this.getNormalValues();
		return values != null && values.length > 0;
	}
});


/*
	Type handler for a multiple-value question with presentation type is 'checkbox'.
*/
var MultipleValueQuestionAnswerCheckboxTypeHandler = Class.create(AbstractMultipleValueQuestionAnswerTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix, otherValuePickerController)
	{
		$super(ifdKey, idPrefix);
		this.otherValuePickerController = otherValuePickerController;
		if (otherValuePickerController)
		{
			var picker = $(this.baseId +'_OtherValue');
			picker.observe('focus', this.onOtherValueFocus.bindAsEventListener(this));
			this.getOtherValueCheckboxBtn().observe('click', this.setFocusOnOtherValuePicker.bindAsEventListener(this));
		}		
	},
	/** PRIVATE: return the id of the other value picker. PRE: otherValueAllowed = true */
	getOtherStringValue: function()
	{
		return $(this.baseId + '_otherStringValue').value;
	},

	isOtherValueAllowed: function()
	{
		return this.otherValuePickerController != null;
	},

	/* PRIVATE Gets all the picker input elements whatever the presentation type is.*/
	getPickerInputs: function()
	{
		var allInputsByName = $A(document.getElementsByName(this.ifdKey)); /* Get input elements by name.*/
		var pickerInputs = allInputsByName.findAll(function(input) { return input.id.startsWith(this.baseId); }, this); /* Remove the ones who belongs to other pickers.*/
		return pickerInputs.map(Element.extend); /* Extend the DOM elements.*/
	},
	getNormalValues: function()
	{
		var elements = this.getPickerInputs();
		var selectedValues = new Array();
		if (this.isOtherValueAllowed())
		{
			var otherStringValue = this.getOtherStringValue();
			elements.each(function(e){var value = $F(e); if (e.checked && value != otherStringValue) selectedValues.push(value);});
		}
		else
		{
			elements.each(function(e){if (e.checked) selectedValues.push($F(e));});
		}
		return selectedValues;
	},
	isOtherValueSelected: function()
	{
		if (this.isOtherValueAllowed())
		{
			return $(this.baseId +"_Other").checked;
		}
		else
			return false;
	},
	getOtherValue: function()
	{
		if (this.isOtherValueSelected())
		{
			return this.otherValuePickerController.getValue();
		}
		else
			return null;
	},
	disable: function()
	{
		this.getPickerInputs().invoke('disable');
		if (this.isOtherValueAllowed())
			this.otherValuePickerController.disable();
	},
	enable: function()
	{
		this.getPickerInputs().invoke('enable');
		if (this.isOtherValueAllowed())
			this.otherValuePickerController.enable();
	},
	setupOnChangeListener : function()
	{
		var elements = this.getPickerInputs();
		var thisPicker = this;
		elements.each(function(e){e.observe('click', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(thisPicker));});

		if (this.otherValuePickerController)
		{
			if (this.otherValuePickerController.setOnChange)
			{
				this.otherValuePickerController.setOnChange(AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
			}
			else
			{
				/* make the hypothesis that the delegated picker is a one-field picker */
				var picker = $(this.baseId +'_OtherValue');
				picker.observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
			}
		}
	},
	getOtherValueCheckboxBtn : function()
	{
		var otherValueString = this.getOtherStringValue();
		if (otherValueString)
			return this.getPickerInputs().find(function(element) { return (element.value == otherValueString); } );
		else
			return null;
	},
	settingOnFocusOnOtherValuePicker : false,
	onOtherValueFocus : function(event)
	{
		if (this.settingOnFocusOnOtherValuePicker)
			return;
		var elementToCheck = this.getOtherValueCheckboxBtn();
		if (!elementToCheck.checked)
		{
			elementToCheck.checked = true;
			var fct = AbstractQuestionAnswerTypeHandler.onChangeHandler.bind(this);
			fct(event);
		}
	},
	setFocusOnOtherValuePicker : function(event)
	{
		this.settingOnFocusOnOtherValuePicker = true;
		var picker = $(this.baseId +'_OtherValue').focus();
		this.settingOnFocusOnOtherValuePicker = false;
	}
});


/*
	Type handler for a multiple-value question with presentation type is 'hidden'.
*/
var MultipleValueQuestionAnswerHiddenTypeHandler = Class.create(AbstractMultipleValueQuestionAnswerTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix, otherValuePickerController)
	{
		$super(ifdKey, idPrefix);
		this.otherValuePickerController = otherValuePickerController;
	},
	isOtherValueAllowed: function()
	{
		return this.otherValuePickerController != null;
	},
	isOtherValueSelected: function()
	{
		if (!this.isOtherValueAllowed())
			return false;
		return $(this.baseId +"_HasOther").value == 'true';
	},
	/* PRIVATE Gets all the picker input elements whatever the presentation type is.*/
	getPickerInputs: function()
	{
		var allInputsByName = $A(document.getElementsByName(this.ifdKey)); /* Get input elements by name.*/
		var pickerInputs = allInputsByName.findAll(function(input) { return input.id.startsWith(this.baseId); }, this); /* Remove the ones who belongs to other pickers.*/
		return pickerInputs.map(Element.extend); /* Extend the DOM elements.*/
	},
	getNormalValues: function()
	{
		var elements = this.getPickerInputs();
		var selectedValues = new Array();
		elements.each(function(e){selectedValues.push($F(e));});
		return selectedValues;
	},
	getOtherValue: function()
	{
		if (this.isOtherValueAllowed())
		{
			if (this.isOtherValueSelected())
				return this.otherValuePickerController.getValue();
			else
				return null;
		}
		else
			return null;
	},
	disable: function()
	{
		this.getPickerInputs().invoke('disable');
		if (this.isOtherValueAllowed())
			this.otherValuePickerController.disable();
	},
	enable: function()
	{
		this.getPickerInputs().invoke('enable');
		if (this.isOtherValueAllowed())
		{
			if (this.isOtherValueSelected())
				this.otherValuePickerController.enable();
		}
	},
	setupOnChangeListener : function()
	{
		var elements = this.getPickerInputs();
		var thisPicker = this;
		elements.each(function(e){e.observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(thisPicker));});

		if (this.otherValuePickerController)
		{
			$(this.baseId +'_HasOther').observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));

			if (this.otherValuePickerController.setOnChange)
			{
				this.otherValuePickerController.setOnChange(AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
			}
			else
			{
				/* make the hypothesis that the delegated picker is a one-field picker */
				var picker = $(this.baseId +'_OtherValue');
				picker.observe('change', AbstractQuestionAnswerTypeHandler.onChangeHandler.bindAsEventListener(this));
			}
		}
	}
});

var AndOrSearchCriteriumTypeHandler = Class.create(AbstractTypeHandler,
{
	/* Builds a new AndOrSearchCriteriumTypeHandler object given its ifd key, its id prefix and an URL that will be used to draw additional criteria.*/
	initialize: function($super, ifdKey, idPrefix, _logicalOperator, _canUseRelationCriteria, _canUseOrCriteria, _imbricationMaxLevel)
	{
		$super(ifdKey, idPrefix);
		this.logicalOperator = _logicalOperator;
		this.canUseRelationCriteria = _canUseRelationCriteria;
		this.canUseOrCriteria = _canUseOrCriteria;
		this.imbricationMaxLevel = _imbricationMaxLevel;
	},
	/*initialize the nextIndex counter with the init value list size*/
	initHandler: function()
	{
		if ($(this.baseId + 'List'))
			this.nextIndex = $A($(this.baseId + 'List').childElements()).size();
		else
			this.nextIndex = 0;
	},
	/* Adds a criterium picker.*/
	addValue: function(type,operator,level,parentIsWhereClause)
	{
		var parentIsWhereClauseCriterium = false;
		if (parentIsWhereClause)
			parentIsWhereClauseCriterium = true;

		var list = $(this.baseId + 'List');
		if (!list)
		{
			list = document.createElement("ul");
			list.className = "AndOrSearchCriteria bloc_0";
			list.id = this.baseId + 'List';

			/*insert list after last hidden input*/
			$(this.baseId+'ControllerName').insert({after : list});
			list = $(list);
		}
		if (list.childElements().length == 1)/*hide the unindent button if exists*/
		{
			var loneChildLI = list.childElements()[0];
			var elementId = loneChildLI.id.substring(0, loneChildLI.id.lastIndexOf('ListItem'));	/* LI id ends with ListItem*/
			var loneChildType = $F(elementId + 'CriteriumType');
			if (loneChildType == 0)
			{
				var unindentButton = loneChildLI.down(".criteriumElementUnindent");
				if (unindentButton)
					unindentButton.hide();
			}
		}
		var newElementIndex = this.nextIndex++;
		list.insert('<li id=\"' + this.baseId + 'Element' + newElementIndex+'ListItem\" class=\"AndOrSearchCriteriumElement ' + 
				this.logicalOperator + ' ' + window.form2Language.toUpperCase() +'\"></li>'); /* Classes AND/OR and EN/FR/NL alllows to display an image with the operator */
		var newDiv = list.childElements().last();
		new CitobiAjax.Updater(newDiv, getAjaxServletUrl('DrawSearchCriteriumElement'), 
		{ 
			storageId: $F(this.baseId + 'StorageId'), 
			criteriumType: type, 
			criteriumOperator: operator,  
			currentLevel: level, 
			elementIndex: newElementIndex,
			parentIsWhereClauseCriterium:parentIsWhereClauseCriterium 
		},
		{
			onComplete : function()
			{
				list.childElements().first().className = 'AndOrSearchCriteriumElement';	/* Remove the AND/OR image on the first element */
			}
		});
	},
	/*called when clicking on the remove button (also hides unindent button if necessary)*/
	removeValue : function(e,criteriumBaseKey)
	{
		this.removeCriterium(criteriumBaseKey);
	},
	removeCriterium : function(criteriumBaseKey)
	{
		var criteriumId = this.prefixId(criteriumBaseKey);
		var listElement = $(criteriumId +"ListItem");
		var list = listElement.up();

		listElement.remove();
		var upParentId = this.getParentCriteriumPickerId(criteriumId);
		var upParent = $(upParentId + "ListItem");	/* Null if first level */

		/*make the unindent button appear*/
		if (list.childElements().length == 1 && upParent)
		{
			var lastChildLI = list.childElements()[0];
			var lastChildType = lastChildLI.down("div.citField").firstDescendant().value;
			var unindentButton;
			if (lastChildType != '1')/*the last child is not a relation*/
			{
				unindentButton = lastChildLI.down(".criteriumElementUnindent");
			}
			else
			{
				unindentButton = lastChildLI.down("div.relationActionButtonsContainer").firstDescendant();

			}
			if (unindentButton)
				unindentButton.show();
		}

		if (list.childElements().length == 0 && upParent)
		{
			this.removeCriterium(upParentId);
			return;
		}
		else
		{
			/*set the operator background img (none for the first li)*/
			for (var i=0;i<list.childElements().length;i++)
			{
				list.childElements()[i].className = 'AndOrSearchCriteriumElement'+(i > 0 ? ' '+this.logicalOperator.toUpperCase() : '')+' '+window.form2Language.toUpperCase();
			}
		}

		/*for xhtml validity, the empty UL must be removed*/
		if (list.tagName == 'UL' && list.hasClassName('bloc_0') && list.childElements().length == 0)
			list.remove();
	},
	/* PRIVATE Gets the element controllers in display order.*/
	getElementControllers: function()
	{
		var indexFields = $A(document.getElementsByName(this.ifdKey + 'ElementIndex'));
		return indexFields.collect(function(indexField) { return eval(this.baseId + 'Element' + Form.Element.getValue(indexField)); }, this);
	},
	/*returns an array containing all the form inputs that belong to the given picker id (and verifying that those inputs are descendants of the given parent*/
	getRequestParameters: function(pickerBaseKey, parent)
	{
		if (pickerBaseKey)
		{
			var pickerBaseId = this.prefixId(pickerBaseKey);
			var requestParameters = { };
			var form = $(pickerBaseId + 'CriteriumType').up('form');
			var formElements = form.getElements();
			/* transfer all the form element values of the picker i*/
			for (var j = 0; j < formElements.length; ++j)
			{
				if (formElements[j].id)
				{
					if (formElements[j].name
							&& formElements[j].name.startsWith(pickerBaseKey)
							&& $(formElements[j]).descendantOf(parent))
					{
						var value = $F(formElements[j].id);
						if (value != null && value != '')
						{
							if (requestParameters[formElements[j].name])
							{
								if (Object.isArray(requestParameters[formElements[j].name]))
								{
									requestParameters[formElements[j].name].push(value);
								}
								else
								{
									var newArray = new Array();
									newArray.push(requestParameters[formElements[j].name]);
									newArray.push(value);
									requestParameters[formElements[j].name] = newArray;
								}
							}
							else
								requestParameters[formElements[j].name] = value;
						}
					}
				}
			}
			return requestParameters;
		}
	},
	/* PRIVATE Finds all this criterium type handler images*/
	getCriteriumButtons: function()
	{
		var criteriumTypeHandlerImages = $$('img.citArrayTypeHandlerButton');
		return criteriumTypeHandlerImages.findAll(function(image) { return image.identify().startsWith(this.baseId + 'Element'); }, this);
	},
	disable: function()
	{
		var controllers = this.getElementControllers();
		controllers.invoke('disable');
		var buttons = this.getCriteriumButtons();
		buttons.invoke('hide');
	},
	enable: function()
	{
		var controllers = this.getElementControllers();
		controllers.invoke('enable');
		var buttons = this.getCriteriumButtons();
		buttons.invoke('show');
	},
	getValue: function()
	{
		var controllers = this.getElementControllers();
		return controllers.invoke('getValue');
	},
	/*creates a And/Or search criterion containing the associated criterium from which it is called*/
	indent: function(operator, level, criteriumPickerKey)
	{
		var list = $(this.baseId + 'List');	/* ul */
		var criteriumPickerId = this.prefixId(criteriumPickerKey);
		var andOrSearchCriteriumElement = $(criteriumPickerId+'ListItem'); /* li */
		if (andOrSearchCriteriumElement)
		{
			var requestParameters = this.getRequestParameters(criteriumPickerKey, andOrSearchCriteriumElement);
			requestParameters['storageId'] = $F(this.baseId + 'StorageId');
			requestParameters['currentLevel'] = level;
			requestParameters['parentOperator'] = operator;
			requestParameters['associatedCriteriumId'] = criteriumPickerKey;
			var criteriumElementIndex = parseInt($F(criteriumPickerId + 'Index'));
			requestParameters['elementIndex'] = criteriumElementIndex;
			new CitobiAjax.Updater(andOrSearchCriteriumElement, getAjaxServletUrl('DrawIndentedSearchCriteriumElement'), requestParameters,
			{
				onComplete : function()
				{
					var unindentButtonDiv = $(criteriumPickerId +'Unindent');
					if (unindentButtonDiv)
						unindentButtonDiv.show();
					list.childElements().first().className = 'AndOrSearchCriteriumElement';	/* Remove AND/OR button on first element */
				}
			}
			);
		}
	},
	/*replace the And/Or search criterion from which it is called by its last child*/
	unindent: function(operator, level, criteriumPickerKey)
	{
		var criteriumPickerId = this.prefixId(criteriumPickerKey);
		var pickerDiv = $(criteriumPickerId+'_picker');
		var andOrSearchCriteriumElement = $(criteriumPickerId+'ListItem');
		if (andOrSearchCriteriumElement)
		{
			var pickerType = $F(criteriumPickerId+'CriteriumType');
			var parentPickerId = this.getParentCriteriumPickerId(criteriumPickerId);
			if (parentPickerId)
			{
				var parentPickerDiv = $(parentPickerId+'_picker');
				var upParent = $(parentPickerId+'ListItem');
				if (upParent)
				{
					var upParentPickerId = this.getParentCriteriumPickerId(parentPickerId);
					var parentOperator = $F(parentPickerId + "CriteriumOperator");
					var requestParameters = this.getRequestParameters(criteriumPickerKey,andOrSearchCriteriumElement);
					requestParameters['storageId'] = $F(this.baseId + 'StorageId');
					requestParameters['criteriumKey'] = criteriumPickerKey;
					requestParameters['parentOfParentControllerName'] = $F(upParentPickerId + "ControllerName");
					var parentOfParentOperator = $F(upParentPickerId + "CriteriumOperator");
					requestParameters['parentOfParentOperator'] = parentOfParentOperator;
					var upParentList = upParent.up();
					requestParameters['parentTotalChildren'] = upParentList.childElements().length;
					var upParentClassNames = upParentList.className;
					requestParameters['parentLevel'] = upParentClassNames.substring(upParentClassNames.indexOf('bloc_')+5);
					requestParameters['parentOfParentKey'] = $F(upParentPickerId + "CriteriumId");
					var criteriumElementIndex = parseInt($F(parentPickerId + "Index"));
					requestParameters['elementIndex'] = criteriumElementIndex;
					if (pickerType == 3) /*reduction : send the already used indexes not to have doubles*/
						requestParameters['reservedElementIndexes'] = this.getReservedElementIndexes(upParent.up());
	
					var topParent;
					if (pickerType == 3) /*reduction : put the children in the parent (same operator)*/
					{
						topParent = upParent.up();
						var newUL = document.createElement("ul");
						upParent.update();/*empty the upParent*/
						upParent.insert(newUL);/*insert a temporary UL that will receive the LIs*/
						new CitobiAjax.Updater(newUL, getAjaxServletUrl('DrawSearchCriteriumElementForReduction'), requestParameters,
						{
							onComplete : function()
							{
								var i=0;
								while (i<newUL.childElements().length)
								{
									topParent.insertBefore(newUL.childElements()[i],upParent);
								}
								upParent.remove();
								/*set the operator background img (none for the first li)*/
								for (var i=0;i<topParent.childElements().length;i++)
								{
									topParent.childElements()[i].className = 'AndOrSearchCriteriumElement'+(i > 0 ? ' '+parentOfParentOperator : '')+' '+window.form2Language.toUpperCase();
								}
							}
						}
						);
					}
					else
					{
						topParent = upParent.up();
						new CitobiAjax.Updater(upParent, getAjaxServletUrl('DrawSearchCriteriumElementForReduction'), requestParameters,
						{
							onComplete : function()
							{
								/*set the operator background img (none for the first li)*/
								for (var i=0;i<topParent.childElements().length;i++)
								{
									topParent.childElements()[i].className = 'AndOrSearchCriteriumElement'+(i > 0 ? ' '+this.getLogicalOperator() : '')+' '+window.form2Language.toUpperCase();
								}
							}
						});
	
					}
				}
			}
		}
	},
	getParentCriteriumPickerId : function(pickerCriteriumId)
	{
		/* The ids have the form: baseIdElementXElementYElementZ...*/
		var index = pickerCriteriumId.lastIndexOf("Element");
		if (index > 0)
			return pickerCriteriumId.substring(0, index);
		else
			return null;
	},
	/*retrieves an array containing all the visible criteria indexes of the given criteria list*/
	getReservedElementIndexes : function(list,element)
	{
		if (list)
		{
			var reservedIndexes = new Array();
			for (var i=0;i<list.childElements().length;i++)
			{
				var indexField = list.childElements()[i].firstDescendant().firstDescendant();
				if (indexField && indexField != element)
					reservedIndexes.push(indexField.value);
			}
			return reservedIndexes;
		}
	},
	/*reposition the buttons indent and remove just behind the relation target parameter picker*/
	repositionRelationActionButtons : function(buttonId,relationCriteriumId)
	{
		if ($(buttonId))
		{
			var button = $(buttonId).remove();
			$(relationCriteriumId+"ActionButtonsContainer").appendChild(button);
			var parent = $(relationCriteriumId+"ActionButtonsContainer").up("ul.AndOrSearchCriteria");
			if (button.hasClassName("criteriumElementUnindent") &&(parent.childElements().length != 1 || parent.up().hasClassName("WhereClauseCriterium") || parent.hasClassName('bloc_0')))
			{
				$(buttonId).hide();
			}
		}
	}
});
AndOrSearchCriteriumTypeHandler.criteriumChangeHandler = function(event, pickerIndex)
{
	if (pickerIndex < (this.maxNbOfPickers - 1))
	{
		var requestParameters = { storageId: $F(this.baseId + 'StorageId') };
		var nbPickers = parseInt($F(this.baseId + '_nbElements'));
		requestParameters[this.ifdKey + '_presentationType'] = $F(this.baseId + '_presentationType');
		requestParameters[this.ifdKey + '_elementChanged'] = 'true';
		requestParameters[this.ifdKey + '_changedElementIndex'] = pickerIndex.toString();
		requestParameters[this.ifdKey + '_nbElements'] = nbPickers.toString();
		var form = $(this.baseId + '_presentationType').up('form');
		var formElements = form.getElements();
		for (var i = 0; i < nbPickers; i++)
		{
			/* transfer all the form element values of the picker i*/
			for (var j = 0; j < formElements.length; ++j)
			{
				if (formElements[j].id)
				{
					if (formElements[j].name && formElements[j].id.startsWith(this.baseId + "_" + i.toString()))
					{
						var value = $F(formElements[j].id);
						if (value != null && value != '')
							requestParameters[formElements[j].name] = value;
					}
				}
			}
		}
		this.lastChangeEvent = event;	// for elementPickerChangedCallback, which will be called when redrawing the picker
		/*
		 *	onChangeHandler is called after the Ajax update, in the callback method elementPickerChangedCallback
		 */
		new CitobiAjax.Updater(this.baseId + '_picker', getAjaxServletUrl('DrawBusinessCascadingPicker'), requestParameters);
	}
	else
	{
		this.lastChangeEvent = event;
		this.triggerOnChangeIfNeeded();
	}
};
var RelationSearchCriteriumTypeHandler = Class.create(AbstractTypeHandler,
{
	/* Builds a new RelationSearchCriteriumTypeHandler object given its ifd key, its id prefix and an URL that will be used to draw additional criteria.*/
	initialize: function($super, ifdKey, idPrefix, relationTargetController, _level, _canUseRelationCriteria, _canUseOrCriteria, _imbricationMaxLevel)
	{
		$super(ifdKey, idPrefix);
		this.relationTargetController = relationTargetController;
		this.whereClauseCriteriumControllerName = this.baseId+'WhereClauseSearchCriteriumController';
		this.canUseRelationCriteria = _canUseRelationCriteria;
		this.canUseOrCriteria = _canUseOrCriteria;
		this.imbricationMaxLevel = _imbricationMaxLevel;
		this.level = _level;
	},
	/*called when changing the relation target value => so as to reload the where clause criterium andor picker*/
	relationTargetChanged: function()
	{
		var whereClauseSearchCriteriumContainer = $(this.baseId+'WhereClauseSearchCriteriumContainer');
		new CitobiAjax.Updater(whereClauseSearchCriteriumContainer, getAjaxServletUrl('DrawRelationWhereClauseSearchCriterium'),
			{
				storageId: $F(this.baseId + 'StorageId'),
			 	relationTarget: this.relationTargetController.getValue(),
			 	pickerId: this.baseId,
			 	controllerVar:this.whereClauseCriteriumControllerName,
			 	currentLevel:this.level,
			 	canUseOrCriteria:this.canUseOrCriteria,
			 	canUseRelationCriteria:this.canUseRelationCriteria,
				allowOrCriteriaAboveLevel:this.imbricationMaxLevel
			}
		);
	},
	disable: function()
	{
		this.relationTargetController.disable();
		try
		{
			var whereClauseController = eval(this.whereClauseCriteriumControllerName);
			whereClauseController.disable();
		}
		catch(exception)
		{

		}
	},
	enable: function()
	{
		this.relationTargetController.enable();
		try
		{
			var whereClauseController = eval(this.whereClauseCriteriumControllerName);
			whereClauseController.enable();
		}
		catch(exception)
		{

		}
	}
});

var PageContentTypeHandler = new Class.create(AbstractTypeHandler,
{
	initialize: function($super, ifdKey, idPrefix, _booleanTypeHandler, _paragraphTypeHandler, _fileTypeHandler, _fileIdTypeHandler)
	{
		$super(ifdKey, idPrefix);
		this.booleanTypeHandler = _booleanTypeHandler;
		this.paragraphTypeHandler = _paragraphTypeHandler;
		this.fileTypeHandler = _fileTypeHandler;
		this.fileIdTypeHandler = _fileIdTypeHandler;
		/*this.editableChanged();*/
	},
	editableChanged: function()
	{
		var editable = this.booleanTypeHandler.getBooleanValue();
		if (editable)
		{
			$(this.baseId+"_paragraphContainer").show();
			this.paragraphTypeHandler.enable();
			if (this.paragraphTypeHandler.initEditor)
				this.paragraphTypeHandler.initEditor();
			$(this.baseId+"_fileContainer").hide();
		}
		else
		{
			$(this.baseId+"_paragraphContainer").hide();
			$(this.baseId+"_fileContainer").show();
		}
	},
	enable: function()
	{
		this.booleanTypeHandler.enable();
		this.paragraphTypeHandler.enable();
		if (this.paragraphTypeHandler.initEditor)
			this.paragraphTypeHandler.initEditor();
		this.fileTypeHandler.enable();
	},
	disable: function()
	{
		this.booleanTypeHandler.disable();
		this.paragraphTypeHandler.disable();
		this.fileTypeHandler.disable();
	},
	download: function()
	{
		var zipFileUrl = getAjaxServletUrl("GetPageContentZipFile") + "?id=" + this.fileIdTypeHandler.getValue();
		window.open(zipFileUrl);
	}
});