
/*******************************************************************************************
STARR: EXPLORER.JS
********************************************************************************************/

/** The Explorer object constructor. */
Explorer = function () {
	this.useCookie		= false; /*should i use a cookie for persistence?*/
	this.xmlPath		= null; /*what is the path to the xml to load the nodes from*/
	this.persistencePath= null; /*what is the path to the CF file that persists nodes (adds/removes) in the CF application. Use this only when useCookie is false */
	this.permsMask		= 'T'; /*the default/minimum perms*/
	this.displayArea	= null; /*the display area*/
	this.canExpandAll	= null;
	this.uuid 			= null; /*what type of explorer is this*/
	this.type 			= null; /*what type of explorer is this*/
	this.timeout		= null; /*how long is the time out on loaded content in hours, leave null to never timeout*/
	this.blankImage		= null;
	this.labelClick 	= null;
	this.labelInnerHTML	= null;
	
	this.node 			= null; /*the root node*/
	this.nodes			= new Array(0); /*the root node*/
	this.breadcrumb 	= new String(); /*a list of all the uuids that are open on this tree*/

	/**EVENT STUFF*/
	this.expandEvent 	= null;
	this.collapseEvent 	= null;

	this.blur 			= null;
	this.focus 			= null;

	this.click 			= null;
	this.dblclick 		= null;
	
	this.mousedown 		= null;
	this.mouseup 		= null;
	this.mouseover 		= null;
	this.mousemove 		= null;
	this.mouseout		= null;
	
	this.keypress 		= null;
	this.keydown 		= null;
	this.keyup 			= null;
	
	this.expand 		= null;
	this.collapse 		= null;
	/**END: EVENT STUFF*/

	this.scrollToUUID	= null; /*the node uuid to scroll to on init*/

	this.uuid			= null;
	this.name 			= 'explorer';
	this.className 		= 'explorer';
	
	this.id				= null; 
}


// ** constants
/// "static", needed for event handlers.
Explorer._C = null;

/// detect a special case of "web browser"
Explorer.is_ie = ( /msie/i.test(navigator.userAgent) &&
		   !/opera/i.test(navigator.userAgent) );

Explorer.is_ie5 = ( Explorer.is_ie && /msie 5\.0/i.test(navigator.userAgent) );

/// detect Opera browser
Explorer.is_opera = /opera/i.test(navigator.userAgent);

/// detect KHTML-based browsers
Explorer.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);

// BEGIN: UTILITY FUNCTIONS; 
Explorer.getAbsolutePos = function(el) {
	var SL = 0, ST = 0;
	var is_div = /^div$/i.test(el.tagName);
	if (is_div && el.scrollLeft)
		SL = el.scrollLeft;
	if (is_div && el.scrollTop)
		ST = el.scrollTop;
	var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
	//is this ie and is the parent 
	//a relatively positioned element?
	if(Explorer.is_ie && el.offsetParent && /^relative$/i.test(el.offsetParent.currentStyle.position)){
		//use getComputedstyle()  for moz etc
		if(/^relative$/i.test(el.currentStyle.position)){
			//recurse ok		
			var tmp = this.getAbsolutePos(el.offsetParent);
			r.x += tmp.x;
			r.y += tmp.y;
		}else if(/^absolute$/i.test(el.currentStyle.position)){
			//this is broken as well as IE will append the body offset to this elements offset.. ffs.
			//and recurse
			var tmp = this.getAbsolutePos(el.offsetParent);
			r.x += tmp.x;
			r.y += tmp.y;
		}else{
			//do nothing more, just return this :)
		}
	}else if (el.offsetParent ) {
		var tmp = this.getAbsolutePos(el.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}
	return r;
};


Explorer.removeClass = function(el, className) {
	if (!(el && el.className)) {
		return;
	}
	var cls = el.className.split(" ");
	var ar = new Array();
	for (var i = cls.length; i > 0;) {
		if (cls[--i] != className) {
			ar[ar.length] = cls[i];
		}
	}
	el.className = ar.join(" ");
};

Explorer.addClass = function(el, className) {
	Explorer.removeClass(el, className);
	el.className += " " + className;
};

Explorer.setClass = function(el, className) {
	el.className = className;
};


Explorer.createElement = function(type, parent) {
	var el = null;
	if (document.createElementNS) {
		// use the XHTML namespace; IE won't normally get here unless
		// _they_ "fix" the DOM2 implementation.
		el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
	} else {
		el = document.createElement(type);
	}
	if (typeof parent != "undefined") {
		//document.appendChild(el);
		parent.appendChild(el);
	}
	//el.innerHTML = 'rhysiswoo';
	return el;
};

// END: UTILITY FUNCTIONS

//BEGIN: STATIC EXPLORER FUNCTIONS

Explorer.getElement = function(ev) {
	if (Explorer.is_ie) {
		return window.event.srcElement;
	} else {
		return ev.currentTarget;
	}
};

Explorer.getTargetElement = function(ev) {
	if (Explorer.is_ie) {
		return window.event.srcElement;
	} else {
		return ev.target;
	}
};


Explorer.stopEvent = function(ev) {
	ev || (ev = window.event);
	if (Explorer.is_ie) {
		ev.cancelBubble = true;
		ev.returnValue = false;
	} else {
		ev.preventDefault();
		ev.stopPropagation();
	}
	return false;
};


Explorer.addEvent = function(el, evname, func) {
	if (el.attachEvent) { // IE
		el.attachEvent("on" + evname, func);
	} else if (el.addEventListener) { // Gecko / W3C
		el.addEventListener(evname, func, true);
	} else {
		el["on" + evname] = func;
	}
};

Explorer.removeEvent = function(el, evname, func) {
	if (el.detachEvent) { // IE
		el.detachEvent("on" + evname, func);
	} else if (el.removeEventListener) { // Gecko / W3C
		el.removeEventListener(evname, func, true);
	} else {
		el["on" + evname] = null;
	}
};

//END : STATIC EXPLORER FUCNTIONS


// BEGIN: EXPLORER OBJECT FUNCTIONS

/**
 *  This function creates the explorer inside the given parent.  
 */
Explorer.prototype.create = function (params) {
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	
	param_default("useCookie"		, this.useCookie);
	this.useCookie 					= params.useCookie;
	
	param_default("uuid"			, this.uuid); /*the root uuid*/
	this.uuid 						= params.uuid;

	var sCookieCrumb 				= Explorer.getCookie('explorer_bc_'+this.uuid);
	sCookieCrumb 					= sCookieCrumb!=null?sCookieCrumb:new String();
	var sDefaultCrumb 				= this.useCookie?sCookieCrumb:this.breadcrumb;
	
	param_default("xmlPath"			, this.xmlPath);
	param_default("persistencePath" , this.persistencePath);
	param_default("permsMask"		, this.permsMask);
	param_default("displayArea"		, this.displayArea);
	param_default("breadcrumb"		, sDefaultCrumb);
	param_default("canExpandAll"	, this.canExpandAll);
	param_default("type"			, this.type); /*the type of the very root node*/
	param_default("timeout"			, this.timeout); /*the timeout of the child nodes*/
	param_default("blankImage"		, this.blankImage); /*the path to the blank image*/
	param_default("labelClick"		, this.labelClick); /*the function to use as the onclick event handler on the node label*/
	param_default("labelInnerHTML"	, this.labelInnerHTML); /*the label to use for the explorer*/
	param_default("scrollToUUID"	, this.scrollToUUID); /*the uuif to initially scroll to*/

	param_default("expandEvent"		, this.expandEvent); /*which event is used to expand nodes etc*/
	param_default("collapseEvent"	, this.collapseEvent); /*which event is used to collapse nodes etc*/
	/*default event handlers. these can be overridden on a node by node basis*/
	param_default("blur" 			, this.blur);
	param_default("focus" 			, this.focus);

	param_default("click" 			, this.click);
	param_default("dblclick" 		, this.dblclick);
	
	param_default("mousedown" 		, this.mousedown);
	param_default("mouseup" 		, this.mouseup);
	param_default("mouseover" 		, this.mouseover);
	param_default("mousemove" 		, this.mousemove);
	param_default("mouseout" 		, this.mouseout);
	
	param_default("keypress" 		, this.keypress);
	param_default("keydown" 		, this.keydown);
	param_default("keyup" 			, this.keyup);

	param_default("expand" 			, this.expand);
	param_default("collapse" 		, this.collapse);

	this.xmlPath 					= params.xmlPath;
	this.persistencePath			= params.persistencePath;
	this.permsMask 					= params.permsMask;
	this.displayArea 				= params.displayArea;
	this.breadcrumb 				= params.breadcrumb;
	this.canExpandAll 				= params.canExpandAll;
	this.dragging 					= params.dragging;
	this.type 						= params.type;
	this.timeout					= params.timeout;
	this.blankImage					= params.blankImage;

	this.labelClick					= params.labelClick;
	this.labelInnerHTML				= params.labelInnerHTML;

	this.scrollToUUID				= params.scrollToUUID;


	/**EVENT STUFF*/
	this.expandEvent 				= params.expandEvent;
	this.collapseEvent 				= params.collapseEvent;

	this.blur 						= params.blur;
	this.focus 						= params.focus;
	
	this.click 						= params.click;
	this.dblclick 					= params.dblclick;
	
	this.mousedown 					= params.mousedown;
	this.mouseup 					= params.mouseup;
	this.mouseover 					= params.mouseover;
	this.mousemove 					= params.mousemove;
	this.mouseout					= params.mouseout;
	
	this.keypress 					= params.keypress;
	this.keydown 					= params.keydown;
	this.keyup 						= params.keyup;
	
	this.expand 					= params.expand;
	this.collapse 					= params.collapse;
	/**END: EVENT STUFF*/

	//the explorer's elem is it's parent HTML element.. a bit confusing i'll admit, but shouldn't matter.
	//create the element for this one
	this.id 						= this.displayArea.id != "String"? '' : this.displayArea.id; + '_explorer';
	this.elem 						= this.displayArea;

	//create a root node.
	this.node 						= new Node(this,this);

	this.node.create({	
					 	labelClick		: this.labelClick,
					 	labelInnerHTML	: this.labelInnerHTML,
					 	name			: this.name,
						type 			: this.type,
						uuid			: this.uuid, 
					 	id 				: this.id, 
						className 		: this.className,
						canCollapse 	: false,
						canExpand	 	: true,
						expandEvent 	: this.expandEvent,
						collapseEvent 	: this.collapseEvent,
						blur 			: this.blur,
						focus 			: this.focus,
						click 			: this.click,
						dblclick 		: this.dblclick,
						mousedown 		: this.mousedown,
						mouseup 		: this.mouseup,
						mouseover 		: this.mouseover,
						mousemove 		: this.mousemove,
						mouseout 		: this.mouseout,
						keypress 		: this.keypress,
						keydown 		: this.keydown,
						keyup 			: this.keyup,
						expand 			: this.expand,
						collapse		: this.collapse
						});
						
	/*because this is set to canCollapse = false 
	it will expand anyway*/
	this.node.init({});
};

/**
 *  (RE)Initializes the explorer to the given breadcrumb trail list
 */
Explorer.prototype.init = function (params) {
	var sCookieCrumb 	= Explorer.getCookie('explorer_bc_'+this.uuid);
	sCookieCrumb 		= sCookieCrumb!=null?sCookieCrumb:new String();
	var sDefaultCrumb 	= this.useCookie?sCookieCrumb:this.breadcrumb;
	
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("breadcrumb"		,sDefaultCrumb);
	param_default("scrollToUUID"	,this.scrollToUUID);
	this.breadcrumb 				= params.breadcrumb;
	
	this.scrollToUUID				= params.scrollToUUID;
	
	//clear the nodes
	while(this.elem.childNodes.length >0){
		var childNode = this.elem.childNodes[0];
		this.elem.removeChild(childNode);
	}
	this.nodes			= new Array(0);

	//now, init everything else?
	this.create({});
};


// Cookie handling
Explorer.setCookie = function ( sName, sValue, nDays ) {
	var expires = "";
	if ( nDays ) {
		var d 	= new Date();
		d.setTime( d.getTime() + nDays * 24 * 60 * 60 * 1000 );
		expires = "; expires=" + d.toGMTString();
	}
	document.cookie = sName + "=" + sValue + expires + "; path=/";
};

Explorer.getCookie = function (sName) {
	var re 		= new RegExp( "(\;|^)[^;]*(" + sName + ")\=([^;]*)(;|$)" );
	var res 	= re.exec( document.cookie );
	var res 	= res != null ? res[3] : null;
	return res;
};

Explorer.removeCookie = function ( name ) {
	setCookie( name, "", -1 );
};

/**
I will sync a node, basically i will refresh the tree to show a node, then scroll to the node

*/
Explorer.prototype.sync = function (params) {
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("breadcrumb"		, null);
	param_default("uuid"			, false);
	param_default("collapseFirst"	, false);
	if(params.collapseFirst){
		this.setBreadCrumb(new String());
		this.init({});
	}

	//now redraw the menu to this node
	this.setBreadCrumb(this.breadcrumb+','+params.breadcrumb);

	this.init({breadcrumb:this.breadcrumb,scrollToUUID:params.uuid});
};

Explorer.prototype.getNodeState 	= function(uuid){
	//if i'm in the bradcrumb then i should be expanded
	if(this.inBreadCrumb(uuid)){
		sState 			= Node.EXPANDED;
	}else{
		sState 			= Node.COLLAPSED;
	}
	return sState;
}

/**
I scroll to a node in the tree, if the node is not visible: tough.
*/
Explorer.prototype.scrollToNode = function (uuid) {
	var node = this.nodes[uuid];
	if(node){
		var tmp = Explorer.getAbsolutePos(node.elem);
		window.scrollTo(tmp.x,tmp.y);
	}
};

/**
i add a node uuid to the breadcrumb 
*/
Explorer.prototype.setBreadCrumb = function (breadcrumb) {
	this.breadcrumb = breadcrumb;
	this.useCookie?Explorer.setCookie('explorer_bc_'+this.uuid,this.breadcrumb):null;
};
Explorer.prototype.addToBreadCrumb = function (uuid) {
	if(!this.inBreadCrumb(uuid)){
		this.setBreadCrumb(this.breadcrumb+','+uuid);
	}
	this.addToPersistence(uuid);
};

Explorer.prototype.removeFromBreadCrumb = function (uuid) {
	if (this.useCookie){
		var regex = new RegExp(uuid,"g");
		this.setBreadCrumb(this.breadcrumb.replace(regex,''));
		this.cleanBreadCrumb();
	}else{
		this.removeFromPersistence(uuid);
	}
};

Explorer.prototype.removeFromPersistence = function (uuid) {
	var url = this.persistencePath+"action=remove&uuid="+uuid;
	this.loadXMLDoc(url);
}

Explorer.prototype.addToPersistence = function (uuid) {
	// Adding through remote call slows the loading of the tree. Therefore commeting this out.
	// ATM the adding can be done in the generateXML() function of the calling template.
	/*
	var url = this.persistencePath+"action=add&uuid="+uuid;
	this.loadXMLDoc(url);
	*/
}

Explorer.prototype.loadXMLDoc = function (url) {
	var req;
	
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
       
        req.open("GET", url, true);
        req.send(null);
    // branch for IE/Windows ActiveX version
    } else if (window.ActiveXObject) {
        req = new ActiveXObject("Microsoft.XMLHTTP");
        if (req) {
            req.open("GET", url, true);
            req.send();
        }
    }
}

Explorer.prototype.cleanBreadCrumb = function() {
	this.breadcrumb = this.breadcrumb.replace(/,{2,}/g,',');
}


Explorer.prototype.inBreadCrumb = function (uuid) {
	var bReturn = false;
	if(this.breadcrumb.indexOf(uuid) > 0 ){
		bReturn = true;
	}
	return bReturn;
};

/** Removes the explorer object from the DOM tree and destroys it. */
Explorer.prototype.destroy = function () {
	var el = this.element.parentNode;
	el.removeChild(this.element);
	Explorer._C = null;
	window.explorer = null;
};

/**
 *  Moves the explorer element to a different section in the DOM tree (changes
 *  its parent).
 */
Explorer.prototype.reparent = function (new_parent) {
	var el = this.element;
	el.parentNode.removeChild(el);
	new_parent.appendChild(el);
};


/**
*  I refresh a given node in the tree.
*/
Explorer.prototype.refreshNode = function (uuid) {
	// get the node
	var node = this.nodes[uuid];

	if(node){
		// all is okay with the node... lets refresh it.
		node.refresh();
	}
};


// global object that remembers the explorer
window.explorer = null;
/*******************************************************************************************
END: EXPLORER.JS
********************************************************************************************/

/*******************************************************************************************
START: EXPLORERELEM.JS
********************************************************************************************/
ExplorerElem = function (parent){
	this.parentObject= parent; /*the parent object, not to be confused with the parent class*/
	this.id 		= null;
	this.name 		= null;
	this.elem 		= null;
	this.tagName 	= 'div';
}


ExplorerElem.prototype.create = function (id,name,tagName,className){
	this.id 		= id;
	this.name 		= name;
	this.className 	= className;
	
	//create the element for this one
	elem	 		= Explorer.createElement(tagName,this.parentObject.elem);
	elem.className	= className;
	elem.name		= name;
	elem.id			= id;
	this.elem 		= elem;
	this.elem.swapNode = function (that) {
		//basically, i want to put this where that is
		//then i want to put that back in where this was
		var thisNextSibling 	= this.nextSibling;
		var thisParentNode 		= this.parentNode;
		
		that.parentNode.replaceChild(this, that);
		thisParentNode.insertBefore(that, thisNextSibling);  
	}
}


ExplorerElem.prototype.setClassName = function (className){
	Explorer.setClass(this.elem,className);
}
/*******************************************************************************************
END: EXPLORERELEM.JS
********************************************************************************************/


/*******************************************************************************************
START: NODE.JS
********************************************************************************************/
Node = function(parent,explorer){
	ExplorerElem.call(this,parent);
	this.type 			= null;
	this.defaultType 	= null;

	this.explorer		= explorer;

	this.name 			= 'node';
	this.tagName 		= 'div';
	this.className		= 'node';
	this.type 			= null;
	this.id 			= null;
	this.uuid 			= null;
	
	this.nodeDetail		= null;
	this.nodeChildren	= null;

	this.state 			= Node.COLLAPSED;
	this.xmlPath 		= null;/*this gets passed the nodeChildren*/
	this.canCollapse	= null;
	this.canExpand		= null;
	
	this.labelInnerHTML = null;
	this.labelClick 	= null;


	this.expandEvent 	= null;
	this.collapseEvent 	= null;
	
	this.click 			= null;
	this.dblclick 		= null;

	this.focus 			= null;
	this.blur	 		= null;

	this.mousedown 		= null;
	this.mouseup 		= null;
	this.mouseover 		= null;
	this.mousemove 		= null;
	this.mouseout 		= null;
	
	this.keypress 		= null;
	this.keydown 		= null;
	this.keyup 			= null;
	
	this.expand 		= null;
	this.collapse 		= null;


}

Node.prototype 				= new ExplorerElem(explorer);
Node.prototype.constructor 	= Node;
Node.prototype.parent 		= ExplorerElem.prototype;

/**CONSTANTS*/
Node.EXPANDED 				= 'expanded';
Node.COLLAPSED		 		= 'collapsed';


Node.prototype.create = function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("type"			, null);
	param_default("defaultType"		, params.type);
	param_default("uuid"			, null);
	param_default("id"				, this.explorer.id+'_node__'+this.uuid);
	param_default("name"			, this.name);
	param_default("canCollapse"		, true);
	param_default("canExpand"		, true);
	param_default("hasChildren" 	, false);
	param_default("labelInnerHTML"	, '');
	param_default("labelClick"		, null);
	param_default("className" 		, this.className);
	param_default("xmlPath" 		, this.parentObject.xmlPath);
	param_default("permsMask" 		, this.parentObject.permsMask);

	/*these will inherit their parent node's settings*/
	param_default("expandEvent" 	, this.parentObject.node.expandEvent);
	param_default("collapseEvent" 	, this.parentObject.node.collapseEvent);

	param_default("blur" 			, this.parentObject.node.blur);
	param_default("focus" 			, this.parentObject.node.focus);

	param_default("click" 			, this.parentObject.node.click);
	param_default("dblclick" 		, this.parentObject.node.dblclick);
	
	param_default("mousedown" 		, this.parentObject.node.mousedown);
	param_default("mouseup" 		, this.parentObject.node.mouseup);
	param_default("mouseover" 		, this.parentObject.node.mouseover);
	param_default("mousemove" 		, this.parentObject.node.mousemove);
	param_default("mouseout" 		, this.parentObject.node.mouseout);
	
	param_default("keypress" 		, this.parentObject.node.keypress);
	param_default("keydown" 		, this.parentObject.node.keydown);
	param_default("keyup" 			, this.parentObject.node.keyup);

	param_default("expand" 			, this.parentObject.node.expand);
	param_default("collapse" 		, this.parentObject.node.collapse);

	this.parent.create.call(this,params.id,params.name,this.tagName,params.className);
	
	this.state 						= this.state;
	
	this.type 						= params.type;
	this.defaultType 				= params.defaultType;
	
	this.uuid 						= params.uuid;
	this.canCollapse 				= params.canCollapse;
	this.canExpand 					= params.canExpand;
	this.hasChildren 				= params.hasChildren;
	this.labelInnerHTML 			= params.labelInnerHTML;
	this.labelClick		 			= params.labelClick;
	this.xmlPath 					= params.xmlPath;
	this.permsMask 					= params.permsMask;

	this.explorer.nodes[this.uuid] 	= this;	
	this.explorer.nodes[this.uuid] 	= this;	

	/**EVENT STUFF*/
	this.expandEvent 				= params.expandEvent;
	this.collapseEvent 				= params.collapseEvent;

	this.blur 						= params.blur;
	this.focus 						= params.focus;
	
	this.click 						= params.click;
	this.dblclick 					= params.dblclick;
	
	this.mousedown 					= params.mousedown;
	this.mouseup 					= params.mouseup;
	this.mouseover 					= params.mouseover;
	this.mousemove 					= params.mousemove;
	this.mouseout					= params.mouseout;
	
	this.keypress 					= params.keypress;
	this.keydown 					= params.keydown;
	this.keyup 						= params.keyup;

	this.expand 					= params.expand;
	this.collapse 					= params.collapse;
	/**END: EVENT STUFF*/

	this.nodeDetail 				= new NodeDetail(this);
	this.nodeDetail.create({	
							labelClick		:this.labelClick,
							labelInnerHTML 	:params.labelInnerHTML});

	this.nodeChildren	 			= new NodeChildren(this);
	this.nodeChildren.create({});

	//put this node in the explorer item array
	this.explorer.nodes[this.uuid] 	= this;	
}

/**init the node.. i.e set the state and the class*/
Node.prototype.init 	= function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("state",   		this.state);
	var node 			= this;
	
	/*so that you can override the state of a node by passing it in explicitly*/
	this.setState(params.state);
	
	/*don't do !this.canCollapse as sometimes a false value will make it equate to true*/
	if(this.canCollapse!=true){
		/*set the state to collapsed so that it will be reversed and expanded*/
		this.setState(Node.COLLAPSED);
		this.expandCollapse();
	}
	/*should it be expanded and isn't?**/
	if(this.explorer.getNodeState(this.uuid)!=this.state){
		//invert the state and call expandCollapse
		this.setState(Node.EXPANDED?Node.COLLAPSED:Node.EXPANDED);
		this.expandCollapse();
	}

	if(this.hasChildren==false){
		//this.setState(null);
		var sBranchClass 		= Branch.CLASSNAMENONE;
		var sIconClass 			= this.type + '_closed';
		var sIconDefaultClass 	= this.defaultType + '_closed';
	}else if(this.explorer.getNodeState(this.uuid) == Node.EXPANDED){
		this.setState(Node.EXPANDED);
		var sBranchClass 		= Branch.CLASSNAMECOLLAPSE;
		var sIconClass 			= this.type + '_open';
		var sIconDefaultClass 	= this.defaultType + '_open';
	}else{
		this.setState(Node.COLLAPSED);
		var sBranchClass 		= Branch.CLASSNAMEEXPAND;
		var sIconClass 			= this.type + '_closed';
		var sIconDefaultClass 	= this.defaultType + '_closed';
	}
	

	node.callHandler = function (ev) {
											if (node[ev.type]) {
												node[ev.type](ev,node);
											}
										}
	/*add all the events to the nodeDetail*/
	var nodeDetailElem = this.nodeDetail.elem;
		
		this.blur?Explorer.addEvent(nodeDetailElem, 'blur', node.callHandler):null;
		this.focus?Explorer.addEvent(nodeDetailElem, 'focus', node.callHandler):null;
		this.click?Explorer.addEvent(nodeDetailElem, 'click', node.callHandler):null;
		this.dblclick?Explorer.addEvent(nodeDetailElem, 'dblclick', node.callHandler):null;
		this.mousedown?Explorer.addEvent(nodeDetailElem, 'mousedown', node.callHandler):null;
		this.mouseup?Explorer.addEvent(nodeDetailElem, 'mouseup', node.callHandler):null;
		this.mouseover?Explorer.addEvent(nodeDetailElem, 'mouseover', node.callHandler):null;
		this.mousemove?Explorer.addEvent(nodeDetailElem, 'mousemove', node.callHandler):null;
		this.mouseout?Explorer.addEvent(nodeDetailElem, 'mouseout', node.callHandler):null;
		this.keypress?Explorer.addEvent(nodeDetailElem, 'keypress', node.callHandler):null;
		this.keydown?Explorer.addEvent(nodeDetailElem, 'keydown', node.callHandler):null;
		this.keyup?Explorer.addEvent(nodeDetailElem, 'keyup', node.callHandler):null;
/**/
	//add the collapseEvent to this thing
	var fExpandNode = function(ev){
																	node.expandCollapse(node)
																	return Explorer.stopEvent(ev);};

	this.nodeDetail.icon.elem['on'+node.expandEvent] 		= fExpandNode;
	if(this.nodeDetail.branch){
		this.nodeDetail.branch.elem['on'+node.expandEvent] 	= fExpandNode;
	}
	this.nodeDetail.icon.elem['on'+node.collapseEvent] 		= fExpandNode;
	if(this.nodeDetail.branch){
		this.nodeDetail.branch.elem['on'+node.collapseEvent] = fExpandNode;
	}

	//set the branch img
	if(this.nodeDetail.branch){
		this.nodeDetail.branch.setClassName(sBranchClass);
	}
	//set the icon img
	this.nodeDetail.icon.setClassName({
									  		className		:sIconClass,
											defaultClassName:sIconDefaultClass
									  });
	
	//is this the node to scroll to?
	if(this.explorer.scrollToUUID == this.uuid){
			this.explorer.scrollToNode(this.uuid);
	}
	
}

Node.prototype.hasAncestor = function(that){
	var bReturn;
	var nodeCheck 	= this;
	//loop up until i get to the top or i find 'that'
	while(nodeCheck.parentObject !=this.explorer){
		if(nodeCheck.parentObject.node==that ){
			bReturn = true;
			break;
		}
		nodeCheck 	= nodeCheck.parentObject.node;
	}
	return bReturn;
}

Node.prototype.getPreviousNode = function(){
	return this.parentObject.getNodePosition(this)?this.parentObject.nodes[this.parentObject.getNodePosition(this)-1]:null;
}

Node.prototype.getNextNode = function(){
	return this.parentObject.getNodePosition(this)!=null?this.parentObject.nodes[this.parentObject.getNodePosition(this)+1]:null;
}

Node.prototype.swapNode 	= function(that){
	var nodeThis 				= this;
	var nodeThat 				= that;
	
	var npThis 					= this.parentObject;
	var npThat 					= that.parentObject;
	
	var iThis 					= this.parentObject.getNodePosition(this);
	var iThat 					= that.parentObject.getNodePosition(that);
	
	//swap the nodes
	this.parentObject.nodes[iThis] 	= nodeThat;
	that.parentObject.nodes[iThat] 	= nodeThis;
	//swap the nodes' parent objects
	this.parentObject 			= npThat;
	that.parentObject 			= npThis;
	
	this.elem.swapNode(that.elem)
}

Node.prototype.refresh = function(){
	//am i the top node?
	if(this.parentObject==this.explorer){
	//if so, you re-init the explorer. 
		this.explorer.init({});
	}else{
		this.parentObject.loaded = false;
		this.parentObject.load();
	}
}

Node.prototype.setState = function(state){
	this.state 			= state;
}

Node.prototype.expandCollapse 		= function(myNode){
	var thisNode 	= myNode?myNode:this;
	if(thisNode.state == Node.EXPANDED){
		if(thisNode.canCollapse){
			//hide the childnodes
			thisNode.nodeChildren.hide();

			//remove me from the breadcrumb
			thisNode.explorer.removeFromBreadCrumb(thisNode.uuid);
			thisNode.setState(Node.COLLAPSED);
			
			/*do whatever function is saved at collapse*/
			thisNode.collapse?thisNode.collapse():null;

		}
	}else{
		if(thisNode.canExpand){
			//show the childnodes
			thisNode.nodeChildren.show();
		}
			//add me to the breadcrumb
			thisNode.explorer.addToBreadCrumb(thisNode.uuid);
			thisNode.setState(Node.EXPANDED);
			
			/*do whatever function is saved at expand*/
			thisNode.expand?thisNode.expand():null;
		
	}
}


/*******************************************************************************************
END: NODE.JS
********************************************************************************************/


/*******************************************************************************************
START: NODEDETAIL.JS
********************************************************************************************/
NodeDetail = function(node){
	ExplorerElem.call(this,node);

	this.name 			= 'nodeDetail';
	this.tagName 		= 'div';
	this.className		= 'nodeDetail';
	this.id 			= null;

	this.node 			= node;

	this.branch 		= null;
	this.label 			= null;
	this.icon 			= null;

	this.branch			= null; 
	this.label			= null; 
	this.icon			= null; 
	this.expandAll		= null; 

	this.labelInnerHTML = null;
	this.labelClick 	= null;
}

NodeDetail.prototype 				= new ExplorerElem();
NodeDetail.prototype.constructor 	= NodeDetail;
NodeDetail.prototype.parent 		= ExplorerElem.prototype;


NodeDetail.prototype.create = function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("id,"				, this.id);
	param_default("className"		, this.className);
	param_default("labelInnerHTML"	, this.labelInnerHTML);
	param_default("labelClick"		, this.labelClick);

	this.parent.create.call(this,params.id,this.name,this.tagName,params.className);


	if(this.node.canCollapse==true){
		this.createBranch({});
	}
	this.createIcon({});
	this.createLabel({	click		: params.labelClick,
					 	innerHTML  	: params.labelInnerHTML});
	if(this.node.explorer.canExpandAll){
		this.createExpandAll();
	}
}

NodeDetail.prototype.createBranch  = function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("className"	, null);

	this.branch = new Branch(this);
	this.branch.create(params);
}
NodeDetail.prototype.createLabel  = function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("className"	, 'nodeLabel');
	param_default("innerHTML"	, '');
	param_default("click"		, null);
	
	this.label = new Label(this);
	this.label.create(params);
}
NodeDetail.prototype.createIcon  = function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("className"	, null);

	this.icon = new Icon(this);
	this.icon.create(params);
}
NodeDetail.prototype.createExpandAll  = function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("className"	, null);

	this.expandAll = new ExpandAll(this);
	this.expandAll.create(params);
}

NodeDetail.prototype.getBranch  = function(){
	this.branch;
}
NodeDetail.prototype.getLabel  = function(){
	this.label;
}
NodeDetail.prototype.getIcon  = function(){
	this.icon;
}
NodeDetail.prototype.getExpandAll  = function(){
	this.expandAll;
}
/*******************************************************************************************
END: NODEDETAIL.JS
********************************************************************************************/

/*******************************************************************************************
START: NODECHILDREN.JS
********************************************************************************************/
NodeChildren = function(node){
	ExplorerElem.call(this,node);

	this.node 			= node; 
	this.explorer		= this.node.explorer; 
	this.nodes			= new Array(0); /*the nodes in this explorer*/
	
	this.name 			= 'nodeChildren';
	this.className 		= 'nodeChildren';
	
	this.loaded 		= null;
	this.lastLoaded 	= null;
	this.xmlDoc 		= null;

	this.permsMask 		= this.node.permsMask;
	this.xmlPath 		= this.node.xmlPath;
	this.xmlURL			= null;

	if (document.implementation && document.implementation.createDocument){
		this.xmlDoc = document.implementation.createDocument("", "", null);
	}else if (window.ActiveXObject){
		this.xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
	}
}

NodeChildren.prototype 				= new ExplorerElem(explorer);
NodeChildren.prototype.constructor 	= NodeChildren;
NodeChildren.prototype.parent 		= ExplorerElem.prototype;


NodeChildren.prototype.create 		= function(params){
	param_default 					= function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("id"			, this.explorer.id+'_nodeChildren__'+this.node.uuid);
	param_default("name"		, this.name);
	param_default("className"	, this.className);
	param_default("className"	, this.className);

	this.parent.create.call(this,params.id,params.name,this.tagName,params.className);
	this.node 						= this.parentObject;
	this.loaded 					= false;
}

NodeChildren.prototype.init = function(){
	var sIconClass 			= this.node.type+'_open';
	var sIconDefaultClass 	= this.node.defaultType+'_open';
	var sIconClassClosed 	= this.node.type+'_closed';
	var sIconDefaultClassClosed 	
							= this.node.defaultType+'_closed';
	
	//are there any children ?
	if(!this.nodes.length > 0){
		//set the branch img
		if(this.node.nodeDetail.branch ){
				this.node.nodeDetail.branch.setClassName(Branch.CLASSNAMENONE);
		}
		//set the icon img
		this.node.nodeDetail.icon.setClassName({
												className		:sIconClassClosed,
												defaultClassName:sIconDefaultClassClosed
									  			});
	}else{
		if(this.node.state == Node.EXPANDED){
			//set the branch img
			if(this.node.nodeDetail.branch ){
					this.node.nodeDetail.branch.setClassName(Branch.CLASSNAMECOLLAPSE);
			}
		}else{
			//set the branch img
			if(this.node.nodeDetail.branch ){
					this.node.nodeDetail.branch.setClassName(Branch.CLASSNAMEEXPAND);
			}
			//set the icon img
			this.node.nodeDetail.icon.setClassName({
												className		:sIconClassClosed,
												defaultClassName:sIconDefaultClassClosed
									  			});
		}
	}
	//set the icon img
	this.node.nodeDetail.icon.setClassName({
									  		className		:sIconClass,
											defaultClassName:sIconDefaultClass
									 		});
	/**loop the children and set their classes*/
	for(var iChild=0;iChild <= this.nodes.length;iChild++){
		if(this.nodes[iChild]){
			this.nodes[iChild].init({});
		}
	}
	
}


NodeChildren.prototype.shuffle 	= function(direction){
	if(direction=='down'){
		this.elem.style.display			='block';
		this.elem.style.height			='auto';
		this.elem.style.overflow		='visible';
	}else{
		this.elem.style.display			='none';
		this.elem.style.height			='0px';
		this.elem.style.overflow		='hidden';
	}
}

NodeChildren.prototype.show 	= function(){
	this.isStale = function(){
		var fReturn = false;

		if(!this.loaded ){
			fReturn = true;
		}else if(isNaN(this.explorer.timeout) || this.explorer.timeout == null){
			//i don't have a timeout
			var fReturn = false;
		}else if(this.loaded && this.lastLoaded){
			//i am loaded, but the lastLoaded date is stale
			fReturn = (new Date().valueOf() - new Date(this.lastLoaded).valueOf()) > this.explorer.timeout
		}
		return fReturn;
	
	}

	if(this.isStale()){
		this.load();
	}else{
		this.shuffle('down');
	}

	//set the branch img
	if(this.node.nodeDetail.branch ){
			this.node.nodeDetail.branch.setClassName(Branch.CLASSNAMECOLLAPSE);
	}
	//set the icon img
	this.node.nodeDetail.icon.setClassName({
									  		className		:this.node.type + '_open',
											defaultClassName:this.node.defaultType + '_open'
									  		});
}

NodeChildren.prototype.hide		= function(){
	if(!this.nodes.length > 0){
		//set the branch img
		if(this.node.nodeDetail.branch ){
				this.node.nodeDetail.branch.setClassName(Branch.CLASSNAMENONE);
		}
		//set the icon img
		this.node.nodeDetail.icon.setClassName({
									  		className		:this.node.type + '_closed',
											defaultClassName:this.node.defaultType + '_closed'
									  		});
	}else{
		//set the branch img
		if(this.node.nodeDetail.branch ){
				this.node.nodeDetail.branch.setClassName(Branch.CLASSNAMEEXPAND);
		}
		//set the icon img
		this.node.nodeDetail.icon.setClassName({
									  		className		:this.node.type + '_closed',
											defaultClassName:this.node.defaultType + '_closed'
									  		});
	}
	this.shuffle('up');
}

NodeChildren.prototype.getNodePosition = function(node){
	iNodePosition = null
	for(var iNode=0;iNode<this.nodes.length;iNode++){
		if(this.nodes[iNode]==node){
			iNodePosition = iNode;
			break;
		}
	}
	return iNodePosition;
}

NodeChildren.prototype.addNode 	= function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("type"			, null);
	param_default("defaultType"		, params.type);
	param_default("uuid"			, null);
	param_default("id"				, this.explorer.id+'_node__'+params.uuid);
	param_default("xmlPath"			, true);
	param_default("permsMask"		, true);
	param_default("canCollapse"		, true);
	param_default("canExpand"		, true);
	param_default("labelInnerHTML"	, null);
	param_default("labelClick"		, this.node.nodeDetail.label.click);

	/**create the node
	add it to this.nodess*/
	var node = new Node(this,this.explorer);
	node.create(params);
	this.nodes[this.nodes.length] = node; 
}

/**this will load the node*/
NodeChildren.prototype.load = function(){
	
	var nc 			= this;

	nc.clear();
	this.addNode({
							type			: 'loading',
							uuid			: 'loading_'+this.node.uuid,
							labelInnerHTML	: 'loading',
							xmlPath			: null
				 	});
	
	this.lastLoaded = Date();
	this.loaded 	= true;
	//get the xml config
	this.xmlURL 	= this.xmlPath.replace(/(\?|$)/,'?uuid='+this.node.uuid+'&');
	//document.write('<a href="'+this.xmlURL+'" target="_blank">Click to see response from server</a>');	// uncomment this for debugging the URL being used by providing the linky
	this.xmlDoc.load(this.xmlURL);	

	nc.writeNodes = function(xmlDoc){
		/**REMOVE white spaces in XML file. Intended mainly for NS6/Mozilla*/
		var notWhitespace = /\S/;
		/**this is the xmlDoc or is it the event?*/
		xmlDoc = xmlDoc.type?this:xmlDoc;
		/**clear the child nodes*/
		nc.clear();
		
		var explorerNodes	= xmlDoc.getElementsByTagName('node');
		for (var iNode=0;iNode<explorerNodes.length;iNode++)
		{
			var node 	 	= explorerNodes[iNode];

			for (i=0;i<node.childNodes.length;i++){
				if ((node.childNodes[i].nodeType == 3)&&
					(!notWhitespace.test(node.childNodes[i].nodeValue))) {
				// that is, if it's a whitespace text node
				node.removeChild(node.childNodes[i])
				i--}
			}
	
			var sType 			= node.getAttribute('type');
			var sDefaultType	= node.getAttribute('defaulttype')?node.getAttribute('defaulttype'):sType;
			var sUUID 			= node.getAttribute('uuid');
			
			var sXmlPath 		= node.getAttribute('xmlpath')?node.getAttribute('xmlpath'):nc.node.xmlPath;
			var sPermsMask 		= node.getAttribute('permsmask')?node.getAttribute('permsmask'):nc.node.permsMask;

			var bCanCollapse	= node.getAttribute('cancollapse')&&node.getAttribute('cancollapse')=='false'?false:true;
			var bCanExpand		= node.getAttribute('canexpand')&&node.getAttribute('canexpand')=='false'?false:true;
			var bHasChildren	= node.getAttribute('haschildren')&&node.getAttribute('haschildren')=='true'?true:false;
			var oLabel 			= node.childNodes[0];
			var sLabelInnerHTML = oLabel.childNodes? oLabel.childNodes[0].data: '';
			var fLabelClick		= oLabel.getAttribute('click')?eval(oLabel.getAttribute('click')):nc.node.nodeDetail.label.click;
			/*check for events stuff*/
			var sExpandEvent	= node.getAttribute('expandevent')?node.getAttribute('expandevent'):nc.node.collapseEvent;
			var sCollapseEvent	= node.getAttribute('collapseevent')?node.getAttribute('collapseevent'):nc.node.collapseEvent;
			
			var fBlur			= node.getAttribute('blur')?eval(node.getAttribute('blur')):nc.node.blur;
			var fFocus			= node.getAttribute('focus')?eval(node.getAttribute('focus')):nc.node.focus;
			var fClick			= node.getAttribute('click')?eval(node.getAttribute('click')):nc.node.click;
			var fDblClick		= node.getAttribute('dblclick')?eval(node.getAttribute('dblclick')):nc.node.dblclick;
			var fMouseDown		= node.getAttribute('mousedown')?eval(node.getAttribute('mousedown')):nc.node.mousedown;
			var fMouseUp		= node.getAttribute('mouseup')?eval(node.getAttribute('mouseup')):nc.node.mouseup;
			var fMouseOver		= node.getAttribute('mouseover')?eval(node.getAttribute('mouseover')):nc.node.mouseover;
			var fMouseMove		= node.getAttribute('mousemove')?eval(node.getAttribute('mousemove')):nc.node.mousemove;
			var fMouseOut		= node.getAttribute('mouseout')?eval(node.getAttribute('mouseout')):nc.node.mouseout;
			var fKeyPress		= node.getAttribute('keypress')?eval(node.getAttribute('keypress')):nc.node.keypress;
			var fKeyDown		= node.getAttribute('keydown')?eval(node.getAttribute('keydown')):nc.node.keydown;
			var fKeyUp			= node.getAttribute('keyup')?eval(node.getAttribute('keyup')):nc.node.keyup;
			var fExpand			= node.getAttribute('expand')?eval(node.getAttribute('expand')):nc.node.expand;
			var fCollapse		= node.getAttribute('collapse')?eval(node.getAttribute('collapse')):nc.node.collapse;
			/*end events stuff*/
			nc.addNode({
							type			: sType,
							defaultType		: sDefaultType,
							uuid			: sUUID,
							labelInnerHTML	: sLabelInnerHTML,
							labelClick 		: fLabelClick,
							xmlPath			: sXmlPath,
							permsMask		: sPermsMask,
							canCollapse		: bCanCollapse,
							canExpand		: bCanExpand,
							hasChildren		: bHasChildren,
							expandEvent		: sExpandEvent,
							collapseEvent	: sCollapseEvent,
							blur			: fBlur,
							focus			: fFocus,
							click			: fClick,
							dblclick		: fDblClick,
							mousedown		: fMouseDown,
							mouseup			: fMouseUp,
							mouseover		: fMouseOver,
							mousemove		: fMouseMove,
							mouseout		: fMouseOut,
							keypress		: fKeyPress,
							keydown			: fKeyDown,
							keyup			: fKeyUp
						});

		}
		nc.init();
		nc.shuffle('down');
	}
	
	if (document.implementation && document.implementation.createDocument){
		this.xmlDoc.onload = 		nc.writeNodes;
	}else if (window.ActiveXObject){
		var xmlDoc = this.xmlDoc;
		this.xmlDoc.onreadystatechange = function () {
			if (xmlDoc.readyState == 4) nc.writeNodes(xmlDoc);
		};
	}
}

NodeChildren.prototype.clear 	= function(){
	//remove the elem child nodes
	while(this.elem.childNodes.length >0){
		var childNode = this.elem.childNodes[0];
		this.elem.removeChild(childNode);
	}
	this.nodes			= new Array(0);
}


/*******************************************************************************************
END: NODECHILDREN.JS
********************************************************************************************/

/*******************************************************************************************
START: ICON.JS
********************************************************************************************/
Icon = function (nodeDetail){
	ExplorerElem.call(this,nodeDetail);
	this.nodeDetail = nodeDetail;
	this.name 		= this.nodeDetail.node.type;
	this.tagName	= 'img';
}

Icon.prototype 				= new ExplorerElem();
Icon.prototype.constructor 	= Icon;
Icon.prototype.parent 		= ExplorerElem.prototype;

Icon.prototype.create = function(params){
	this.parent.create.call(this,params.id,this.name,this.tagName,params.className);
	this.elem.src 		= this.nodeDetail.parentObject.explorer.blankImage;
}

Icon.prototype.setClassName = function (params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("className"				, null);
	param_default("defaultClassName"		, params.className);
	//set the icon class
	this.parent.setClassName.call(this,params.className);
	//is there a default?
	if(params.className != params.defaultClassName){
	//now, is there any background image?
		var sBGImage 	= '';
		if(Explorer.is_ie){
			sBGImage 	= this.elem.currentStyle.getAttribute('backgroundImage');
		}else{
			sBGImage 	= document.defaultView.getComputedStyle(this.elem, '').getPropertyValue('backgroundImage');
		}
		//if not, then use the default
		if(sBGImage==null || sBGImage=='none'){
			//set the icon class
			this.parent.setClassName.call(this,params.defaultClassName);
		}
	}
}

/*******************************************************************************************
END: ICON.JS
********************************************************************************************/

/*******************************************************************************************
START: LABEL.JS
********************************************************************************************/
Label = function (nodeDetail){
	ExplorerElem.call(this,nodeDetail);

	this.nodeDetail 	= nodeDetail;
	this.name 			= 'label';
	this.tagName		= 'span';
}

Label.prototype 			= new ExplorerElem();
Label.prototype.constructor	= Label;
Label.prototype.parent 		= ExplorerElem.prototype;

Label.prototype.create = function(params){
	param_default = function (pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };		
	param_default("id"			, this.nodeDetail.node.explorer.id+'_label__'+this.nodeDetail.node.uuid);
	param_default("className"	, 'nodeLabel');
	param_default("click"		, null);

	this.click 			= params.click;
	
	this.parent.create.call(this,params.id,this.name,this.tagName,params.className);
	this.elem.innerHTML = params.innerHTML;
	this.elem.onclick 	= this.click;
	}/*******************************************************************************************
END: LABEL.JS
********************************************************************************************/

/*******************************************************************************************
START: BRANCH.JS
********************************************************************************************/
Branch = function (nodeDetail){
	ExplorerElem.call(this,nodeDetail);

	this.nodeDetail 	= nodeDetail;
	this.name 			= 'branch';
	this.tagName		= 'img';
}

Branch.prototype 			= new ExplorerElem();
Branch.prototype.constructor= Branch;
Branch.prototype.parent 	= ExplorerElem.prototype;

Branch.CLASSNAMEEXPAND 		= 'plus';
Branch.CLASSNAMECOLLAPSE 	= 'minus';
Branch.CLASSNAMENONE 		= 'none';


Branch.prototype.create 	= function(params){
	this.parent.create.call(this,params.id,this.name,this.tagName,params.className);
	this.elem.src 			= this.nodeDetail.parentObject.explorer.blankImage;
}

/*******************************************************************************************
END: BRANCH.JS
********************************************************************************************/

