/*
 * Confidential and Proprietary for Oracle Corporation
 *
 * This computer program contains valuable, confidential, and
 * proprietary information.  Disclosure, use, or reproduction
 * without the written authorization of Oracle is prohibited.
 * This unpublished work by Oracle is protected by the laws
 * of the United States and other countries.  If publication
 * of this computer program should occur, the following notice
 * shall apply:
 *
 * Copyright (c) 2006-2007 Stellent, Inc.
 * All rights reserved.
 * Copyright (c) 2007-2009 Oracle Corp.
 * All rights reserved.
 *
 * $Id: idc_folio_util.js 85769 2010-06-14 14:49:49Z kjorisse $
 */

if (typeof(idc) == 'undefined') {
	idc = {};
}

if (typeof(idc.folio) == 'undefined') {
	idc.folio = {};
}

if (typeof(idc.folio.util) == 'undefined') {
	idc.folio.util = {};
}

// Compat for the YAHOO.idc namespace which was removed in 11g
if (typeof(YAHOO.idc) == 'undefined')
{
	YAHOO.idc = idc;
}

idc.folio.util._MENU_VERSION = 1;

// Compat for the YAHOO.idc namespace which was removed in 11g
// Set the _VERSION attribute to give the code something to check for two-way calls.
if (typeof(YAHOO.idc.menu) == 'undefined')
{
	YAHOO.idc.menu = idc.widget;
	YAHOO.idc.menu.IdcContextMenuItem = idc.widget.ContextMenuItem;
	YAHOO.idc.menu.createAndDisplayImagePopupMenu = idc.widget.util.createAndDisplayImagePopupMenu;

	idc.folio.util._MENU_VERSION = 2;
}


idc.folio.util._VERSION = "1.0";
idc.folio.util._NAME = "idc.folio.util";

// Require idc base
idc.folio.util._EXPORT_COMPAT =
[
	["isTrue", "isTrue"],
	["convertToBool", "convertToBool"],
	["cpdlc", "cpdlc"]
];

idc.folio.util.initSourceItems = function (targetFolio, itemArray)
{
	idc.log.trace("folios", "source items " + typeof itemArray);
	idc.log.trace("folios", "source items length: " + itemArray.length);
	idc.log.trace("folios", "folio type: " + targetFolio.m_state.m_folioType);

	if (isUndefinedOrNull(targetFolio.m_state.m_itemProps))
	{
		var callback = idc.bind(idc.folio.util.initSourceItems, null, targetFolio, itemArray);
		setTimeout(callback, 100);
		return;
	}

	 if (targetFolio.m_state.m_folioType == "template") // Add items to Source Items window
	{
		idc.log.trace("folios", "Populating content items: " + itemArray.length);
		// If there are already values in searchListing_Data, add the new items to the beginning
		for (var i=0; i<itemArray.length; i++)
		{
			var itemProps = itemArray[i].split(",");
			var itemListingObj = new Object();
			for (x in targetFolio.m_state.m_itemProps)
			{
				var index = new Number(targetFolio.m_state.m_itemProps[x]);
				itemListingObj[x] = itemProps[index];
				idc.log.trace("folios", "set: " + x + "=" + itemProps[index]);
			}
			searchListing_Data[searchListing_Data.length] = itemListingObj;
		}

	}
	else
	{
		var node = targetFolio.getRootNode();
		if (!isUndefinedOrNull(node))
		{
			g_folioState.setActiveElement(node);
		}

		for (var i=0; i<itemArray.length; i++)
		{
			// Use itemArray data to create new nodes and place in fragment
			idc.log.trace("folios", "Adding initial source item (" + i + ")");

			var itemProps = itemArray[i].split(",");
			var itemListingObj = new Object();
			for (x in targetFolio.m_state.m_itemProps)
			{
				var index = new Number(targetFolio.m_state.m_itemProps[x]);
				itemListingObj[x] = itemProps[index];
				idc.log.trace("folios", "set: " + x + "=" + itemProps[index]);
			}

			// Call method that places the new nodes in the folio structure
			targetFolio.addItemToFolio(itemListingObj, node);
		}
	}

	// not qualifying 'searchListing_Data with 'this.' causes a js error when
	// calling 'isUndefinedOrNull
	if (!isUndefinedOrNull(window.searchListing_Data))
	{
		// PAW: TODO: Need to decide what to do with this function...
		idc.folio.util.loadSourceItems(searchListing_Data, searchListing_ColumnDefinition, true);
	}
};

idc.folio.util.pageInitAttempts = 0;
idc.folio.util.initPageContent =  function _initPageContent(targetFolio, sourceItems)
{
	idc.log.trace("folios", "begin initPageContent()");
	idc.log.trace("folios", "sourceItems = " + typeof sourceItems);
	var hasSourceItems = !isUndefinedOrNull(sourceItems);

	// This needs to happen so the tree exists for the initSourceItems call
	targetFolio.initTreeDisplay();

	if (hasSourceItems)
	{
		idc.folio.util.initSourceItems(targetFolio, sourceItems);

		// Set event handler to clear highlighted row in the Source Items table
		// when user clicks an empty area in the region
		YAHOO.util.Event.addListener("sourceItems_scrollbox", 'click', function(e)
			{
				sourceItemsTable.clickDeselect(e);
			});
	}
	idc.log.trace("folios", "finish initPageContent()");
};

// This function is used in the valueExpressions of the source item column definition objects
idc.folio.util.sourceListingMarkup = function(dataElmt)
{
	var childUrl = httpCgiPath + "?IdcService=DOC_INFO&dID=" +  dataElmt.dID +
		"&coreContentOnly=1";
	var childParamStr = "width=600,height=500,resizable,scrollbars";

	var htmlStr =
		'<table style="width:100%;" cellpadding="0" cellspacing="0" border="0" align="';

	if (lcDirection == 'rtl')
	{
		htmlStr += "right";
	}
	else
	{
		htmlStr += "left";
	}

	htmlStr += '"><tr><td style="width:16px;">\n' +
		'    <img src="' + httpSkinRoot + 'InfoIcon_sm.gif" ' +
		'alt="' + lc("wwContentItemInfo") + '" title="' + lc("wwContentItemInfo") + '" onclick="' +
		'idc.folio.util.dom.openChildWindow(\'' + childUrl + '\',\'' + childParamStr + '\');"' +
		' width="16" height="16 border="0"></td>\n' +
		'  <td class="trayA_content" style="width:auto; ';

	if (lcDirection == 'rtl')
	{
		htmlStr += 'padding-right:5px;';
	}
	else
	{
		htmlStr += 'padding-left:5px;';
	}

	htmlStr +=
		'"><div class="xuiListCellDiv">' + dataElmt.dDocTitle + '</div></td>\n' +
		'</tr></table>\n';

	return htmlStr;
};

idc.folio.util.changeStatusFunction = function(obj, state, type)
{
	var containerElmt = YAHOO.util.Dom.get(obj.container);
	var msgElmt = YAHOO.util.Dom.get(obj.msgElmtId);
	var imgElmt = YAHOO.util.Dom.get(obj.statusImageId);
	var infoImgElmt = YAHOO.util.Dom.get(obj.infoImageId);

	var msg = "";
	YAHOO.util.Dom.setStyle(infoImgElmt, 'display', 'none');

	if (isUndefinedOrNull(msgElmt) || isUndefinedOrNull(type))
	{
		return;
	}

	if (type == "changed")
	{
		msg = lc("wwCpdFolioUnsavedChanges");
		imgElmt.src = httpSkinRoot + "/folios/indicator.alert.gif";
	}
	else if (type == "saved")
	{
		msg = lc("wwCpdFolioChangesSaved");
		imgElmt.src = httpSkinRoot + "/folios/indicator.okay.gif";
	}
	else if (type == "error")
	{
		msg = lc("wwCpdFolioActionError");
		imgElmt.src = httpSkinRoot + "/folios/indicator.error.gif";

		// Build error popup
		var overlay = new YAHOO.widget.Overlay("errorOverlay",
			{
				visible:false,
				width:"300px",
				context:[infoImgElmt.id,"tl","br"]
			});


		overlay.setHeader(lc("wwCpdFolioActionError"));
		overlay.setBody(g_folioState.m_statusString);
		overlay.render(document.body);

		YAHOO.util.Dom.setStyle(infoImgElmt, 'display', 'inline');
		YAHOO.util.Event.addListener(infoImgElmt, "click",
			function(evt, args, obj)
			{
				if (this.cfg.getProperty("visible"))
				{
					this.hide();
				}
				else
				{
					this.show();
					this.align();
				}
			}
			, overlay, true);

	}
	else
	{
		msg = lc("wwCpdFolioSavingChanges");
		imgElmt.src = httpSkinRoot + "/folios/indicator.white.gif";
	}
	insertHtml(msg, msgElmt);
	containerElmt.style.display = "block";
};

idc.folio.util.addChangesToForm = function(state, form)
{
	var targetForm = YAHOO.util.Dom.get(form);

	if (state.m_viewType == 'templateBuilder')
	{
		createHiddenInputElement(targetForm, "isTemplate", "1");
	}

	var changes = state.m_changeList;
	if (isUndefinedOrNull(changes))
	{
		return;
	}

	// get the number of changes that may have previously been sent
	if (targetForm["NumChanges"])
	{
		var prevNumChanges = targetForm["NumChanges"].value;
		for (var i = 0; i < prevNumChanges; i++)
		{
			idc.log.trace("folios", "Removing change");
			targetForm.removeChild(targetForm["change_data" + i]);
			targetForm.removeChild(targetForm["change" + i]);
		}
		targetForm.removeChild(targetForm["NumChanges"]);
	}

	var changeIndex = 0;
	var numChanges = changes.length;
	idc.log.trace("folios", "Number of recorded changes: " + numChanges);
	for (var i = 0; i < numChanges; i++)
	{

		var folio = state.m_folioList[changes[i][0]];
		var changeData = new Array();
		var elmtCount = changeData.length;

		// cut out the client side folio id, and replace it with the internal id
		var change = "";
		if (!isUndefinedOrNull(folio.m_dID))
		{
			change = folio.m_dID;
		}
		// escape any ':' in the change
		var changeList = changes[i].slice(1);
		var changeListLen = changeList.length;
		for (var j = 0; j < changeListLen; j++)
		{
			newChange = changeList[j].split(':').join("^");
			changeList[j] = newChange.split(',').join("*");
		}
		change += ":" + changeList.join(':');
		idc.log.trace("folios", "change: " + change);

		idc.log.trace("folios", "change element: " + changes[i][2]);
		var elmt = folio.getNodeById(changes[i][2]);
		if (isUndefinedOrNull(elmt) &&
			changes[i][1] != 'removeNode' &&
			changes[i][1] != 'modifyTemplateAttribute')
		{
			// assuming the element has been deleted by later operations
			continue;
		}
		var elmtData = folio.getConfigData(changes[i][2]);

		// add all the necessary node information to the form.
		// attribute change only requires the two attributes values
		// new node requires all the new node properties. (could derive these on the server...)
		// NOTE: 080317 pasting an item with content will generate an addItem and addContent action
		// with the full list of properties sent twice.  This is a bit redundant, but it's better
		// to send too much data than not enough. Filtering could be added in the future
		// to only send what is necessary for each operation.
		if (changes[i][1] == 'addNode' || changes[i][1] == 'addContent' || changes[i][1] == 'addItem')
		{
			for (x in elmtData)
			{
				if (isUndefinedOrNull(x) || x.length == 0)
				{
					continue;
				}
				var name = idc.folio.util.escapeField(x);

				var value = elmtData[x];
				if (isUndefinedOrNull(value) || value.length == 0)
				{
					continue;
				}
				value = idc.folio.util.escapeField(value);

				changeData[elmtCount] = name + ":" + value;
				idc.log.trace("folios", "changeData: " + changeData[elmtCount]);

				elmtCount++;
			}
		}
		else if (changes[i][1] == 'modifyAttribute' ||
				changes[i][1] == 'modifyTemplateAttribute')
		{

			var targetAttr = changes[i][3];
			var escName = idc.folio.util.escapeField(targetAttr);

			var targetVal = null;
			if (changes[i][1] == 'modifyTemplateAttribute')
			{
				targetVal = folio.getTemplateAttribute(targetAttr);
			}
			else
			{
				targetVal = elmtData[targetAttr];
			}

			targetVal = idc.folio.util.escapeField(targetVal);

			changeData[elmtCount] = escName + ":" + targetVal;
		}
		createHiddenInputElement(targetForm, "change_data" + changeIndex, changeData.join(","));
		createHiddenInputElement(targetForm, "change" + changeIndex, change);
		changeIndex++;
	}
	idc.log.trace("folios", "numChanges: " + numChanges);
	createHiddenInputElement(targetForm, "NumChanges", changeIndex);
}

idc.folio.util.escapeField = function (inVal)
{
	if (isUndefinedOrNull(inVal))
	{
		return null;
	}

	var parts = inVal.split(":");
	var tmpName = parts.join("^");
	parts = tmpName.split(",");
	tmpName=parts.join("*");
	return tmpName;
}

idc.folio.util.addTemplateFieldsToForm = function (targetForm)
{
	createHiddenInputElement(targetForm, "isTemplate", "1");
}

idc.folio.util.checkOrCreateHiddenInputElement = function (form, name, value)
{
	if (isUndefinedOrNull(form[name]))
	{
		idc.log.trace("folios", "Creating input field '" + name + "'");
		createHiddenInputElement(form, name, value);
	}
	else
	{
		idc.log.trace("folios", "Setting input field '" + name + "'");
		form[name].value = value;
	}
}

idc.folio.util.updateFolio = function(state, form)
{
	idc.log.trace("folios", "save changes");
	var changes = state.m_changeList;
	if (isUndefinedOrNull(changes))
	{
		idc.log.trace("folios", "no changes");
		alert(lc("wwCpdNoChangesToSave"));
		return;
	}

	idc.log.trace("folios", "adding changes");
	idc.folio.util.addChangesToForm(state, form);

	idc.log.trace("folios", "sending changes");
	var updateCallback = idc.bind(idc.folio.util.clearFolioChanges,
			idc.folio.util, state);
	var updateErrback = idc.bind(idc.folio.util.folioRequestErrback,
			idc.folio.util, state);
	state.changeStatusFunction(state, "saving");

	var reqForm = form;
	if (typeof form == 'string')
	{
		reqForm = window.document.forms[form];
	}

	idc.async.sendXmlHttpPostRequest(reqForm, updateCallback, updateErrback);
	idc.log.trace("folios", "sent changes");
}

idc.folio.util.addItemsFromSearch = function (allItems, callback)
{
	var selectCount = 0;
	for (var i=0; i<document.forms.length; i++)
	{
		var frm = document.forms[i];
		if ((frm.isDownload != null && frm.isDownload.checked == true) ||
				(frm.isSelected != null && frm.isSelected.checked == true))
		{
			selectCount++;
		}
	}

	if (!isUndefinedOrNull(callback))
	{
		callback(selectCount);
	}
}

idc.folio.util.clearFolioChanges = function(state, req)
{
	idc.log.trace("folios", "resetting change list: " + arguments.length);
	if (idc.soap.getFieldValue(req.responseXML, "StatusCode") < 0)
	{
		var msg = idc.soap.getFieldValue(req.responseXML, "StatusMessage");
		state.reportStatus("error", msg);
	}
	else
	{
		state.clearChanges();
	}
}

// PAW first argument is provider by the bind call to create the true callback
// the section 'status' argument is provided by the xmlhttprequest callback
idc.folio.util.folioRequestErrback = function(state, req)
{
	idc.log.trace("folios", "Error in request: " + req.status);
	var msg;
	if (req.status == 500)
	{
		var pattern = /<body[^>]*?>((.|[\s])*)<\/body>/;
		var result = pattern.exec(req.responseText);
		if (result != null)
		{
			msg = result[1];
		}
	}

	if (isUndefinedOrNull(msg))
	{
		msg = req.statusText;
	}

	state.reportStatus("error", msg);
}

/*
 * This method takes the results of a node xml request and pushes it into the DOM
 * sourceID id for the source folio data node id
 * targetID id for the target dom (drawing) node id
 */
idc.folio.util.refreshChildNodesCallback = function (folio, docSelObj, sourceDataId,
		targetHtmlId, postChildNodeLoadCallback, req)
{
	YAHOO.log("Begin refreshChildNodesCallback");
	var isNewFolio = false;
	idc.log.trace("folios", "sourceID: " + sourceDataId);
	idc.log.trace("folios", "targetID: " + targetHtmlId);

	idc.log.trace("folios", "RESPONSE: " + req.responseText);
	YAHOO.log("RESPONSE: " + req.responseText);

	idc.log.trace("folios", "Finding root node.");
	var rootNode = req.responseXML.firstChild;
	while (rootNode.nodeType != 1)
	{
		idc.log.trace("folios", "Current parent node. " + rootNode.nodeName);
		rootNode = rootNode.nextSibling
	}

	idc.log.trace("folios", "Retrieved XML data for node '" + sourceDataId + "'");
	idc.log.trace("folios", rootNode.childNodes.length + " children returned");

	if (isUndefinedOrNull(folio))
	{
		// skip over the first set of children since this will be the folio root
		// which isn't what should be displayed.  It should display the children
		// of the root node.

		var elements = rootNode.getElementsByTagName("node");
		rootNode = elements[0];
		// loop through the children of this node to find the 'children' node

		idc.log.trace("folios", "New folio text: " + req.responseText);
		idc.log.trace("folios", "New folio dDocName: " + docSelObj.dDocName);
		isNewFolio = true;
		folio = new idc.folio.FolioObject(rootNode.getAttribute("id"),
				targetHtmlId, g_folioState, docSelObj.dDocName, docSelObj.dID);
		folio.m_isNested = true;
		folio.m_tree = g_folioState.m_rootFolio.m_tree;
	}

	// find the first 'children' node and pass that around
	if (rootNode.nodeName != 'children' )
	{
		var elements = rootNode.getElementsByTagName("children");
		rootNode = elements[0];
		// loop through the children of this node to find the 'children' node
	}
	YAHOO.log("NUMNODES: " +rootNode.childNodes.length );
	// IE likes to draw a little too much when passed 0 children.  Just
	// skip the node drawing unless there's something to draw
	if (rootNode.childNodes.length >= 1)
	{
		YAHOO.log("Not Skipped");

		// PAW: This works for subfolios, since they share a treeview with the
		// parent folio.  Kinda of a kludge...
		var curNode = folio.getNodeById(sourceDataId);

		if (isNewFolio)
		{
			folio.m_rootNode = curNode;
		}

		// loop through and add to the yui tree.

		// if this is null, this must be the root node.
		var parentNode = null;
		if (!isUndefinedOrNull(curNode))
		{
			parentNode = folio.m_tree.getNodeByProperty("id", curNode.getAttribute("id"));
		}
		else
		{
			parentNode = folio.m_tree.getRoot();
		}

		idc.folio.util.addChildNodes(folio, parentNode, rootNode);

	}

	YAHOO.log("finish refreshChildNodesCallback");
	postChildNodeLoadCallback();

	// This must go after the postChildNodeLoadCallback() call to ensure that
	// the tree nodes exist in the dom first.  If not, the DnD calls will fail
	// to register the 'onMouseDown' events on the nodes to allow for dragging.
	if (!isUndefinedOrNull(parentNode))
	{
		idc.folio.dragdrop.regFolioElmtDD(parentNode, g_folioState.m_rootFolio);
	}
}

idc.folio.util.addChildNodes = function (folio, parentNode, sourceNode)
{
	if (sourceNode.nodeType != 1)
	{
		return;
	}

	for (var i = 0; i < sourceNode.childNodes.length; i++)
	{
		var node = sourceNode.childNodes[i];
		if (node.nodeType != 1)
		{
			continue;
		}

		var id = node.getAttribute("id");
		var newParent = idc.folio.util.createTreeNode(folio,
			parentNode, node, g_folioState.m_initialExpandedNodesList[id]);
		if (!isUndefinedOrNull(node.childNodes) && node.childNodes.length > 0)
		{
			// recursively add any other nodes.
			idc.folio.util.addChildNodes(folio, newParent, node.childNodes);
		}

		if (parentNode.isRoot())
		{
			// load root folio properties
			var propertiesNode = sourceNode.parentNode.getElementsByTagName("properties")[0];
			for (var i = 0; i < propertiesNode.childNodes.length; i++)
			{
				var propertyNode = propertiesNode.childNodes[i];
				if (propertyNode.nodeType != 1)
				{
					continue;
				}
				var key = propertyNode.getAttribute("key");
				var value = propertyNode.getAttribute("value");
				folio.m_templateAttributes[key] = value;
			}
		}
	}
}

idc.folio.util.createTreeNode = function (folio, parentNode, xmlNode, isExpanded)
{
	var expBool = isTrue(isExpanded);
	var nodeData = {  cpdFolio: folio, cpdXmlNode: xmlNode};

	var nodeType = xmlNode.tagName.toLowerCase();
	if (folio.m_tree.getRoot() == parentNode)
	{
		// The parent is the root of the tree, so this must be the root folio node.
		tmpNode = new YAHOO.idc.widget.folio.IdcFolioNode(nodeData, parentNode, expBool);
	}
	else if (nodeType == "node")
	{
		tmpNode = new YAHOO.idc.widget.folio.IdcFolioContainerNode(nodeData, parentNode, expBool);

		// Set the dynamic load function for each collection node.  This
		// works around problems where the global dynamic load function isn't set
		// since it's a new folio, causing any subfolio container nodes to not
		// have their dynamic load function set.
		tmpNode.setDynamicLoad(idc.folio.display.yDrawNode, 1);
	}
	else if (nodeType == "slot")
	{
		tmpNode = new YAHOO.idc.widget.folio.IdcFolioItemNode(nodeData, parentNode, expBool);
	}
	else
	{
		alert(lc("wwCpdUnknownNodeType", nodeType));
	}
	return tmpNode;
}

idc.folio.util.displayChildNodes = function (folio, docSelObj, sourceDataId, targetHtmlId)
{
}

idc.folio.util.expandChildErrback = function(folio, onCompleteCallback)
{
	idc.log.trace("folios", "Problem retrieving XML data")
	alert("Problem retrieving XML data")
	onCompleteCallback();
}

idc.folio.util.loadChildFolio = function (sourceDataId, docTarget, docTargetType,
	nodeHtmlId, callback, errback, postLoadCallback)
{
	var docSelObj = {};
	docSelObj[docTargetType] = docTarget;
	docSelObj.RevisionSelectionMethod =
		(docTargetType == 'dID') ? "Specific":"LatestReleased";

	idc.log.trace("folios", "Loading child folio " + docTargetType + "=" + docTarget);
	return idc.folio.util.loadFolioData(null, docSelObj, sourceDataId, null, false,
			callback, errback, postLoadCallback);
}

idc.folio.util.loadChildNodes = function (folio, nodeDataId, loadAll,
	onLoadCallback, onErrCallback, postLoadCallback)
{
	YAHOO.log("Begin loadChildNodes");
	idc.log.trace("folios", "folioID: " + folio.m_id);

	if (!isUndefinedOrNull(folio.m_dID))
	{
		var docSelObj = new Object();
		docSelObj.RevisionSelectionMethod = 'Specific';
		docSelObj.dID = folio.m_dID;
	}
	else if (folio.m_isNested)
	{
		// This is here since sub folios are loaded by docname, and
		// they never have a did reference.
		// This should be fixed so that once a subfolio is loaded, the
		// same folio is loaded
		var docSelObj = new Object();
		docSelObj.RevisionSelectionMethod = 'LatestReleased';
		docSelObj.dDocName = folio.m_docName;
	}

	if (isUndefinedOrNull(docSelObj) && isUndefinedOrNull(folio.m_tempKey))
	{
		idc.log.trace("folios", "DID empty, returning");

		// Since this is a new folio, the client holds the authoritative version
		// of this node. Mark this node as loaded.
		// PAW:YUI folio.m_state.m_loadedNodes[nodeDataId] = true;

		return;
	}

	YAHOO.log("Finish loadChildNodes");
	return idc.folio.util.loadFolioData(folio, docSelObj, nodeDataId, null,
			loadAll, onLoadCallback, onErrCallback, postLoadCallback);
}

idc.folio.util.loadFolioData = function (folio, docSelObj, sourceDataId,  targetHtmlId,
		loadAll, onLoadCallback, onErrCallback, postLoadCallback)
{
	YAHOO.log("Begin loadFolioData");
	var childService = idc.folio.util._determineFolioService(folio, sourceDataId, docSelObj, loadAll);

	idc.log.trace("folios", "Calling service: " + childService);
	YAHOO.log("Calling Service: " + childService );

	if (isUndefinedOrNull(postLoadCallback))
	{
		postLoadCallback = idc.folio.util.displayChildNodes;
	}
	var postCallback = idc.bind(postLoadCallback, null, folio,
			docSelObj, sourceDataId, targetHtmlId);

	if (isUndefinedOrNull(onLoadCallback))
	{
		onLoadCallback = idc.folio.util.refreshChildNodesCallback;
	}
	var callback = idc.bind(onLoadCallback, null, folio,  docSelObj,
			sourceDataId, targetHtmlId, postCallback);

	if (isUndefinedOrNull(onErrCallback))
	{
		onErrCallback = idc.folio.util.expandChildErrback;
	}
	var errback = idc.bind(onErrCallback, null, folio, postCallback);

	var yCallback =
	{
		success:callback,
		failure:errback
	};

	YAHOO.util.Connect.asyncRequest("GET", childService, yCallback);

	YAHOO.log("Finish loadFolioData");
	return true;
}

idc.folio.util._determineFolioService = function(folio, sourceDataId, docSelObj, loadAll)
{
	var childService = '';

	idc.log.trace("folios", "Requesting children for '" + sourceDataId + "'");
	if (!isUndefinedOrNull(folio) && !isUndefinedOrNull(folio.m_tempKey) )
	{
		childService = httpCgiPath + "?IdcService=LOAD_TEMP_FOLIO_NODE&TempFolioKey=" +
			folio.m_tempKey;
	}
	else if (!isUndefinedOrNull(docSelObj))
	{
		// regular folio or subfolio

		var selMethod = docSelObj.RevisionSelectionMethod;
		var type = (selMethod == 'Specific') ? "dID" : "dDocName";
		var val = docSelObj[type];
		childService = httpCgiPath + "?IdcService=LOAD_FOLIO_NODE&" + type + "=" +
			val + "&RevisionSelectionMethod=" + selMethod;
	}
	else
	{
		// new folio?
		return false;
	}

	// determine options
	if (isUndefinedOrNull(folio))
	{
		//  If a subfolio is being loaded, skip the root node, since
		// that is already represented by the containing 'slot'
		childService += "&retrievalDepth=2";
	}
	else if (!isUndefinedOrNull(sourceDataId))
	{
		childService += "&parentNodeId=" + sourceDataId;
	}
	if (!isUndefinedOrNull(loadAll) && loadAll == true)
	{
		childService += "&retrieveAllChildren=1";
	}

	// add some random data to prevent IE from caching requests
	childService += "&ts=" + new Date().getTime();

	return childService;
}

idc.folio.util.getTextNodeFromEventTarget = function (target)
{
	if(!isUndefinedOrNull(target.tagName) &&
		target.tagName.toUpperCase() == "TD" &&
		target.id.indexOf("_tree_display_container") > 0)
	{
		var id = target.id.replace(/_tree_display_container/,'');
		return g_folioState.m_treeNodeMap[id];
	}
	else
	{
		if(target.parentNode)
		{
			return idc.folio.util.getTextNodeFromEventTarget(target.parentNode);
		}
	}
}

idc.folio.util.onTriggerContextMenu = function (p_oEvent, p_oMenu)
{
	var oTextNode = idc.folio.util.getTextNodeFromEventTarget(this.contextEventTarget);
	if(oTextNode) {
		g_folioState.setActiveElement(oTextNode);
	}
	else {
		this.cancel();
	}
}

idc.folio.util.onTreeClick = function (e, p_oMenu)
{
	var startNode = YAHOO.util.Event.getTarget(e);
	var oTextNode = idc.folio.util.getTextNodeFromEventTarget(startNode);
	if(oTextNode) {
		g_folioState.setActiveElement(oTextNode);
	}
}

idc.folio.util.getGUID = function()
{
	var r = [];
	for (var i = 0; i < 16; i++)
	{
		if (i != 0 && i % 4 == 0)
		{
			r[r.length] = "-";
		}
		r[r.length] = Math.floor(Math.random()*16).toString(16).toUpperCase();
	}
	return r.join('');
}

// SCHEMA
idc.folio.util._loadViewTryCount = 0;
idc.folio.util.loadViewOptionList = function (viewName, relName, parent, fieldID,
		fieldName, defaultValue /*, targetDocument */)
{
	idc.log.trace("folios", "start loadViewOptionList");
	var target = document;
	if (arguments.length > 6)
	{
		target = arguments[6];
	}
	var targetElmt = target.getElementById(fieldID);
	if (isUndefinedOrNull(targetElmt) || !isUndefinedOrNull(targetElmt['viewFinished']))
	{
		idc.log.trace("folios", "target " + fieldID + "doesn't exist. Try later.");
		// This handles the case where the loadViewOptionList is set up
		// before the target html element exists in the dom.
		// PAW: TODO: set a max retry count so this doesn't spin forever
		// when the element is truly missing
		var newFunc = idc.bind(idc.folio.util.loadViewOptionList, null,
				viewName, relName, parent, fieldID, fieldName, defaultValue, target);
		setTimeout(newFunc, 100);
		return;
	}
	idc.log.trace("folios", "Get schema array.");

	// mark the field so the redraw logic knows when the new field appears.
	targetElmt['viewFinished'] = true;

	if (relName)
	{
		var parentValueInfo = {}
		parentValueInfo.value = g_folioState.m_username;

		var field = document.getElementById(fieldID);
		field.addEmptyOption = 0;
		field.allowPreselect = 1;

		var childPathInfo = constructViewPathInfo(viewName, relName, parent);

		updateDependentChoice(viewName, relName, parentValueInfo, childPathInfo,
				field, defaultValue, false);
	}
	else
	{
		idc.log.trace("folios", "start registerView: " + viewName + "," + fieldID);
		var field = getOrCreateSchemaArray(
				g_schemaDefinition.fieldSettings, fieldName, "fieldName");
		field.addEmptyOption = 0;
		field.allowPreselect = 1;
		registerView(viewName, fieldID, defaultValue);
		idc.log.trace("folios", "finish registerView");
	}

	idc.log.trace("folios", "finish doCallbacks");

	// PAW: The target elements may not exist at this time, so this call
	// may need to do a getElementById/setTimeout trick to be safe on
	// slow clients...

	finalizeDisplayImplementor();
}

idc.folio.util.loadViewByName = function (viewName , relationship, parentValue )
{
	var viewInfo = constructViewPathInfo(viewName, relationship, parentValue);
	var srcPath = viewInfo.path;
	if (viewInfo.url != null)
	{
		srcPath = viewInfo.url;
	}
	loadViewValues(new Array(null, srcPath, null));
}

idc.folio.util.loadFolioView = function (viewName, relName, parentVal, afterLoadCallback /*, doLoadCallback */)
{
	if (typeof registerGlobalCallback == "undefined")
	{
		var callback = idc.bind(idc.folio.util.loadFolioView, null, viewName, relName,
			parentVal, afterLoadCallback);
		setTimeout(callback, 100);
		return;
	}

	var loadCallback = idc.folio.util.loadViewByName;
	var callback = idc.bind(loadCallback, null, viewName, relName, parentVal);
	registerGlobalCallback(new Array(callback));

	var info = constructViewPathInfo(viewName, relName, parentVal);
	registerViewCallback(info.path, new Array(afterLoadCallback));
}

idc.folio.util.initFolioViewArray = function(viewName, targetArray)
{
	idc.log.trace("folios", "loading " + viewName + ". ");
	var viewDef = getViewDefinition(viewName);
	var valueIndex = viewDef.schInternalColumn;
	for (var i = 0; i < viewDef.rows.length; i++)
	{
		var key = viewDef.rows[i][valueIndex];
		targetArray[key] = viewDef.rows[i];
		idc.log.trace("folios", "loading item prop: " + key);
	}
}

idc.folio.util.pneFolioDisplayScript = function (folio, dataElmt, propName, propDef, fieldID, propValue)
{
	var viewName = propDef.fieldType.slice(5); // remove 'view:'
	//add the username to the pne
	viewName += "/" + folio.m_state.m_username;
	var viewArray = viewName.split('/');
	idc.log.trace("folios", "property viewname: " + viewName);

	idc.folio.util.loadViewOptionList(viewArray[0], viewArray[1], viewArray[2], fieldID, "xcst:" + propName, propValue);

	return '<select id="' + fieldID + '" name="xcst:' +
		propName +  '" style="width:100%; font-size:7pt;"' +
		' onchange="idc.folio.display.setInputChange(' +
		idc.folio.display.generateFolioLookupScript(folio, false) +
		', this, \'select\');"></td>\n';
}

idc.folio.util.loadSourceItems = function (dataObj, columnDefObj, isSearch)
{
	// Instantiate the sourceItems object using the passed-in data objects
	//sourceItemsTable = new DisplayTable("sourceItemsContainer", dataObj,  columnDefObj);
	sourceItemsTable = new idc.table("sourceItemsContainer", dataObj,  columnDefObj);
	sourceItemsTable.hasSelectableRows = true;

	var searchControls = document.getElementById("searchControls");

	if (isSearch)
	{
		searchControls.style.display = "block";
		sourceItemsTable.emptyTableMsg = lc('wwCpdNoSearchItems');
	}
	else
	{
		searchControls.style.display = "none";
		sourceItemsTable.emptyTableMsg = lc('wwCpdNoItems');
	}

	// Append code to the standard row-select/deselect methods so that the popup
	// actions menu for any currently-selected folio element will dynamically
	// update to hide/show the 'Insert Selected Source Item' based upon
	// selection/de-selection of items in the Source Items listing. Also override
	// standard deselect method to confine click area to Scroll Items region.
	var stdRowSelectMethod = sourceItemsTable.selectRow;
	sourceItemsTable.selectRow = function(clickedRowElmt, e)
		{
			idc.folio.util.fixCtrlClick(clickedRowElmt, e);
			stdRowSelectMethod.call(sourceItemsTable, clickedRowElmt, e);
		};
	sourceItemsTable.deselectRow = function(e)
		{
			sourceItems_deselectRow.call(sourceItemsTable, e);
		};

	// Override updateRowPointers method with custom version that includes instantiation of dragdrop objects
	sourceItemsTable.updateRowPointers = sourceItems_updatePointers;

	// Generate the table display
	sourceItemsTable.makeTable();
}

idc.folio.util.checkIfRootNode = function (node)
{
	return (node.data.cpdFolio.getRootNode() != node);
}

idc.folio.util.fixCtrlClick = function (clickedRowElmt, e)
{
 	var index = sourceItemsTable.getRowObjectIndex(clickedRowElmt);
	if (e.ctrlKey && sourceItemsTable.isRowSelected(index))
	{
		sourceItemsTable.deselectRowEx(index);
	}
}

// PAW: TODO: These are here only temporarily.
// Override of the standard DisplayTable method to confine de-select click area to Source Items region
function sourceItems_deselectRow(e)
{
	if (!isUndefinedOrNull(this.selectedItemsList) && this.selectedItemsList.length > 0)
	{
		if (!e) e = window.event;

		// Get a pointer to the object that was clicked
		if (typeof e.target != "undefined")
		{
			var clickedElmt = e.target;
		}
		else if (typeof e.srcElement != "undefined")
		{
			var clickedElmt = e.srcElement;
		}

		// Perform operations only if the click originated outside the body of the display table
		// AND within the bounds of the Source Items region
		// Note: the containsNode function is a standard utility from layouts/common.js
		if (!containsNode(this.tbodyElmt, clickedElmt)
				&& containsNode(document.getElementById("sourceItems_cell"), clickedElmt))
		{
			var selectedRowPosition = this.contentRows[this.selectedItemIndex].sectionRowIndex;
			var selectedRow = this.tbodyElmt.rows[selectedRowPosition];
			selectedRow.className = selectedRow.className.replace(/_highlight/, "_normal");
			this.selectedItemIndex = null;
		}
	}
}


function sourceItems_updatePointers()
{
	// Clear previous row contents from pointer arrays
	this.contentRows.length = 0;
	this.dividerRows.length = 0;

	// Crear dragdrop objects related to previous source items list
	var ddObjects = idc.folio.dragdrop.sourceItemDD;
	for (x in ddObjects)
	{
		if (ddObjects[x].unreg)
		{
			ddObjects[x].unreg();
		}
	}
	ddObjects = {};

	// Generate array content used as pointers to content rows and divider rows and dragdrop objects
	var tableRows = this.tbodyElmt.rows;
	for (var i=0; i<tableRows.length; i++)
	{
		if (tableRows[i].getAttribute("name") == this.objRef + "_contentRow")
		{
			this.contentRows[this.contentRows.length] = tableRows[i];

			// Add id to row elmt, and use that to register corresponding draddrop object
			var rowId = "sourceItem_" + (i-1);
			tableRows[i].id = rowId;
			idc.folio.dragdrop.sourceItemDD[rowId] = new idc.folio.dragdrop(rowId, "source_items", this);
			idc.folio.dragdrop.sourceItemDD[rowId].addToGroup("common");
		}

		if (tableRows[i].getAttribute("name") == this.objRef + "_dividerRow")
			this.dividerRows[this.dividerRows.length] = tableRows[i];
	}
	//DEBUG_showData(this.dataArray)
}

function sourceItemSearch(include)
{
	if (isUndefinedOrNull(include))
	{
		include = "cpd_add_items_2a";
	}
	var childUrl = httpCgiPath + "?IdcService=GET_DOC_PAGE&Action=GetTemplatePage" +
		"&Page=FOLIO_BASE&Include=" + include +
		"&cpdSearchType=collectItems&coreContentOnly=1";
	var childParams = "width=600,height=500,resizable,scrollbars";
	idc.folio.util.dom.openChildWindow(childUrl, childParams);
}

idc.folio.util.isTrue = function (val)
{
	if (isUndefinedOrNull(val))
	{
		return false;
	}

	var b = new Boolean(val);
	if (b == false)
	{
		// 0, '', undefined, NaN, null
		return false;
	}

	if (val == "1" ||
		toLower(val) == "true" ||
		toLower(val) == "t")
	{
		return true;
	}
	return false;
}

idc.folio.util.convertToBool = function (val, defVal)
{
	var retVal = val;
	if (isUndefinedOrNull(retVal))
	{
		retVal = defVal;
	}
	return isTrue(retVal);
}

idc.folio.util.cpdlc = function (key)
{
	if (isUndefinedOrNull(key))
	{
		return;
	}

	var originalKey = key;
	var lookupKey = key;
	var escList = [".", "{", "}", "/"];

	for (var i = 0; i < escList.length; i++)
	{
		lookupKey = lookupKey.split(escList[i]).join("-");
	}

	var retVal = lc(lookupKey);

	if (retVal == lookupKey)
	{
		retVal = originalKey;
	}

	return retVal;
}

idc.folio.util.computeRenditionFilename = function (params, prefix)
{
	if (isUndefinedOrNull(prefix))
	{
		prefix = "";
	}
	var renFlag = params[prefix + "dRendition1"];
	var url = "";

	if (isUndefinedOrNull(renFlag) || renFlag.length == 0)
	{
		var viewData = getViewDefinition("docTypes");
		if (viewData)
		{
			var valueIndex = viewData.schInternalColumn;
			for (var i = 0; i < viewData.rows.length; i++)
			{
				var row = viewData.rows[i];
				if (row.dDocType == params[prefix + "dDocType"])
				{
					url = httpWebRoot + "images/docgifs/" + row.dGif;
				}
			}
		}
	}
	else
	{
		var docUrl = params[prefix + "docURL"];
		var index = docUrl.lastIndexOf('/');
		var path = docUrl.slice(0, index+1);

		var docName = params[prefix + "dDocName_encoded"];
		var revLabel = params[prefix + "dRevLabel"];

		// PAW:TODO fix this to use the AdditionalRenditions table to determine extension
		var ext = "jpg";

		var fileName = toLower(docName + '@' + renFlag + "~" + revLabel + "." + ext);

		url = path + fileName;
	}

	return url;
}

idc.folio.util._init = function()
{
	idc.nameFunctions(this);
}

idc.folio.util._init();
idc._exportSymbols(this, idc.folio.util);
/*
 * Confidential and Proprietary for Oracle Corporation
 *
 * This computer program contains valuable, confidential, and
 * proprietary information.  Disclosure, use, or reproduction
 * without the written authorization of Oracle is prohibited.
 * This unpublished work by Oracle is protected by the laws
 * of the United States and other countries.  If publication
 * of this computer program should occur, the following notice
 * shall apply:
 *
 * Copyright (c) 2006-2007 Stellent, Inc.
 * All rights reserved.
 * Copyright (c) 2007-2009 Oracle Corp.
 * All rights reserved.
 *
 * $Id: idc_folio_util_dom.js 69585 2009-01-06 21:33:22Z pwalters $
 */

if (typeof(idc) == 'undefined') {
	idc = {};
}

if (typeof(idc.folio) == 'undefined') {
	idc.folio = {};
}

if (typeof(idc.folio.util) == 'undefined') {
	idc.folio.util = {};
}

if (typeof(idc.folio.util.dom) == 'undefined') {
	idc.folio.util.dom = {};
}

idc.folio.util.dom._VERSION = "1.0";
idc.folio.util.dom._NAME = "idc.folio.util.dom";

// Require idc base
idc.folio.util.dom._EXPORT_COMPAT = [];

//**** General **************************************************************************************************************
//***************************************************************************************************************************

// By using the same window obj for all child windows, we prevent them from accumulating
var childWindow;

idc.folio.util.dom.openChildWindow = function (childUrl, childParamStr)
{
	// Parse width/height values out of paremeter string
	var params = childParamStr.split(",");
	var winWidth = null;
	var winHeight = null;
	for (var i=0; i<params.length; i++)
	{
		if (params[i].split("=")[0] == "width")
			winWidth = parseInt(params[i].split("=")[1]);
		if (params[i].split("=")[0] == "height")
			winHeight = parseInt(params[i].split("=")[1]);
	}

	// Set width/height defaults if no values provided in parameter string
	if (!winWidth)
	{
		winWidth = YAHOO.util.Dom.getViewportWidth();
		childParamStr += ",width=" + winWidth;
	}
	if (!winHeight)
	{
		winHeight = YAHOO.util.Dom.getViewportHeight();
		childParamStr += ",height=" + winHeight;
	}

	var winPosX;
	var winPosY;
	if (typeof window.screenX != "undefined") // W3C Mozilla branch
	{
		var clientXCenter = top.screenX + (top.outerWidth / 2);
		winPosX = clientXCenter - (winWidth / 2);
		winPosY = top.screenY + (top.outerHeight - top.innerHeight);
		childParamStr += ",left=" + winPosX + ",top=" + winPosY;
	}
	else if (typeof window.screenLeft != "undefined") // IE Branch
	{
		var clientXCenter = top.screenLeft + (top.document.body.clientWidth / 2);
		winPosX = clientXCenter - (winWidth / 2);
		winPosY = top.screenTop + 25;
		childParamStr += ",left=" + winPosX + ",top=" + winPosY;
	}

	if (typeof childWindow == "undefined" || childWindow.closed)
	{
		childWindow = window.open(childUrl, "childWin", childParamStr);
	}
	else
	{
		// resizeTo seems to include chrome whereas initial sizing doesn't (hence the compensation values)
		childWindow.resizeTo(winWidth + 10, winHeight + 25);
		if (winPosX != null && winPosY != null) childWindow.moveTo(winPosX, winPosY);
		childWindow.location = "about:blank"; // This instantaneously clears the window prior to load (only works in IE)
		childWindow.location = childUrl;
		childWindow.focus();
	}
}

idc.folio.util.dom.toggleInfoBox = function (targetID)
{
	var infoBox = document.getElementById(targetID);
	var height = idc.folio.util.dom.getHiddenElmtHeight(targetID);

	var doExpand = (height > infoBox.offsetHeight);

	if (doExpand)
	{
		YAHOO.util.Dom.setStyle(infoBox, "display", "block");
	}

	var expandAnim = new YAHOO.util.Anim(targetID, {}, .2);
	expandAnim.attributes.height = {
		to:(doExpand? height: 0),
		from:(doExpand ? 0 : height)
	};
	expandAnim.animate();

	if (!doExpand)
	{
		expandAnim.onComplete.subscribe(function(event, args, obj)
		{
			YAHOO.util.Dom.setStyle(this.getEl(), "display", "none");
		});
	}
}

idc.folio.util.dom.getHiddenElmtHeight = function (hiddenElmtID)
{
	var hiddenElmt = document.getElementById(hiddenElmtID);
	var hiddenElmtClone = hiddenElmt.cloneNode(true);
	hiddenElmt.parentNode.appendChild(hiddenElmtClone);

	hiddenElmtClone.style.left = "-3000px";
	hiddenElmtClone.style.position = "absolute";
	hiddenElmtClone.style.height = "auto";
	hiddenElmtClone.style.display = "block";
	var renderedHeight = hiddenElmtClone.offsetHeight;

	hiddenElmt.parentNode.removeChild(hiddenElmtClone);

	return renderedHeight;
}

idc.folio.util.dom.hoverOn = function (displayElmt)
{
	var classList = displayElmt.className.split(" ");
	var baseClass = classList[0].split("_")[0];
	YAHOO.util.Dom.addClass(displayElmt, baseClass + "_hover");
}


idc.folio.util.dom.hoverOff = function (displayElmt)
{
	var classList = displayElmt.className.split(" ");
	var baseClass = classList[0].split("_")[0];
	YAHOO.util.Dom.removeClass(displayElmt, baseClass + "_hover");
}

idc.folio.util.dom.propDisplayHandler = function()
{
	var folio = arguments[0];
	var dataElmt = arguments[1];
	var retVal = dataElmt.getAttribute("xcsd:dDocName");
	if (isUndefinedOrNull(retVal))
	{
		retVal = "";
	}
	return '<span class="folioNodeText">' + retVal + '</span>';
}

idc.folio.util.dom.resizeToParent = function(el, target, config)
{
	var minHeight = 150;
	config = (isUndefinedOrNull(config)) ? {} : config;

	// normalize to the Dom element
	var element = YAHOO.util.Dom.get(el);
	if (isUndefinedOrNull(target))
	{
		target = element.parentNode;
	}

	var elRegion = YAHOO.util.Dom.getRegion(element);
	var targetRegion = YAHOO.util.Dom.getRegion(target);

	var viewport = [
			YAHOO.util.Dom.getViewportWidth(),
			YAHOO.util.Dom.getViewportHeight()
		];

	var noMin = isTrue(config["noMinimumSize"]);
	var newBottom = (viewport[1] < targetRegion["bottom"]) ? viewport[1] : targetRegion["bottom"];
	var hDelta = newBottom - elRegion["bottom"];
	var boxCurHeight = elRegion["bottom"] - elRegion["top"];
	var newHeight = boxCurHeight + hDelta;

	idc.log.trace("folios", "el.id: " + element.getAttribute("id"));
	idc.log.trace("folios", "elRegion: " + elRegion);
	idc.log.trace("folios", "targetRegion: " + targetRegion);
	idc.log.trace("folios", "viewport: " + viewport);
	idc.log.trace("folios", "oldheight: " + boxCurHeight + " newHeight: " + newHeight);

	// find any extra elements to consider.
	var elList = config["extraEls"];
	if (!isUndefinedOrNull(elList))
	{
		for (var i = 0; i < elList.length; i++)
		{
			var exEl = YAHOO.util.Dom.get(elList[i]);
			if (isUndefinedOrNull(exEl))
			{
				continue;
			}
			var region = YAHOO.util.Dom.getRegion(exEl);
			newHeight = newHeight - (region["bottom"] - region["top"]);
			idc.log.trace("folios", "extra region: " + region);
			idc.log.trace("folios", "adjusted height: " + newHeight);
		}
	}

	var extraPadding = config["extraPadding"];
	if (!isUndefinedOrNull(extraPadding))
	{
		newHeight -= extraPadding;
	}

	newHeight = (newHeight < minHeight) ? minHeight : newHeight;

	YAHOO.util.Dom.setStyle(element, "height", newHeight + "px");

	var elRegion = YAHOO.util.Dom.getRegion(element);
	idc.log.trace("folios", "elRegion (post): " + elRegion);

	if (its.firefox)
	{
		if (newHeight == minHeight)
		{
			document.body.style.overflow = "auto";
		}
		else
		{
			document.body.style.overflow = "hidden";

			var overflow = config["overflow"];
			YAHOO.util.Dom.setStyle(element, "overflow",
				(isUndefinedOrNull(overflow)) ? "auto" : overflow);
		}
	}
}


idc.folio.util.dom._init = function ()
{
	idc.nameFunctions(this);
}

idc.folio.util.dom._init();
idc._exportSymbols(this, idc.folio.util.dom);
/*
 * Confidential and Proprietary for Oracle Corporation
 *
 * This computer program contains valuable, confidential, and
 * proprietary information.  Disclosure, use, or reproduction
 * without the written authorization of Oracle is prohibited.
 * This unpublished work by Oracle is protected by the laws
 * of the United States and other countries.  If publication
 * of this computer program should occur, the following notice
 * shall apply:
 *
 * Copyright (c) 2006-2007 Stellent, Inc.
 * All rights reserved.
 * Copyright (c) 2007-2009 Oracle Corp.
 * All rights reserved.
 *
 * $Id: idc_folio_display.js 85769 2010-06-14 14:49:49Z kjorisse $
 */

if (typeof(idc) == 'undefined') {
	idc = {};
}

if (typeof(idc.folio) == 'undefined') {
	idc.folio = {};
}

if (typeof(idc.folio.display) == 'undefined') {
	idc.folio.display = {};
}

idc.folio.display._VERSION = "1.0";
idc.folio.display._NAME = "idc.folio.display";

idc.folio.display._popupEditActions;
idc.folio.display._elementInfoCurrentFocusObj = {};

idc.folio.display.yDrawNode = function (node, onCompleteCallback)
{
	YAHOO.log("Begin DrawNode");

	if (node.m_displayParams.isFolioItem && g_folioState.m_viewType != 'templateBuilder')
	{
		var retVal = idc.folio.util.loadChildFolio(node.getAttribute("id"),
			node.m_displayParams.linkTarget, node.m_displayParams.linkTargetType,
			null, null, null, onCompleteCallback);
	}
	else
	{
		var retVal = idc.folio.util.loadChildNodes(node.data.cpdFolio,
			node.getAttribute("id"), false, null, null, onCompleteCallback);
	}

	YAHOO.log("Finish DrawNode");
	if (!retVal)
	{
		// The AJAX call was skipped since the nodes are already loaded.
		// This means the onCompleteCallback should be called now.
		onCompleteCallback();
	}
}

idc.folio.display.onSetNodeActive = function (event, args, obj)
{
	// Without the setTimeout here, any info displayed in the element info pane
	// (aside from info in the input fields) will lag one edit behind.
	// The setTimeout breaks this call out of the execution stream long enough
	// to allow the 'nodeChangeEvent' handlers to fire as a result of any element
	// info changes and recalculate the display info.
	// While this doesn't guarantee the change handlers will be finished by the
	// time it calls, it seems to be 'good enough' in testing.  The alternative
	// to this is to have the redraw be handled at the end of the change handlers,
	// but this comes with it's own set of complications since the change event
	// may fire on a node outside of it being the activly display node.
	setTimeout(idc.bind(idc.folio.display.displayElementInfo, null, obj) , 0);
}

idc.folio.display.displayElementInfo = function (containerId)
{
	if (isUndefinedOrNull(g_folioState.m_activeDataElmt))
	{
		return;
	}

	// Render the display code in the Folio Element Info region
	var propertiesContainer = document.getElementById(containerId);
	insertHtml(g_folioState.m_activeDataElmt.getInfoNodeHtml(), propertiesContainer);

	// Make sure that the element info redraw is due to an edit of node info and not
	// because a new node has been selected.  Also make sure that the node has an
	// 'id' attribute before trying to attach to it.
	if (g_folioState.m_activeDataElmt == idc.folio.display._elementInfoCurrentFocusObj.node
		&& !isUndefinedOrNull(idc.folio.display._elementInfoCurrentFocusObj.inputId))
	{
		// The setTimeout is required here since IE seems to have problems with the DOM
		// not being entirely ready as a result of the insertHtml() call that preceeds this.
		// Without the setTimeout, the onAvailable attaches to the previous instance of the
		// 'inputId' node in the DOM.  The setTimeout allows the browser time to clear the
		// previous set of nodes out of the DOM so the onAvailable attaches to the new
		// version of the node.
		setTimeout(function(){
		YAHOO.util.Event.onAvailable(
			idc.folio.display._elementInfoCurrentFocusObj.inputId,
			function() {
				var el =  YAHOO.util.Dom.get(
					idc.folio.display._elementInfoCurrentFocusObj.inputId);
				el.focus();
				if (el.select)
				{
					// If possible, highlight the node.  This keeps the input navigation
					// similar to what a user would expect in a form that isn't being
					// redrawn.
					el.select();
				}
			});
		}, 100);
	}
}

idc.folio.display.checkForEnter = function (folio, input, event)
{
	event = (!event) ? window.event : event;
	var key = (event.keyCode) ? event.keyCode : event.which;
	if (key == 13)
	{
		idc.folio.display.finishInputChange(folio, input);
	}
}

idc.folio.display.generateFolioLookupScript = function (folio, doEscape)
{
	var quoteStr = "'";
	if (doEscape)
	{
		quoteStr = "\\'";
	}
	var result = "g_folioState.m_folioList[" + quoteStr + folio.m_id + quoteStr + "]";
	return result;
}

idc.folio.display.checkPopupAllowed = function (folio, obj)
{
	var msg = null;
	if (folio.m_state.m_activeDataElmt.isCollectionNode())
	{
		// active data elmt must be a container type node
		msg = folio.checkActionAllowed("addItem", folio.m_state.m_activeDataElmt);
		if (!isUndefinedOrNull(msg))
		{
			// items not allowed, return
			alert(msg);
			return false;
		}
	}
	else
	{
		msg = folio.checkActionAllowed("addContent", null,
						folio.m_state.m_activeDataElmt, obj);
		if (!isUndefinedOrNull(msg))
		{
			// external items not allowed, return
			alert(msg);
			return false;
		}
	}
	return true;
}

function populateBySearch()
{
	if (!idc.folio.display.checkPopupAllowed(this, {}))
	{
		return;
	}

	var childUrl = httpCgiPath + "?IdcService=GET_DOC_PAGE&Action=GetTemplatePage" +
		"&Page=FOLIO_BASE&Include=cpd_add_items_3&coreContentOnly=1" +
		"&cpdSearchType=selectContentItem";
	var childParamStr = "width=600,height=500,resizable,scrollbars";

	var profile = this.m_state.m_activeDataElmt.getAttribute("xcst:contentProfile");
	if (isUndefinedOrNull(profile))
	{
		profile = this.m_state.m_rootFolio.getTemplateAttribute("xcst:defaultContentProfile");
	}
	if (!isUndefinedOrNull(profile))
	{
		idc.log.trace("folios", "using profile: " + profile);
		var profileObj = this.m_state.m_docProfileInfo[profile];
		if (!isUndefinedOrNull(profileObj))
		{
			childUrl += "&dpTriggerValue=" + profileObj.dpTriggerValue;
		}
	}
	idc.folio.util.dom.openChildWindow(childUrl, childParamStr);
}

function populateByCheckin()
{
	if (!idc.folio.display.checkPopupAllowed(this, {}))
	{
		return;
	}

	var childUrl = httpCgiPath + "?IdcService=GET_SECURE_PAGE&Action=GetTemplatePage" +
		"&Page=FOLIO_BASE&Include=folio_checkin_2&coreContentOnly=1" +
		"&cpdSearchType=selectContentItem";
	var childParamStr = "width=600,height=500,resizable,scrollbars";

	var profile = this.m_state.m_activeDataElmt.getAttribute("xcst:contentProfile");
	if (isUndefinedOrNull(profile))
	{
		profile = this.m_state.m_rootFolio.getTemplateAttribute("xcst:defaultContentProfile");
	}
	if (!isUndefinedOrNull(profile))
	{
		idc.log.trace("folios", "using profile: " + profile);
		var profileObj = this.m_state.m_docProfileInfo[profile];
		if (!isUndefinedOrNull(profileObj))
		{
			childUrl += "&dpTriggerValue=" + profileObj.dpTriggerValue;
		}
	}

	idc.folio.util.dom.openChildWindow(childUrl, childParamStr);
}

function populateWithHypertext()
{
	// Create a dummy object to test if external items are allowed
	var testObj = new Object();
	testObj["xcst:isExternalItem"] = "1";

	if (!idc.folio.display.checkPopupAllowed(this, testObj))
	{
		return;
	}

	var childUrl = httpCgiPath + "?IdcService=GET_DOC_PAGE&Action=GetTemplatePage" +
		"&Page=FOLIO_BASE&Include=cpd_enter_hypertext&coreContentOnly=1" +
		"&cpdSearchType=selectContentItem";
	var childParamStr = "width=600,height=500,resizable,scrollbars";

	idc.folio.util.dom.openChildWindow(childUrl, childParamStr);
}

idc.folio.display.startInputChange = function (folioObj, element)
{
	folioObj.m_state.m_targetInputObj = {
		value : element.value,
		id : element.id,
		folioID : folioObj.m_id
	};
}

idc.folio.display.finishInputChange = function (folioObj, element)
{
	var changeObj = folioObj.m_state.m_targetInputObj;
	if (isUndefinedOrNull(changeObj))
	{
		return;
	}

	if (changeObj.value != element.value)
	{
		idc.log.trace("saving change for element '" + element.name + "'");
		// delay this call a bit to avoid a race condition with a click
		// setting a child node active.  If there is no delay, the
		// 'changeConfig' event will wipe out the HTML of child nodes
		// and lose the 'labelClick' event which sets the element active.

		var obj = {
			folio: folioObj,
			element: element,
			node: g_folioState.m_activeDataElmt
		}
		var callback = function(obj)
		{
			obj.folio.setElementAttribute(obj.element.name, obj.element.value, obj.node);
		}
		setTimeout(idc.bind(callback, null, obj) , 100);
	}

	folioObj.m_state.m_targetInputObj = null;
}

idc.folio.display.setInputChange = function (folioObj, element, type)
{
	if (isUndefinedOrNull(type) || type == "radio")
	{
		idc.log.trace("saving change for element '" + element.name + "'");
		folioObj.setElementAttribute(element.name, element.value);
	}

	else if (type == "select")
	{
		var index = element.selectedIndex;
		var value = element.options[index].value;
		var name = element.name;
		folioObj.setElementAttribute(name, value);
	}
}

idc.folio.display.invalidLinkPrompt = function(folio, dataElmtID)
{
	if (confirm(lc('wwCpdLinkBrokenPrompt')))
	{
		var childParamStr = "width=600,height=500,resizable,scrollbars";
		var childUrl = httpCgiPath + "?IdcService=LOAD_ARCHIVED_FOLIO_ITEM_CHECKIN_FORM" +
			"&RevisionSelectionMethod=Specific" +
			"&targetID=" + dataElmtID + "&dID=" + folio.m_dID +
			"&coreContentOnly=1&cpdSearchType=archiveItemCheckin";
		idc.folio.util.dom.openChildWindow(childUrl, childParamStr);
	}
}

idc.folio.display.elementInfoFocusHandler = function(e)
{
	// Since this isn't called through the YAHOO.util.Event interface, all the
	// usual event normalization is required.
	e = (!e) ? window.event : e;
	var item;
	if (typeof e.target == "undefined")
	{
		item = e.srcElement;
	}
	else
	{
		item = e.target;
	}

	
	// Make sure the event is targeting something under the element info pane.
	if (YAHOO.util.Dom.isAncestor("cpd_tray_body_cell_0", item))
	{
		idc.folio.display._elementInfoCurrentFocusObj.inputId = item.getAttribute("id");
		idc.folio.display._elementInfoCurrentFocusObj.node = g_folioState.m_activeDataElmt;
	}
}

idc.folio.display._init = function _init()
{
	idc.nameFunctions(this);
}

idc.folio.display._init();

/*
 * Confidential and Proprietary for Oracle Corporation
 *
 * This computer program contains valuable, confidential, and
 * proprietary information.  Disclosure, use, or reproduction
 * without the written authorization of Oracle is prohibited.
 * This unpublished work by Oracle is protected by the laws
 * of the United States and other countries.  If publication
 * of this computer program should occur, the following notice
 * shall apply:
 *
 * Copyright (c) 2006-2007 Stellent, Inc.
 * All rights reserved.
 * Copyright (c) 2007-2009 Oracle Corp.
 * All rights reserved.
 *
 * $Id: idc_table.js 86518 2010-07-12 18:03:15Z kjorisse $
 */

if (typeof(idc) == 'undefined') {
	idc = {};
}

/**
 * @class Defines the interface and base operation for table-display creation and manipulation on the client.
 * The idc.table object uses a Javascript data object to dynamically render an HTML table in the style of the currently-acitve skin
 * (the table design can easily be customized by modifying the methods that generate the markup: initHtmlTable, writeContentRow, and
 * writeDividerRow). The object also provides methods that make it easy to dynamically modify the table (insert, modify, and delete
 * rows), as well as client-side behaviors that allow rows to be selected and/or rearranged by the user.

 * idc.table.dataArray is an ordered array of JavaScript objects containing named properties that can be used as data fields in
 * an HTML display table (Note: the IDC Include, 'rs_make_javascript_objects', can be used to convert a CS record set into a client-side
 * Javascript object for this purpose).

 * In addition to 'dataArray', the idc.table.displayColumns array must be provided to define the columns in the rendered table.
 * 'displayColumns' is also an ordered array of objects; each object represents a column to be rendered in the HTML table.
 * Each object contains properties that define the contents and appearance of the heading and value cells. The following
 * properties are used by the current implementation:

 *    dataField - The name of one of the fields in the data object dataArray
 *    headingExpression - A string that evaluates to a valid Javascript expression in the scope of the 'initHtmlTable' method
 *    headingCellCSS - A valid CSS string
 *    valueExpression - A string that evaluates to a valid Javascript expression in the scope of the 'writeContentRow' method
 *    valueCellCSS - A valid CSS string

 * In the simplest usage, only the dataField property needs to be defined for each column; in this case, the field name
 * will be used as the display heading, and the value of that field will be used in the value cells of the column. The
 * headingExpression and valueExpression properties can be used to customize the display of these cells. In most
 * cases a headingExpression will be used (often with an expression like: lc('wwMyHeading')). The valueExpression property will
 * be needed less frequently to create derived values for column cells. If either headingExpression or valueExpression are
 * defined, these will be used to generate the heading/column content. If either of these properties are not defined, then
 * the dataField property *must* be defined.

 * The headingCellCSS and valueCellCSS properties can be used optionally to alter the standard style properties of the
 * table cells using inline CSS code. This will be used most often to affect colunn width, alignment, and cell padding.

 * Two properties can be set to control the bahaviors of the table: idc.table.hasMoveableRows, and idc.table.hasSelectableRows.
 * Setting 'hasMoveableRows=true' adds controls to the table that enable the user to multi-select rows and move them to a different
 * position in the table. Setting 'hasSelectableRows=true' causes rows to highlight when clicked, and the index of the
 * corresponding data element to be stored in the object's 'selectedItemIndex' property (useful for performing edits, deletes,
 * and other operations pertaining to the data in a single row).
 *
 */

idc.table = function(parentId, dataArrayObj, displayColumnsObj)
{
  this.dataArray = dataArrayObj;
  this.displayColumns = displayColumnsObj;
  this.parentContainerId = parentId;
  this.objRef = parentId;

  this.selectedItemsList = [];
  this.selectedItemIndex = null;

  // This provides a unique identifier string that can be used to reference the object from within the markup code
  window[this.objRef] = this;
};

idc.table.VERSION = "1.0";
idc.table.NAME = "idc.table";

idc.table.errors =
 {
headingExpressionNotDefined: "idc.table error: A value must be defined for either the 'dataField' or 'headingExpression' property of each column in the 'displayColumns' array.",
valueExpressionNotDefined: "idc.table error: A value must be defined for either the 'dataField' or 'valueExpression' property of each column in the 'displayColumns' array.",
columnPropertiesNotDefined: "idc.table error: You must define properties for each table column using the idc.table.displayColumns object. Required properties are 'headingExpression' and 'valueExpression' (e.g. myTable.displayColumns[n].headingExpression = 'lc(\"myLocalizedHeading\")'; myTable.displayColumns[n].valueExpression = 'this.dataArray[i].myDataColumnName')",
newRowIndexOutOfRange: "idc.table error: insertTableRow() method: New row index is greater than dataArray.length. New row position must be contiguous with existing rows."
 };

idc.table.prototype =
{
  clipboardArray: new Array(),
  workingArray: new Array(),
  selectedRows: new Array(),
  tableMarkup: "",
  tableElmt: null,
  tbodyElmt: null,
  contentRows: new Array(),
  dividerRows: new Array(),
  emptyTableMsg: "wwCpdNoItems",

  // Properties used with re-arrangeable table row functionality
  hasMoveableRows: false,
  reorderingMode: false,
  useAlternatingRows: true,
  dividerRowNormalHeight: 4,
  dividerRowExpandedHeight: 15,
  intervalMilleseconds: 15,
  intervalId: null,
  intervalCount: 0,
  intervalDelayCount: 2,

  // Properties used with row selection functionality
  hasSelectableRows: false,
  selectedItemIndex: null,
  selectedItemsList: null,

  // Flag to enable checkboxes
  hasCheckboxes: false,

  // Main control method that loops through data to generate markup
  makeTable: function()
  {
	// Clear existing property values
	this.tableMarkup = "";
	this.contentRows.length = 0;
	this.dividerRows.length = 0;
	this.selectedRows.length = 0;
	this.tableElmt = null;
	this.tbodyElmt = null;

	var parentContainer = document.getElementById(this.parentContainerId);

	if (this.dataArray.length == 0)
	{
	// If no data, display an empty-table message in place of the table
	insertHtml(this.displayEmptyTableMsg(), parentContainer);
	}
	else
	{
	// Generate the markup string for the table structure
	this.initHtmlTable();

	// Render HTML code and set pointers to table and tbody elements
	insertHtml(this.tableMarkup, parentContainer);
	
	// BUG 9246583 - This setTimeout is introduced to work around an odd IE
	// redraw issue when dealing with multiple idc_tables.  If an idc_table
	// already exists in the area this idc_table is targeting, it will find
	// the old idc_tables tdbody/table elmt and draw into that instead of
	// the table created in the previous insertHtml() call.  The problem
	// arises when the above insertHtml() statement is actually rendered by
	// IE since it will replace the old table element that the initRows
	// call used, causing the rows to disappear.  There seems to be a delay
	// between replacing the innerHtml in IE and when the DOM actually
	// reflects that change.  Adding the setTimeout() call seems to give IE
	// enough time to settle the DOM down and allow the initRows() call to
	// find the correct table elmt.
	setTimeout(idc.bind(this.initRows, this), 10);
	}
  },
  
   initRows: function()
  {
	idc.log.trace("folios", "calling initRows()" );
	this.tableElmt = document.getElementById(this.objRef + "_contentTable");
	this.tbodyElmt = document.getElementById(this.objRef + "_tbody");
	
	idc.log.trace("folios", "initRows::tableElmt: " + this.tableElmt);
	idc.log.trace("folios", "initRows::tableElmt: " + this.tbodyElmt);
	
  	if (!this.tbodyElmt)
  	{
		idc.log.trace("folios", "calling setTimeout()" );
		//setTimeout(idc.bind(this.initRows, this), 100);
		//return;
  	}
  	
	// Write the initial divider row if moveable rows are being used
	if (this.hasMoveableRows)
		this.writeDividerRow(-1);

	// Loop through the data object to create the content rows in the table
	idc.log.trace("folios", "initRows::dataArray: " + this.dataArray.length);
	for (var i=0; i<this.dataArray.length; i++)
	{
		this.writeContentRow(i);

		if (this.hasMoveableRows)
		this.writeDividerRow(i);
	}

	idc.log.trace("folios", "initRows::exit");
	this.updateRowPointers();
  },

  // Default method that generates base markup for empty table; override to change look-and-feel
  initHtmlTable: function()
  {
	if (this.displayColumns.length == 0)
	throw new Error(idc.table.errors["columnPropertiesNotDefined"]);
	else
	{
	this.tableMarkup +=
		'<table id="' + this.objRef + '_contentTable" class="xuiListTable" style="width:100%;" border="0" cellpadding="0" cellspacing="0">\n' +
		'<thead>\n' +
		' <tr>\n';

	var extraColCount = 0;
	var extraColsWidth = 0;

	if (this.hasMoveableRows)
	{
		extraColCount++;
		extraColsWidth += 18;
	}

	if (this.hasCheckboxes)
	{
		extraColCount++;
		extraColsWidth += 18;
	}

	if (extraColCount > 0)
	{
		this.tableMarkup +=
		'   <th class="xuiListHeaderCell" colspan="' + extraColCount +
		'" style="width:' + extraColsWidth +
		//'px;font-size:1px;border-right:0px">' +
		'px;border-right:0px">' +
		'<div class="xuiListHeaderDiv" nowrap>&nbsp;</div></th>\n';
	}

	for (var colCount=0; colCount<this.displayColumns.length; colCount++)
	{
		var headingString = "&nbsp;"

		if (typeof this.displayColumns[colCount].headingExpression != "undefined") // Use headingExpression for heading content
		{
		var evaluatedHeadingResult = eval(this.displayColumns[colCount].headingExpression);
		if (evaluatedHeadingResult != "") headingString = evaluatedHeadingResult;
		}
		else if (typeof this.displayColumns[colCount].dataField != "undefined") // Use plain field name for heading content
		headingString = this.displayColumns[colCount].dataField;
		else
		throw new Error(idc.table.errors["headingExpressionNotDefined"]);

		if (typeof this.displayColumns[colCount].headingCellCSS != "undefined")
		var headingStyle = this.displayColumns[colCount].headingCellCSS;
		else
		var headingStyle = "";

		if (colCount != 0)
		{
		this.tableMarkup +=
			'   <td class="xuiListResizeDragCell_Header" onmousedown="resizeColumns_horizontal(\'column_' + this.objRef + '_' + (colCount-1) + '\', \'column_' + this.objRef + '_' + colCount + '\', event)" onmouseover="this.style.cursor=\'e-resize\'" onmouseout="this.style.cursor=\'default\'"></td>\n';
		}
		else
		headingStyle = (extraColCount > 0) ? "border-left:0px;" + headingStyle : headingStyle;

		this.tableMarkup +=
		'   <th id="column_' + this.objRef + '_' + colCount + '" class="xuiListHeaderCell" style="' + headingStyle + '">\n' +
		'     <div class="xuiListHeaderDiv" nowrap>\n';
		this.tableMarkup += headingString;
		this.tableMarkup +=
		'     </div></th>\n';
	}

	this.tableMarkup +=
		' </tr>\n' +
		'</thead>\n' +
		'<tbody id="' + this.objRef + '_tbody">\n' +
		' <tr>\n';

	if (extraColCount > 0)
	{
		this.tableMarkup +=
		'   <td colspan="' + extraColCount + '" style="font-size:1px">&nbsp;</td>\n';
	}

	this.tableMarkup +=
		'   <td style="font-size:1px">&nbsp;</td>\n';

	for (var dragColCount = 1; dragColCount < colCount; dragColCount++)
	{
		this.tableMarkup +=
		'   <td rowspan="1000" class="xuiListResizeDragCell_Item" style="font-size:1px"\n' +
		'     onmousedown="resizeColumns_horizontal(\'column_' + this.objRef + '_' + (dragColCount-1) + '\', \'column_' + this.objRef + '_' + dragColCount + '\', event)" onmouseover="this.style.cursor=\'e-resize\'" onmouseout="this.style.cursor=\'default\'">&nbsp;</td>\n' +
		'   <td style="font-size:1px">&nbsp;</td>\n';
	}

	this.tableMarkup +=
		' </tr>\n' +
		'</tbody>\n' +
		'</table>\n';
	}
  },

  // Default method that generates markup for content rows; override to change look-and-feel
  writeContentRow: function(i)
  {
	var trElmt, tdElmt, tdContent, cellClass, contentStyle;

	if (this.displayColumns.length == 0)
	throw new Error(idc.table.errors["columnPropertiesNotDefined"]);
	else
	{
	// Create Row
	if (typeof this.contentRows[i] == "undefined")
		trElmt = this.tbodyElmt.insertRow(this.tbodyElmt.rows.length);
	// Update Existing Row
	else
	{
		trElmt = this.contentRows[i];

		// Delete existing cells from row
		for (var cellPos=trElmt.cells.length-1; cellPos>=0; cellPos--)
		trElmt.deleteCell(cellPos);
	}

	trElmt.setAttribute("name", this.objRef + "_contentRow"); // Note: "name" can't be set using dot-property syntax
	trElmt.className = "ContentRow_normal";

	if (!this.hasSelectableRows && !this.hasMoveableRows)
	{
		if (this.useAlternatingRows)
		{
			cellClass = (i % 2 == 0) ? "xuiListContentCell_Odd" : "xuiListContentCell_Even";
		}
		else
		{
			cellClass = "xuiListContentCell_Odd";
		}
	}
	else
	{
		if (this.hasSelectableRows)
		{
		var displayTableObj = this;
		trElmt.onmouseover = function() { this.style.cursor = "pointer"; };
		trElmt.onmouseout = function() { this.style.cursor = "default"; };
		trElmt.onclick = function(e) { if (!e) e=window.event; displayTableObj.selectRow(this, e); };
		cellClass = "selectableContentCell";
		}

		if (this.hasMoveableRows)
		{
		cellClass = "moveableContentCell";
		var toggleRowMovePick_Method = this.objRef + ".toggleRowMovePick(this);";

		tdElmt = trElmt.insertCell(trElmt.cells.length);
		tdElmt.className = cellClass;
		tdElmt.style.padding = "0px";
		tdContent =
			'<img src="' + httpSkinRoot + 'folios/RowSelectBtn_normal.gif" onclick="' + toggleRowMovePick_Method + '" style="display:' + (this.reorderingMode ? 'inline' : 'none') + ';" width="16" height="16" border="0">' +
			'<img src="' + httpSkinRoot + 'space.gif" width="1" height="16">\n';
		insertHtml(tdContent, tdElmt);
		}
	}

	if (this.hasCheckboxes)
	{
		tdContent =
		'<form style="display:inline" method=post action="dummy.exe">\n' +
		'  <input type="checkbox" name="rowSelect">\n' +
		'  <input type="hidden" name="tableName" value="' + this.objRef + '">\n' +
		'  <input type="hidden" name="dataRowIndex" value="' + i + '">\n' +
		'</form>\n';
		tdElmt = trElmt.insertCell(trElmt.cells.length);
		tdElmt.className = cellClass;
		tdElmt.style.padding = "0px";
		insertHtml(tdContent, tdElmt);
	}

	for (var colCount=0; colCount<this.displayColumns.length; colCount++)
	{
		var valueString = "&nbsp;"
		var colDisplayPos = colCount;
		if (this.hasMoveableRows) colDisplayPos++;
		if (this.hasCheckboxes) colDisplayPos++;

		if (typeof this.displayColumns[colCount].valueExpression != "undefined") // Use valueExpression for cell content
		{
		var evaluatedValueResult = eval(this.displayColumns[colCount].valueExpression);
		if (evaluatedValueResult != "") valueString = evaluatedValueResult;
		}
		else if (typeof this.displayColumns[colCount].dataField != "undefined") // Use plain field value for cell content
		{
		var columnValue = this.dataArray[i][this.displayColumns[colCount].dataField];
		if (columnValue != "") valueString = columnValue;
		}
		else
		throw new Error(idc.table.errors["valueExpressionNotDefined"]);

		if (typeof this.displayColumns[colCount].valueCellCSS != "undefined")
		contentStyle = this.displayColumns[colCount].valueCellCSS;
		else
		contentStyle = "";

		var test = false;
		if (typeof trElmt.cells[colDisplayPos] == "undefined") // Add a column into a new row
		tdElmt = trElmt.insertCell(trElmt.cells.length);
		else // Update a column in an existing row
		{
		tdElmt = trElmt.cells[colDisplayPos];
		}

		tdElmt.className = cellClass;
		tdContent = '<div class="xuiListCellDiv" style="' + contentStyle + '" nowrap>' + valueString + '</div>';

		insertHtml(tdContent, tdElmt);
	}
	}
  },

  // Default method that generates markup for divider rows; override to change look-and-feel.
  // This method is only used with re-arrangeable rows.
  writeDividerRow: function(i)
  {
	var insertIndex = i+1;
	var dividerCellClass = (this.reorderingMode == true) ? "RowSpacerCell_engaged" : "RowSpacerCell_normal";
	var dividerCellHeight = (this.reorderingMode == true) ? this.dividerRowNormalHeight + "px" : "1px";
	var setMoveTargetRowState_Method = eval(this.objRef + ".setMoveTargetRowState");
	var displayTableObj = this;
	var extraColCount = 0;
	if (this.hasMoveableRows) extraColCount++;
	if (this.hasCheckboxes) extraColCount++;

	trElmt = this.tbodyElmt.insertRow(this.tbodyElmt.rows.length);
	trElmt.setAttribute("name", this.objRef + "_dividerRow"); // Note: 'name' can't be set as a property
	tdElmt = trElmt.insertCell(trElmt.cells.length);
	tdElmt.className = dividerCellClass;
	tdElmt.onmouseover = function() { displayTableObj.setMoveTargetRowState("highlight", this) };
	tdElmt.onmouseout = function() { displayTableObj.setMoveTargetRowState("engaged", this) };
	tdElmt.onclick = function() { displayTableObj.moveRowsAndRefresh(insertIndex) };
	tdElmt.colSpan = (this.displayColumns.length * 2) - 1 + extraColCount;
	tdElmt.style.height = dividerCellHeight;
  },

  updateRowPointers: function()
  {
	// Create content row and divider row collection arrays
	var tableRows = this.tbodyElmt.rows;
	this.contentRows.length = 0;
	this.dividerRows.length = 0;
	for (var i=0; i<tableRows.length; i++)
	{
	if (tableRows[i].getAttribute("name") == this.objRef + "_contentRow")
	{
		//alert("tableRow: " + tableRows[i]);
		this.contentRows[this.contentRows.length] = tableRows[i];
	}

	if (tableRows[i].getAttribute("name") == this.objRef + "_dividerRow")
		this.dividerRows[this.dividerRows.length] = tableRows[i];
	}
	//DEBUG_showData(this.dataArray)
  },

  // This function provides a way to find the index of the data item to which an HTML table row corresponds
  // (since HTML table row indexes don't necessarily match data indexes due to divider rows, etc.).
  getRowObjectIndex: function(rowElmt)
  {
	var rowObjectIndex = null;

	//alert(" rowElmt: " + rowElmt);
	for(i=0; i<this.contentRows.length; i++)
	{
		//alert("row: " + this.contentRows[i]);
	if (this.contentRows[i] == rowElmt)
	{
		rowObjectIndex = i;
		break;
	}
	}

	if (rowObjectIndex != null)
	return rowObjectIndex;
	else
	alert("display_table.js: 'getRowObjectIndex' method failed to find the table row in the contentRows collection of " + this.objRef);
  },

  selectRow: function(clickedRowElmt, e)
  {
	var supressAction = false;

	var shiftPressed = false;
	var ctrlPressed = false;
	// Test for specified cases where we don't want the row-select action to be performed (such as when a link or form element
	// in the row is clicked). Tests are based upon the identity of the actual event target.
	// Note: The branching is set up so that the event parameter is optional (to permit this method to be used outside the
	// context of a click event).
	if (typeof e != "undefined")
	{
	if (typeof e.srcElement != "undefined") var eventTarget = e.srcElement;
	else if (typeof e.target != "undefined") var eventTarget = e.target;

	shiftPressed = e.shiftKey;
	ctrlPressed = e.ctrlKey;

	if (typeof eventTarget != "undefined")
	{
		if (eventTarget.nodeName == "INPUT" || eventTarget.nodeName == "SELECT") supressAction = true;

		var linkTestObj = eventTarget;
		while (linkTestObj != clickedRowElmt && linkTestObj.nodeName != "BODY") // The second condition is just for defensive purposes
		{
		if (linkTestObj.nodeName == "A") supressAction = true;
		linkTestObj = linkTestObj.parentNode;
		}
	}
	}
	idc.log.trace("folios", "Calling selectRow()");

	if (!this.reorderingMode && !supressAction)
	{
	var lastSelectedItem = this.selectedItemIndex;
	var newSelectedIndex = this.getRowObjectIndex(clickedRowElmt); // Index location of row in contentRows collection (and corresponding data elemnent in dataArray)

	if (ctrlPressed == false)
	{
		idc.log.trace("folios", "Deselect already selected rows ");
		while (this.selectedItemsList.length > 0)
		{
			this.deselectRowEx(this.selectedItemsList[0]);
		}

		if (shiftPressed)
		{
			var start = lastSelectedItem;
			var end = newSelectedIndex;
			if (start == end)
			{
				idc.log.trace("folios", "caller: " + arguments.callee);
			}
			else
			{
				idc.log.trace("folios", "caller: " + arguments.callee);
			}
			idc.log.trace("folios", "caller: " + arguments.callee);
			idc.log.trace("folios", "Last: " + start + " NEW: " + end);
			if (lastSelectedItem > newSelectedIndex)
			{
				start = newSelectedIndex;
				end = lastSelectedItem;
			}
			for (var i = start; i <= end; i++)
			{
				this.selectRowEx(i);
			}
		}
		else
		{
			idc.log.trace("Regular click on item " + newSelectedIndex);
			// PAW: TODO - handle multiple clicks
			this.selectRowEx(newSelectedIndex);
		}
	}
	else
	{
		// Check if the target is already selected, and unselect it if that is the case
		if (this.isRowSelected(newSelectedIndex))
		{
			idc.log.trace("Deselect already selected row " + newSelectedIndex);
			this.deselectRowEx(newSelectedIndex);
		}
		else
		{
			// select and add to list
			idc.log.trace("Ctrl click on item " + newSelectedIndex);
			this.selectRowEx(newSelectedIndex);
		}
	}

	if (!shiftPressed)
	{
	    this.selectedItemIndex = newSelectedIndex;
	  }
	}
  },

  isRowSelected: function(index)
  {
  	var len = this.selectedItemsList.length;
  	for (var i = 0; i < len; i++)
  	{
		if (this.selectedItemsList[i] == index)
		{
			return true;
		}
  	}
  	return false;
  },

  selectRowEx: function(index)
  {
  	idc.log.trace("folios", "row " + index + " selected");
  	var rowPosition = this.contentRows[index].sectionRowIndex;
	var selectedRow = this.tbodyElmt.rows[rowPosition];
	selectedRow.className = selectedRow.className.replace(/_normal/, "_highlight");
	this._addItemToSelectedList(index);
  },

  deselectRow: function()
  {
	if (this.selectedItemIndex != null)
	{
	this.deselectRowEx(this.selectedItemIndex);
	this.selectedItemIndex = null;
	}
  },

  deselectAll: function()
  {
	if (this.selectedItemIndex != null)
	{
		this.selectedItemIndex = null;
	}

  	var len = this.selectedItemsList.length;
  	for (var i = 0; i < len; i++)
  	{
	  this.deselectRowEx(i);
  	}
  },

  deselectRowEx: function(index)
  {
	if (!isUndefinedOrNull(this.contentRows[index]))
	{
	  idc.log.trace("folios", "row " + index + " deselected");
	var selectedRowPosition = this.contentRows[index].sectionRowIndex;
	var selectedRow = this.tbodyElmt.rows[selectedRowPosition];
	selectedRow.className = selectedRow.className.replace(/_highlight/, "_normal");

	}
	this._removeItemFromSelectedList(index);
  },

  _addItemToSelectedList: function(index)
  {
  	// Check the current selected list to prevent duplicates.
  	var len = this.selectedItemsList.length;
  	var itemExists = false;
  	var selIndex = 0;
  	for (; selIndex < len; selIndex++)
  	{
		if (this.selectedItemsList[selIndex] == index)
		{
			itemExists = true;
			break;
		}
  	}
  	if (!itemExists)
  	{
		this.selectedItemsList[len] = index;
  	}
  	return selIndex;
  },

  _removeItemFromSelectedList: function(rowIndex)
  {
	var i = 0;
	while (i < this.selectedItemsList.length)
	{
		if (this.selectedItemsList[i] == rowIndex)
		{
			this.selectedItemsList.splice(i, 1);
			return i;
		}
		i++;
	  }
	  return -1;
  },

  clickDeselect: function(e)
  {
	if (this.selectedItemsList.length > 0)
	{
	if (!e) e = window.event;

	// Get a pointer to the object that was clicked
	if (typeof e.target != "undefined")
		var clickedElmt = e.target;
	else if (typeof e.srcElement != "undefined")
		var clickedElmt = e.srcElement;

	// Perform the operations only if the click originated outside the body of the display table
	// Note: the containsNode function is a standard utility from layouts/common.js
	if (!containsNode(this.tbodyElmt, clickedElmt))
	{
		this.deselectAll(); // Passed event for flexibility in custom overrides of deselectRow method
	}
	}
  },

  deleteRowAtIndex: function(i)
  {
	if (this.selectedItemIndex != null)
	{
	if (i == this.selectedItemIndex)
		this.selectedItemIndex = null;
	else // This branch ensures that no row remains selected after a delete, even if delete is performed outside the context of active row processing
		this.toggleRowSelection(this.contentRows[this.selectedItemIndex]);
	}

	this.dataArray.splice(i, 1);

	// If last row has been deleted, run the makeTable() method to clear all properties and display empty-table message
	if (this.dataArray.length == 0)
	{
	this.makeTable();
	}
	// Otherwise remove individual table row and update the pointers
	else
	{
	var contentRowPosition = this.contentRows[i].sectionRowIndex; // Index of actual row position in HTML TBODY section
	this.tbodyElmt.deleteRow(contentRowPosition);

	if (this.dividerRows.length > 0)
	{
		var dividerRowPosition = this.dividerRows[i].sectionRowIndex;
		this.tbodyElmt.deleteRow(dividerRowPosition);
	}

	this.updateRowPointers();
	}
  },

  deleteAllRows: function()
  {
	this.dataArray.length = 0;
	this.makeTable();
  },

  toggleRowMoveMode: function()
  {
	var selectBtns = new Array();

	this.deselectRow();

	// Make an array containing all row-pick buttons for this table (check for objRef string value
	// in string representation of onclick handler for validation)
	for (var i=0; i<document.images.length; i++)
	{
	if (document.images[i].onclick && document.images[i].onclick.toString().indexOf(this.objRef + ".toggleRowMovePick") > -1)
	{
		selectBtns[selectBtns.length] = document.images[i];
	}
	}

	// Establish toggle mode and set row-pick button display value
	if (this.reorderingMode == false)
	{
	this.reorderingMode = true;
	var displayValue = "inline";
	}
	else
	{
	this.reorderingMode = false;
	var displayValue = "none";
	this.rowMoveUnpickAll();
	}

	// Change display status of row-pick buttons
	for (var i=0; i<selectBtns.length; i++)
	{
	selectBtns[i].style.display = displayValue;
	selectBtns[i].src = selectBtns[i].src.replace(/_highlight/, "_normal");
	}

	// Change height and class of row cells
	for (var i=0; i<this.dividerRows.length; i++)
	{
	var cell = this.dividerRows[i].getElementsByTagName("TD")[0];

	if (this.reorderingMode == false)
	{
		cell.className = cell.className.replace(/_engaged|_highlight/, "_normal");
		var rowHeight = 1;
	}
	else
	{
		cell.className = cell.className.replace(/_normal/, "_engaged");
		var rowHeight = this.dividerRowNormalHeight;
	}
	cell.style.height = rowHeight + "px";
	}
  },

  toggleRowMovePick: function(clickElmt)
  {
	// Find parent row of clicked element
	var row = clickElmt.parentNode;
	while (row.nodeName != "TR")
	row = row.parentNode;

	// Find index value of selected row
	for (var i=0; i<this.contentRows.length; i++)
	{
	if (this.contentRows[i] == row)
	{
		var rowIndex = i;
		break;
	}
	}

	// Select the row
	if (clickElmt.src.indexOf("_normal") > -1)
	{
	this.selectedRows[this.selectedRows.length] = rowIndex;
	row.className = row.className.replace(/_normal/, "_highlight");
	clickElmt.src = clickElmt.src.replace(/_normal/, "_highlight");
	}
	// De-select the row
	else
	{
	for (var i=0; i<this.selectedRows.length; i++)
	{
		if (this.selectedRows[i] == rowIndex)
		this.selectedRows.splice(i, 1);
	}
	row.className = row.className.replace(/_highlight/, "_normal");
	clickElmt.src = clickElmt.src.replace(/_highlight/, "_normal");
	}
  },

  rowMoveUnpickAll: function()
  {
	// Reset temp data arrays
	this.selectedRows.length = 0;
	this.clipboardArray.length = 0;
	this.workingArray.length = 0;

	// Reset table row classNames
	for (var i=0; i<this.contentRows.length; i++)
	this.contentRows[i].className = this.contentRows[i].className.replace(/_highlight/, "_normal");
  },

  setMoveTargetRowState: function(toState, rowCell)
  {
	if (this.selectedRows.length > 0)
	{
	if (toState == "highlight")
	{
		var thisObj = this;
		// This function is declared here so the rowCell variable is within scope
		function slideRowOpen()
		{
		if (thisObj.intervalCount > thisObj.intervalDelayCount) // This provides a short delay before sizing begins to reduce jumpiness
		{
			var rowCellHeight = parseInt(rowCell.style.height);
			if (rowCellHeight < thisObj.dividerRowExpandedHeight)
			{
			rowCellHeight = rowCellHeight + 1;
			rowCell.style.height = rowCellHeight + "px";
			}
			else
			clearInterval(thisObj.intervalId)
		}
		else
			thisObj.intervalCount++;
		}

		rowCell.className = rowCell.className.replace(/_engaged/, "_" + toState);
		this.intervalCount = 0;
		this.intervalId = setInterval(slideRowOpen, this.intervalMilleseconds);
	}
	else
	{
		rowCell.className = rowCell.className.replace(/_highlight/, "_" + toState);
		clearInterval(this.intervalId);
		rowCell.style.height = this.dividerRowNormalHeight;
	}
	}
  },

  moveRowsAndRefresh: function(insertionIndex)
  {
	// Null these out to prevent errors in writeContentRow
	this.contentRows.length = 0;
	this.dividerRows.length = 0;

	this.moveRowsDataOnly(insertionIndex);
	this.rowMoveUnpickAll();
	this.makeTable();
  },

  moveRowsDataOnly: function(insertionIndex)
  {
	if (this.selectedRows.length > 0)
	{
	// Create a duplicate working array from the data array
	copyArrayXToY(this.dataArray, this.workingArray);

	// The selected indexes must be sorted to keep them in the order they appear rather than the order selected
	this.selectedRows.sort();

	// Loop through selected index values to create clipboard array
	// (null out the values of the corresponding elements in working array)
	for (var i=0; i<this.selectedRows.length; i++)
	{
		var rowIndex = this.selectedRows[i];
		this.clipboardArray[i] = this.workingArray[rowIndex];
		this.workingArray[rowIndex] = null;
	}

	// Insert clipboard values into working array at insertion index point
	var tempSpliceArray = this.workingArray.splice(insertionIndex, 1000); // Temporarily cut off all rows from insertion point on
	this.workingArray = this.workingArray.concat(this.clipboardArray);
	this.workingArray = this.workingArray.concat(tempSpliceArray);

	// Loop upward through working array to remove null elements
	for (var i=this.workingArray.length-1; i>=0; i--)
	{
		if (this.workingArray[i] == null)
		this.workingArray.splice(i, 1);
	}

	// Transfer working elements to data array and clean up
	copyArrayXToY(this.workingArray, this.dataArray);
	this.clipboardArray.length = 0;
	this.workingArray.length = 0;
	}
  },

  // Additional arguments following 'rIndex' should be strings representing name/value pairs of data to be
  // changed for this row (e.g. myTable.updateContentRow(i, "field1=value1", "field2=value2", etc.))
  updateTableRow: function(rIndex)
  {
	for (var i=1; i<arguments.length; i++)
	{
	var fieldName = arguments[i].split("=")[0];
	var fieldValue = arguments[i].split("=")[1];
	this.dataArray[rIndex][fieldName] = fieldValue;
	}

	this.writeContentRow(rIndex);
  },

  // Additional arguments following 'rIndex' should be strings representing name/value pairs of data to be
  // used to create this row (e.g. myTable.insertTableRow(i, "field1=value1", "field2=value2", etc.))
  insertTableRow: function(rIndex)
  {
	// Create the new data object using the name/value pairs from the arguments list
	var newDataObj = new Object();
	if (arguments.length > 1)
	{
		var tmpArray = arguments[1];
		for (var i = 0; i < tmpArray.length; i++)
		{
			var fieldName = tmpArray[i].split("=")[0];
			var fieldValue = tmpArray[i].split("=")[1];
			newDataObj[fieldName] = fieldValue;
		}
	}

	this.dataArray.splice(rIndex, 0, newDataObj);

	if (this.dataArray.length == 1)
	{
	// If the dataArray was previously empty, create a new table from scratch
	this.makeTable();
	}
	else
	{
	if (rIndex == this.contentRows.length) // APPEND
	{
		//var newRowElmt = this.tbodyElmt.insertRow(this.tbodyElmt.rows.length);
		this.writeContentRow(rIndex);

		if (this.hasMoveableRows)
		this.writeDividerRow(rIndex);

		this.updateRowPointers();
	}
	else if (rIndex < this.contentRows.length) // INSERT
	{// This is buggy, it insists upon deleting the origninal node in that position.
	// Actually it's weirder than that: the 'removed' node reappears in the next insertion

		var trElmt = this.tbodyElmt.insertRow(rIndex);

		if (this.hasMoveableRows)
		this.writeDividerRow(rIndex);

		this.updateRowPointers();

		this.writeContentRow(rIndex);
	}
	}
  },

  displayEmptyTableMsg: function()
  {
	var htmlStr = '<div class="xuiDescription" style="padding:10px; text-align:left; white-space:normal;"><span style="white-space:normal;">' + lc(this.emptyTableMsg) + '</span></div>';
	return htmlStr;
  }

};



function alternateLinkText(linkObj)
{
  // Toggle to alternate link text
  for (var i=0; i<linkObj.childNodes.length; i++)
  {
	if (linkObj.childNodes[i].nodeType == 1)
	{
	if (linkObj.childNodes[i].style.display == "none")
		linkObj.childNodes[i].style.display = "inline";
	else if (linkObj.childNodes[i].style.display == "inline")
		linkObj.childNodes[i].style.display = "none";
	}
  }
}

// Utility function used to transfer elements of one array to another
function copyArrayXToY(x, y)
{
  y.length = 0;

  for (var i=0; i<x.length; i++)
	y[i] = x[i];
}

function DEBUG_showData(dataArray)
{
  var str = "";

  for (var i=0; i<dataArray.length; i++)
  {
	for (prop in dataArray[i]) str += prop + "=" + dataArray[i][prop] + ", ";

	str += "\n";
  }

  alert(str);
}


/*
 * Confidential and Proprietary for Oracle Corporation
 *
 * This computer program contains valuable, confidential, and
 * proprietary information.  Disclosure, use, or reproduction
 * without the written authorization of Oracle is prohibited.
 * This unpublished work by Oracle is protected by the laws
 * of the United States and other countries.  If publication
 * of this computer program should occur, the following notice
 * shall apply:
 *
 * Copyright (c) 2006-2007 Stellent, Inc.
 * All rights reserved.
 * Copyright (c) 2007-2009 Oracle Corp.
 * All rights reserved.
 *
 * $Id: idc_traydisplay.js 83034 2010-03-23 20:35:22Z kjorisse $
 */

/**
 * @class TrayDisplay is used to create an accordion-type display panel consisting of a vertical series of
 * collapsible content areas with headings. The display panel automatically sizes to fill its parent
 * container; the individual content areas are scrollable.
 *
 * Usage:
 * The constructor requires the ID value of the parent container in which the tray display table
 * will reside. The tray table can be configured from markup that already exists on the page, or it
 * can be dynamically generated and inserted upon page load, depending upon how the properties
 * are set. CSS class name properties must be provided for open/closed-states of headers (hover-state
 * classes are optional). Header classes must specify a pixel height for the header cell (other than
 * this, classes are simply used for design purposes -- attributes required for tray operation are set
 * dynamically). Buffer cell class is also required (the buffer cell fills the table space when all
 * trays are closed). allowMultipleOpenTrays property defaults to 'true' if not explicitly set.

 var traysObject = new idc.TrayDisplay("trayParent");
 traysObject.headerClosedClass = "tray_Header_closed";
 traysObject.headerClosedHoverClass = "tray_Header_closed_hover";
 traysObject.headerOpenClass = "tray_Header_open";
 traysObject.headerOpenHoverClass = "tray_Header_open_hover";
 traysObject.bufferCellClass = "tray_BufferCell";
 traysObject.allowMultipleOpenTrays = true;

 * To dynamically generate tray table markup in an empty container upon page load, use the addTray method
 * to define the trays to be created. Pass in values for tray header label, base ID, and optionally,
 * a string of text or HTML to fill the tray content area. The base ID is used as a prefix in constructing
 * IDs for tray table elements (e.g. '<baseId>_headerCell', '<baseId>_contentCell', '<baseId>_scrollBox').

 traysObject.addTray("Tray 1", "tray1", "content string 1");
 traysObject.addTray("Tray 2", "tray2", "content string 2");
 traysObject.addTray("Tray 3", "tray3", "content string 3");

 * When creating trays from existing page markup, the table structure must be configured as shown below.
 * Important specifics: Deprecated table attributes must be set as shown. <TH> tags must be used for
 * heading cells; <TD> tags for content cells. Content rows must be initially set to 'display:none'.
 * The last row in the table is the buffer row, it should contain nothing other than '&nbsp;'.
 *
 * For compatiblity with Safari, class attributes must be included for the header cells and
 * width/height must be set to '100%' on the table element (other supported browsers can successfully
 * set these values upon initialization).
 *
 * It should be possible to add most any markup inside the header cell and scroll box (content <div>
 * container). Otherwise the table structure shouldn't be altered, and the tray table should be the
 * only child element of the parent container.
 *
 * Note that if ID properties are not provided for header cell, content cell, and scrollbox, they will
 * be autogenerated in the format:
 * 		<parentContainerID>_headerCell_<rowIndex>
 * 		<parentContainerID>_contentCell_<rowIndex>
 * 		<parentContainerID>_scrollBox_<rowIndex>

 * Example markup:
 <table style="width:100%; height:100%;" border="0" cellspacing="0" cellpadding="0">
	<tr>
		<th id="header_cell_1" class="closedHeaderClass">Tray 1 Label</th>
	</tr>
	<tr style="display:none;">
		<td id="content_cell_1">
			<div id="scrollbox_1">Tray 1 HTML Content</div>
		</td>
	</tr>

 ...additional tray rows

	<tr>
		<td class="bufferClass" style="height:auto;">&nbsp;</td> <!-- Buffer Cell -->
	</tr>
 </table>
 */


if (typeof(idc) == 'undefined')
{
	idc = {};
}

/**
 * @constructor
 * Create new TrayDisplay object.
 * @param {String} parentId The id of the container in which this TrayDisplay table is to be rendered/defined.
 */
idc.TrayDisplay = function(parentId)
{
	this.parentId = parentId;
	window[parentId] = this; // This provides a global reference to the object based upon the trayTableId
   	YAHOO.util.Event.onContentReady(this.parentId, this.initTrays, this, true); 
}


idc.TrayDisplay.VERSION = "1.0";
idc.TrayDisplay.NAME = "idc.TrayDisplay";
idc.TrayDisplay.trayObs = new Array();


idc.TrayDisplay.prototype =
{
	allowMultipleOpenTrays: true,
	trayData: new Array(),
	headerClosedClass: null,
	headerClosedHoverClass: null,
	headerOpenClass: null,
	headerOpenHoverClass: null,
	bufferCellClass: null,
	trayCount: 0,
	openTrayCount: 0,
	boxHeightAdjust: 0,

	/**
	 * Defines a tray for which HTML table code will be generated
	 * @param {String} headerLabel The string to be displayed in the tray's header cell.
	 * @param {String} baseId String used as a prefix in element IDs generated by makeTray() method.
	 * @param {String} content (optional) Text/HTML code to be displayed in the content area of the tray.
	 */
	addTray: function(headerLabel, baseId, content)
	{
		// Tray data is initially stored to be used by the 'make' functions during onload initilization
		var trayDataObj = new Object();
		trayDataObj.headerLabel = headerLabel;
		trayDataObj.baseId = baseId;
		trayDataObj.content = content;
		this.trayData[this.trayData.length] = trayDataObj;
	},

	initTrays: function()
	{
		this.parentContainer = document.getElementById(this.parentId);
		if (isUndefinedOrNull(this.parentContainer))
		{
			setTimeout(idc.bind(this.initTrays, this), 10);
			return;
		}
		
		this.trayTable = this.parentContainer.getElementsByTagName("TABLE")[0];
		this.containerHeight = parseInt(this.parentContainer.offsetHeight);

		// This section is optionally used to create the tray table elements in the DOM. This is used when
		// the addTray() method is used in place of putting the markup in the document.
		if (typeof this.trayTable == "undefined" || this.trayTable == null)
		{
			this.makeTable();
		}
		if (this.trayData.length > 0)
		{
			for (var i=0; i<this.trayData.length; i++)
			{
				this.makeTray(this.trayData[i]);
			}
			this.makeBuffer();
		}

		var tableRows = this.trayTable.rows;
		var traysArray = idc.TrayDisplay.trayObs;

		// Create Tray objects from existing table elememts and store pointers in the trayObs array
		for (var i=0; i<tableRows.length; i++)
		{
			var thisCell = tableRows[i].cells[0];
			if (thisCell.nodeName == "TH")
			{
				traysArray[traysArray.length] = new idc.Tray(this, tableRows[i]);
				this.customTrayInit(traysArray[traysArray.length]);
			}
		}

		// Calculate reduction of available height in parent due to space taken by border or padding
		if (this.trayTable.parentNode.currentStyle)
		{
			this.boxHeightAdjust += parseInt(this.parentContainer.currentStyle.borderTopWidth);
			this.boxHeightAdjust += parseInt(this.parentContainer.currentStyle.borderBottomWidth);
			this.boxHeightAdjust += parseInt(this.parentContainer.currentStyle.paddingTop);
			this.boxHeightAdjust += parseInt(this.parentContainer.currentStyle.paddingBottom);
		}
		else if (document.defaultView && document.defaultView.getComputedStyle)
		{
			this.boxHeightAdjust += parseInt(document.defaultView.getComputedStyle(this.parentContainer, "").getPropertyValue("border-top-width"));
			this.boxHeightAdjust += parseInt(document.defaultView.getComputedStyle(this.parentContainer, "").getPropertyValue("border-bottom-width"));
			this.boxHeightAdjust += parseInt(document.defaultView.getComputedStyle(this.parentContainer, "").getPropertyValue("padding-top"));
			this.boxHeightAdjust += parseInt(document.defaultView.getComputedStyle(this.parentContainer, "").getPropertyValue("padding-bottom"));
		}

		// Initialize tray table style properties
		this.trayTable.style.height = (this.containerHeight - this.boxHeightAdjust) + "px";
		this.trayTable.style.width = "100%";
		this.trayTable.style.verticalAlign = "top";

		// Last row in table must always be the buffer that takes up space when all trays are closed
		this.bufferRow = tableRows[tableRows.length-1];
		this.bufferCell = this.bufferRow.cells[0];
		this.bufferCell.className = this.bufferCellClass;
		this.bufferCell.style.height = "auto";
		this.bufferCell.style.lineHeight = "1px"; // Keeps the nbsp; from forcing the buffer cell larger
		this.bufferCell.style.fontSize = "1px"; // The same, but for Safari

		// Set resize handler to update display when window size changes (doesn't work with Safari)
		var parentId = this.parentId;
		if (window.addEventListener)
		{
			window.addEventListener("resize", function() { window[parentId].resize(); }, false);
		}
		else if (window.attachEvent)
		{
			window.attachEvent("onresize", function() { window[parentId].resize(); });
		}

		this.customFinishInitTrays();
	},

	/**
	 * Hook to allow custom code to be inserted at the end of the initTrays() method.
	 */
	customFinishInitTrays: function() {  },

	/**
	 * Hook to allow customization of methods/properties for tray instances.
	 */
	customTrayInit: function(trayOb) {  },

	updateDisplay: function()
	{
		// Update tray table height setting
		this.containerHeight = parseInt(this.parentContainer.offsetHeight);
		this.trayTable.style.height = (this.containerHeight - this.boxHeightAdjust) + "px";

		// Calculate values used to define content cell heights
		if (this.openTrayCount > 0)
		{
			var totalHeight = parseInt(this.trayTable.style.height);
			var headerHeight = parseInt(this.trayTable.getElementsByTagName("TH")[0].offsetHeight);
			var availHeight = totalHeight - (this.trayCount * headerHeight) - 1; // Extra 1 pixel is for min buffer cell height
			var trayContentHeight = Math.floor(availHeight / this.openTrayCount) - 1;
			var availHeightRemainder = availHeight % this.openTrayCount;
		}

		// Configure height and display properties of tray row elements
		var remainderAlreadyAdded = false;
		var trays = idc.TrayDisplay.trayObs;
		for (var i=0; i<trays.length; i++)
		{
			if (trays[i].trayDisplayOb == this)
			{
				if (trays[i].isOpen)
				{
					if (remainderAlreadyAdded)
					{
						trays[i].contentCell.style.height = trayContentHeight + "px";
					}
					else
					{
						trays[i].contentCell.style.height = (trayContentHeight + availHeightRemainder) + "px";
						remainderAlreadyAdded = true;
					}

					trays[i].headerCell.className = this.headerOpenClass;
					trays[i].contentRow.style.display = "";
				}
				else
				{
					trays[i].headerCell.className = this.headerClosedClass;
					trays[i].contentRow.style.display = "none";
				}
			}
		}

		// Configure buffer cell
		this.bufferCell.style.height = (this.openTrayCount == 0) ? "auto" : "1px";
	},

	makeTable: function()
	{
		var tableElmt = document.createElement("TABLE");
		tableElmt.setAttribute("border", "0");
		tableElmt.setAttribute("cellSpacing", "0");
		tableElmt.setAttribute("cellPadding", "0");
		tableElmt.style.height = "100%";
		tableElmt.style.width = "100%";
		this.parentContainer.appendChild(tableElmt);
		this.trayTable = tableElmt;
	},

	makeTray: function(trayData)
	{
		if (document.getElementById(trayData.baseId + "_headerCell") == null)
		{
			var headerRow = this.trayTable.insertRow(this.trayTable.rows.length);
			var headerCell = document.createElement("TH");
			headerCell.id = trayData.baseId + "_headerCell";
			var headerLabel = document.createTextNode(trayData.headerLabel);
			headerCell.appendChild(headerLabel);
			headerRow.appendChild(headerCell);
			var contentRow = this.trayTable.insertRow(this.trayTable.rows.length);
			var contentCell = contentRow.insertCell(contentRow.cells.length);
			contentCell.id = trayData.baseId + "_contentCell";
			var scrollBox = document.createElement("DIV");
			scrollBox.id = trayData.baseId + "_scrollBox";
			contentCell.appendChild(scrollBox);
			contentRow.style.display = "none";
			if (typeof trayData.content != "undefined")
			{
				scrollBox.innerHTML = trayData.content;
			}
		}
	},

	makeBuffer: function()
	{
		if (document.getElementById(this.parentId + "_bufferCell") == null)
		{
			var bufferRow = this.trayTable.insertRow(this.trayTable.rows.length);
			var bufferCell = bufferRow.insertCell(bufferRow.cells.length);
			bufferCell.id = this.parentId + "_bufferCell";
		}
	},

	resize: function()
	{ // TO DO: fix or bypass for Safari
		this.trayTable.style.display = "none";
		var openTrays = new Array();

		var trays = idc.TrayDisplay.trayObs;
		for (var i=0; i<trays.length; i++)
		{
			if (trays[i].trayDisplayOb == this)
			{
				if (trays[i].isOpen)
				{
					openTrays[openTrays.length] = trays[i];
					trays[i].isOpen = false;
					this.openTrayCount--;
				}
			}
		}

		this.updateDisplay();
		this.trayTable.style.display = "";

		for (var i=0; i<openTrays.length; i++)
		{
			openTrays[i].isOpen = true;
			this.openTrayCount++;
		}

		this.updateDisplay();
	}
}

idc.TrayDisplay.getTrayByHeaderCell = function(headerCell)
{
	var trays = idc.TrayDisplay.trayObs;
	for (var i=0; i<trays.length; i++)
	{
		if (trays[i].headerCell == headerCell)
		{
			return trays[i];
		}
	}
}


idc.Tray = function (trayDisplayOb, tableRow)
{
	this.trayIndex = tableRow.rowIndex/2;

	// Set object pointers
	this.trayDisplayOb = trayDisplayOb;
	this.headerRow = tableRow;
	this.headerCell = this.headerRow.cells[0];
	this.contentRow = trayDisplayOb.trayTable.rows[this.headerRow.rowIndex+1];
	this.contentCell = this.contentRow.cells[0];
	this.scrollBox = this.contentCell.getElementsByTagName("DIV")[0];

	// Set element classes and attributes to display tray in closed state initially
	this.headerCell.className = this.trayDisplayOb.headerClosedClass;

	this.contentRow.style.display = "none";

	// Set persistent attributes
	this.scrollBox.style.height = "100%";
	this.scrollBox.style.width = "100%";
	this.scrollBox.style.overflow = "auto";

	// Set IDs on cell elements if not already provided
	if (this.headerCell.id == "")
	{
		this.headerCell.id = this.trayDisplayOb.parentId + "_headerCell_" + this.trayIndex;
	}
	if (this.contentCell.id == "")
	{
		this.contentCell.id = this.trayDisplayOb.parentId + "_contentCell_" + this.trayIndex;
	}
	if (this.scrollBox.id == "")
	{
		this.scrollBox.id = this.trayDisplayOb.parentId + "_scrollBox_" + this.trayIndex;
	}

	// Set event handlers
	this.headerCell.onclick = function(e) { idc.TrayDisplay.getTrayByHeaderCell(this).toggleDisplay(e); };
	// Commented out hover effect; implementation needs work to prevent flicker.
	//this.headerCell.onmouseover = function() { idc.TrayDisplay.getTrayByHeaderCell(this).hoverOn(); };
	//this.headerCell.onmouseout = function() { idc.TrayDisplay.getTrayByHeaderCell(this).hoverOff(); };

	this.isOpen = false;
	this.trayDisplayOb.trayCount++;
}


idc.Tray.prototype =
{
	hoverOn: function()
	{
		this.customHoverOnCode();

		if (this.isOpen)
		{
			if (this.trayDisplayOb.headerOpenHoverClass != null)
			{
				this.headerCell.className = this.trayDisplayOb.headerOpenHoverClass;
			}
		}
		else
		{
			if (this.trayDisplayOb.headerClosedHoverClass != null)
			{
				this.headerCell.className = this.trayDisplayOb.headerClosedHoverClass;
			}
		}
		this.headerCell.style.cursor = "pointer";
	},

	/**
	 * Hook to allow custom code to be inserted at end of hoverOn() method.
	 */
	customHoverOnCode: function() {  },

	hoverOff: function()
	{
		this.customHoverOffCode();

		if (this.isOpen)
		{
			if (this.trayDisplayOb.headerOpenHoverClass != null)
			{
				this.headerCell.className = this.trayDisplayOb.headerOpenClass;
			}
		}
		else
		{
			if (this.trayDisplayOb.headerClosedHoverClass != null)
			{
				this.headerCell.className = this.trayDisplayOb.headerClosedClass;
			}
		}
		this.headerCell.style.cursor = "default";
	},

	/**
	 * Hook to allow custom code to be inserted at end of hoverOff() method.
	 */
	customHoverOffCode: function() {  },

	toggleDisplay: function(e)
	{
		YAHOO.log("TrayEvent " + arguments[0]);
		if (!this.customToggleCode(e))
		{
			return;
		}

		if (this.isOpen)
		{
			this.isOpen = false;
			this.trayDisplayOb.openTrayCount--;
		}
		else
		{
			if (!this.trayDisplayOb.allowMultipleOpenTrays)
			{
				var trays = idc.TrayDisplay.trayObs;
				for (var i=0; i<trays.length; i++)
				{
					var tray = trays[i];
					if (tray.trayDisplayOb == this.trayDisplayOb)
					{
						if (tray.isOpen)
						{
							tray.isOpen = false;
							this.trayDisplayOb.openTrayCount--;
						}
					}
				}
			}

			this.isOpen = true;
			this.trayDisplayOb.openTrayCount++;
		}

		this.trayDisplayOb.updateDisplay();
	},

	/**
	 * Hook to allow custom code to be inserted at end of toggleDisplay() method.
	 */
	customToggleCode: function() {  }
}
/*
 * Confidential and Proprietary for Oracle Corporation
 *
 * This computer program contains valuable, confidential, and
 * proprietary information.  Disclosure, use, or reproduction
 * without the written authorization of Oracle is prohibited.
 * This unpublished work by Oracle is protected by the laws
 * of the United States and other countries.  If publication
 * of this computer program should occur, the following notice
 * shall apply:
 *
 * Copyright (c) 2006-2007 Stellent, Inc.
 * All rights reserved.
 * Copyright (c) 2007-2009 Oracle Corp.
 * All rights reserved.
 *
 * $Id: table_cell_sizing.js 69585 2009-01-06 21:33:22Z pwalters $
 */

function sizeElmtToParent(elmtId)
{
	idc.log.trace("folios", elmtId);
  var elmt = document.getElementById(elmtId);
  parentElmt = elmt.parentNode;

  while (parentElmt.nodeName != "DIV" && parentElmt.nodeName != "TD" && parentElmt.nodeName != "BODY")
	parentElmt = parentElmt.parentNode;

  if (parentElmt.offsetHeight)
  {
	var parentHeight = parentElmt.offsetHeight;
  }
  else if (document.defaultView && document.defaultView.getComputedStyle)
  {
	var parentHeight = parseInt(document.defaultView.getComputedStyle(parentElmt, null).getPropertyValue("height"));
  }

  elmt.style.height = parentHeight;
}


function computeElmtDimension(elmtId, dimensionAxis)
{
  var elmt = document.getElementById(elmtId);
  if (dimensionAxis.toLowerCase() == "x") var dimensionPropertyName = "width";
  else if (dimensionAxis.toLowerCase() == "y") var dimensionPropertyName = "height";
  else alert("computeElmtDimension - Unknown parameter value: dimensionAxis = " + dimensionAxis);

  if (document.defaultView && document.defaultView.getComputedStyle)
	var dimensionValue = parseInt(document.defaultView.getComputedStyle(elmt, null).getPropertyValue(dimensionPropertyName));
  else
	var dimensionValue = elmt.offsetHeight;

  return dimensionValue;
}


function dragBarColumnResize(event)
{
  document.body.style.cursor = "e-resize";

  /* Declare object variables */
  var leftCol = document.getElementById("leftNavCell");
  var rightCol = document.getElementById("contentCell");
  var dragBar = document.getElementById("dragBar");
  var frameDividerCell = dragBar.parentNode;
  var pageMask = document.getElementById("pageMask");
  var frameParentCell = pageMask.parentNode;

  /* Make the drag bar object visible, and set its size and position to match those of
	the frame divider cell. */
  dragBar.style.display = "block";
  var frameDividerCellInfo = new dimensionFinder(frameDividerCell);
  dragBar.style.height = frameDividerCellInfo.height;
  dragBar.style.width = frameDividerCellInfo.width;
  dragBar.style.top = frameDividerCellInfo.relativeTop;
  dragBar.style.left = frameDividerCellInfo.relativeLeft;

  /* Place a mask over the page area to prevent iframe documents from taking control
	of events away from the top-level document. */
  pageMask.style.display = "block";
  var frameParentCellInfo = new dimensionFinder(frameParentCell);
  pageMask.style.height = frameParentCellInfo.height;
  pageMask.style.width = frameParentCellInfo.width;
  pageMask.style.top = frameParentCellInfo.relativeTop;
  pageMask.style.left = frameParentCellInfo.relativeLeft;

  // Set the starting X-position of the mouse.
  var startPosX = event.clientX;

  // Register the event handlers that will respond to the mousemove events
  // and the mouseup event that follow this mousedown event.
  if (document.addEventListener)
  { // DOM Level 2 Event Model
	// (Note: I left the DOM branching in case IE supports these standards in the future).
	document.addEventListener("mousemove", moveHandler, true);
	document.addEventListener("mouseup", upHandler, true);
  }
  else if (document.attachEvent)
  { // IE 5+ Event Model
	document.attachEvent("onmousemove", moveHandler);
	document.attachEvent("onmouseup", upHandler);
  }

  // We've handled this event.  Don't let anybody else see it.
  if (event.stopPropagation) event.stopPropagation();   // DOM Level 2
  else event.cancelBubble = true;                       // IE

  // Now prevent any default action.
  if (event.preventDefault) event.preventDefault();     // DOM Level 2
  else event.returnValue = false;                       // IE

  /**
   * This is the handler that captures mousemove events when an element
   * is being dragged.  It is responsible for moving the element.
   **/
  function moveHandler(e)
  {
	if (!e) e = window.event;  // IE event model

	var startDragBarPosX = dragBar.offsetLeft;
	var maxDragPosX = document.body.clientWidth - 100;

	// Set new dragBar position based upon cursor location (conditionals limit the max an min drag positions).
	if (startDragBarPosX - (startPosX - e.clientX) > 25 && startDragBarPosX - (startPosX - e.clientX) < maxDragPosX)
	{
	var newDragBarPosX = startDragBarPosX - (startPosX - e.clientX);
	dragBar.style.left = newDragBarPosX + "px";
	}

	// Reset starting mouse position.
	startPosX = e.clientX;

	// And don't let anyone else see this event.
	if (e.stopPropagation) e.stopPropagation();       // DOM Level 2
	else e.cancelBubble = true;                       // IE
  }

  /**
   * This is the handler that captures the final mouseup event that
   * occurs at the end of a drag.
   **/
  function upHandler(e)
  {
	if (!e) e = window.event;  // IE event model

	/* Resize the left table cell based upon the final position of the dragBar. The right cell
	(content area) should size automatically to take the remaining space in the window. */
	var dragBarEndPosX = dragBar.offsetLeft;
	leftCol.style.width = (dragBarEndPosX) + "px";

	// Unregister the capturing event handlers.
	if (document.removeEventListener)
	{ // DOM Event Model
		document.removeEventListener("mouseup", upHandler, true);
		document.removeEventListener("mousemove", moveHandler, true);
	}
	else if (document.detachEvent)
	{ // IE 5+ Event Model
		document.detachEvent("onmouseup", upHandler);
		document.detachEvent("onmousemove", moveHandler);
	}

	/* Hide the IE mask. */
	pageMask.style.display = "none";

	/* Hide the drag bar object. */
	dragBar.style.display = "none";

	document.body.style.cursor = "auto";

	// And don't let the event propagate any further.
	if (e.stopPropagation) e.stopPropagation();       // DOM Level 2
	else e.cancelBubble = true;                       // IE
  }
}

