/****h* top/PortletUtil 
* *****************************************************************************
* DESCRIPTION
* PortletUtil is a javascript module for manipulating Portlets entities, and 
* for making easier the navigation in the DOM.
* It contains Portlet class, and useful functions to navigate and modify DOM elements within a portlet.
* HREF_FILE_NAME
*
******************************************************************************/

/****f* PortletUtil/loadMinimizeResult
* *****************************************************************************
* FUNCTION
* Asynchronous function called on minized.
*
* PARAMETERS
* oXML
******************************************************************************/

function loadMinimizeResult(oXML){
	var data = oXML.httpRequest.responseText;
	// timeout case (PTR 03420442)
	if(data.substr(0, 8)=="|timeout"){
		var tokens = data.split("|");
		var loginURL = tokens[2];
		document.location.href=loginURL;
	}
}

/****c* PortletUtil/portlet
* *****************************************************************************
* DESCRIPTION
* A portlet object. One is instantiated by portlet.jsp for every portlet
******************************************************************************/
function portlet(uid,isInPopup,func)
{
	this.uid = uid;
	
	// the portlet container HTML DOM element.
	this.pcElement = document.getElementById(uid);
	
	// create an attribute of the DOM node which links to this js object.
	this.pcElement.jsoPortlet = this;

	// An array of error messages
	this.aMessages = new Array();
		
	// An array of footnotes
	this.aFootNotes = new Array();

	// An array of fields with errors.
	this.aErrFields = new Array();
	
	//a boolean to know if we are within a popup
	this.isInPopup = isInPopup;
	
	//a boolean to manage cleanButtons function
	this.hasCleanedButtons = false;
}

//a boolean to know if error messages are displayed
portlet.prototype.callbackfunction = null;
		
/****h* portlet/PortletDomManipulation
* *****************************************************************************
* DESCRIPTION
* These utility functions are usefull for DOM manipulation within a portlet.
******************************************************************************/
	
/****f* PortletDomManipulation/getElementsByTagName
* *****************************************************************************
* FUNCTION
* Find all elements in this portlet with given Tag Name
* Note : this function is more optimized than WalkDOMForNodes, in terms of performances.
*
* USAGE
* portlet.getElementsByTagName(tagName, maxToFind)
*
* PARAMETERS
* tagName is the name of an HTML tag ("INPUT", "DIV"...), it is not case sensitive
* maxToFind is an optional numerical value indicating that the search should stop after
* this many nodes have beed found. This is a performance optimization if you already
* know the number of elements expected.
*
* RETURN VALUE
* This function returns an array of DOM elements having the given tag name.
* If no elements have been found, it returns an empty array.
* If an exception occurred, it returns null.
******************************************************************************/
portlet.prototype.getElementsByTagName = function portlet_getElementsByTagName(tagName, maxToFind)
{ 
	try {
	  if (!maxToFind)
	  	return this.pcElement.getElementsByTagName(tagName);
	  
	  var n = this.pcElement.getElementsByTagName(tagName);
	  var a = new Array();
	  for(var i = 0; i<n.length && i<maxToFind; i++)
	  a.push(n[i]);
	  return a;
	}
	catch(x) { 
	   return null;
	}
}
	
/****f* PortletDomManipulation/getElementsByClassName
* *****************************************************************************
* FUNCTION
* Find all elements in this portlet with given Class Name.
* Note : as this function is using the walkDOMForNodes recursive function, it is very costy in terms of performances.
* Therefore, it should be avoided if possible.
*
* USAGE
* portlet.getElementsByClassName(className, maxToFind)
*
* PARAMETERS
* className is the name of an HTML tag
* maxToFind is an optional numerical value indicating that the search should stop after
* this many nodes have beed found. This is a performance optimization if you already
* know the number of elements expected.
*
* RETURN VALUE
* This function returns an array of DOM elements having the given class name.
* If no elements have been found, it returns an empty array.
******************************************************************************/
portlet.prototype.getElementsByClassName = function  portlet_getElementsByClassName(className, maxToFind)
{	
	var a = new Array();
	walkDOMForNodes(this.pcElement, a, null, className, null, null, null, maxToFind);
	return a;
}

	
/****f* PortletDomManipulation/getElementsById
* *****************************************************************************
* FUNCTION
* Find all elements in this portlet with given Id.
* Note : as this function is using the walkDOMForNodes recursive function, it is very costy in terms of performances.
* Therefore, it should be avoided if possible.
*
* USAGE
* portlet.getElementsById(id, maxToFind)
*
* PARAMETERS
* id is the id of an HTML tag
* maxToFind is an optional numerical value indicating that the search should stop after
* this many nodes have beed found. This is a performance optimization if you already
* know the number of elements expected.
*
* RETURN VALUE
* This function returns an array of DOM elements having the given Id.
* If no elements have been found, it returns an empty array.
******************************************************************************/
portlet.prototype.getElementsById = function portlet_getElementsById(id, maxToFind,bForgetOptions)
{	
	var a = new Array();
	walkDOMForNodes(this.pcElement, a, null, null, id, null, null, maxToFind,bForgetOptions);
	return a;
}
	
/****f* PortletDomManipulation/getElementById
* *****************************************************************************
* FUNCTION
* Find first element in this portlet with given Id
* Note : as this function is using the walkDOMForNodes recursive function, it is very costy in terms of performances.
* Therefore, it should be avoided if possible. A workaround would be to use the native javascript function getElementById.
*
* USAGE
* portlet.getElementById(id)
*
* PARAMETERS
* id is the id of an HTML tag
*
* RETURN VALUE
* This function returns a DOM element having the given Id.
* If no elements have been found, it returns an empty array.
* If an exception occurred, it returns null.
******************************************************************************/
portlet.prototype.getElementById = function portlet_getElementById(id)
{	
	try{
		return this.getElementsById(id, 1, false)[0];
	}catch(x)
	{   return null; }
}
	
/****f* PortletDomManipulation/getElementsByAttribute
* *****************************************************************************
* FUNCTION
* Find all elements in this portlet which have the given attribute
* Note : as this function is using the walkDOMForNodes recursive function, it is very costy in terms of performances.
* Therefore, it should be avoided if possible. A workaround would be to use the native javascript function getElementById.
*
* USAGE
* portlet.getElementsByAttribute(attribute, maxToFind)
*
* PARAMETERS
* attribute is the attribute of an HTML tag
* maxToFind is an optional numerical value indicating that the search should stop after
* this many nodes have beed found. This is a performance optimization if you already
* know the number of elements expected.
*
* RETURN VALUE
* This function returns an array of DOM elements having the given attribute.
* If no elements have been found, it returns an empty array.
******************************************************************************/
portlet.prototype.getElementsByAttribute = function portlet_getElementsByAttribute(attribute, maxToFind,bForgetOptions)
{	
	var a = new Array();
	walkDOMForNodes(this.pcElement, a, null, null, null, attribute, null, maxToFind,bForgetOptions);
	return a;
}
	
/****f* PortletDomManipulation/getElementsByName
* *****************************************************************************
* FUNCTION
* Find all elements in this portlet which have the given name
* Note : as this function is using the walkDOMForNodes recursive function, it is very costy in terms of performances.
* Therefore, it should be avoided if possible. A workaround would be to use the native javascript function getElementById.
*
* USAGE
* portlet.getElementsByName(name, maxToFind)
*
* PARAMETERS
* name is the name of an HTML tag
* maxToFind is an optional numerical value indicating that the search should stop after
* this many nodes have beed found. This is a performance optimization if you already
* know the number of elements expected.
*
* RETURN VALUE
* This function returns an array of DOM elements having the given name.
* If no elements have been found, it returns an empty array.
******************************************************************************/
portlet.prototype.getElementsByName = function portlet_getElementsByName(name, maxToFind)
{	
	var a = new Array();
	walkDOMForNodes(this.pcElement, a, null, null, null, null, name, maxToFind);
	return a;
}
	
/****f* portlet/getContentElement
* *****************************************************************************
* FUNCTION
* Find the container for the 'user generated' part of the portlet
* We are looking for a TD node which is a descendant of PortletContainer and 
* has an id of "portletContent"
*
* USAGE
* portlet.getContentElement()
*
* RETURN VALUE
* This function returns a DOM element corresponding to the content of the portlet.
******************************************************************************/
portlet.prototype.getContentElement = function portlet_getContentElement()
{	
	return this.getElementById("portletContent");
}
	
/****f* portlet/setTitleText
* *****************************************************************************
* FUNCTION
* Set the title text of a portlet
*
* USAGE
* portlet.setTitleText(text)
*
* PARAMETERS
* text is the title text of the portlet
*
* RETURN VALUE
* This function returns the portlet object
******************************************************************************/
portlet.prototype.setTitleText = function  portlet_setTitleText(text)
{
	try
	{
		var nTitle = this.getElementById("portletTitleText");
		if (nTitle) {
			nTitle.innerHTML = text;
		}
	}catch(ex){};
	return this;
}
	
/****f* portlet/setHelpId
* *****************************************************************************
* FUNCTION
* Set the Help id (contextual help) of a portlet
*
* USAGE
* portlet.setTitleText(helpId, helpProject)
*
* PARAMETERS
* helpId is the Map id of the help file (it should be an integer)
* helpProject if the name of the help project "TravelMain" or "AdminMain"
*
* RETURN VALUE
* This function returns the portlet object
******************************************************************************/
portlet.prototype.setHelpId = function  portlet_setHelpId(helpId, helpProject)
{
	try
	{
		if( helpId != null )
		{
			// Save help context in the portlet scope
			this.helpId = helpId;
			if( helpProject == null )
				this.helpProject = 'TravelMain';
			else
				this.helpProject = helpProject;
			// Change classes
			this.getElementById("portletHelp").className = "iconBackground";
			this.getElementById("portletHelpDiv").className = "iconHelp";
		}
	}catch(ex){};
	return this;
}
	
/****f* portlet/hideTitle
* *****************************************************************************
* FUNCTION
* Hide the title of a portlet
*
* USAGE
* portlet.hideTitle()
*
* RETURN VALUE
* This function returns the portlet object
******************************************************************************/
portlet.prototype.hideTitle = function portlet_hideTitle()
{
	try
	{
		var nTitle = this.getElementById("portletHeaderId");
		nTitle.style.display = "none";
	}catch(ex){};
	return this;
}

/****f* portlet/doMinimize
* *****************************************************************************
* FUNCTION
* Minimize the current portlet, by hiding the portlet content and changing the icon.
* This function makes asynchronous calls by using xmlHttp object, and then calls back loadMinimizeResult function.
*
* USAGE
* portlet.doMinimize(minimizeURL, unminimizeURL)
*
* PARAMETERS
* minimizeURL and unminimizeURL are the URLs to call asynchronously.
******************************************************************************/
portlet.prototype.doMinimize = function  portlet_doMinimize(minimizeURL, unminimizeURL)
{
	// Change classes to hide the portlet content and change the icon
	var pc = this.getElementById("portletContent");
	var ic= this.getElementById("portletMinimizeDiv");
	var xmlHttp = new XMLHttp();
		
	if(pc.style.display == "none")
	{
		pc.style.display = "";
		ic.className = "iconMin";
		if(unminimizeURL)
			xmlHttp.quickCallAsync(unminimizeURL, '', loadMinimizeResult);
	}
	else
	{
		pc.style.display = "none";
		ic.className = "iconUnmin";
		if(minimizeURL)
			xmlHttp.quickCallAsync(minimizeURL, '', loadMinimizeResult);
	}
}
	
/****f* portlet/showPleaseWait
* *****************************************************************************
* FUNCTION
* Hide the portlet content, Show a Please Wait.
* It builds the Please Wait message and inserts it before the divToHide.
*
* USAGE
* portlet.showPleaseWait(divToHide)
*
* PARAMETERS
* divToHide (optional) is the div content to be hidden. If this parameter doesn't exist,
* this function will hide the portletContent.
*
* RETURN VALUE
* This function returns the portlet object
******************************************************************************/
portlet.prototype.showPleaseWait = function  portlet_showPleaseWait(divToHide)
{
	if (!divToHide)
		divToHide = this.getElementById("portletContent");
	
	divToHide.style.display="none";
	
	var h = '<table width="100%"><tr><td class="fareWaitTDRight"><span id="farePWText">'+stxGPleaseWait+'</span><div>&nbsp;</div></td></tr></table>';
	var element = document.createElement("div");
	element.className = "fareWaitBox";
	element.width = "100%";
	element.innerHTML=h;
	
	divToHide.parentNode.insertBefore(element, divToHide);
}
	
/****f* portlet/init
* *****************************************************************************
* FUNCTION
* This function initializes a portlet by displaying eventual error messages.
* portlet.jsp inserts a call to this method in the page after the html of the portlet
*
* USAGE
* portlet.init()
******************************************************************************/
portlet.prototype.init = function portlet_init()
{	
     if(this.countMessages()) 
     {
     	// Insert error message & highlight error fields
	this.displayMessages();
     }
}
	
/****h* portlet/PortletErrorHandling
* *****************************************************************************
* DESCRIPTION
* These utility functions are usefull to display errors and highlight fields.
******************************************************************************/

/****f* PortletErrorHandling/addMessage
* *****************************************************************************
* FUNCTION
* Add error message to list,  'sev' and 'code' are optional parameters
*
* USAGE
* portlet.addMessage(text,sev,code)
*
* PARAMETERS
* text is the text of the message (mandatory parameter)
* sev is the severity of the message (should be a letter amongst F, E, C, W, I, O)
* code is the code number of the message
******************************************************************************/
portlet.prototype.addMessage = function portlet_addMessage(text,sev,code)
{	
	if ("FECWIO".indexOf(sev) < 0)
		sev='E';
	
	// don't display the same error message more than once
	for (var i=0; i < this.aMessages.length; i++)
		if (this.aMessages[i][0] == text)
			return;

	this.aMessages.push(new Array(text,sev,code));
}
	
	
/****f* PortletErrorHandling/removeErrorMessage
* *****************************************************************************
* FUNCTION
* Remove an error message to list, identified by its 'code'
*
* USAGE
* portlet.removeErrorMessage(code)
*
* PARAMETERS
* text is the text of the message (mandatory parameter)
* sev is the severity of the message (should be a letter amongst F, E, C, W, I, O)
* code is the code number of the message
******************************************************************************/
portlet.prototype.removeErrorMessage = function portlet_removeErrorMessage(code) {
	var aM = new Array();
	var j=0;
	var foundMessage = false;
	
	for (var i=0; i < this.aMessages.length; i++) {
		if (this.aMessages[i][2] != code) {
			aM[j] = this.aMessages[i];
			j++;
		} else {
			foundMessage = true;	
		}
	}
	this.aMessages = aM;
	return foundMessage;
}
	
/****f* portlet/addFootNotes
* *****************************************************************************
* FUNCTION
* Add footnotes to list
*
* USAGE
* portlet.addFootNotes(text)
*
* PARAMETERS
* text is the text of the message (mandatory parameter)
******************************************************************************/
portlet.prototype.addFootNotes = function portlet_addFootNotes(text)
{	
	// don't display the same foot note more than once
	for (var i=0; i < this.aFootNotes.length; i++)
		if (this.aFootNotes[i] == text)
			return;

	this.aFootNotes.push(text);
}	
	
/****f* PortletErrorHandling/addErrField
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* Add an invalid field to the list of error fields
*
* USAGE
* portlet.addErrField(errMap)
*
* PARAMETERS
* errMap parameter is a etv:errMap value
******************************************************************************/
portlet.prototype.addErrField = function portlet_addErrField(errMap)
{	
	this.aErrFields.push(errMap);
}
	
/****f* PortletErrorHandling/addValidationError
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* Add an invalid field and error message to the list of errors
*
* USAGE
* portlet.addValidationError(oField, sMessage)
*
* PARAMETERS
* oField is the input field object to hilight.
* sMessage is the error message text
******************************************************************************/
portlet.prototype.addValidationError = function portlet_addValidationError(oField, sMessage)
{	
	this.addMessage(sMessage);
	
	// get this fields errMap value. if none defined, create a random unique value
	var errMap = oField.getAttribute("etv:errMap");
	if (!errMap)
	{
		errMap = "err"+Math.random();
		oField.setAttribute("etv:errMap", errMap);
	}
					
	if(browser.isSafari && oField.name){
		this.aErrFields.push(oField.name);
	}
	else{
		this.aErrFields.push(errMap);
	}
}
	
/****f* PortletErrorHandling/clearMessages
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* Clears the error messages of the portlet
*
* USAGE
* portlet.clearMessages()
******************************************************************************/
portlet.prototype.clearMessages = function portlet_clearMessages()
{	
	var c = this.aMessages.length;
	this.aMessages = new Array();
	this.aErrFields = new Array();
	this.aFootNotes = new Array();

	// remove all field level error icons and colors
	var aDivs = this.getElementsById("etvErrIcon",null,true);
	for (var i=0; i < aDivs.length; i++)
	{
		var nodeDivErr = aDivs[i];
		this.colorErrorLine(nodeDivErr, "transparent");
		nodeDivErr.parentNode.removeChild(nodeDivErr);
	}
	
	return c;
}
	
/****f* PortletErrorHandling/countMessages
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* Returns number of messages in list. Can be used to decide if it is ok to submit. 
* It builds the Please Wait message and inserts it before the divToHide.
*
* USAGE
* portlet.countMessages()
*
* RETURN VALUE
* This function returns the number of error messages
******************************************************************************/
portlet.prototype.countMessages = function portlet_countMessages()
{	
	return this.aMessages.length;
}
	
/****f* PortletErrorHandling/displayMessages
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* It display the error messages and highlight the fields 
*
* USAGE
* portlet.displayMessages()
******************************************************************************/
portlet.prototype.displayMessages = function portlet_displayMessages()
{
	try
	{
		// Delete any existing messages
		var pc = this.getContentElement();	
		var nodeErr = pc.childNodes[0];

		if (document.getElementById("errorDiv")){
			var err=document.getElementById("errorDiv");
			var parent=err.parentNode;
			parent.removeChild(err);
		}
	}catch(e){return;} // unclosed HTML tags can cause failure here

	// now build new message output TRs from the message array
	var h = "";
	for (var i=0; i < this.aMessages.length; i++)
	{
		var a = this.aMessages[i];
		var sType='';
		switch (a[1]) // prepend type text to message for some types...
		{	
			case 'E': sType=stxMsgError;   break;
			case 'W': sType=stxMsgWarning; break;
		}
		// test message to display is not empty
		var trimString = a[0].replace(/(^\s*)|(\s*$)/g,'');
		if(trimString != '') {
			h += '<tr><td class="'+a[1]+'">'+sType+a[0]+'</td></tr>';
		}
	}
	
	// if messages exist, create a new div element to display the messages table
	if (h)
	{
		if(this.isInPopup)
		  h = '<table class="tableErrorMessages"  style="margin-left:5px;margin-right=5px;width=98%;"><tbody>'+h+'</tbody></table>';
	    else{
		  h = '<table class="tableErrorMessages" id="errorTable" style="width:90%"><tbody>'+h+'</tbody></table>';			  
		}
		
		if(this.aFootNotes.length>0){
		  h+='<table>';
		  for (var f=0;f<this.aFootNotes.length;f++)
		    h += '<tr><td>' + this.aFootNotes[f] + '</td></tr>';
		  h+='</table>';
		}
		
		var element=document.createElement("div");
		element.id = "errorDiv";
		element.width = "100%";
		element.innerHTML=h;
		pc.insertBefore(element, pc.firstChild);

		if (!browser.isSafari) {
			pc.scrollIntoView();
		} else {
			var leftpos = 0;
			var toppos = 0;
			var aTag = pc;
			// calculates the position of the pc element
			do {
				aTag = aTag.offsetParent;
				leftpos += aTag.offsetLeft;
				toppos += aTag.offsetTop;
			} while(aTag.tagName!="BODY");
			window.scrollTo(leftpos, toppos);
		}
	}
	
	// Now hilight error fields 
	// Get all elements with a etv:errMap attribute 
	var aErrMap = this.getElementsByAttribute("etv:errMap",null,true);
	
	// Loop thru the list of fields with errors in this portlet.
	for (var j=0; j < this.aErrFields.length; j++)
	{
		var errFieldName=this.aErrFields[j];
	
		// Find all fields which have a NAME attribute matching the error field name.
		var a = this.getElementsByName(errFieldName);
		for (var i=0; i < a.length; i++)
			this.addFieldError(a[i]);
	
		// Find all fields which have an etv:errMap attribute matching the error field name.
		for (var i=0; i < aErrMap.length; i++)
		{
			var field = aErrMap[i];
			if (field.getAttribute("etv:errMap") == errFieldName)
				this.addFieldError(field);
		}
	}
	if(this.callbackfunction != null)
	  this.callbackfunction();
}
	
/****f* PortletErrorHandling/addFieldError
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* It insert an error icon into the HTML before the given fieldNode
* Style is position absolute, so it appears above page flow.
* It also changes the background color of the parent element.
*
* USAGE
* portlet.addFieldError(fieldNode)
*
* PARAMETERS
* fieldNode is the input field object with error.
******************************************************************************/
portlet.prototype.addFieldError = function portlet_addFieldError(fieldNode)
{
	// Apply method on first element in a cell (PTR2532072)
	var siblings = fieldNode.parentNode.childNodes;
	var i=0;
	while(i < siblings.length){
		if(siblings[i].nodeType == 1 && siblings[i].tagName != "SCRIPT" && siblings[i].tagName != "BR"){ // siblings[i] is a DOM element, not SCRIPT, not BR
			if(siblings[i] == fieldNode){
				var showWarning = fieldNode.getAttribute("etv:showWarningIconIfError");
				var element = document.createElement("div");
				if(showWarning!="false"){
					element.id  = "etvErrIcon";
					element.className = "errorFieldIconOwner";
					element.innerHTML = '<div class="errorFieldIcon"></div>';			
					fieldNode.parentNode.insertBefore(element, fieldNode);
				}
				this.ErrorLineInitClass(fieldNode, "errorHighlight");
			}else{
				this.addFieldError(siblings[i]);
			}
			return;
		}
		i++;
	}
}

/****f* PortletErrorHandling/ErrorLineInitClass
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* It sets the background color of the nearest TD and its previous sibling
*
* USAGE
* portlet.ErrorLineInitClass(fieldNode, className)
*
* PARAMETERS
* fieldNode is the input field object with error.
* className is the name of the style class to be applied to the TD.
******************************************************************************/
portlet.prototype.ErrorLineInitClass = function portlet_errorLineInitClass(fieldNode, className)
{
	// find the nearest TD
	var td = this.getAncestorElement(fieldNode, "TD");
	if (!td)
	{	return;  }

	// set the color of this TD
	td.style.backgroundColor="";
	td.className=td.className + " " + className;

	// now find the previous td
	do
	{
		td = td.previousSibling;
	}while (td && td.nodeType!=1)

	if (!td)
	{	return;  }

	// make this TD yellow
	td.style.backgroundColor="";
	td.className=td.className + " " + className;
}
	

/****f* PortletErrorHandling/DisplayMandatoryIcon
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* It insert an mandatory icon in the previous TD
*
* USAGE
* portlet.DisplayMandatoryIcon(fieldNode, className)
*
* PARAMETERS
* fieldNode is the input field object with error.
* className is the name of the style class to be applied to the TD.
******************************************************************************/
portlet.prototype.DisplayMandatoryIcon = function portlet_displayMandatoryIcon(fieldNode, className)
{
	// find the nearest TD
	var td = this.getAncestorElement(fieldNode, "TD");
	if (!td)
	{	return;  }

	// now find the previous td
	do
	{
		td = td.previousSibling;
	}while (td && td.nodeType!=1)

	if (!td)
	{	return;  }

	// Mandatory Icon is displayed in this TD 
	td.className=className;
}

/****f* PortletErrorHandling/colorErrorLine
* *****************************************************************************
* FUNCTION
* This is a utility function for error messages
* It sets the background color of the nearest TD and its previous sibling
*
* USAGE
* portlet.colorErrorLine(fieldNode, color)
*
* PARAMETERS
* fieldNode is the input field object with error.
* color is the background color to be applied to the TD.
******************************************************************************/
portlet.prototype.colorErrorLine = function portlet_colorErrorLine(fieldNode, color)
{
	// find the nearest TD
	var td = this.getAncestorElement(fieldNode, "TD");
	if (!td)
	{	return;  }

	// set the color of this TD
	td.style.backgroundColor=color;

	// now find the previous td
	do
	{
		td = td.previousSibling;
	}while (td && td.nodeType!=1)

	if (!td)
	{	return;  }

	// make this TD yellow
	td.style.backgroundColor=color;
}

/****f* PortletDomManipulation/getAncestorElement
* *****************************************************************************
* FUNCTION
* This is a utility function for DOM manipulation
* It finds an ancestor element with the given tag name.
*
* USAGE
* portlet.getAncestorElement(node, tagName)
*
* PARAMETERS
* fieldNode is the input field object with error.
* color is the background color to be applied to the TD.
******************************************************************************/
portlet.prototype.getAncestorElement = function portlet_getAncestorElement(node, tagName)
{
	tagName=tagName.toUpperCase();
	while (node && node.tagName != tagName)
		node = node.parentNode;

	return node;
}

/****f* portlet/validate
* *****************************************************************************
* FUNCTION
* Call the validation function for every field with a "etv:validate" attribute
* Note : this function contains a Safari workaround, it validates the fields with a
* "Validate" attribute.
*
* USAGE
* portlet.validate(node)
*
* PARAMETERS
* node is any html node contained in the form to be validated
*
* UNIT TESTS
* href:UNIT_TEST_PATH/FieldValidation/index.htm
******************************************************************************/
portlet.prototype.validate = function portlet_validate(node)
{
	var form = this.getAncestorElement(node, "FORM");
	
	var a = new Array();		
	walkDOMForNodes(form, a, null, null, null, "etv:validate", null,null,true);
	
	if (browser.isSafari) {
		// workaround for Safari : we add the fields with Validate attributes
		// (for some reason, etv:validate attribute disappears when added via javascript,
		// and is replaced by "Validate" attribute)
		var validateVisible = new Array();
		walkDOMForNodes(form, validateVisible, null, null, null, "Validate", null);
		a=a.concat(validateVisible);
	}

	for (var i=0; i < a.length; i++)
	{
		var field = a[i];
		var aVal = "";
		var bVal = "";
		var valFn = "";
		var valPattern;
		
		if (field.getAttribute("etv:validate")) {
			aVal = field.getAttribute("etv:validate").split(",");
			valFn=aVal[0]; // the validation function name
		}
		else if(browser.isSafari && field.getAttribute("Validate")) { // Safari
			aVal = field.getAttribute("Validate").split(",");
			valFn=aVal[0]; // the validation function name				
		}
		
		// In case etvValidatePattern, we have a problem if the pattern contains commas (,) since the split is done on the comma
		// Then we have to rebuild  the pattern
		if (aVal[2]) {
			// Copy aVal to work on it
			bVal = aVal.slice(2); // Removes the 2 first elements (function and message) to keep only pattern parts
			aVal[2] = bVal.join(","); // Concatenate with commas
		}
		
		// Don't validate fields which are hidden by their parent's visibility 
		// when the field has a "etv:validateIfVisible" attribute 
		var idV = field.getAttribute("etv:validateIfVisible");
		
		if (idV=="true" &&  !isElementDisplayed(field))
		{				
				continue;
		}

		// Don't validate fields which are disabled 
		// when the field has a "etv:validateIfEnabled" attribute 
		var idE = field.getAttribute("etv:validateIfEnabled");
		if (idE=="true" && field.disabled)
		{
				continue;
		}

		
		// Test if a function with the name given exists, if not try prepending "etvValidate" to the name
		if (typeof(window[valFn]) != "function")
		{
			valFn = "etvValidate"+valFn;
		
			// Test if it is a function now.. if not give up..
			if (typeof(window[valFn]) != "function")
			{
				this.addValidationError(field, "### Unknown validation function:"+aVal[0]);
				continue;
			}
		}
			
		//call the validation function..
		valMsg = window[valFn](field,aVal[2]);		

		if (valMsg)
		{
			if (aVal[1])  // override the default error message ?
				valMsg=eval(aVal[1]);
			this.addValidationError(field, valMsg);
		}
	}
}


/****f* portlet/cleanButtons
* *****************************************************************************
* FUNCTION
* Update all buttons with our look and feel and wait processing.
* This function calls back itself while all the buttons of the portlet are not cleaned.
* It ignores the buttons with display="none" attribute, but it it doesn't ignore them if their 
* parent element is hidden.
* Note : We don't change colors display for Safari on MAC.
*
* USAGE
* portlet.cleanButtons()
******************************************************************************/
portlet.prototype.cleanButtons = function portlet_cleanButtons()
{
	if (!browser.isSafari) {
		var aIn = this.getElementsByTagName("INPUT");
		for (var i=0; i < aIn.length; i++)
		{
			var oIn = aIn[i];
			if (oIn.type == "button" || oIn.type == "submit")
			{
				// sometimes the page is not ready yet, so try again later.		
				if (!oIn.offsetHeight && oIn.style.display!="none") {
					if (!this.hasCleanedButtons && browser.isIE) {
						this.hasCleanedButtons = true;
						setTimeout("jso"+this.uid+".cleanButtons()", 0);
					}
					else {
						this.hasCleanedButtons = true;
						setTimeout("jso"+this.uid+".cleanButtons()", 1000);
					}
					return;	
				}	
			
				
				// button already has a class or a hidden button! dont change it.
				// don't skip disabled class buttons except if they already have a height
				if ((oIn.className && (oIn.className!="buttonCoreDisabled" || oIn.style.height)) || oIn.style.display=="none")
					continue;
			
				if (!oIn.className) {
					// insert a span around the button input tag
    				var element = document.createElement("span");
    				oIn.parentNode.insertBefore(element, oIn);
    				element.insertBefore(oIn,null);
    				
    				element.className = "buttonBorder";
    				oIn.className = "buttonCore";
				}
		
		        if (browser.isNS7up || browser.isFirefox) {
		            // button algorithm is different for NS7.01
		            if (browser.versionMinor==7.01) {
        				// make button 25% less wide than the default
        				var w = parseInt(oIn.offsetWidth * 0.75);
        				oIn.style.width=w+"px";
		            } else {
		                // algorithm for all other NS versions
		               //oIn.style.height=(oIn.offsetHeight-2)+"px";
				    }
		        }

                if (browser.isIE) 
                {
					// make button 25% less wide than the default
					if(oIn.className!="buttonCoreDisabled"){
	    				var w = parseInt(oIn.offsetWidth * 0.75);
	    				oIn.style.width=w+"px";
	    				//oIn.style.height=(oIn.offsetHeight-2)+"px";
					}
			    }
			}
		}
	}
	return this;
}

/****f* portlet/setInnerHTML
* *****************************************************************************
* FUNCTION
* Clears all the portlet content and writes HTML data in the portlet.
* This function is used in case of Configuration error or Navigation error pages
* retrieved in Ajax.
*
* USAGE
* portlet.setInnerHTML(htmlData)
******************************************************************************/
portlet.prototype.setInnerHTML = function portlet_setInnerHTML(htmlData) {
	var contentDiv = this.getElementById("portletContent");
	contentDiv.innerHTML = "<div> <!-- Content inserted by setInnerHTML function --> <span style='display:none'>Fix for IE</span> \n\n "+htmlData+" </div>";
}


/****h* portlet/StaticDomManipulation
* *****************************************************************************
* DESCRIPTION
* These static utility functions are usefull for DOM manipulation.
******************************************************************************/

/****f* StaticDomManipulation/getPortletContainerElement
* *****************************************************************************
* FUNCTION
* Gets the container div of the portlet containing a given node
* Gets the parent node whose className looks like "portletContainerDiv"
*
* USAGE
* getPortletContainerElement(node)
*
* RETURN VALUE
* This function returns a node element.
******************************************************************************/
function getPortletContainerElement(node)
{	
	while (node)
	{
		if (node.className && node.className.indexOf("portletContainerDiv")==0)
			break;
		node = node.parentNode;
	}
	return node;
}

/****f* StaticDomManipulation/isElementDisplayed
* *****************************************************************************
* FUNCTION
* Indicates if an HTML node is visible or not
*
* USAGE
* isElementDisplayed(node)
*
* RETURN VALUE
* This function returns true if the element is visible in the page false otherwise.
******************************************************************************/
function isElementDisplayed(node)
{	
	while (node)
	{
		if (node.style && ((node.style.display && node.style.display=="none") || (node.style.visibility && node.style.visibility=="hidden"))) {
			return false;	
		}
		node = node.parentNode;
	}
	return true;
}

/****f* StaticDomManipulation/walkDOMForNodes
* *****************************************************************************
* FUNCTION
* Walks the DOM for all descendants of "node"
* Adds to the array "a" any node which has a tagName, className, id, or attribute
* matching the those given as parameters. 
*
* USAGE
* walkDOMForNodes(node, a, tagName, className, id, attribute, name, maxItems)
* 
* PARAMETERS
* node is any html node
* a is an array of nodes
* tagName, className, id, attribute, name can be empty
* maxItems is the limit of the "a" array size
* bForgetOptions is a boolean indicating not to go recursively into the 
* "options" of a "select" element (perfs used for errormanagement and form validation)
*
* RETURN VALUE
* This function returns an array of nodes
******************************************************************************/

function walkDOMForNodes(node, a, tagName, className, id, attribute, name, maxItems, bForgetOptions)
{	
	if (!node || node.nodeType!=1 || (maxItems && a.length == maxItems))
		return;
	
	//document.getElementById('debug').innerHTML+=node.tagName+"; "
	if (	(tagName && node.tagName == tagName) 
	 	 || (className && node.className == className)
		 || (id && node.id == id)
		 || (attribute && node.tagName!="TABLE" && node.getAttribute(attribute))
		 || (name && (node.name == name || (node.tagName!="TABLE" && node.getAttribute("name")==name))))
	{
		a.push(node);
	}
	
	// now recursively call this function for all children of "node".
	if(bForgetOptions==undefined || bForgetOptions==null) bForgetOption=false;
	if(!bForgetOptions || (!(node.tagName=='SELECT') && !(node.tagName=='select'))){
		var children = node.childNodes;               
    		for (var i=0; i < children.length; i++){    											
			walkDOMForNodes(children[i], a, tagName, className, id, attribute, name, maxItems, bForgetOptions); 			
		}
	}
}


/****f* PortletUtil/getPortlet
* *****************************************************************************
* FUNCTION
* Returns the javascript object associated with a portlet
*
* USAGE
* getPortlet(nodeContext)
*
* - if called from an event handler on the page, use:
*		var portlet = getPortlet(this);  
* - if called from where there is no object as a context, use:
* 		var portlet = getPortlet("<%= uniquePortletId %>");
* 
* PARAMETERS
* nodeContext can be either any node which is a child of the portlet 
* or a string containing the unique ID of the portlet.
*
* RETURN VALUE
* This function returns an array of nodes
******************************************************************************/
function getPortlet(nodeContext)
{
	if (typeof(nodeContext) == "string")
	{	return eval("jso"+nodeContext);}  
	else
	{	if (getPortletContainerElement(nodeContext)) 
		{	return (getPortletContainerElement(nodeContext).jsoPortlet);} 
		else
		{	return false;}
	}
}
	
/****f* StaticDomManipulation/nodeRemoveJunkNodes
* *****************************************************************************
* FUNCTION
* DOM helper function
* Remove the empty string elements from Netscape's DOM :
* blank text nodes, spaces and carriage returns nodes
* Perf note : this function is not necessary for IE browser.
*
* USAGE
* nodeRemoveJunkNodes(node)
* 
* PARAMETERS
* node is any DOM element
******************************************************************************/
function nodeRemoveJunkNodes(node)
{
	if (!browser.isIE) {
		var a = node.childNodes;
		if (browser.isSafari) {
			for (var i=a.length-1; i>=0; i--)
			{
				var n = a[i];if(isJunkNode(n)){node.removeChild(n);}
		        	else
				{ nodeRemoveJunkNodes(n);}
			}
		}
		else {
			for (var i=a.length-1; i>=0; i--)
			{
				var n = a[i];
				if (n.nodeType==3 && n.nodeValue.indexOf("\n")!=-1)
				{	node.removeChild(n);	}
				nodeRemoveJunkNodes(n);
	
			}
		}
	}

}

/****f* StaticDomManipulation/isJunkNode
* *****************************************************************************
* FUNCTION
* Check if a node is empty (if it's a text node containing only spaces and '\n')
*
* USAGE
* isJunkNode(node)
* 
* PARAMETERS
* node is any DOM element
******************************************************************************/
function isJunkNode(node) {
			
	if (node.nodeType!=3)
	    return false;

	if(node==null || node.nodeValue==null)
		return true;
			
	for (j=0;j<node.nodeValue.length;j++)
	{
		if (node.nodeValue.charAt(j) != '\n' && node.nodeValue.charAt(j) != ' ' && node.nodeValue.charAt(j) != '\t')
		{
		    return false;
		}
	}
	return true;
}

/****f* StaticDomManipulation/nodeGetDescendant
* *****************************************************************************
* FUNCTION
* Get a childNode of a node, with a depth of n
*
* USAGE
* nodeGetDescendant(node, n)
* 
* PARAMETERS
* node is any DOM element
* n is an integer
******************************************************************************/
function nodeGetDescendant(node, n)
{	
	while(n-- && node.childNodes[0]!=null)
	{
		node=node.childNodes[0];
	}
	return node;
}

/****f* StaticDomManipulation/nodeSetText
* *****************************************************************************
* FUNCTION
* Set the text of a node and its siblings
*
* USAGE
* nodeSetText(node)
* 
* PARAMETERS
* node is any DOM element
******************************************************************************/
function nodeSetText(node)
{
	var a = nodeSetText.arguments;
	var numNodes = a.length;
	for (i=1; i<numNodes; i++)
	{
		var t=a[i];
		if (t.indexOf('&')!=-1 || t.indexOf('<')!=-1)
		{	node.innerHTML=t;}
		else
		{node.appendChild(document.createTextNode(t));}
		node=node.nextSibling;
	}
}
/****f* StaticDomManipulation/nodeGetAncestorByAttribute
* *****************************************************************************
* FUNCTION
* Get a parent node having a given attribute
*
* USAGE
* nodeGetAncestorByAttribute(node, att)
* 
* PARAMETERS
* node is any DOM element
* attribute is the name of an attribute
******************************************************************************/
function nodeGetAncestorByAttribute(node, att)
{	
	while (node && !node[att])
		node = node.parentNode;
	return node;
}

/****f* PortletUtil/hidePortlet
* *****************************************************************************
* FUNCTION
* Hide a portlet by setting its style.display attribute to "none"
*
* USAGE
* hidePortlet(nodeContext)
* 
* PARAMETERS
* nodeContext can be either any node which is a child of the portlet 
* or a string containing the unique ID of the portlet.
******************************************************************************/
function hidePortlet(nodeContext)
{	
	var portlet = getPortlet(nodeContext);
	portlet.pcElement.style.display="none";
}
