/* InspectIE.js
 * Class for inspecting and debugging javascript and the DOM in Internet Explorer.
 * Also works in Mozilla, of course.
 *
 * To use, add <link rel="stylesheet" type="text/css" href="inspectIE.css"> to your head section.
 * Add <script language="javascript" src="inspectIE.js"></script> BEFORE any other <script>s
 * Add InspectIE.init() to your body onLoad statement after any other JS statements.
 *
 * To track a variable, insert the following function call in your code:
 *		InspectIE.track(identifier, trackedVariable);
 * where identifier is a string identifying the variable to you, and trackedVariable is the variable
 * to be tracked.  The variable's contents will be output in a panel when the page loads.
 *
 * @author john@REMOVEME.novomancy.org
 * @version 1.02.20040824
 */

//In order to keep the namespace clear, InspectIE contains all of the support code necessary
//to run the inspection panel.
InspectIE = new Object();
InspectIE.trackedVariables = new Array();			//Stores the state of all tracked vars
InspectIE.DOMToMenu = new Object(); 				//SubObject that translates the DOM tree to a menu
InspectIE.arrayToMenu = new Object(); 				//SubObject that translates an array to a menu

/** Public Functions **/

/* InspectIE.init
 * Initializes the DOM Inspector Panel
 */
InspectIE.init = function(){
	var windowArray = {titleBar: "DOM Inspector", contents: "DOM"};
	var windowPanel = InspectIE.newWindow(windowArray)
	windowPanel.parentNode.id = "InspectIE";
	InspectIE.topWindow = windowPanel.parentNode;
	InspectIE.DOMToMenu.generateDOM(windowPanel);
	for(var key in InspectIE.trackedVariables) InspectIE.showTracked(key);
}

/* InspectIE.track
 * Track the passed variable
 *
 * @argument	identifier		string		identifies the variable being tracked
 * @argument	trackedVariable	var			variable (inc. array) to be tracked
 */
InspectIE.track = function(identifier, trackedVariable){
	if(!InspectIE.defined(InspectIE.trackedVariables[identifier])) InspectIE.trackedVariables[identifier] = new Array();
	InspectIE.trackedVariables[identifier].push(trackedVariable);
}

/** Private Methods **/

/* InspectIE.DOMToMenu.generateDOM
 * Appends a heirarchal menu-style DOM tree to the passed container element
 *
 * @argument	container		DOM Element	container of the DOM tree to be created
 */ 
InspectIE.DOMToMenu.generateDOM = function(container){
	container.innerHTML = "";
	var DOMArray = new Array();
	InspectIE.DOMToMenu.walkDOM(document.body, DOMArray);
	container.appendChild(InspectIE.DOMToMenu.newMenu(DOMArray, container));
}

/* InspectIE.DOMToMenu.walkDOM
 * Recursively walks the DOM tree and creates an associative array from it
 *
 * @argument	inputNode		DOM Element	current level of the DOM tree
 * @argument	container		Array		current level of the output array
 *
 */
InspectIE.DOMToMenu.walkDOM = function(inputNode, container){
	if(String(inputNode.id).indexOf("InspectIE")==-1){
		if(inputNode.childNodes.length==0){
			var nodeArray = {labelText: inputNode.nodeName, finalNode: true};
			if(inputNode.nodeType=="1") nodeArray.contents = inputNode.id;
			else if (inputNode.nodeType=="3") nodeArray.contents = inputNode.data;
			nodeArray.nodeObject = inputNode;
			container.push(nodeArray);
		} else {
			var label = InspectIE.defined(inputNode.name) ? inputNode.name : inputNode.id;
			container.push({labelText: inputNode.nodeName + ": " + label, contents: new Array(), nodeObject: inputNode});
			var thisArray = container[container.length-1].contents;
			for(var i=0; i<inputNode.childNodes.length; i++){
				InspectIE.DOMToMenu.walkDOM(inputNode.childNodes[i], thisArray);
			}
		}
	}
}	

/* InspectIE.DOMToMenu.newMenu
 * Initializes a DOM tree menu created from a walkDOM array
 *
 * @argument	menuArray		Array		output array from walkDOM
 * @argument	topContainer	DOM Element	Container of the new menu
 */

InspectIE.DOMToMenu.newMenu = function(menuArray, topContainer){
	var menu = document.createElement("ul");
	menu.style.listStyleType = "none";
	InspectIE.DOMToMenu.renderArray(menuArray, menu, topContainer);
	return menu;
}

/* InspectIE.DOMToMenu.renderArray
 * Translates a walkDOM array to a set of nested lists (recursive)
 *
 * @argument	inputArray		Array		current level of the walkDOM array
 * @argument	container		DOM Element	current level of the nested list menu
 * @argument	topContainer	DOM Element	container for the entire menu
 */

InspectIE.DOMToMenu.renderArray = function(inputArray, container, topContainer){
	for(var key in inputArray){
		
		//Build buttons
		
		var blinkElement = document.createElement("span");
		blinkElement.onmouseup = InspectIE.blinkElement;
		blinkElement.onmouseover = InspectIE.showHelp;
		blinkElement.onmouseout = InspectIE.clearHelp;
		blinkElement.className = "InspectIEBlinkElement";
		blinkElement.innerHTML = "B";		

		var computedElement = document.createElement("span");
		computedElement.onmouseup = InspectIE.showComputed;
		computedElement.onmouseover = InspectIE.showHelp;
		computedElement.onmouseout = InspectIE.clearHelp;
		computedElement.className = "InspectIEComputedElement";
		computedElement.innerHTML = "C";

		var styleElement = document.createElement("span");
		styleElement.onmouseup = InspectIE.showStyle;
		styleElement.onmouseover = InspectIE.showHelp;
		styleElement.onmouseout = InspectIE.clearHelp;
		styleElement.className = "InspectIEStyleElement";
		styleElement.innerHTML = "S";
		
		var evalElement = document.createElement("span");
		evalElement.onmouseup = InspectIE.showEval;
		evalElement.onmouseover = InspectIE.showHelp;
		evalElement.onmouseout = InspectIE.clearHelp;
		evalElement.className = "InspectIEEvalElement";
		evalElement.innerHTML = "E";

		if(inputArray[key].nodeObject.nodeName=="DIV"){
			var javascriptElement = document.createElement("span");
			javascriptElement.onmouseup = InspectIE.showJS;
			javascriptElement.onmouseover = InspectIE.showHelp;
			javascriptElement.onmouseout = InspectIE.clearHelp;
			javascriptElement.className = "InspectIEJavascriptElement";
			javascriptElement.innerHTML = "J";
		}		
		
		if(inputArray[key].finalNode==true){
			//No children
			var dataElement = document.createElement("li");
			dataElement.style.cursor = "default";
			dataElement.style.position = "relative";
			dataElement.style.left = "-30px";
			dataElement.style.color = "black";
			
			if(inputArray[key].nodeObject.nodeName!="#text"){
				dataElement.appendChild(blinkElement);
				if(inputArray[key].nodeObject.nodeName=="DIV") dataElement.appendChild(javascriptElement);
				dataElement.appendChild(computedElement);
				dataElement.appendChild(styleElement);	
				dataElement.appendChild(evalElement);		
			}
			
			var outputElement = document.createElement("span");
			outputElement.className = "InspectIEOutputElement";
			outputElement.innerHTML = "&nbsp;<b>"+inputArray[key].labelText + ":</b>  "+ inputArray[key].contents;
			outputElement.onmouseover = InspectIE.showHelp;
			outputElement.onmouseout = InspectIE.clearHelp;
			outputElement.style.cursor="default";
			dataElement.appendChild(outputElement);
			container.appendChild(dataElement);
			dataElement.nodeObject = inputArray[key].nodeObject;
			dataElement.statusNode = topContainer.statusNode;
			if(InspectIE.browsers.ie) dataElement.style.cursor = "hand";
			else dataElement.style.cursor = "pointer";			
		} else {
			//Children
			var headerElement = document.createElement("li");
			headerElement.nodeObject = inputArray[key].nodeObject;
			container.appendChild(headerElement);
			headerElement.statusNode = topContainer.statusNode;
			
			headerElement.appendChild(blinkElement);
			if(inputArray[key].nodeObject.nodeName=="DIV") headerElement.appendChild(javascriptElement);
			headerElement.appendChild(computedElement);
			headerElement.appendChild(styleElement);
			headerElement.appendChild(evalElement);
			
			var labelElement = document.createElement("span");
			labelElement.onmouseup = InspectIE.DOMToMenu.toggleMenu;
			labelElement.onmouseover = InspectIE.showHelp;
			labelElement.onmouseout = InspectIE.clearHelp;
			labelElement.className = "InspectIELabelElement";
			labelElement.innerHTML = inputArray[key].labelText;
			headerElement.appendChild(labelElement);
						
			if(InspectIE.browsers.ie) headerElement.style.cursor = "hand";
			else headerElement.style.cursor = "pointer";
			headerElement.style.position = "relative";
			headerElement.style.left = "-30px";
			container.appendChild(headerElement);
			var menuElement = document.createElement("ul");
			menuElement.style.listStyleType="none";
			menuElement.style.display = "none";
			headerElement.appendChild(menuElement);
			InspectIE.DOMToMenu.renderArray(inputArray[key].contents, menuElement, topContainer);
		}
	}						
}

/* InspectIE.DOMToMenu.toggleMenu
 * Toggles the display of all children of this element in the DOM tree nested list menu
 */
InspectIE.DOMToMenu.toggleMenu = function(){
	var headerElement = this.parentNode;
	var menuElements = headerElement.getElementsByTagName("ul");
	for(var i = 0; i < menuElements.length; i++){
		if(menuElements[i].parentNode==headerElement){
			if(menuElements[i].style.display=="none")	menuElements[i].style.display="";
			else menuElements[i].style.display="none";
		}
	}
}

/* InspectIE.blinkElement
 * Blinks the background color of an element on the page.
 * Note that while there are no arguments for this function, it requires that parentNode.nodeObject
 * exists and points to the DOM element to be blinked.
 */
InspectIE.blinkElement = function(){
	if(InspectIE.blinkObject){
		clearTimeout(InspectIE.blinkTimeout);
		InspectIE.blinkOff();
	}
	InspectIE.blinkColor = InspectIE.browsers.calculatedStyle(this.parentNode.nodeObject, "backgroundColor");
	if(!InspectIE.blinkColor) InspectIE.blinkColor="transparent";
	InspectIE.blinkObject = this.parentNode.nodeObject;
	InspectIE.blinkTimer = 3;
	InspectIE.blinkTimeout = setTimeout(InspectIE.blinkOn, 100);
}

/* InpsectIE.blinkOn
 * Blinks an element
 * Used by InspectIE.blinkElement
 */
InspectIE.blinkOn = function(){
	InspectIE.blinkObject.style.backgroundColor = "rgb(255,0,0)";
	setTimeout(InspectIE.blinkOff, 200);
	InspectIE.blinkTimer--;
	if(InspectIE.blinkTimer > 0) InspectIE.blinkTimeout = setTimeout(InspectIE.blinkOn, 400);
}

/* InpsectIE.blinkOff
 * Blinks an element
 * Used by InspectIE.blinkElement
 */
InspectIE.blinkOff = function(){
	InspectIE.blinkObject.style.backgroundColor = InspectIE.blinkColor;
	if(InspectIE.blinkTimer <= 0) delete InspectIE.blinkObject;
}

/* InspectIE.showTracked
 * Opens a new panel containing a menu with the values of InspectIE.trackedVariables[identifier]
 *
 * @argument	identifier		string		The identifier string used when defining a tracker
 *											using InspectIE.track
 */
InspectIE.showTracked = function(identifier){
	var windowArray = new Array();
	windowArray.nodeObject = InspectIE.trackedVariables[identifier];
	windowArray.contents = "tracker";
	windowArray.titleBar = "Tracking: " + identifier;
	var trackPanel = InspectIE.newWindow(windowArray);
	trackPanel.nodeObject = windowArray.nodeObject;
	InspectIE.refreshTracked(trackPanel);
}

/* InspectIE.refreshTracked
 * Refreshes content in a tracking panel
 *
 * @argument	trackPanel		DOM Element	Panel in which to refresh the content
 */
InspectIE.refreshTracked = function (trackPanel){
	trackPanel.innerHTML = "";
	trackPanel.appendChild(InspectIE.arrayToMenu.newMenu(trackPanel.nodeObject));	
}

/* InspectIE.showComputed
 * Opens a new panel containing a menu with the computed style of a DOM Element
 *
 * Note that while there are no arguments for this function, it requires that parentNode.nodeObject
 * exists and points to the DOM element to be inspected.
 */
InspectIE.showComputed = function(){
	var windowArray = new Array();
	windowArray.nodeObject = this.parentNode.nodeObject;
	windowArray.contents = "computed";
	windowArray.titleBar = windowArray.nodeObject.nodeName + ": " + windowArray.nodeObject.id + " Computed Style";
	var computedPanel = InspectIE.newWindow(windowArray);
	computedPanel.nodeObject = windowArray.nodeObject;
	InspectIE.refreshComputed(computedPanel);
}

/* InspectIE.refreshComputed
 * Refreshes content in a computed style panel
 *
 * @argument	computedPanel		DOM Element	Panel in which to refresh the content
 */
InspectIE.refreshComputed = function (computedPanel){
	computedPanel.innerHTML = "";
	var computedArray = new Array();
	var computedStyle = InspectIE.browsers.ie ? computedPanel.nodeObject.currentStyle : document.defaultView.getComputedStyle(computedPanel.nodeObject, "");
	for(var key in computedStyle){
		if(InspectIE.defined(computedStyle[key])!="object" && String(computedStyle[key])!=""){
			computedArray[key] = String(computedStyle[key]);
		}
	}

	if(InspectIE.browsers.ie){
		computedArray['offsetTop'] = String(computedPanel.nodeObject.offsetTop);
		computedArray['offsetLeft'] = String(computedPanel.nodeObject.offsetLeft);
		computedArray['offsetHeight'] = String(computedPanel.nodeObject.offsetHeight);
		computedArray['offsetWidth'] = String(computedPanel.nodeObject.offsetWidth);
	}	
	
	var orderedStyleKeys = new Array();
	for(var key in computedArray) orderedStyleKeys.push(key);
	orderedStyleKeys.sort();	
		
	var orderedArray = new Array;
	for(var i=0; i<orderedStyleKeys.length; i++) orderedArray[orderedStyleKeys[i]] = computedArray[orderedStyleKeys[i]];
	computedPanel.appendChild(InspectIE.arrayToMenu.newMenu(orderedArray));
}

/* InspectIE.showJS
 * Opens a new panel containing a menu with the Javascript object properties of a DOM Element
 *
 * Note that while there are no arguments for this function, it requires that parentNode.nodeObject
 * exists and points to the DOM element to be inspected.
 */
InspectIE.showJS = function(){
	var windowArray = new Array();
	windowArray.nodeObject = this.parentNode.nodeObject;
	windowArray.contents = "js";
	windowArray.titleBar = windowArray.nodeObject.nodeName + ": " + windowArray.nodeObject.id + " Javascript Object";
	var jsPanel = InspectIE.newWindow(windowArray);
	jsPanel.nodeObject = windowArray.nodeObject;
	InspectIE.refreshJS(jsPanel);
}

/* InspectIE.refreshJS
 * Refreshes content in a Javascript object panel
 *
 * @argument	jsPanel			DOM Element	Panel in which to refresh the content
 */
InspectIE.refreshJS = function(jsPanel){
	jsPanel.innerHTML = "";
	var jsArray = new Array();
	for(var key in jsPanel.nodeObject){
		if(jsPanel.nodeObject[key] != ""){
			var objectString = String(jsPanel.nodeObject[key]);
			if(objectString.indexOf("function") != -1){
				jsArray[key] = String(jsPanel.nodeObject[key]);
			} else 
			if (InspectIE.defined(jsPanel.nodeObject[key])!="object"){
				jsArray[key] = String(jsPanel.nodeObject[key]);
			}
		}
	}
	jsPanel.appendChild(InspectIE.arrayToMenu.newMenu(jsArray));
}

/* InspectIE.showStyle
 * Opens a new panel containing a menu with the Javascript style object of a DOM Element
 *
 * Note that while there are no arguments for this function, it requires that parentNode.nodeObject
 * exists and points to the DOM element to be inspected.
 */
InspectIE.showStyle = function(){
	var windowArray = new Array();
	windowArray.nodeObject = this.parentNode.nodeObject;
	windowArray.contents = "style";
	windowArray.titleBar = windowArray.nodeObject.nodeName + ": " + windowArray.nodeObject.id + " Javascript Style";
	var stylePanel = InspectIE.newWindow(windowArray);
	stylePanel.nodeObject = windowArray.nodeObject;
	InspectIE.refreshStyle(stylePanel);
}

/* InspectIE.refreshStyle
 * Refreshes content in a Javascript style object panel
 *
 * @argument	stylePanel		DOM Element	Panel in which to refresh the content
 */
InspectIE.refreshStyle = function(stylePanel){
	stylePanel.innerHTML = "";
	var styleArray = new Array();
	for(var key in stylePanel.nodeObject.style){
		if(InspectIE.defined(stylePanel.nodeObject.style[key])!="object" && stylePanel.nodeObject.style[key]!=""){
			styleArray[key] = stylePanel.nodeObject.style[key];
		}
	}
	stylePanel.appendChild(InspectIE.arrayToMenu.newMenu(styleArray));	
}

/* InspectIE.showEval
 * Opens a new panel containing an interactive Javascript evaluator for a DOM object
 *
 * Note that while there are no arguments for this function, it requires that parentNode.nodeObject
 * exists and points to the DOM element to be used as context for running scripts.
 */
InspectIE.showEval = function(){
	var windowArray = new Array();
	windowArray.nodeObject = this.parentNode.nodeObject;
	windowArray.contents = "evalForm";
	windowArray.titleBar = windowArray.nodeObject.nodeName + ": " + windowArray.nodeObject.id + " Javascript Evaluator";
	var evalPanel = InspectIE.newWindow(windowArray);
	evalPanel.nodeObject = windowArray.nodeObject;
	InspectIE.refreshEval(evalPanel);
}

/* InspectIE.refreshEval
 * Refreshes content in a Javascript evaluator panel
 *
 * @argument	evalPanel		DOM Element	Panel in which to refresh the content
 */
InspectIE.refreshEval = function(evalPanel){
	evalPanel.innerHTML = "";
	evalPanel.style.textAlign = "center";
	var helpText = document.createElement("div");
	evalPanel.appendChild(helpText);
	helpText.className = "InspectIEEvalText";
	helpText.innerHTML = "Enter a Javascript command to be evaluated.<br>";
	helpText.innerHTML += "Use '[this]' to refer to the DOM element. ex:<br>";
	helpText.innerHTML += "[this].style.backgroundColor = 'rgb(255,255,0)';";

	var formElement = document.createElement("input");
	formElement.type = "text";
	formElement.align = "left";
	formElement.size = "30";
	evalPanel.appendChild(formElement);
	
	var submitElement = document.createElement("span");
	submitElement.className = "InspectIESubmitElement";
	submitElement.onmouseup = InspectIE.evalCommand;
	submitElement.onmouseover = InspectIE.showHelp;
	submitElement.onmouseout = InspectIE.clearHelp;
	submitElement.innerHTML = "Evaluate Script";
	evalPanel.appendChild(submitElement);

	var spacerElement = document.createElement("div");
	spacerElement.innerHTML = "&nbsp;<br>Command History:";
	spacerElement.style.color = "black";
	evalPanel.appendChild(spacerElement);

	var logElement = document.createElement("textarea");
	logElement.readOnly = true;
	logElement.cols = "35";
	logElement.rows = "10";
	logElement.style.fontSize = "11px";
	logElement.style.backgroundColor = "rgb(200,200,255)";
	evalPanel.appendChild(logElement);
	
	if(InspectIE.browsers.ie) submitElement.style.cursor = "hand";
	else submitElement.style.cursor = "pointer";
	
	evalPanel.formNode = formElement;
	evalPanel.logNode = logElement;
}

/* InspectIE.evalCommand
 * Evaluates the command entered in a Javascript evaluator panel
 *
 * Note that while there are no arguments for this function, it requires that parentNode.nodeObject
 * exists and points to the DOM element to be used as context for running scripts and
 * parentNode.logNode exists and points to the log form element.
 */
InspectIE.evalCommand = function(){
	var targetElement = this.parentNode.nodeObject;
	var command = this.parentNode.formNode.value;
	var resolvedCommand = command.replace("[this]", "targetElement");
	eval(resolvedCommand);
	this.parentNode.logNode.value = command + this.parentNode.logNode.value;
}

/* InspectIE.refreshPanel
 * Generic function attached to the refresh button on all panels
 *
 * Note that while there are no arguments for this function, it requires that parentNode.contents
 * exists and defines the type of panel being refreshed.
 */
InspectIE.refreshPanel = function(){
	var windowHandle = this.parentNode;
	switch(windowHandle.contents){
		case "style":		InspectIE.refreshStyle(windowHandle.panelNode);					break;
		case "js":			InspectIE.refreshJS(windowHandle.panelNode);					break;
		case "computed":	InspectIE.refreshComputed(windowHandle.panelNode);				break;
		case "DOM":			InspectIE.DOMToMenu.generateDOM(windowHandle.panelNode);		break;
		case "tracker":		InspectIE.refreshTracked(windowHandle.panelNode);				break;
		case "evalForm":	InspectIE.refreshEval(windowHandle.panelNode);					break;
		default:			alert("error");
	}
}

/* InspectIE.newWindow
 * Creates a new empty panel
 *
 * @argument	windowArray		Array		Parameters for the new window.  This associative array
 *											must contain at least the following elements:
 *												titleBar	str			Name of the panel
 *												contents	str			Type of the panel's contents
 *												nodeObject	DOM Element	Target element for the panel
 */
InspectIE.newWindow = function(windowArray){
	var windowHandle = document.createElement("div");
	document.body.appendChild(windowHandle);
	windowHandle.id = "InspectIE" + Math.round(Math.random() * 100000, 6);
	windowHandle.className = "InspectIEHandle";
	windowHandle.innerHTML = "&nbsp;"+windowArray.titleBar;
	windowHandle.contents = windowArray.contents;
	InspectIE.makeDraggable(windowHandle);
	
	var windowStatus = document.createElement("div");
	windowHandle.appendChild(windowStatus);
	windowStatus.className = "InspectIEStatus";
	windowStatus.innerHTML = "&nbsp;";
	windowHandle.statusNode = windowStatus;
		
	var windowPanel = document.createElement("div");
	windowHandle.appendChild(windowPanel);
	windowPanel.className = "InspectIEPanel";
	windowPanel.onmousedown = InspectIE.browsers.stopBubbling;
	windowHandle.panelNode = windowPanel;
	windowPanel.statusNode = windowStatus;
	
	var closeButton = document.createElement("div");
	windowHandle.appendChild(closeButton);
	closeButton.className = "InspectIECloseButton";
	closeButton.innerHTML = "x";
	closeButton.onmouseup = InspectIE.closeWindow;
	closeButton.onmouseover = InspectIE.showHelp;
	closeButton.onmouseout = InspectIE.clearHelp;
	if(InspectIE.browsers.ie) closeButton.style.cursor = "hand";
	else closeButton.style.cursor = "pointer";
	
	var refreshButton = document.createElement("div");
	windowHandle.appendChild(refreshButton);
	refreshButton.className = "InspectIERefreshButton";
	refreshButton.innerHTML = "o";
	refreshButton.onmouseup = InspectIE.refreshPanel;
	refreshButton.onmouseover = InspectIE.showHelp;
	refreshButton.onmouseout = InspectIE.clearHelp;
	if(InspectIE.browsers.ie) refreshButton.style.cursor = "hand";
	else refreshButton.style.cursor = "pointer";
	
	var minButton = document.createElement("div");
	windowHandle.appendChild(minButton);
	minButton.className = "InspectIEMinButton";
	minButton.innerHTML = "-";
	minButton.onmouseup = InspectIE.minimizeWindow;
	minButton.onmouseover = InspectIE.showHelp;
	minButton.onmouseout = InspectIE.clearHelp;
	if(InspectIE.browsers.ie) minButton.style.cursor = "hand";
	else minButton.style.cursor = "pointer";
	
	var maxButton = document.createElement("div");
	windowHandle.appendChild(maxButton);
	maxButton.className = "InspectIEMaxButton";
	maxButton.innerHTML = "+";
	maxButton.onmouseup = InspectIE.maximizeWindow;
	maxButton.onmouseover = InspectIE.showHelp;
	maxButton.onmouseout = InspectIE.clearHelp;
	if(InspectIE.browsers.ie) maxButton.style.cursor = "hand";
	else maxButton.style.cursor = "pointer";
	
	if(InspectIE.topWindow) windowHandle.style.zIndex = parseInt(InspectIE.topWindow.style.zIndex) + 1;
	InspectIE.topWindow = windowHandle;
	
	return windowPanel;
}

/* InspectIE.showHelp
 * Generic function attached to the mouseover on all active elements.  Sets the help text.
 *
 * Note that while there are no arguments for this function, it requires that the className of the
 * element moused over specify the function of the element.
 */
InspectIE.showHelp = function(){
	var helpText = new String();
	switch(this.className){
		case "InspectIEMinButton":			helpText = "Rollup this window";					break;
		case "InspectIEMaxButton":			helpText = "Rolldown this window";					break;
		case "InspectIECloseButton":		helpText = "Close this window";						break;
		case "InspectIERefreshButton":		helpText = "Refresh the contents of this window";	break;
		case "InspectIEComputedElement":	helpText = "Show element's computed style";			break;
		case "InspectIEStyleElement":		helpText = "Show element's Javascript style array";	break;
		case "InspectIELabelElement":		helpText = "Show element's children";				break;
		case "InspectIEJavascriptElement":	helpText = "Show element's Javascript object";		break;
		case "InspectIEBlinkElement":		helpText = "Blink element";							break;
		case "InspectIEOutputElement":		helpText = "No Children!";							break;
		case "InspectIESubmitElement":		helpText = "Evaluate Javascript Statement";			break;
		case "InspectIEEvalElement":		helpText = "Evaluate Javascript in context of this Element";	break;
		default: helpText = "Don't know what to do";
	}
	this.parentNode.statusNode.innerHTML = helpText;
}

/* InspectIE.clearHelp
 * Generic function attached to the mouseout on all active elements.  Clears the help text.
 */
InspectIE.clearHelp = function(){
	this.parentNode.statusNode.innerHTML = "&nbsp;";
}

/* InspectIE.closeWindow
 * Generic function attached to the close button on all panels.  Closes the panel.
 */
InspectIE.closeWindow = function(){
	this.parentNode.parentNode.removeChild(this.parentNode);
}

/* InspectIE.minimizeWindow
 * Generic function attached to the minimize button on all panels.  Rolls up the panel.
 */
InspectIE.minimizeWindow = function(){
	this.parentNode.panelNode.style.display = "none";
	this.parentNode.statusNode.style.display = "none";
	this.parentNode.style.borderBottomWidth = "2px";
}

/* InspectIE.maximizeWindow
 * Generic function attached to the maximize button on all panels.  Rolls down the panel.
 */
InspectIE.maximizeWindow = function(){
	this.parentNode.panelNode.style.display = "block";
	this.parentNode.statusNode.style.display = "block";
	this.parentNode.style.borderBottomWidth = "0px";
}

/* InspectIE.arrayToMenu.newMenu
 * Initializes a new menu created from an x-dimensional array
 *
 * @argument	menuArray		Array		Array to be converted to a menu
 */
InspectIE.arrayToMenu.newMenu = function(menuArray){
	var menu = document.createElement("ul");
	menu.style.listStyleType = "none";
	InspectIE.arrayToMenu.renderArray(menuArray, menu);
	return menu;
}

/* InspectIE.arrayToMenu.renderArray
 * Renders an array to a set of nested lists
 *
 * @argument	inputArray		Array		Current level of the array (recursive)
 * @argument	container		DOM Element	Current level of the nested list menu
 */
InspectIE.arrayToMenu.renderArray = function(inputArray, container){
	for(key in inputArray){
		if(typeof(inputArray[key])=="string"){
			var dataLabel = document.createElement("li");
			dataLabel.style.cursor = "default";
			dataLabel.style.position = "relative";
			dataLabel.style.backgroundColor = "rgb(155,155,155)";
			dataLabel.style.left = "-30px";
			dataLabel.innerHTML = "<b>"+key + "</b>";
			var dataElement = document.createElement("li");
			dataElement.style.cursor = "default";
			dataElement.style.position = "relative";
			dataElement.style.left = "-15px";
			if(key=="outerHTML" || key=="innerHTML") {
				var textNode = document.createTextNode(inputArray[key]);
				dataElement.appendChild(textNode);
			}
			else dataElement.innerHTML = inputArray[key];
			container.appendChild(dataLabel);
			container.appendChild(dataElement);
		}
		if(typeof(inputArray[key])=="object"){
			if(key!="parentNode" && key != "ownerDocument"){
				var headerElement = document.createElement("li");
				headerElement.innerHTML = "<span onmouseup='InspectIE.arrayToMenu.toggleMenu(this)' style='font-weight: bold; color: red; text-decoration:none'>"+key+"</span>";
				if(typeof document.all!="undefined") headerElement.style.cursor = "hand";
				else headerElement.style.cursor = "pointer";
				headerElement.style.position = "relative";
				headerElement.style.left = "-30px";
				container.appendChild(headerElement);
				var menuElement = document.createElement("ul");
				menuElement.style.listStyleType="none";
				menuElement.style.display = "none";
				headerElement.appendChild(menuElement);
				InspectIE.arrayToMenu.renderArray(inputArray[key], menuElement);
			}
		}
	}						
}

/* InspectIE.arrayToMenu.toggleMenu
 * Toggles the display of all children of this level in a menu
 *
 * @argument	anchorElement		DOM Element		Anchor tag in the header line of this menu level
 */
InspectIE.arrayToMenu.toggleMenu = function(anchorElement){
	var headerElement = anchorElement.parentNode;
	var menuElements = headerElement.getElementsByTagName("ul");
	for(var i = 0; i < menuElements.length; i++){
		if(menuElements[i].parentNode==headerElement){
			if(menuElements[i].style.display=="none")	menuElements[i].style.display="";
			else menuElements[i].style.display="none";
		}
	}
}

/* InspectIE.makeDraggable
 * Makes the passed div into a draggable div
 *
 * @argument	draggedDiv		DIV Element			Div to be made draggable
 */
InspectIE.makeDraggable = function(draggedDiv){
	draggedDiv.mouseOffset = {x:0,y:0};
	draggedDiv.dragOn = function(e){
		draggedDiv.style.zIndex = parseInt(InspectIE.browsers.calculatedStyle(InspectIE.topWindow, "zIndex")) + 1;
		InspectIE.topWindow = draggedDiv;
		draggedDiv.mouseOffset = InspectIE.browsers.mousePosition(e);
		draggedDiv.originalLocation = { left: parseInt(InspectIE.browsers.calculatedStyle(draggedDiv, "left")),
										top: parseInt(InspectIE.browsers.calculatedStyle(draggedDiv, "top"))};
		InspectIE.browsers.addEventListener("mousemove", document, draggedDiv.drag, false); 
		InspectIE.browsers.addEventListener("mouseup", document, draggedDiv.dragOff, false);
		return false;
	}
	draggedDiv.dragOff = function(){
		InspectIE.browsers.removeEventListener("mousemove", document, draggedDiv.drag, false);
		InspectIE.browsers.removeEventListener("mouseup", document, draggedDiv.dragOff, false);
		return false;
	}
	draggedDiv.drag = function(e){
		var mousePosition = InspectIE.browsers.mousePosition(e);
		var newTop = (mousePosition.y - draggedDiv.mouseOffset.y) + draggedDiv.originalLocation.top;
		var newLeft = (mousePosition.x - draggedDiv.mouseOffset.x) + draggedDiv.originalLocation.left;
		draggedDiv.style.top = newTop+"px";
		draggedDiv.style.left = newLeft+"px";
		return false;
	}
	InspectIE.browsers.addEventListener("mousedown", draggedDiv, draggedDiv.dragOn, false);
}

/** Generic utilities **/
//Defined.js
InspectIE.defined = function(obj){
	if(typeof obj != "undefined") return typeof obj;
	else return 0;
}

//Browser detection and compensation code
//Taken from divplus' sniff.js file
//See that file for more information
InspectIE.sniff = function(){
	var exclude=1;
	var agt=navigator.userAgent.toLowerCase();
	var win=0;var mac=0;var lin=1;
	if(agt.indexOf('win')!=-1){win=1;lin=0;}
	if(agt.indexOf('mac')!=-1){mac=1;lin=0;}
	var lnx=0;if(lin){lnx=1;}
	var ice=0;
	var ie=0;var ie4=0;var ie5=0;var ie6=0;var com=0;var dcm;
	var op5=0;var op6=0;var op7=0;
	var ns4=0;var ns6=0;var ns7=0;var mz7=0;var kde=0;var saf=0;
	if(typeof navigator.vendor!="undefined" && navigator.vendor=="KDE"){
		var thisKDE=agt;
		var splitKDE=thisKDE.split("konqueror/");
		var aKDE=splitKDE[1].split("; ");
		var KDEn=parseFloat(aKDE[0]);
		if(KDEn>=2.2){
			kde=1;
			ns6=1;
			exclude=0;
			}
		}
	else if(agt.indexOf('webtv')!=-1){exclude=1;}
	else if(typeof window.opera!="undefined"){
		exclude=0;
		if(agt.indexOf("opera/5")!=-1||agt.indexOf("opera 5")!=-1){op5=1;}
		if(agt.indexOf("opera/6")!=-1||agt.indexOf("opera 6")!=-1){op6=1;}
		if(agt.indexOf("opera/7")!=-1||agt.indexOf("opera 7")!=-1){op7=1;}
		}
	else if(typeof document.all!="undefined"&&!kde){
		exclude=0;
		ie=1;
		if(typeof document.getElementById!="undefined"){
			ie5=1;
			if(agt.indexOf("msie 6")!=-1){
				ie6=1;
				dcm=document.compatMode;
				if(dcm!="BackCompat"){com=1;}
				}
			}
		else{ie4=1;}
		}
	else if(typeof document.getElementById!="undefined"){
		exclude=0;
		if(agt.indexOf("netscape/6")!=-1||agt.indexOf("netscape6")!=-1){ns6=1;}
		else if(agt.indexOf("netscape/7")!=-1||agt.indexOf("netscape7")!=-1){ns6=1;ns7=1;}
		else if(agt.indexOf("gecko")!=-1){ns6=1;mz7=1;}
		if(agt.indexOf("safari")!=-1 || (typeof document.childNodes!="undefined" && typeof document.all=="undefined" && typeof navigator.taintEnabled=="undefined")){mz7=0;ns6=1;saf=1;}
		}
	else if((agt.indexOf('mozilla')!=-1)&&(parseInt(navigator.appVersion)>=4)){
		exclude=0;
		ns4=1;
		if(typeof navigator.mimeTypes['*']=="undefined"){
			exclude=1;
			ns4=0;
			}
		}
	if(agt.indexOf('escape')!=-1){exclude=1;ns4=0;}
	if(typeof navigator.__ice_version!="undefined"){exclude=1;ie4=0;}
	
	return {exclude:exclude,win:win,mac:mac,lin:lin,ice:ice,ie:ie,ie4:ie4,ie5:ie5,ie6:ie6,com:com,dcm:dcm,op5:op5,op6:op6,op7:op7,ns4:ns4,ns6:ns6,ns7:ns7,mz7:mz7,kde:kde,saf:saf};
}

InspectIE.browsers = InspectIE.sniff();

InspectIE.browsers.addEventListener = function(eventName, objectName, eventFunction, bubble){
	if(InspectIE.browsers.ie){
		objectName.attachEvent("on"+eventName, eventFunction);
	} else {
		objectName.addEventListener(eventName, eventFunction, bubble);
	}
}

InspectIE.browsers.removeEventListener = function(eventName, objectName, eventFunction, bubble){
	if(InspectIE.browsers.ie){
		objectName.detachEvent("on"+eventName, eventFunction);
	} else {
		objectName.removeEventListener(eventName, eventFunction, bubble);
	}
}

InspectIE.browsers.mousePosition = function(e){
	if(InspectIE.browsers.ns6){
		var x = e.pageX;
		var y = e.pageY;
	}
	if(InspectIE.browsers.ie){
		var x = event.clientX + document.body.scrollLeft;
		var y = event.clientY + document.body.scrollTop;
	}
	return {x:x, y:y};
}

InspectIE.browsers.stopBubbling = function(e){
	if(InspectIE.browsers.ie){
		window.event.cancelBubble = true;
	} else {
		e.stopPropagation();
	}
}

InspectIE.browsers.calculatedStyle = function(objectDiv, propertyName){
	if(InspectIE.browsers.ns6){
		if (propertyName == "clip") {
	    	var propertyValue = objectDiv.style.clip;
	    	if (propertyValue == "") {
				return {top:0, right: InspectIE.browsers.calculatedStyle(objectDiv, "width"), bottom: InspectIE.browsers.calculatedStyle(objectDiv, "height"), left: 0};
			}
			return propertyValue;
		}
		
		propertyValue = document.defaultView.getComputedStyle(objectDiv, "")[propertyName];
		return (propertyValue == "") ? false : propertyValue;
    }
	if(InspectIE.browsers.ie){
		if (propertyName == "width") return objectDiv.offsetWidth;
		if (propertyName == "height") return objectDiv.offsetHeight;
		if (propertyName == "top") return objectDiv.offsetTop;
		if (propertyName == "left") return objectDiv.offsetLeft;
		if (propertyName == "clip") {
	    	var propertyValue = objectDiv.style.clip;
			if (propertyValue == "") {
				return {top:0, right: InspectIE.browsers.calculatedStyle(objectDiv, "width"), bottom: InspectIE.browsers.calculatedStyle(objectDiv, "height"), left: 0};
	    	}
			return propertyValue;
		}
		var propertyValue = objectDiv.currentStyle[propertyName];
		return (propertyValue == "") ? false : propertyValue;
    }
}

InspectIE.browsers.readStyleSheet = function(ruleName){
	var styleArray = new Array();
	var rulesLabel = InspectIE.browsers.ie ? "rules" : "cssRules";
	var cleanArray = new Array();		
	var realProperties = "styleArray[property]!='' && InspectIE.defined(styleArray[property])!='object'";
		realProperties += "&& InspectIE.defined(styleArray[property])!='function' && property != 'cssText'";
		realProperties += "&& property != 'accelerator' && property != 'length'";
	
	for (var i = 0; i < document.styleSheets.length; i++ ){
		for(var j=0; j<eval("document.styleSheets[i]."+rulesLabel+".length"); j++){
			if(eval("document.styleSheets[i]."+rulesLabel+"[j].selectorText.toLowerCase()") == ruleName.toLowerCase()){
				styleArray = eval("document.styleSheets[i]."+rulesLabel+"[j].style");
				for(var property in styleArray){
					if(eval(realProperties)){
						cleanArray[property] = styleArray[property];
					}
				}
			}
		}
	}
	return cleanArray;
}

//Add easter egg so you don't have to include/remove InspectIE.init() in the body onload all the time.
//To activate InspectIE in IE, click on the document and type 'killbill'
if(InspectIE.browsers.ie){
	InspectIE.keyBuffer = [0,0,0,0,0,0,0,0];
	InspectIE.keyMatch = [107,105,108,108,98,105,108,108];
	InspectIE.keyDetect = function (){
		var badKey = false;
		InspectIE.keyBuffer.splice(0,1);
		InspectIE.keyBuffer.push(event.keyCode);
		for(var i=0; i<InspectIE.keyMatch.length; i++){
			if(InspectIE.keyMatch[i]!=InspectIE.keyBuffer[i]) badKey = true;
		}
		if(badKey==false) InspectIE.init();
	}
	document.attachEvent("onkeypress", InspectIE.keyDetect);
}
