/**
 * Javascript Adaptive Display Library
 *
 * These functions support two different types of Adaptive Display. The "standard"
 * Adaptive Display which uses the ADObject and methods and the "plus-minus" type
 * of adaptive display in which table rows are progressively shown or hidden
 * using DOM function calls.
 *
 * Copyright 2003-2006 CollegeNET, Inc. All rights reserved.
 *
 * @author  Jim Maskus
 * @version 1.0
 */


/**
 * Adaptive Display Object Constructor
 * 
 */
function ADObject(name) {
	this.name = (name) ? name : ""		// the name of the adaptive display object
	this.id = ""						// the adaptive display id attribute
	this.property = ""					// the display style propery (ie block, table-row, table-row-group)
	this.stateElement = ""				// the state variable name
	this.defaultState = 'N'				// the default adaptive display state (usually hidden 'N')
	this.rule = ""						// the rule to evaluate
	this.parentObj = ""					// ** deprecated **
	this.childNames = ""				// ** deprecated **
}


/*******************************************************************************
 *  Global Variables 
 ******************************************************************************/

var isMac = (navigator.userAgent.toLowerCase().indexOf("mac")!=-1);
var isSafari = (navigator.userAgent.toLowerCase().indexOf('safari')!=-1);
var isNested = false;				// flag nested content unsupported in mac ie5


/*******************************************************************************
 *  Object Methods 
 ******************************************************************************/

/**
 * Method handleEvent()
 * 
 * This is an event handler function that is called by the adaptive display
 * "triggering" element. The context adaptive display objects rule is evaluated
 * and the content block is shown or hidden accordingly.
 */
ADObject.prototype.handleEvent = function() {
	if (isMac && document.all && isNested) return;
	
	if (eval(this.rule)==true) {
		this.showContent();
	} else {
		this.hideContent();
	}
}

/**
 * Method showContent()
 * 
 * Shows a display block by setting its style display property to the given
 * property. If a state element is specified sets its value to 'Y'.
 */
ADObject.prototype.showContent = function() {
	
	var property = (document.all) ? "inline" : this.property;
	
	// Show the adaptive display block
  if (document.getElementById(this.id)) {
  	document.getElementById(this.id).style.display = property;
  }
	
	if (this.stateElement!="") {
		document.forms[0].elements[this.stateElement].value = 'Y';
	}
}

/**
 * Method hideContent()
 * 
 * Hides a display block by setting its style display property to none. If a
 * state element is specified sets its value to 'N'.
 */
ADObject.prototype.hideContent = function() {
	
	// Hide the adaptive display block
  if (document.getElementById(this.id)) {
  	document.getElementById(this.id).style.display = "none";
  }
	
	if (this.stateElement!="") {
		document.forms[0].elements[this.stateElement].value = 'N';
	}
}

/**
 * Method toString()
 * 
 * Returns the object name or object type as a string.
 * 
 * @return object name
 */
ADObject.prototype.toString = function() {
	
	var retVal = (this.name) ? this.name : 'ADObject';
	
	return retVal;
}


/**
 *  Submit Functions
 *
 */
function adSubmitHandler() {
	try {
		
		// On submit clear any data entries in blocks whose rule is false (block is hidden)
		for (var i=0; i<ADArray.length; i++) {
			var adObject = ADArray[i];
			
			// If the rule is false clear all fields within the adaptive display block
			if (eval(adObject.rule)==false) {
				clearFormElements(adObject.id);
				
				// Reset the adaptive display state element
				document.forms[0].elements[adObject.stateElement].value = adObject.defaultState;
			}
		}
		return true
		
	} catch(ex) {
	  throw new Error("adSubmitHandler "+ex);
	}

}

/**
 * Plus-Minus Functions
 *
 * These functions are used to support the plus-minus type of adaptive display.
 */
 
/**
 * Function plus_minus_load
 *
 * NOTE: Once a page is loaded the last visible blocks display property has been
 * set to visible. Next we need to set the blocks button(s) to visible. 
 */
function plus_minus_load(tagName) {
	if (! tagName) { tagName = 'tr'; }
	
	try {
		for (var key in AD_HashMap) {
		
			// parentNode should be a table or tbody with matching id
			var parentNode = document.getElementById(key);
			
			// collect all of the child table rows
			var rowNode = parentNode.getElementsByTagName(tagName);    	
			
			// show the buttons (+ -) in the last visible row
			for (var i=rowNode.length-1; i>=0; i--) {
				var displayProperty = (document.all) ? rowNode[i].style.display : rowNode[i].style.getPropertyValue('display');

				if (displayProperty!='none') {
					rowNode[i].getElementsByTagName('div')[0].style.display = 'block';
					break;
				}
			}
		}
	
	} catch(ex) {
	  throw new Error("plus_minus_load "+ex);
	}
}

function plus_minus_submit(tagName) {
	if (! tagName) { tagName = 'tr'; }
	
	try {
		for (var key in AD_HashMap) {
			
			// parentNode should be a table or tbody with matching id
			var parentNode = document.getElementById(key);
			
			// collect all of the child table rows
			var rowNode = parentNode.getElementsByTagName(tagName);    	
			
			// clear fields in rows with display property of 'none'
			for (var i=rowNode.length-1; i>0; i--) {
				var display = (document.all) ? rowNode[i].style.display : rowNode[i].style.getPropertyValue('display');
				
				if (display=='none') {
					clearFormElements(rowNode[i].id);
				} else {
					break;
				}
			}
		}
		
		return true;
	
	} catch(ex) {
	  throw new Error("plus_minus_submit "+ex);
	}
}

/**
 * DOM Functions
 *
 * These functions are used to support the plus-minus type of adaptive display.
 * Generally we are toggling the the display state of a table whose rows are 
 * progressively shown or hidden.
 */
var ELEMENT_NODE = 1;

function adShowBlockByTagName(eventObj) {
	var propertyMap = { 
	    'div'   : 'block',
	    'table' : 'table',
	    'tr'    : 'table-row'
	}

	try {
		var tagName = (arguments.length==2) ? arguments[1] : 'tr';
		var containerTag = 'div';	    

		// get the event objects parent by tagName
		var parentNode = getParentByTagName(eventObj,tagName);
		
		// get the next sibling by tagName
		var nextSibling = getNextSiblingByTagName(parentNode,tagName);
		
		// show the next sibling block
		nextSibling.style.display = (document.all) ? 'inline' : propertyMap[tagName];
				
		// show the next blocks (+ -) buttons parent container is a div
		nextSibling.getElementsByTagName(containerTag)[0].style.display = 'block';
		
		// hide the previous block buttons (+ -)
		parentNode.getElementsByTagName(containerTag)[0].style.display = 'none';
		
		incrementStateValue(parentNode);
		
	} catch(ex) {
	  throw new Error("adShowBlockByTagName "+ex);
	}
}

function adHideBlockByTagName(eventObj) {		
	try {
		var tagName = (arguments.length==2) ? arguments[1] : 'tr';
		var containerTag = 'div';
		
		// get the event objects parent by tagName
		var parentNode = getParentByTagName(eventObj,tagName);
		
		// get the previous sibling by tagName
		var previousSibling = getPreviousSiblingByTagName(parentNode,tagName);
		
		// hide the current block
		parentNode.style.display = 'none';
		
		// show the previous blocks (+ -) buttons parent container is a div
		previousSibling.getElementsByTagName(containerTag)[0].style.display = 'block';
		
		decrementStateValue(previousSibling);
		
	} catch(ex) {
	  throw new Error("adHideBlockByTagName "+ex);
	}
}

function incrementStateValue(node) {
	try {
		var id = node.parentNode.id;
				
		// get the state element (name is mapped by id)
		var stateElement = document.forms[0].elements[AD_HashMap[id]];
		
		// inc the state element value
		var value = Number(stateElement.value) + 1;
		stateElement.value = value.toString();
		
	} catch(ex) {
	  throw new Error("incrementStateValue "+ex);
	}
}

function decrementStateValue(node) {
	try {
		var id = node.parentNode.id;
				
		// get the state element (name is mapped by id)
		var stateElement = document.forms[0].elements[AD_HashMap[id]];
		
		// dec the state element value
		var value = Number(stateElement.value) - 1;
		stateElement.value = value.toString();
		
	} catch(ex) {
	  throw new Error("incrementStateValue "+ex);
	}
}

function getParentByTagName(node,tagName) {
	try {
		var node = node.parentNode;
		while (node.tagName.toLowerCase()!=tagName) { node = node.parentNode; }
		
		return node;
		
	} catch (ex) {
		throw new Error(ex);
	}
}

function getPreviousSiblingByTagName(node,tagName) {
	try {
		var node = node.previousSibling;
		while (node.nodeType!=ELEMENT_NODE || node.tagName.toLowerCase()!=tagName) { node = node.previousSibling; }
		
		return node;
		
	} catch (ex) {
		throw new Error(ex);
	}
}

function getNextSiblingByTagName(node,tagName) {
	try {
		var node = node.nextSibling
		while (node.nodeType!=ELEMENT_NODE || node.tagName.toLowerCase()!=tagName) { node = node.nextSibling; }
		
		return node;
		
	} catch (ex) {
		throw new Error(ex);
	}
}

/**
 * Utility Functions
 *
 * These functions are shared by both the Adaptive Display object and the 
 * plus-minus adaptive display submit handler functions.
 */
function clearFormElements(id) {
	// Use id to get the container element of the adaptive display block
	var node = (document.getElementById) ? document.getElementById(id) : null;
	
	// Clear all form elements that are children of the block element
	if (node!=null) {
		var inputNodeset = node.getElementsByTagName('input');
		if (inputNodeset) {
			clearInputElement(inputNodeset);
		}
		
		var selectNodeset = node.getElementsByTagName('select');
		for (var i=0; i<selectNodeset.length; i++) {
			selectNodeset[i].selectedIndex = 0;
		}
		
		var textAreaNodeset = node.getElementsByTagName('textarea');
		for (var i=0; i<textAreaNodeset.length; i++) {
			textAreaNodeset[i].value = " ";		// use space due to Nav 6.x bug
		}
	}
}

function clearInputElement(inputNodeset) {
	for (var i=0; i<inputNodeset.length; i++) {
		var inputNode = inputNodeset[i];
		var type = inputNode.type.toLowerCase();
		
		switch(type) {
			case "text":
			case "hidden":
				inputNode.value = "";
				break;
			
			case "checkbox":
				inputNode.checked = false;
				break;
			
			case "radio":
				inputNode.checked = false;
				
				// Look ahead to see if this node is the last radio element in the set
				var lastRadio = false;
				if (i<inputNodeset.length-1) {
					var nextNode = inputNodeset[i+1];
					if (inputNode.name!=nextNode.name || type!=nextNode.type) { lastRadio = true; }
				}
				else lastRadio = true;
				
				// Force applyweb to clear the radio element by inserting a hidden DOM element
				if (lastRadio==true) {
					// Create a DOM input element and set its attributes
					var domNode = document.createElement('input');
					domNode.setAttribute('name',inputNode.name);
					if (! (isMac && document.all)) { domNode.setAttribute('type','hidden'); }
					domNode.setAttribute('value',"");
					
					// Get the radio elements parent node
					var parentNode = inputNode.parentNode;
					
					// If the parentNode is a <nobr> tag then get nobr's parent element
					if (parentNode.tagName.toLowerCase()=='nobr') { parentNode = parentNode.parentNode; }
					
					// Insert our node after the radio element
					if (parentNode) { parentNode.appendChild(domNode); }
				}
				break;
		}
	}	
}

