var baseUrl = ItemPickerBaseUrl ? ItemPickerBaseUrl : '';
function cache(src){
	var img=new Image();
	img.src=baseUrl + src;
	return img.src;
}
var treeConfig = {
	loadIcon		: cache('tree/loading.gif'),
	errorIcon		: baseUrl + 'tree/error.gif',
	defaultIcon		: baseUrl + 'tree/folder.gif',
	defaultIconOpen	: baseUrl + 'tree/openfolder.gif',
	iIcon           : baseUrl + 'tree/i.gif',
	lIcon           : baseUrl + 'tree/l.gif',
	lMinusIcon      : baseUrl + 'tree/lminus.gif',
	lPlusIcon       : baseUrl + 'tree/lplus.gif',
	tIcon           : cache('tree/t.gif'),
	tMinusIcon      : baseUrl + 'tree/tminus.gif',
	tPlusIcon       : baseUrl + 'tree/tplus.gif',
	blankIcon		: baseUrl + 'tree/blank.gif',
	checkboxIcon	: baseUrl + 'tree/check_disabled.gif',
	checkboxCheckedFullIcon : baseUrl + 'tree/check_full.gif',
	checkboxCheckedPartialIcon : baseUrl + 'tree/check_partial.gif',
	defaultLabel    : 'new node',
	defaultAction   : 'javascript:void(0);',
	defaultOnNodeSelectEventHandler : function(){void(0)},
	defaultOnNodeDragStartHandler : function(){return true},
	defaultOnNodeDragEndHandler : function(){void(0)},
	selectOnReload : true,
	maxRetryCount : 0,
	retryDelayTime : 500,
	tagName : 'tree',
	createRootNode : false
};

var labels = {
	loading : 'one moment, loading',
	errors : {
		throwError : 'error occured while executing {0}:\n\ndetails:\n{1}' ,
		unhandledException : 'Unknown error occured in the treeview component.',
		noDatasource : 'Datasource not set on \'{0}\'.',
		noChildren : 'No children available when trying to select a child of \'{0}\'',
		labelNotFound : 'No children available when trying to select a child of \'{0}\' with the label {1}',
		onDemandMissing : 'error:on demand library missing',
		indexOutOfBound : 'Index out of range when trying to select a child of \'{2}\'. The passed index was {0} and the maximum index available is {1}',	
		pasteCircular : '{1} cannot be moved to {0}',
		pasteInvalid : 'Object \'{0}\' cannot be moved to itself'
	}
};



String.prototype.format = function(){
	var str = this;
	var i = arguments.length;while(i--){
		var arg = arguments[i];
		var arr_arg = arg.split('');
		
		if(arr_arg[0] == '{' && arr_arg[arr_arg.length-1] == '}'){
			// string formatter
			
		}else{
			// indexed formatter
			str = str.replace(new RegExp("\\{"+i+"\\}","gi"),arguments[i]);
		}
	}
	return str;
};


function EventManager(){
	this.types = new Array();
}
EventManager.prototype.addListener = function(type,action,lifespan){
	if(this.types[type] == null){
		this.types[type] = new EventQueue();
	}
	
	this.types[type].add(action,lifespan);
}
EventManager.prototype.dispatch = function(type){
	if(this.types[type] != null){
		var eventQueue = this.types[type];
		var events = eventQueue.getEvents();
		
		for(var i=0,j=eventQueue.getLength();i<j;i++){
			
			events[i].action();
			

			switch(events[i].lifespan){
				// once 	: event triggered only once
				// default 	: event is always triggered
				case 'once':
					events[i] = null;
				break;
			}
		}
	}
}
function EventQueue(){
	this.events = new Array();
}
EventQueue.prototype.add = function(action,lifespan){
	this.events.push({action:action,lifespan:lifespan});
}
EventQueue.prototype.getEvents = function(){
	return this.events;
}
EventQueue.prototype.getLength = function(){
	return this.getEvents().length;
}


// failsafe, if drag and drop library is unavailable
function oNode_onMousedown(){}


function treeview(id,parentNode,src,onNodeSelectEventHandler,useCheckBox){
	this.useCheckBox = useCheckBox;
	this.parentNode = parentNode;
	this.id = id;
	this.label = 'treeview';
	this.src = src;
	this.treeview = this;
	this.selectedNode = null;
	this.selectOnReload = treeConfig.selectOnReload;
	this.onNodeSelectEventHandler = onNodeSelectEventHandler ? onNodeSelectEventHandler : treeConfig.defaultOnNodeSelectEventHandler;
	this.onNodeDragStartHandler = treeConfig.defaultOnNodeDragStartHandler;
	this.onNodeDragEndHandler = treeConfig.defaultOnNodeDragEndHandler;
	this.eventManager = new EventManager();
	this.init();
}
treeview.prototype.init = function(){
	this.loaded = false;
	this.loading = false;
	this.focussed = false;
	
	this.oNode = document.createElement("DIV");
	this.oNode.className = 'tree';
	this.oNode.id = this.id;
	this.oNode.obj = this;
	
	
	this.oNodeChilds = document.createElement("DIV");
	this.oNode.oNodeChilds = this.oNode.appendChild(this.oNodeChilds);

	// set focus to receive key events
	
	//this.oNode.onclick = function(){this.focus()};
	var self = this;
	var kd = function(event){self.onkeydown(event)};
	if(document.addEventListener){ 
		this.oNode.addEventListener("keydown",kd,false);
	}else if(document.attachEvent){
		this.oNode.attachEvent("onkeydown",kd);
	}
	
	// Gecko requires tabIndex for keyEvents
	this.oNode.tabIndex = -1;
	
	var self = this;
    this._onunload = function(){self.dispose()};
	if(document.addEventListener) { 
    	window.addEventListener("unload",this._onunload,false ); // Moz
   	}else if(window.attachEvent) {
     	window.attachEvent("onunload",this._onunload); // IE
   	}
	
	this.append();
	
	if(this.src){
		if(!this.load){
			alert(labels.errors.onDemandMissing);
		}else{
			this.load();
		}
	}
}
treeview.prototype.dispose = function(){
	
	if(document.removeEventListener) {
	  window.removeEventListener("unload",this._onunload,false ); // Moz
	}else if(window.detachEvent) { 
	  window.detachEvent("onunload",this._onunload); // IE
	}
		
	this.selectOnReload = null;
	this.onNodeSelectEventHandler = null;
	this.onNodeDragStartHandler = null;
	this.onNodeDragEndHandler = null;	
	
	// nodechilds
	this.oNodeChilds = null;
	
	// node
	this.oNode.obj = null;
	this.oNode.oNodeChilds = null;
	this.oNode = null;
				
	this._onunload = null;
}
treeview.prototype.append = function(){
	this.parentNode.appendChild(this.oNode);
	this.selectedNode = this;
}
treeview.prototype.getFirstChild = function(){
	if(this.hasChildren()){
		return this.oNode.oNodeChilds.childNodes[0].obj;
	}
	return null;
}
treeview.prototype.getLastChild = function(){
	if(this.hasChildren()){
		return this.oNode.oNodeChilds.childNodes[this.oNode.oNodeChilds.childNodes.length-1].obj;
	}
	return null;
}
treeview.prototype.hasChildren = function(){
	return this.oNode.oNodeChilds.childNodes.length > 0 ? true : false;
}
treeview.prototype.focus = function(){
	this.oNode.focus();	
}
treeview.prototype.getSelectedNode = function(){
	return this.selectedNode;	
}



function treenode(label,parentNode,src,href,target,icon,iconOpen,additionalIcons,onloadEvent,suspendRender, typeId, itemId,  itemText, itemLanguage, vat, currency){

	this.itemId = itemId;
	this.typeId = typeId;
	this.itemText = itemText;
	this.itemLanguage = itemLanguage;

	this.id;
	this.label = label || treeConfig.defaultLabel;
	this.parentNode = parentNode;
	this.treeview = parentNode.treeview;
	this.vat = vat;
	this.currency = currency;
	
	this.opened = false;
	this.loaded = false;
	this.loading = false;
	this.src = src;
	this.xmlNode;
	this.suspendRender = suspendRender || false;
	
	this.eventManager = new EventManager();
	var self = this;

	if(onloadEvent){
		var test = function(){new Function(onloadEvent).call(self)};
		this.eventManager.addListener("append",test);
	}


	if(!this.suspendRender){
		this.init();
	}
}
treenode.prototype.render = function(){
	this.init();	
}
treenode.prototype.setXMLAttributeCollection = function(xmlNode){
	this.xmlNode = xmlNode;
}

treenode.prototype.init = function(){
	// node container
	this.oNode = document.createElement("DIV");
	this.oNode.className = 'node';
	
	// toggle / t / l sections
	this.oNodeImg = document.createElement("IMG");
	this.oNodeImg.className = 'toggle';
	this.oNodeImg.src = treeConfig.blankIcon;

	// checkbox
	if(this.treeview.useCheckBox) {
	  this.oNodeCheckbox = document.createElement("INPUT");
		this.oNodeCheckbox.type = 'checkbox';
	}
		
	// node label
	this.oNodeLabel = document.createElement("SPAN");
	this.oNodeLabel.innerHTML = this.label.replace('&', '&amp;');
	this.oNodeLabel.action = this.href;
	this.oNodeLabel.className = 'label';
	
		// node label
	this.oNodeVat = document.createElement("SPAN");
	this.oNodeVat.innerHTML = this.vat;
	this.oNodeVat.action = this.href;
	this.oNodeVat.className = 'label';
	
	// node currency
	this.oNodeCurrency = document.createElement("SPAN");
	this.oNodeCurrency.innerHTML = this.currency;
	this.oNodeCurrency.action = this.href;
	this.oNodeCurrency.className = 'label';
	
	// childnodes container
	this.oNodeChilds = document.createElement("DIV");
	this.oNodeChilds.style.display = 'none';

	this.oNode.oNodeImg = this.oNode.appendChild(this.oNodeImg);
	if(this.treeview.useCheckBox) {
		this.oNode.oNodeCheckbox = this.oNode.appendChild(this.oNodeCheckbox);
	}
	
	this.oNode.oNodeLabel = this.oNode.appendChild(this.oNodeLabel);
//		this.oNode.oNodeVat = this.oNode.appendChild(this.oNodeVat);
	this.oNode.oNodeChilds = this.oNode.appendChild(this.oNodeChilds);
	
	// add events and listeners
	this.oNode.obj = this;
	if(this.treeview.useCheckBox) {
		this.oNodeCheckbox.obj = this;
	}
	this.oNodeLabel.obj = this;
	this.oNodeVat.obj = this;
	this.oNodeCurrency.obj = this;
	this.oNodeImg.obj = this;

	this.addEventHandlers();
	
	var self = this;
    this._onunload = function(){self.dispose()};
	if(document.addEventListener) { 
    	window.addEventListener("unload",this._onunload,false ); // Moz
   	}else if(window.attachEvent) {
     	window.attachEvent("onunload",this._onunload); // IE
   	}

	this.append();
	
	if(this.treeview.useCheckBox) {
		if(this.parentNode && this.parentNode.oNode.oNodeCheckbox && this.parentNode.oNode.oNodeCheckbox.state == 1) {
			this.oNodeCheckbox.checked = 'checked';
			this.oNodeCheckbox.state = 1;
		} else {
			this.oNode.oNodeCheckbox.checked = null;
			this.oNodeCheckbox.state = 0;
		}
	}

	

}

treenode.prototype.addEventHandlers = function(){
	this.oNodeLabel.onmousedown = oNodeLabel_onMousedown;
	this.oNodeLabel.onmouseover = oNodeLabel_onMouseover;
	this.oNodeLabel.onmouseout = oNodeLabel_onMouseout;
	this.oNodeLabel.oncontextmenu = oNodeLabel_onContextMenu;

	this.oNodeImg.onmousedown = oNodeImg_onMousedown;
	this.oNodeImg.ondragstart = function(){return false;}
	this.oNode.onmouseover = oNode_onMouseover;
	//eventhandler for drag & drop
	this.oNode.onmousedown = oNode_onMousedown;
	//eventhandler for checkboxes
	if(this.treeview.useCheckBox) {
		this.oNodeCheckbox.onclick = oNodeCheckbox_onClick;
	}
}

treenode.prototype.dispose = function(){
	// internal call, no this.obj needed
	// clear event handlers, and cyclic references
	// so the garbage collector actually can collect
	
	if(document.removeEventListener) {
	  window.removeEventListener("unload",this._onunload,false ); // Moz
	}else if(window.detachEvent) { 
	  window.detachEvent("onunload",this._onunload); // IE
	}
	
	// img
	this.oNodeImg.onmousedown = null;
	this.oNodeImg.obj = null;
	this.oNodeImg = null;
						
	// label
	this.oNodeLabel.onmousedown = null;
	this.oNodeLabel.oncontextmenu = null;
	this.oNodeLabel.obj = null;
	this.oNodeLabel = null;
	
	// nodechilds
	this.oNodeChilds = null;
	
	// node
	this.oNode.onmouseover = null;
	this.oNode.onmousedown = null;
	this.oNode.obj = null;
	this.oNode.oNodeImg = null;
	this.oNode.oNodeLabel = null;
	this.oNode.oNodeChilds = null;
	this.oNode = null;
	
	this.xmlNode = null;
	
	// remove all function calls (treeview config)
	for(var ref in this){
		this[ref] = null;
	}
	
	this.parentNode = null;
	this._onunload = null;
}



treenode.prototype.append = function(){

	this.parentNode.oNode.oNodeChilds.appendChild(this.oNode);
	
	// set toggle icons
	if(this.getPreviousSibling()){
		
		if(this.getPreviousSibling().obj.src || this.getPreviousSibling().oNodeChilds.childNodes.length){
			if(this.getPreviousSibling().obj.opened){
				this.getPreviousSibling().oNodeImg.src = treeConfig.tMinusIcon;
				this.getPreviousSibling().style.backgroundImage = 'url('+treeConfig.iIcon+')';
			}else{
				this.getPreviousSibling().oNodeImg.src = treeConfig.tPlusIcon;
			}
		}else{
			this.getPreviousSibling().oNodeImg.src = treeConfig.tIcon;
		}
	}
	
	if(this.src){
		this.oNodeImg.src = treeConfig.lPlusIcon;
	}else{
		this.oNodeImg.src = treeConfig.lIcon;
	}
	
	// plus icon if necessary on parentNode
	if(this.parentNode.oNode.oNodeImg){
		if(this.parentNode.opened){
			if(this.parentNode.getNextSibling()){
				this.parentNode.oNode.oNodeImg.src = treeConfig.tMinusIcon;
			}else{
				this.parentNode.oNode.oNodeImg.src = treeConfig.lMinusIcon;
			}
		}else{
			if(this.parentNode.getNextSibling()){
				this.parentNode.oNode.oNodeImg.src = treeConfig.tPlusIcon;
			}else{
				this.parentNode.oNode.oNodeImg.src = treeConfig.lPlusIcon;
			}
		}
	}
	
	this.eventManager.dispatch('append');
}

treenode.prototype.selectOnload = function(){
	if(!this.parentNode.loading){
		this.select();
	}else{
		//var self = this;
		//this.parentNode.eventManager.addListener("onload",function(){self.selectOnload()});
		var self = this;
		setTimeout(function(){self.selectOnload()},300);
	}
	
}

treenode.prototype.remove = function(disableBehaviour){
	// windows explorer behavior
	// on removal, select next available node
	// if it does not exist, select the parentNode
	// disableBehaviour is used for ex. the reload method where we do not want a new node to be selected
	
	if(!disableBehaviour){
		if(this.getNextSibling()){
			// prevent selection of the treenode, because the moveup command 
			// can only be initialized after the loadnode has been removed
			if(this.parentNode.oNode && this.parentNode.loading){
				this.getNextSibling().obj.selectOnload();
			}else{
				this.getNextSibling().obj.select();
			}
		}else if(this.parentNode.oNode && this.parentNode.select){
			this.parentNode.select();
		}
	}
				
	// change icons for parentNode (if childs == 0)
	if(!this.getPreviousSibling() && !this.getNextSibling()){
		if(this.parentNode.getNextSibling){
			if(this.parentNode.getNextSibling()){
				this.parentNode.oNodeImg.src = treeConfig.tIcon;
			}else{
				this.parentNode.oNodeImg.src = treeConfig.lIcon;
			}
		}
	}
	
	// change icons for previousSibling if it changes to a L intersection
	if(!this.getNextSibling() && this.getPreviousSibling()){
		if(this.getPreviousSibling().obj.opened){
			this.getPreviousSibling().oNodeImg.src = treeConfig.lMinusIcon;
		}else{
			if(this.getPreviousSibling().obj.src || this.getPreviousSibling().oNodeChilds.childNodes.length){
				this.getPreviousSibling().oNodeImg.src = treeConfig.lPlusIcon;
			}else{
				this.getPreviousSibling().oNodeImg.src = treeConfig.lIcon;
			}
		}
		this.getPreviousSibling().style.backgroundImage = 'url()';
	}
	
// GC mark & sweep suk it
	
	this.oNode.removeChild(this.oNode.oNodeImg);
	
	this.oNode.removeChild(this.oNode.oNodeLabel);
	this.oNode.removeChild(this.oNode.oNodeChilds);
	this.parentNode.oNodeChilds.removeChild(this.oNode);
	this.dispose();
}
treenode.prototype.select = function(){

	if(this.treeview.selectedNode && this.treeview.selectedNode.unselect){
		this.treeview.selectedNode.unselect();
	}
	
	this.setCssClass('select');
	this.treeview.selectedNode = this;
	
	if(this.treeview.onNodeSelectEventHandler){
		this.treeview.onNodeSelectEventHandler();
	}
}
treenode.prototype.unselect = function(){

	if(this.treeview.selectedNode.oNode.oNodeLabel){
		this.setCssClass('unselect');
	}
	this.treeview.selectedNode = null;
}
treenode.prototype.selectNext = function(){
	if(this.getNextSibling()){
		this.getNextSibling().obj.select();	
		return true;
	}
	return false;
}
treenode.prototype.selectPrevious = function(){
	if(this.getPreviousSibling()){
		this.getPreviousSibling().obj.select();
		return true;
	}
	return false;
}
treenode.prototype.getFirstChild = function(){
	if(this.hasChildren()){
		return this.oNode.oNodeChilds.childNodes[0].obj;
	}
	return null;
}
treenode.prototype.getLastChild = function(){
	if(this.hasChildren()){
		return this.oNode.oNodeChilds.childNodes[this.oNode.oNodeChilds.childNodes.length-1].obj;
	}
	return null;
}

treenode.prototype.hasChildren = function(){
	return this.oNode.oNodeChilds.childNodes.length > 0 ? true : false;
}
treenode.prototype.cut = function(){
	if(this.treeview.cuttedNode){
		this.treeview.cuttedNode.setCssClass('clear');	
	}
	this.treeview.cuttedNode = this;
	this.setCssClass('cut');
}
treenode.prototype.setLabel = function(label){
	this.label = label;
	this.oNodeLabel.innerHTML = label;
}
treenode.prototype.setVat = function(vat){
	this.vat = vat;
	this.oNodeVat.innerHTML = vat;
}

treenode.prototype.setCurrency = function(currency){
	this.currency = currency;
	this.oNodeCurrency.innerHTML = currency;
}

treenode.prototype.setCssClass = function(type){
	switch(type){
		case 'cut':
			this.oNodeLabel.className = 'label cutted';
		break;
		case 'select':
			this.oNodeLabel.className = 'label selected';
		break;
		case 'unselect':
			this.oNodeLabel.className = this.treeview.cuttedNode == this ? 'label cutted' : 'label';
		break;
		case 'target':
			this.oNodeLabel.className = 'label target';
		break;
		case 'clear':
			this.oNodeLabel.className = 'label';
		break;
		case 'over':
			if(this != this.treeview.getSelectedNode()){
				this.oNodeLabel.className = 'label hover';
			}
		break;
		case 'out':
			if(this != this.treeview.getSelectedNode()){
				this.oNodeLabel.className = 'label';
			}
		break;
	}
}

// return previous oNode DIV (use oNode.obj to get the treenode object)
treenode.prototype.getPreviousSibling = function(){
	return this.oNode.previousSibling;
}

// return next oNode DIV (use oNode.obj to get the treenode object)
treenode.prototype.getNextSibling = function(){
	return this.oNode.nextSibling;
}
treenode.prototype.isChildOf = function(parentNode){
	var tmp = this;
	
	while(!tmp.isRoot()){
		if(tmp == parentNode){
			return true;
		}else{
			if(tmp.parentNode.constructor != treeview){
				tmp = tmp.parentNode;
			}
		}
	}
	return false;
}

/* EVENTS AND HELPERS */
function cancelEventBubbling(e){
	if (!e){e=event}
	if(document.all){
		event.cancelBubble=true;
	}else if(e){
		e.preventDefault();
		e.stopPropagation();
	};
	return false;
};


function oNode_onMouseover(e){
	if (!e){e=event}
    cancelEventBubbling(e);
			
	// reset previous targetNode timer
	if((typeof(targetNode) != 'undefined' && targetNode != null)){
		if(targetNode.expandTimer != null){
			window.clearTimeout(targetNode.expandTimer);
		}
		targetNode.setCssClass('clear');
		//targetNode.oNodeLabel.className = 'label';
	}
	
	
	if((typeof(draggingNode) != 'undefined' && draggingNode != null)){
		targetNode = this.obj;
		
		this.obj.setCssClass('target');
		var self = this.obj;
		// expand when dragging another node, and the mouse waits for the closed node 3/4 of a second
		this.obj.expandTimer = window.setTimeout(function(){self.expand();},750);
	}
}
function oNodeLabel_onMousedown(){
	this.obj.select();
	if(this.obj.action != null){
		eval(this.obj.action);
	}
}
function oNodeLabel_onContextMenu(e){
    if (!e){e = event;}
	this.obj.select();
	
	// custom function found? execute it and pass the node with it
	if(window.onNodeContextMenu){
		onNodeContextMenu(this,e.clientX,e.clientY);
		return false;
	}
}

function oNodeLabel_onMouseover(e) {
	this.obj.setCssClass('over');
}

function oNodeLabel_onMouseout(e) {
	this.obj.setCssClass('out');
}

function oNodeImg_onContextMenu(e){
  if (!e){e = event;}
  oNodeLabel_onContextMenu(e);
}
function oNodeLabel_onRename(){
	this.obj.onRename();
}
function oNodeImg_onMousedown(){
	this.obj.toggle();
}
function oNodeCheckbox_onClick(){
	this.obj.toggleCheckbox();
}