/*
	TREE.JS:
	This file is an extention of the regular SELECT control that can show
	a hirarchical list instead of the regular option list in a select control.
	The hirarchical list is represented by an XML file, that it's url is set in
	the xmlSrc attribute of the select.
	If the xmlSrc attribute does not exist, the select is a regulat select.
	The HTC is doing the following:
	1. It creates a DIV that willl contain the HTML tree itself, and a combination
		 of an EDIT control and IMAGE button that will replace the original select in
		 the user interface.
	2. The oroginal select will be hidden, but it still remain the main object
		 (the 'this' in this HTC), and will be submitted with the FORM as it was
		 a regular select.
	3. If the select has SIZE attribute, the EDIT and IMAGE will be hidden.	
	4. Hebrew version: different stylesheet, align right, different xsl, inverted plus and minus.
*/
var rootPath = "/ssi" ;
var defaultHTML ;
var hiddenHTML ;
var defaultText ;
var languageInfo ;
window.mozilla = (window.document.implementation && window.document.implementation.createDocument ) ? true : false ;

function LanguageInfo( language )
{
    hiddenHTML = '<img src="' + rootPath + '/tree/images/loading.gif" style="display:none">' ;
    defaultHTML = '<center><br><img src="' + rootPath + '/tree/images/loading.gif"></center>' ;
    var s = window.document.styleSheets[0] ;
    if ( language == 'Hebrew' )
    {        
        defaultText = "èåòï..." ;
        this.stylesheetName = rootPath + '/tree/htree.css' ;
        if ( mozilla )
        {
            try
            {
		        s.insertRule('DIV.tree{	font-family: sans-serif; font-weight: normal; font-size: 8pt;	background-color: white;	overflow: auto ;}',0) ;
		        s.insertRule('DIV.tree .plain{text-decoration : none;}',0) ;
		        s.insertRule('DIV.tree .hover{text-decoration : none;	font-color : #FFFFFF;	background-color : #FF9900;	cursor: pointer ;}',0) ;
		        s.insertRule('DIV.tree .disabled{	font-family: sans-serif;			cursor: default; color: navy; font-weight: bold;	text-decoration : none;}',0) ;
		        s.insertRule('DIV.tree .selected{	color:white ;	background-color:darkblue ;	text-decoration : none;}',0) ;
		        s.insertRule('DIR	{ margin-top: 0 ; margin-bottom: 0 ; }',0) ;		        
		    }
		    catch(x)
		    {
		        //alert ( "insertRule is not supported") ;
		    }
		}
		this.align="right" ;
    }
    else
    {   
        defaultText = 'Loading...' ;
        this.stylesheetName = rootPath + '/tree/tree.css' ;
        if ( mozilla )
        {
            try
            {
		    s.insertRule('DIV.tree{	font-family: Verdana, Helvetica, sans-serif;font-weight: normal;font-size:  xx-small;	background-color: white;	overflow: auto ;}',0) ;
		    s.insertRule('DIV.tree .plain{text-decoration : none;}',0) ;
		    s.insertRule('DIV.tree .hover{text-decoration : none;	color : #FFFFFF;	background-color : #FF9900;	cursor: pointer ;}',0) ;
		    s.insertRule('DIV.tree .disabled{	font-family: Verdana, Helvetica, sans-serif;cursor: default;color: gray;	text-decoration : none;}',0) ;
		    s.insertRule('DIV.tree .selected{	color:white ;	background-color:darkblue ;	text-decoration : none;}',0) ;
		    s.insertRule('DIV.tree SPAN.indent{	position:relative; left: 10 ;	display: block ;}',0) ;            
		    }
		    catch(x)
		    {
		        //alert ( "insertRule is not supported") ;
		    }
		    
		}
		this.align = "left" ;
    }    
}

/*
var this = document.getElementById("mytree") ;
this.cursorHand = 'hand' ;
this.log = '' ;
this.htcState = 0 ;
*/

var INIT_BASIC = 1 ;
var INIT_GUI = 2 ;
var INIT_XSL = 4 ;
var INIT_XML = 8 ;
var INIT_HTML = 16 ;
var INIT_VALUE = 32 ;
var INIT_PARENT = 64 ;
var INIT_XML_ONLOAD = 512 ;
var INIT_ERROR = 1024 ;
var INIT_READY = 2048 ;

var INIT_ALL = INIT_BASIC|INIT_GUI|INIT_XSL|INIT_XML ;

var system_elements = new Array( 'SELECT', 'OBJECT' ) ;

function initializeAll( root, lang )
{
    if ( root ) rootPath = root ;
    languageInfo = new LanguageInfo( lang ) ;
    
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Pre Load images to prevent the browser loading every image again
	if ( !document.plusImg )
	{
		document.plusImg = new Image; document.plusImg.src = rootPath + "/tree/images/hplus.gif";
		document.minusImg = new Image; document.minusImg.src = rootPath + "/tree/images/hminus.gif";
		document.browseImg = new Image; document.browseImg.src = rootPath + "/tree/images/browse.gif" ;
		document.eraseImg = new Image; document.eraseImg.src = rootPath + "/tree/images/btn_erase.gif" ;
		document.loadingImg = new Image; document.loadingImg.src = rootPath + "/tree/images/loading.gif" ;
		try
		{ window.ampFilter= new RegExp("\&amp;","mg"); }
		catch(xxx){ window.ampFilter=null; } // Support Peter's machine		
	}  
    
	var elements = document.getElementsByTagName('SELECT' ) ;
	var i ;
	for ( i = 0 ; i < elements.length ; i++ )
	{
		elements[i].initialize = initialize ;
		elements[i].initialize() ;
	}
}

///////////////////////////////////////////////////////////////////////
// PROPERTIES
///////////////////////////////////////////////////////////////////////
// These are the properties have a 'get' and 'put' handlers.
// If the HTML source contains property="value" for a property has a 'put'
// handler, this handler will be called BEFORE the 'oncontentready' event
// (in Explorer, but not in Mozilla)
// ---------------------------------------------------------------------

function getDefaultValue()
{
	return this.defaultValueSave ? this.defaultValueSave : '' ;
}

// The VALUE property is the one that will be submitted with the FORM.
// If the select is not defined as multiple selection it is just the value of
// the selected option, In multiple selection control, it is a comma separated list.
function getValue()
{
	var i ;
	if ( !this.xmlSrc && this.options.length ) return this.options[this.selectedIndex].value ;
	if ( !(htcState & INIT_VALUE) ) return this.rawValue ;
	if ( options.length == 0 ) return '' ;
	if ( !multiple ) return this.options[selectedIndex].value ;
	var v='', i ;
	for ( i = 0 ; i < this.options.length ; i++ ) v+= this.options[i].value + ',' ;
	v = v.slice(0,v.length-1) ;
	return v ;
}

// This handler is doing a much more than just set the value.
// It ensuring initialization (because it can be called before the onthisready event)
// It reveals the node set, it slected it, it rebuilds the node's HTML if it is
// shown, it does NOT fire the 'onchange' (onchange is fired only by user's operations).
function putValue(v)
{
	if ( v && this.parent ) this.onParentChange() ; // Ensure assignment will not be deleted by parent
	this.rawValue = v ;
	this.log += 'Raw Value: ' + v + '\r\n' ;

	if ( !this.htcState ) this.htcState = 0 ;
	if ( !(this.htcState & INIT_READY) ) return ;

	// Multiple selection accepts a comma separated list
	if ( this.multiple )
	{
		// Delete previous options
		while ( this.options.length > 0 )
		{
			var node = this.getNode(this.options[0].value ) ;
			if ( node ) node.removeAttribute('selected') ;
			this.options.remove(0) ;
		}
		this.text = '' ;
		if (v)
		{
			var arrV = v.split(',') ;
			for ( var i = 0 ; i < arrV.length ; i++ )
			{
				var node = this.getNode( arrV[i] ) ;
				if ( node )
				{
					node.setAttribute( 'selected','yes') ;
					this.revealNode(node) ;
					this.text += node.getAttribute('description') + ',' ;
					this.options.add( window.document.createElement('option') ) ;
					this.selectedIndex = 0 ;
					this.options[this.options.length - 1].value = arrV[i] ;
					this.options[this.options.length - 1].selected = true ;
					this.options[this.options.length - 1].innerText = node.getAttribute('description') ;
				}
			}
		}
		if ( this.div && this.div.style.diaplay != 'none' )
			this.rebuildNode( this.xml ) ;
		else
			this.htcState &= ~INIT_HTML ;
		return ;
	}
	// Ensure v is a string object
	if ( !v ) v = '' ;
	if ( (this.htcState & INIT_VALUE) && v == this.value && !this.level ) return ; // Nothing changed

	// Remove the previous selection
	if ( this.value )
	{
		var oldnode = this.getNode( this.value ) ;
		if ( oldnode )
		{
			oldnode.setAttribute( 'selected', 'no' ) ;
			this.rebuildNode( oldnode ) ;
		}
	}

	// Here we actually setting the value in most cases.
	this.options[0].value = v ;
	this.selectedIndex = 0 ;
	
	if ( v )
	{
		var node = this.getNode(v) ;
		if ( node )
		{
			this.htcState |= INIT_VALUE ;
			var nodeToOpen = node ;
			node.setAttribute( 'selected', 'yes' ) ;
			// Reveal the selected node and find the closest open ancestor.
			for ( var e = node.parentNode ; e.nodeName == 'node' ; e = e.parentNode )
			{
				if ( e.getAttribute ( 'open' ) != 'yes' )
				{
					e.setAttribute( 'open', 'yes' ) ;
					nodeToOpen = e ;
				}
			}
//			if ( this.edit )
			{
				var txt='' ;
				if ( Exist(this.showFullPath) )
				{
					while( node.nodeName == 'node' )
					{
						txt = node.getAttribute('description') + '/' + txt ;
						node = node.parentNode ;
					}
					txt = txt.slice( 0, txt.length-1) ;
				}
				else txt = node.getAttribute('description') ;
				// translate unicode characters
				this.text = txt.replace(/\&\#(\d{4});/g,function($0,$1){return String.fromCharCode($1) ;} ) ;
			}
			if ( this.htcState & INIT_HTML )
			{
				this.rebuildNode( nodeToOpen ) ;
				if ( this.div.style.display != 'none' ) // The div is visible now
					this.scroll();
			}
		}
	}
	else // new value is an empty string or null
	{
		this.text = '' ; // Clear the edit control
		if ( this.to ) window.clearTimeout(this.to) ;
		if ( this.level ) // multi level select
		{
			// Reset all to first level
			this.level = 0 ;
			this.initXml() ;
		}
		this.htcState |= INIT_VALUE ;
	}
	if (!mozilla ) this.fireEvent('onafterupdate') ;
	if ( mozilla ) this.changeProprty( 'text' ) ;
}

///////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
///////////////////////////////////////////////////////////////////////

function setFocus()
{
	try
	{
		if ( this.size > 0 )
			this.div.focus() ;
		else
			this.edit.focus() ;
	}
	catch (xxx) {} ;
}

// Open all ancestor nodes to reveal the desired one
function revealNode(node)
{
	node = getNode(node) ;
	if ( node )
	for ( n=node.parentNode ; n.nodeName != 'root' ; n = n.parentNode ) n.setAttribute('open','yes') ;
}

// This function is really belongs to XML not to the tree obkect.
// It runs the function func for the node node and all its children hierarchy.
function forBranch( node, func, sum )
{
	for ( var i = 0 ; i < node.childNodes.length ; i++ )
		sum+=forBranch(node.childNodes[i], func) ;
	func(node,sum) ;
	return sum ;
}

// Set a level's value in a multilevel tree, and andvance the level by one
function setLevelValue(levelbefore, v)
{
	if ( this.base_level_input ) this.base_level_input.value = v ;
	this.options[levelbefore].value = v ;
	this.level = levelbefore+1 ;
	selectedIndex = this.level ;
	initXml() ;
}

// Show only nodes defined in the dataXML.
// dataXML is in format returned by ADO. meaning fot every node: <row><ID>myid</ID></row>
function showPickedNodes( dataXML )
{
	if ( dataXML.childNodes.length == 0 ) return ; // Nothing means all
	forBranch( this.xml, function(n){n.setAttribute('hidden', 'yes');n.removeAttribute('appendix')} ) ;
	this.xml.removeAttribute('hidden') ;
	for ( var i = 0 ; i < dataXML.childNodes.length ; i++ )
	{
		var id = dataXML.childNodes[i].getAttribute('id') ;
		var node = getNode(id) ;
		if ( node )
		{
			forBranch(node, function(n){n.setAttribute('hidden', 'no' ) ;} ) ;
			for ( var n = node.parentNode ; n.getAttribute('hidden') == 'yes'; n=n.parentNode )
			{
				n.setAttribute ( 'hidden', 'no' ) ;
				n.setAttribute ( 'gray', 'yes' ) ;
			}
		}
	}
	rebuildNode( this.xml ) ;
}

// return the XML node that has the ID specified or the node itself if it is
// already a node, or the root node if id is null
function getNode(id)
{
	if ( !this.xml ) return null ;
	if ( id == null ) return this.xml ;
	if ( id.nodeName ) return id ;
	return this.cbXmlDoc.findNode ( '//node[@id="'+id+'"]' ) ;
}


// Convert the node to HTML and update it in the DIV.
// If the node passed is the root node then all the HTML is rebuilded, else,
// only the specified node and it's children
function rebuildNode(node)
{	
	if ( !(this.htcState & INIT_READY) ) { alert ('NOT READY' ) ; return ; }
	if ( !this.div )  return ;
	node = this.getNode(node) ;
	if ( !node )  { alert ('NO NODE' ) ; return ; }
	var img ;
	if ( node.nodeName == 'node' )
	{
		img = this.findHTMLNode( node.getAttribute('id' )) ;
	}
	else // Build all
	{
		img=this.div.firstChild ;
		this.htcState |= INIT_HTML ;
	}

	if ( !img ) { alert ('NO IMG' ) ; return ; }
	
	if ( this.emptyMessage && this.xml.childNodes.length == 0 )
	{
		this.div.innerHTML = emptyMessage ;
		return ;
	}

	if ( this.customProperty > '' )
	{
		var parentValue = '' ;
		if ( this.hierarchicalCustomProperty=='yes' )
		{
				for ( var e = node ;
							e.nodeName != 'root' && !e.getAttribute(customProperty) ;
							e=e.parentNode ){}
			this.parentValue = e.getAttribute(customProperty) ;
		}
		this.buildCustomProperty(node, parentValue ? parentValue.toLowerCase(): '')
	}
	if ( this.hierarchicalSelection )
	{
		this.forBranch( this.xml, function(n){ n.removeAttribute('parentSelected') ; }) ;

		var selectedNodes = this.cbXmlDoc.xmlDoc.selectNodes('//*[@selected="yes"]') ;
		for ( var i = 0 ; i < selectedNodes.length ; i++ )
		{
			if ( selectedNodes[i].getAttribute('selected') == 'yes' )
			{
				this.forBranch( selectedNodes[i], function(n){
					n.setAttribute('parentSelected','yes') ;
					n.removeAttribute('selected') ;
				}) ;
				// Return back attributes of selected node
				selectedNodes[i].setAttribute('selected','yes') ;
				selectedNodes[i].removeAttribute('parentSelected') ;
			}
		}				
	}
	
	this.cbXmlDoc.transform(node,img) ;	
   
	while (img.nextSibling && img.nextSibling.nodeName.toUpperCase() != 'IMG' )
	{
		img.nextSibling.parentNode.removeChild(img.nextSibling) ;	
	}
	img.parentNode.removeChild(img) ;
}

function removeChildren( node )
{
    var i ;
    for ( i = 0 ; i < node.childNodes.length ; i++ )
    {
        RemoveChildren( node.childNodes[i] ) ;
    }
    node.parentNode.removeChild(node) ;
}

function showLongDescription( node )
{
	var text_to_show = '' ;
	this.initializeDiv() ;
	if ( node )
	{
		node = this.getNode( node ) ;
		text_to_show = node.getAttribute ( 'long_description' ) ;
		if ( !text_to_show ) return ;
	}
	else
		text_to_show = this.log ;

	if (!this.popup)
	{
		this.popup = createPopup();
		this.popup.document.createStyleSheet( languageInfo.stylesheetName ) ;
		this.popup.document.body.className = 'tree_popup' ;
		this.popup.document.body.style.border = '2px solid #497FB7' ;
		this.popup.document.body.style.overflow = 'auto' ;
	}
	this.popup.document.body.innerHTML = text_to_show.replace(/\n/g, '<BR>') ;
	if ( this.div.offsetWidth )
	{
		var leftPos = this.div.offsetWidth ;
		var left = 0 ;
		var e = this.div ;
		while ( e ) { left += e.offsetLeft ; e = e.offsetParent ; }
		if ( this.document.body.offsetWidth - left < leftPos * 2 ) leftPos = -leftPos ;
		this.popup.show(leftPos,0,this.div.offsetWidth,this.div.offsetHeight,this.div) ;
	}
	else
		this.popup.show(0,0,this.width,this.height,event.srcElement) ;
}

function setXmlSrc()
{
	this.level = 0 ;
	this.arrXmls = new Array('') ;
	
	// xmlSrc can be an XML document object
	
	if ( this.xmlSrc && typeof(this.xmlSrc) == 'object' )
	{
		if ( typeof(this.xmlSrc.documentElement)=='object' )
		{
			if (!this.cbXmlDoc) this.cbXmlDoc = new CrossBrowserXml ;
			this.cbXmlDoc.xmlDoc = this.xmlSrc ;
		}
		else
		{
			var txt = this.xmlSrc.value ? this.xmlSrc.value : this.xmlSrc.innerHTML ;
			this.cbXmlDoc = new CrossBrowserXml( txt ) ;
		}
		onXmlLoaded(this) ;
		if ( this.htcState & INIT_READY ) this.rebuildNode() ;
		return ;
	}

	// It can ber also a url string or an empty string or just null
	if ( typeof(this.xmlSrc) != 'string' ) this.xmlSrc = '' ;
	// Can be an ID of an xml data island
	if ( this.xmlSrc && this.xmlSrc.indexOf('.') < 0 ) 
	{
		this.xmlSrc = window.document.getElementById(this.xmlSrc) ;
		this.changeProprty('xmlSrc') ;
		return ;
	}	
	this.arrXmls = this.xmlSrc.split(',') ;
	this.initXml() ;
}

///////////////////////////////////////////////////////////////////////
// EVENT HANDLERS
///////////////////////////////////////////////////////////////////////

function onPropertyChanged(e)
{
	var ev = new CrossBrowserEvent(e) ;
	var element = ev.srcElement ;
	
	try	{ if ( !element.xmlSrc ) return ; } catch(xxx){return;}

	switch ( ev.event.propertyName )
	{
		case 'parentValue':
			element.log += 'Parent Value: ' + element.parentValue +'\r\n' ;
			break ;

		case 'align':
			element.edit.align = this.align ;
			break ;

		case 'text':
			if (element.edit) element.edit.value = element.text ;
			break ;

		case 'xmlSrc':
			element.setXmlSrc() ;
			break ;

		case 'parent':
			element.setParent() ;
			break ;

		case 'readonly':
		case 'disabled':
			{
				var editable = !( element.readonly || element.disabled ) ;
				if ( element.btn )	element.btn.style.display = editable? '' : 'none' ;
				if ( element.erasebutton ) element.erasebutton.style.display  = editable? '' : 'none' ;
				if ( element.edit && element.xml ) element.edit.readonly = !editable ;
			}
			break ;
	}
}

function onParentChange()
{
	if ( this.parentValue != parent.value )
	{
		this.parentValue = this.parent.value ;
		if ( this.htcState & INIT_PARENT )
		{
			this.value = '' ;
			this.htcState &= ~INIT_VALUE ;
		}
		else
			this.htcState |= INIT_PARENT ;
		initXml() ;
	}
}

function onParentUserChange()
{
	onParentChange() ;
	fireEvent('onchange') ;
}

function onXslLoaded( element )
{
	try	{	if ( (element.htcState & INIT_XSL) == 0 && window.cbXslTree.getStatus() == 'ok' )
		{
			element.onAfterInit(INIT_XSL) ;
			
			if ( window.cbXslTree.owner == element ) //xslEvent.fire(createEventObject());
			{
				var i ;
				var elements = document.getElementsByTagName("SELECT") ;
				for ( i = 0 ; i < elements.length ; i++ )
				{
					if ( elements[i] != element && elements[i].xmlSrc )
					{
						onXslLoaded( elements[i] ) ;
					}
				}
			}
		}
	}
	catch(xxx) {} ;
}

function onSharedXMLLoaded()
{
	if ( !(this.htcState & INIT_XML_ONLOAD) ) return ;
	this.htcState &= ~INIT_XML_ONLOAD ;
	this.log += 'Clone of shared XML source: ' + cbXmlDoc.origin.name + '\r\n' ;
	this.xml = cbXmlDoc.origin.xml.cloneNode(true) ;
	onAfterInit(INIT_XML) ;
	onXslLoaded() ; // Ensure XSL is also existing
}

function onXmlLoaded(element)
{
	if ( !element ) element = this ;
	
	switch (element.cbXmlDoc.getStatus())
	{
		case 'ok':
			element.htcState &= ~INIT_ERROR ;
			// Synchronize
			if ( !element.mozilla && !element.cbXmlDoc.badProperty && element.cbXmlDoc.getProperty ( "ForcedResync" ) == false )
			{
				// XML got possibly from cache
				var valid = (this.cbXmlDoc.xmlDoc.documentElement ? true : false ) ;
				element.log += 'Time in Xml: ' + element.cbXmlDoc.xmlDoc.documentElement.getAttribute('time') + '\r\n' ;
				if ( valid && this.time &&
						element.time != this.cbXmlDoc.xmlDoc.documentElement.getAttribute('time')) valid = false ;
				if ( !valid )
				{
					element.log += 'Got from server because cache was not updated\r\n' ;
					element.cbXmlDoc.setProperty ( "ForcedResync", true ) ;
					//element.cbXmlDoc.xmlDoc.load( element.cbXmlDoc.xmlDoc.url ) ;
					element.cbXmlDoc.load( element.cbXmlDoc.xmlDoc.url, element ) ;
					return ;
				}
				element.log += 'Got from cache\r\n' ;
			}
			element.cbXmlDoc.setProperty("SelectionLanguage", "XPath");
			element.xml = element.cbXmlDoc.xmlDoc.documentElement ;
			element.htcState &= ~INIT_XML_ONLOAD ;
			element.onAfterInit(INIT_XML) ;
			onXslLoaded(element) ; // Ensure XSL is also existing
			return ;

		case 'error':
			if ( !element.mozilla && !element.cbXmlDoc.badProperty && element.cbXmlDoc.getProperty ( "ForcedResync" ) == false )
			{
					element.log += 'Got from server after an error\r\n' ;
					element.cbXmlDoc.setProperty ( "ForcedResync", true ) ;
					element.cbXmlDoc.load( element.cbXmlDoc.xmlDoc.url, element ) ;
					return ;
			}

			element.htcState &= ~INIT_XML_ONLOAD ;
			element.htcState |= INIT_ERROR ;
			if ( element.edit )
			{
				element.edit.title = cbXmlDoc.errorMessage +cbXmlDoc.xmlDoc.url ;
				element.edit.value = 'XML Error' ;
			}
			else if (element.div)
			{
				element.div.innerHTML = element.cbXmlDoc.errorMessage + element.cbXmlDoc.xmlDoc.url ;
			}
			return ;
	}
}

function autoFill(e)
{
	var ev = new CrossBrowserEvent(e) ;
	var element = ev.srcElement.parent ;
	
	if ( ev.event.keyCode==9 ) return ;
	if ( ev.event.ctrlKey ) return ;
	if ( ev.event.altKey ) return ;
	ev.abort() ;

	if ( element.edit.readonly ) 
	{
		return ; // Read only can still acsept events!
	}

	element.edit.onblur=AutoFillChangedValue ;
	// only do for letters, spaces, and number keys
	if (!((ev.event.keyCode==46) ||
		(ev.event.keyCode==188) ||
		(ev.event.keyCode==189) ||
		(ev.event.keyCode==190) ||
		(ev.event.keyCode==191) ||
		(ev.event.keyCode==8) ||
		(ev.event.keyCode > 64 && ev.event.keyCode < 123) ||
		 ev.event.keyCode==32 ||
		(ev.event.keyCode>47 && ev.event.keyCode< 58)))
		{
			return ;
		}
	if(mozilla)
		element.edit.value = element.edit.value.slice(0,element.edit.selectionStart) ;
	else
		window.document.selection.clear();
	var curText = element.edit.value ;
	if ( ev.event.keyCode!=46 ) // Delete
	{
			switch( ev.event.keyCode )
			{
				case 8:
					curText = curText.slice(0,curText.length-1) ; break ;
				case 188:
					curText += ',' ; break ;					
				case 189:
					curText += '-' ; break ;
				case 190:
					curText += '.' ; break ;				
				case 191:
					curText += '/' ; break ;				
				default:
					curText += String.fromCharCode(ev.event.keyCode)
			}
//	curText + ((ev.event.keyCode == 189)?'-':String.fromCharCode(ev.event.keyCode));
	}
	element.tmpValue='' ; // If text is not found, delete it...
	if ( curText )
	{
		var strExp = "//node[(not(@gray = 'yes')) and (not(@hidden = 'yes')) and starts-with(translate(@description,'àáâãäåæçèéëìîðñòôö÷øùúABCDEFGHIJKLMNOPQRSTUVWXYZ', 'àáâãäåæçèéëìîðñòôö÷øùúabcdefghijklmnopqrstuvwxyz'),'" + curText.toLowerCase() + "')]" ;
		var nd = element.cbXmlDoc.findNode( strExp ) ;

		if ( !nd )
		{
			curText = element.edit.value ;
			if ( curText ) nd = element.cbXmlDoc.findNode("//node[(not(@gray = 'yes')) and (not(@hidden = 'yes')) and starts-with(translate(@description,'àáâãäåæçèéëìîðñòôö÷øùúABCDEFGHIJKLMNOPQRSTUVWXYZ', 'àáâãäåæçèéëìîðñòôö÷øùúabcdefghijklmnopqrstuvwxyz'),'" + curText.toLowerCase() + "')]");
		}
		if (nd)
		{
			element.edit.value = nd.getAttribute("description");
			element.tmpValue=nd.getAttribute("id");
			if ( mozilla)
				element.edit.setSelectionRange( curText.length,element.edit.value.length) ;
			else
			{
				var tmpRange = element.edit.createTextRange();
				tmpRange.moveStart("character", curText.length);
				tmpRange.select();
			}
		}
	}
	if ( !element.tmpValue ) { text = '' ; }
}

function AutoFillChangedValue(e)
{
	var ev = new CrossBrowserEvent(e) ;
	var element = ev.srcElement.parent ;
	
	if ( element.arrXmls.length > element.level + 1 ) // Multi level
	{
		element.setLevelValue(element.level,element.tmpValue) ;
	}
	else
	{
		element.putValue(element.tmpValue) ;
		element.edit.onblur = null ;
		element.fireEvent('onchange') ;
	}
}

function setPosition()
{
	try {

// Read positioning
	var left=0, top=0, e, height = parseInt(this.div.style.height) ;
	var topSave, leftSave ;
	var relativeObj = this.button ? this.btn : this.edit ;
	e = relativeObj ;
	while ( e )
	{
		left += e.offsetLeft ;
		top += e.offsetTop ;
		e = e.offsetParent ;
	}
	topSave=top ; leftSave = left ;
	if ( top > height + 50 )
		top -= height ;
	else
		top += relativeObj.offsetHeight ;
	if ( this.button && left > parseInt(this.div.style.width) )
		left -= parseInt(this.div.style.width) - this.btn.offsetWidth ;
	if ( this.edit && this.btn )
		this.div.style.width = parseInt(this.edit.offsetWidth) + parseInt(this.btn.offsetWidth) ;
	this.div.style.left = left - (this.pop ? leftSave : 0 ) ;
	this.div.style.top = top  - (this.pop ? topSave : 0 ) ;;
	} catch(xxx){}
}

function Hide(e)
{
	window.document.detachEvent( 'onmousedown', Hide ) ;
	var element = window.activeTree ;
	if(!element ) return ;

	if ( element.pop )	element.pop.hide() ;
	element.div.style.display='none' ;

	for ( var se = 0 ; se < system_elements.length ; se++ )
	{
		var selects = window.document.getElementsByTagName(system_elements[se]) ;
		for ( var i = 0 ; i < selects.length ; i++ ) selects[i].style.visibility = 'visible' ;
	}
}

// Called when the user is clicking on the tree itself (not the button)
function onDivClick(e)
{
	var ev = new CrossBrowserEvent(e) ;
	var element = FindMe(ev.srcElement) ;
	var changed = false ;
	status = '' ;
	if ( element.disabled || element.readonly ) return ;
	if ( ev.className == 'disabled' || ev.className == 'parentSelected' ) return ;
	var img = ev.srcElement.previousSibling ;
	if ( !img ) return ;
	if ( img.nodeName.toUpperCase() != 'IMG' ) return ;
	// Find the node object in HTML	
	
	var node = element.getNode( img.name ) ;
    if ( node.getAttribute('href'))
    {
        if ( element.getAttribute('target') )
        {
            target = document.getElementById(element.getAttribute('target') ) ;
            if ( target )
            {
                target.src = node.getAttribute('href') ;
                return ;
            } 
        }
        location.href = node.getAttribute('href') ;
        return ;
    }

	// Go to the URL
	if ( element.getAttribute('href') > '' )
	{
		try {window.location.href = element.getAttribute('href') + img.name;} catch(xxx){}
	}
	else
	if ( element.multiple )
	{
		if ( node.getAttribute( 'selected') == 'yes' )
		{
			for ( var i = 0 ; i < this.options.length ; i++ )
			{
				if ( element.options[i].value == img.name )
					element.options.remove(i) ;
			}
		}
		else
		{
			element.options.add( window.document.createElement("option")) ;
			element.options[this.options.length-1].value=img.name ;
			element.options[this.options.length-1].innerText=node.getAttribute('description') ;
			element.options[this.options.length-1].selected=true ;
		}
		node.setAttribute( 'selected',
		node.getAttribute( 'selected') == 'yes' ? 'no' : 'yes' ) ;
		element.rebuildNode( node ) ;

		element.text = '' ;
		for ( var i = 0 ; i < options.length ; i++ ) 	element.text += options[i].innerText + ',' ;
		changed = true ;
		element.htcState |=  INIT_VALUE ;
	}
	else
	{
		if ( element.arrXmls.length > element.level + 1 ) // Multi level
		{
			element.setLevelValue(element.level,img.name) ;
			return ;
		}
		else
		{
			if ( element.value != img.name )
			{
				element.putValue( img.name ) ;
				//element.value = img.name ;
				changed = true ;
			}
		}
	}
	if ( element.size <= 0 ) element.Hide() ;
	
	if ( changed  ) 
	{	
	    if ( element.onchange ) element.onchange() ;
//    	    element.fireEvent('onchange', e ) ;	
	}
	
}

function onDivMouseOver(e)
{    
	var ev = new CrossBrowserEvent(e) ;
	
	switch ( ev.className )
	{
		case 'plain':
		case 'selected':
		{			
			ev.srcElement.classSave = ev.srcElement.className ;
			ev.srcElement.className = 'hover' ;
			if (!mozilla)
			   status = ev.srcElement.innerText + ' [' +  ev.srcElement.previousSibling.name + ']' ;
			ev.element.currentSelection = ev.name ;
			if ( ev.element.onmouseover ) ev.element.onmouseover() ;
		}		
	}	
}

function onDivMouseOut(e)
{
	var ev = new CrossBrowserEvent(e) ;
	if ( ev.className == 'hover' )
		ev.srcElement.className = ev.srcElement.classSave ;
	status = '' ;
	if ( ev.element.onmouseout ) ev.element.onmouseout() ;
}

function onButtonRightClick(e)
{
		var ev = new CrossBrowserEvent(e) ;
		//alert ( event.srcElement.parent.log ) ;
		var src = mozilla ? this : ev.srcElement ;
		var element = FindMe(src) ;
		
		ev.stop() ; 	ev.abort() ;
		element.showLongDescription() ;
}

function onDivRightClick(e)
{
		var ev = new CrossBrowserEvent(e) ;
		var src = mozilla ? this : ev.srcElement ;
		var element = FindMe(src) ;
		var img = src.previousSibling ;
		if ( !img ) return ;
		if ( img.nodeName == 'IMG' )
		{
			ev.stop() ; 	ev.abort() ;
			element.showLongDescription(img.name) ;
		}
		else // display log...
			if (!mozilla && event.ctrlKey && event.shiftKey ) showLongDescription() ;
}

function FindMe( obj )
{
	while ( obj.nodeName != 'DIV' )
	{
		obj = obj.parentNode ;
		if ( !obj ) return null ;
	}
	return obj.owner ;
}

function onDivMouseDown(e)
{
	var ev = new CrossBrowserEvent(e) ;
	ev.stop() ; // Dont pass it to body
	var element = FindMe( ev.srcElement ) ;
	var img = ( ev.className == 'disabled' ) ? ev.srcElement.previousSibling : ev.srcElement ;
	if ( img.nodeName.toUpperCase() == 'IMG' )
	{
		var node = element.getNode(img.name) ;
		if ( node )
		{
			var op = node.getAttribute("open") ;
			node.setAttribute ( "open", ( op == 'yes' )?  'no' : 'yes' ) ;
			element.rebuildNode(node, img) ;
		}
		ev.abort() ;
	}
}

function animation()
{
	if( parseInt(this.div.style.height) < this.height )
	{
		this.div.style.height = parseInt(this.div.style.height) + 20 ;
		setTimeout(animation, 6 ) ;
		return ;
	}
	this.div.style.height = this.height ;
	this.div.style.overflowY = 'auto' ;
}

function onButtonClicked(e)
{
	var ev = new CrossBrowserEvent(e) ;	
	var element = mozilla ? this.parent : ev.srcElement.parent ;
	window.activeTree = element ;
	ev.stop() ;
	ev.abort() ;
	
	if ( element.disabled || element.readonly ) return ;
	if ( element.htcState & INIT_ERROR )
	{
		alert ( 'XML load failed\r\n' + cbXmlDoc.errorMessage ) ;
		return ;
	}

	// Is object ready?
	if ( (element.htcState & INIT_READY) == 0 )
	{
		// waiting for parent to be set
		if ( element.parent && element.parent.value == '' )
		{
			var ae = 'AEIOUaeiou' ;
			alert('Please choose a' + ( ae.indexOf(element.parent.description.charAt(0)) >= 0 ? 'n ' : ' ' ) + element.parent.description ) ;
			if (	 element.parent.size <= 0 )	element.parent.focus();
		}
//		else	alert ( 'Loading, please wait...\r\n(' + htcState +')' ) ;
		return ;
	}

	element.initializeDiv() ; // ensure div is existing
	if(!mozilla)window.document.fireEvent('onmousedown') ;
	element.setPosition() ;
	if ( element.button ) element.value = '' ;
//	this.div.style.overflowY = ( this.div.scrollHeight > this.div.clientHeight ) ?	'scroll' : 'hidden' ;
//	this.div.style.height = 10 ;
	element.div.style.display='' ;
//	setTimeout(animation, 100 ) ;

	if ( element.pop )
	{
		element.div.style.display = '' ;
		var t = parseInt(this.div.style.top) ;
		var l = parseInt(this.div.style.left) ;
		element.div.style.top = 0 ;
		element.div.style.left = 0 ;
		//this.pop.show( l,t,parseInt(this.div.style.width),parseInt(this.div.style.height), window.document.body ) ;
		element.pop.show( l,t,parseInt(this.div.style.width),parseInt(this.div.style.height), this.button ? this.btn: this.edit ) ;
	}
	else
	for ( var se = 0 ; se < system_elements.length ; se++)
	{
		var selects = window.document.getElementsByTagName(system_elements[se]) ;
		for ( var i = 0 ; i < selects.length ; i++ )
		{
			if ( element.div.isOverlapped(selects[i]))
				selects[i].style.visibility = 'hidden' ;
		}
	}

	ev.stop() ;
	ev.abort() ;
	//window.document.onmousedown = Hide ;
	window.document.attachEvent("onmousedown", Hide );

	if ( !(element.htcState & INIT_HTML) )
	{
		element.rebuildNode() ;
		if ( element.value ) element.scroll() ;
	}
}

///////////////////////////////////////////////////////////////////////
// INITIALIZATION PRIVATE FUNCTIONS
///////////////////////////////////////////////////////////////////////

function initialize()
{
	this.xmlSrc = this.getAttribute( "xmlSrc" ) ;
	if ( !this.xmlSrc ) return ;

	this.cursorHand = 'pointer' ;
	this.log = '' ;
	if ( !this.htcState ) this.htcState = 0 ;
	if ( this.htcState & INIT_BASIC ) return ; // Explorer and mozilla do it in diffrent events
	this.htcState |= INIT_BASIC ;
	this.initializeDiv = initializeDiv ;
	this.setXmlSrc = setXmlSrc ;
	this.onAfterInit = onAfterInit ;
	this.initXml = initXml ;
	this.onXmlLoaded = onXmlLoaded ;
	this.rebuildNode = rebuildNode ;
	this.setPosition = setPosition ;
	this.getNode = getNode ;
	this.Hide = Hide ;
	this.findHTMLNode = findHTMLNode ;
	this.putValue = putValue ;
	this.scroll = scroll ;
	this.changeProprty = changeProprty ;
	this.setProperty = setProperty ;
	this.showLongDescription = showLongDescription ;

	// Compute dimentions
	this.width = this.offsetWidth ? parseInt(this.offsetWidth) : parseInt(this.style.width) ;
	if ( isNaN( this.width ) || this.width < 50 ) this.width = 200 ;
	this.height = 300 ;
	if ( this.size > 0 && this.offsetHeight ) this.height = this.offsetHeight ;
	if ( this.style.height ) this.height = this.style.height ;
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// In a Regular popup tree, First of all replace the original select with an input control and browse button
	// We are doing it first of all for quick change.
	if ( this.size <= 0 )
	{
		if ( this.button )
		{
			this.style.display = 'none' ;
			this.btn = window.document.getElementById(this.button) ;
		}
		else
		{
			var buttonsWidth = 0 ;
			if(Exist(this.buttonsOverWidth)) this.buttonsWidth = 16 + Exist(this.clearButton) ? 16 : 0 ;
			this.nobr = window.document.createElement("nobr") ;
			// Edit
			this.edit = window.document.createElement("input") ;
			this.log += 'Style width: ' + this.style.width +'\r\n' ;
			if(!Exist(this.buttonsOverWidth)&&this.style.width)
				//this.edit.style.width = this.style.width ;
				this.edit.style.width = this.width ;
			else
				this.edit.style.width = this.width - buttonsWidth ;
			//this.edit.disabled = true ;
			this.edit.readonly = true ;
			this.edit.parent = this ;
			this.edit.value = this.text ;
			this.edit.defaultValue = this.text ;
			this.edit.onkeypress = autoFill ;
			this.edit.onkeydown = autoFill ;
			if (this.editClass) this.edit.className = this.editClass;
			this.edit.onclick=function() { event.srcElement.focus(); }
			this.edit.onbeforepaste = function() {event.returnValue = false ;}
			this.edit.onpaste = function() {event.returnValue = false ;}
			this.edit.ondrop = function() {event.returnValue = false ;}
			if ( this.xmlSrc && this.xmlSrc != 'null' && !this.parent && !this.text ) 
				this.edit.value = defaultText ;
			this.nobr.appendChild( this.edit ) ;
			// Browse button
			this.btn = new Image ;
			this.btn.src = window.document.browseImg.src ;
			this.btn.align="middle" ;
			this.btn.hspace=1 ;
			this.btn.alt = 'B R O W S E' ;
			this.btn.width = "15"
			this.btn.height = "14"
			this.btn.style.cursor= this.cursorHand ;
			if ( this.disabled || this.readonly ) this.btn.style.display='none' ;
			this.nobr.appendChild( this.btn ) ;

			// Erease Button
			if ( Exist(this.clearButton) )
			{
				this.erasebutton = new Image ;
				this.erasebutton.src = window.document.eraseImg.src ;
				this.erasebutton.align="middle" ;
				this.erasebutton.hspace=1 ;
				this.erasebutton.alt = 'C L E A R' ;
				this.erasebutton.width = "15"
				this.erasebutton.height = "14"
				this.erasebutton.onclick = function(){this.value='';this.fireEvent('onchange');}
				if ( this.disabled || this.readonly ) this.erasebutton.style.display='none' ;
				this.erasebutton.style.cursor = this.cursorHand ;
				this.nobr.appendChild( this.erasebutton ) ;
			}
			// Replace the original select box by the new set of objects
			this.style.display = 'none' ;
			this.parentNode.insertBefore(this.nobr, this) ;
		} 
		this.btn.onclick=onButtonClicked ;
		this.btn.oncontextmenu = onButtonRightClick ;
		this.btn.parent = this ;
	} 


	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Do once for all tree controls in document
	if ( !window.cbXslTree )
	{
		// Build the XSL file common to all trees in this window
		window.cbXslTree = new CrossBrowserXml ;
		window.cbXslTree.setHandler( onXslLoaded ) ;
		window.cbXslTree.setProperty ("ForcedResync", false) ; // load it from cache
		cbXslTree.load(rootPath + '/tree/hmaketree.xsl', this) ;

		this.log += 'First Tree, XSL Creator\r\n' ;

		if ( mozilla )
		{
		    // stylesheet

			// Define browser based functions
			Object.prototype.attachEvent = function(e,h) {this.addEventListener(e.slice(2),h,0) ; }
			Object.prototype.detachEvent = function(e,h) {this.removeEventListener(e.slice(2),h,0) ; }
			window.xsltProcessor = new XSLTProcessor();
			window.htmlDoc = document.implementation.createDocument("", "", null);
			xsltProcessor.importStylesheet( cbXslTree.xmlDoc );
		}
		else
		{
	  	window.document.createStyleSheet( languageInfo.stylesheetName ) ;
		}
	}
	else
	{
			// All windows except the xsl creator, will get the xsl when it finish loading.
			// window.cbXslTree.owner.attachEvent('onxslloaded',onXslLoaded) ; (replaced by polling)
	} 

	/////////////////////////////////////////////////////////////////////////////
	// Initialize attributes that can be null to an empty string
	if ( !this.rawValue ) this.rawValue = '' ;
	if ( !this.text ) this.text = '' ;
	this.defaultValueSave = this.rawValue ;
	
	// Parse xmlSrc (to support multi level tree)
	if ( this.xmlSrc.indexOf(',') > 1 && typeof(defaultValueSave) == 'string' )
	{
		while ( defaultValueSave.indexOf( ',' ) >= 0 )
			defaultValueSave =defaultValueSave.slice(defaultValueSave.indexOf( ',' )+1) ;	 // Default value is only the last item
	}
	
	// Description is needed primarily for child selects, but you can use it elsewhere
	if ( !this.description ) this.description  = this.name.replace( '_id', '' ) ;	
	if ( this.size > 0 ) this.initializeDiv() ; // Make the div area only in case of an open list

	this.attachEvent( 'onpropertychange', onPropertyChanged ) ;

	/////////////////////////////////////////////////////////////////////////////
	// Initialize the XML
	/////////////////////////////////////////////////////////////////////////////
	this.setXmlSrc() ;
	//initXml() ;
	
	/////////////////////////////////////////////////////////////////////////////
	// Initialize other parts
	/////////////////////////////////////////////////////////////////////////////

	// Create options
	if ( !Exist(this.multiple)  )
	{
		for ( var i = 0 ; i < this.arrXmls.length ; i++ )
			this.options.add(window.document.createElement('option')) ;
		this.selectedIndex = this.options.length - 1 ;
		// Now getValue will be active...
		if ( this.options.length == 1 ) this.options[0].value = this.defaultValueSave ;
	}
		
	// Give a name to the text input control so that it can be submitted
	if ( this.textName ) this.edit.name=this.textName ;
	// Create a hidden input to submit the base level of a multipart tree

	if ( this.baseLevelName )
	{
		this.base_level_input = window.document.createElement("input") ;
		this.base_level_input.type="hidden" ;
		this.base_level_input.name=this.baseLevelName ;
		this.parentNode.insertBefore(this.base_level_input, this);
	}

	try
	{this.focus = setFocus ;} catch(xxx){} ;

	if ( this.form )
	{
		this.form.attachEvent('onreset', function(){
		if ( this.value != this.defaultValue ){ this.htcState &= ~INIT_HTML ; this.value = this.defaultValue ;}
				} ) ;
	}
	this.log += 'Name: ' + this.name + '\r\n' ;
	this.log += 'Source: ' + this.xmlSrc + '\r\n' ;
	this.log += 'Default Value:' + this.defaultValue + '\r\n' ;
	this.log += 'Popup: ' + (this.usePopup  ? 'yes\r\n' : 'no\r\n') ;
	this.log += 'Parent: ' + (this.parent  ? this.parent : 'none' ) + '\r\n' ;

	if ( this.parent ) 	setParent() ;
	this.onAfterInit (INIT_GUI) ;
}

function initializeDiv()
{
	if ( this.div ) return ;
	// Create the popup part of the list box
	if ( this.size <= 0 && Exist(this.usePopup) )
	{
		this.pop = createPopup() ;
		this.div = this.pop.document.createElement("div") ;
		this.pop.document.createStyleSheet( languageInfo.stylesheetName ) ;
		this.pop.document.body.appendChild( this.div ) ;
	}
	else
	{
		this.div = window.document.createElement("div") ;
		this.style.display = 'none' ; // In case of an open list hide it just before inserting the div
		if ( this.size > 0 )
			this.parentNode.insertBefore(this.div, this);
		else
			window.document.body.insertBefore ( this.div,window.document.body.firstChild ) ; // Insert in the begining of the document
	}

	// Set the DIV properties
	this.div.owner = this ;
	this.div.className = 'tree' ;
	this.div.align=languageInfo.align ;
	//this.div.dir="rtl" ;
	this.div.innerHTML = ( this.xmlSrc != 'null' && !this.parent ) ? defaultHTML : hiddenHTML ;
	this.div.isOverlapped = isOverlapped ;

	this.div.onclick = onDivClick ;
	this.div.oncontextmenu = onDivRightClick ;
	this.div.onmousedown = onDivMouseDown ;
	this.div.onmouseover = onDivMouseOver ;
	this.div.onmouseout = onDivMouseOut ;
	this.div.style.width = this.width ;
	this.div.style.height= this.height ;

	if ( this.size > 0 )
	{
		this.div.style.border = mozilla ? '1pt inset' : '2pt window-inset' ;
	}
	else
	{
		this.div.style.display='none' ;
		this.div.style.position='absolute' ;
		this.div.style.border = '1px solid black' ;
		this.div.style.zIndex=1000 ;
		if ( mozilla )
			window.addEventListener( 'resize', setPosition, false ) ;
		else
			window.attachEvent('onresize', setPosition ) ;
	}
}

function initXml()
{
	// Build the URL
	this.url = this.arrXmls[this.level] ;	

	if ( this.parent )
	{
		if ( this.parentValue > ' ' )
			this.url += this.parentValue ;
		else
			this.url = '' ;
	}
	if ( this.level ) this.url += this.options[this.level-1].value ;
	this.log += 'Loading: ' + this.url + '\r\n' ;
	this.htcState &= ~(INIT_READY | INIT_ERROR | INIT_XML | INIT_HTML ) ;
	this.xml = null ;
	if ( this.edit ) this.edit.readonly = true ;
	
	if ( this.url && this.url != 'null' ) // 'null' is a string, not null!
	{
		if ( this.div ) this.div.innerHTML = defaultHTML ;
		if ( this.edit )
		{
			if ( !this.text ) this.edit.value = defaultText ;
			this.edit.readonly = true ;
		}
		this.htcState |= INIT_XML_ONLOAD ;

		if ( Exist(this.shared ))
		{
			var i ;
			if ( !window.arrSharedTrees ) window.arrSharedTrees = new Array ;
			for ( i = 0 ; i < window.arrSharedTrees.length ; i++ )
			{
				if ( window.arrSharedTrees[i].url == url )
				{
					this.cbXmlDoc = new CrossBrowserXml(window.arrSharedTrees[i].xmlDoc) ;
					this.cbXmlDoc.origin = window.arrSharedTrees[i].owner ;
					this.cbXmlDoc.origin.attachEvent( 'onafterupdate', this.onSharedXMLLoaded ) ;
					if (this.cbXmlDoc.getStatus() == 'ok') this.onSharedXMLLoaded() ;
					return ;
				}
			}
		}

		// Create the xml object
		if ( !this.cbXmlDoc )
		{
			this.cbXmlDoc = new CrossBrowserXml ;
			this.cbXmlDoc.setHandler(  onXmlLoaded ) ;
			// Load it
			this.log += 'Time requested: ' + this.time + '\r\n' ;
			if ( this.time ) this.cbXmlDoc.setProperty ( "ForcedResync", false ) ;
			if ( this.cbXmlDoc.badProperty ) this.log += 'DOES NOT SUPPORT "ForcedResync"!!!!\r\n' ;
		}

		if ( Exist(this.shared ))
		{
			this.cbXmlDoc.url = url ;
			window.arrSharedTrees.push( cbXmlDoc ) ;
			this.sharedSource = true ;
		}

		this.log += 'First loading... resync=' + this.cbXmlDoc.getProperty ( "ForcedResync" ) + '\r\n' ;
		this.cbXmlDoc.load(this.url, this) ;
	}
	else
		if ( this.div ) this.div.innerHTML = hiddenHTML ;
}

function onAfterInit(newstate)
{
	var element = this ;
	
//	this.log += 'State changed: ' + newstate + ' (' + htcState + ')\r\n' ;
	switch(newstate)
	{
		case INIT_GUI: element.log += 'Gui initialized\r\n' ;break;
		case INIT_XSL: element.log += 'XSL initialized\r\n' ;break;
		case INIT_XML: element.log += 'XML initialized\r\n' ;break;
	}

	if ( element.htcState & INIT_READY ) return ;
	element.htcState |= newstate ;


	if ( (element.htcState & INIT_ALL)==INIT_ALL )
	{
		element.htcState |= INIT_READY ;

		if ( !Exist(element.shared ) || element.sharedSource )
		{
			eval(element.xml.getAttribute("onload")) ;
			if( !mozilla) element.fireEvent('onafterupdate') ;
			// !!!!!!!!!!!
		}
		eval(element.onload) ;
		
		// The funcion of onload can change the ready state...
		if ( !(element.htcState & INIT_READY) ) return ;

		if ( (element.htcState & INIT_VALUE) == 0 ) 	element.value = element.rawValue ;

		if ( element.div && element.div.style.display != 'none'  )
		{
			element.rebuildNode(element.xml) ;
			if ( element.value) element.scroll() ;
		}
		if (element.edit)
		{
			element.edit.value = element.text ; // Remove the "loading..."
			if ( !element.disabled && !element.readonly ) element.edit.readonly = false ;
		}
	}
}

///////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
///////////////////////////////////////////////////////////////////////

// The function check if the area of two thiss is overlapped, the first
// this is an object that this function attached as a method to it, and the
// second is the 'o' parameter.
// In our case the first is the DIV and the second is a regular 'select' this.
// Because select is a system this it does not support the z-index, so we have
// to hide every select if it is overlapped the current this's DIV.
function isOverlapped( o )
{
	if ( o.style.display == 'none' || o.style.visibility == 'hidden' ) return false ;
	var left=0, top=0, right, bottom ;
	for ( e = o ; e ; e = e.offsetParent ) { left += e.offsetLeft ; top += e.offsetTop ; }
	right = left + o.offsetWidth ; bottom = top + o.offsetHeight ;
//	right = left + o.offsetWidth ; bottom = top + this.height ;
	if ( this.offsetLeft > right || this.oofsetTop > bottom ) return false ;
	if ( left > this.offsetLeft + this.offsetWidth ) return false ;
	if ( top > this.offsetTop + this.offsetHeight ) return false ;
//	if ( top > this.offsetTop + this.height ) return false ;
	return true ;
}

// Retrurn the top of an object relative to the DIV.
function getTop(o)
{
	var top=0, e = o ;
	while ( e.nodeName != 'DIV' )
	{
		top += e.offsetTop ;
		e = e.offsetParent ;
	}
	return top ;
}

// Every img in HTML has a 'name' attribute that can identify it.
// The reason name used instead of ID is that the name is not alwais unique in
// document scope. (but it defenetly unique in the elemen's scope).
function findHTMLNode(id)
{
	try
	{
		return this.div.all.namedItem(id) ;
	}
	catch(xxx)
	{
		var items = window.document.getElementsByName(id) ;
		if (!items) return null ;
		var i, o ;
		for ( i = 0 ; i < items.length ; i++ )
		{
			for ( o = items[i] ; o ; o = o.parentNode )
			{
				if ( o == this.div ) 	return(items[i] );
			}
		}
	}
}

// When we need to show an additional info, attached to node's description,
// This function is making the 'appendix' attribute from the attribute defined
// as 'customProperty'.
function buildCustomProperty(node, value )
{
	var i, newVal ;
	newVal = node.getAttribute(customProperty) ;
	if ( !newVal ) newVal = '' ;
	if ( hierarchicalCustomProperty=='yes' && !(newVal>'')) newVal = value ;
	node.setAttribute( 'appendix', newVal > '' ? ' {'+newVal+'}' : '' ) ;
	if ( newVal > '' )
		newVal = hierarchicalCustomProperty=='yes' ? newVal.toLowerCase() : '' ;
	for ( i = 0 ; i < node.childNodes.length ; i++ )
		buildCustomProperty(node.childNodes[i], newVal )
}

// If the selected node is hidden behind the bottom of the DIV, this function
// is scrolling in order to show it.
function scroll()
{
	if ( this.xmlSrc ) window.scrolltree = this ;
	var element = window.scrolltree ;
	
	if ( mozilla ) return ; // not supported yet.
	var img = null, top = 0, prevtop = 0 ;

	// Wait for HTML to be prepared
	if ( !element.countDown )
	{
		element.countDown = 5 ;
		element.to = window.setTimeout( scroll, 100 ) ;
		return ;
	}

	if ( element.countDown == 1 ) // Failed to scroll
	{
		element.countDown = 0 ;
		return ;
	}

	var arrV = element.value.split(',') ;
	img = element.findHTMLNode( arrV[0] ) ;
	if ( img ) element.top = getTop(img) ;

	if ( element.top )
	{
		element.countDown = 0 ;
		if ( element.top > element.div.clientHeight + element.div.scrollTop - 10 )
			element.div.scrollTop = element.top - element.div.clientHeight/2 ;
	}
}

function setParent()
{
	if ( this.parentSave )
	{
		this.parentSave.detachEvent ( 'onafterupdate', onParentChange ) ;
		this.parentSave.detachEvent ( 'onchange', onParentUserChange ) ;
		this.parentSave = null ;
	}		
	
	if ( !this.parent ) return ; // Delete the parent
	
	if ( typeof(this.parent) == 'string')
	{
		// parent can be specified by: 1) ID, 2) Name, 3) string(evalueted with eval)
		var p = window.document.getElementById(this.parent) ;
		if ( !p ) p = window.document.getElementsByName(this.parent)[0] ;
		if ( !p ) p = eval ( this.parent ) ;
		this.parent = p ;
		if ( this.disabled ) setParent() ;
	}
	else
	{
		this.parentValue = this.parent.value ;
		if ( this.parentValue > ' ' )
		{
			this.htcState |= INIT_PARENT ;
			initXml() ;
		}
   	this.parent.attachEvent ( 'onafterupdate', onParentChange ) ;
		this.parent.attachEvent ( 'onchange', onParentUserChange ) ;
		this.parentSave = this.parent ;		
	}
}

function CrossBrowserEvent(e)
{
	this.event = window.mozilla ? e : event ;	
	
	if ( !this.event && this.pop )
		this.event = this.pop.document.parentWindow.event ;
	this.srcElement = window.mozilla ? this.event.target : this.event.srcElement ;
	this.className = ( this.srcElement&&this.srcElement.className ) ? this.srcElement.className : '' ;
	this.stop = function() {window.mozilla? this.event.stopPropagation() : this.event.cancelBubble = true ;}
	this.abort = function() { window.mozilla? this.event.preventDefault() : this.event.returnValue=false ; }	
	this.element = FindMe(this.srcElement) ;
	this.img = this.srcElement.previousSibling ;
	this.name = ( this.img && this.img.nodeName == 'IMG' ) ? this.img.name : '' ;
}

function removeNodeFromText( text, node )
{
    var start = text.indexOf( "<" + node ) ;
    if ( start >= 0 )
    {
        var end = text.indexOf( '>', start ) ; 
        text = text.replace( text.substring( start, end+1 ), "" ) ;
        if ( node[0] != '/' ) text = removeNodeFromText( text, '/' + node ) ;
    }
    return text ;
}

function CrossBrowserXml(doc)
{	
	this.xmlDoc = doc ;
	this.setHandler = function(func) { this.onload=func ; }
	if ( mozilla )
	{
		this.status = "loading" ;
		if ( typeof(doc) == 'string' )
		{
			var parser=new DOMParser();
			this.xmlDoc=parser.parseFromString(doc,"text/xml");
			this.status = this.xmlDoc.documentElement ? "ok" : "error" ;
		}
		if ( !doc )	this.xmlDoc = window.document.implementation.createDocument("", "", null);
		var frag ;
		this.transform = function(node,img) 
		{                     
           frag = xsltProcessor.transformToFragment( node, window.htmlDoc ) ;
           //alert(new XMLSerializer().serializeToString( frag ));
           img.parentNode.insertBefore(  frag, img ) ;
		}
		this.findNode = function(xpath)	{	return this.xmlDoc.evaluate(xpath, this.xmlDoc.documentElement, null, XPathResult.ANY_TYPE,null).iterateNext() }
		this.getStatus = function() { return this.status ; }
		this.setHandler = function(func) { this.onload=func ; }
		this.setProperty = function(n,v) {}
		this.load = function(url, tree) 
		{
             var xmlReq=new window.XMLHttpRequest();
             xmlReq.open("GET",url,false);
             xmlReq.send(null) ;
             this.xmlDoc = xmlReq.responseXML ;
			this.owner = tree ;
			this.xmlDoc.owner = this ;
			//this.xmlDoc.onload=this.loaded ;
			//this.xmlDoc.load(url) ; 
			this.xmlDoc.loaded=this.loaded ;
			this.xmlDoc.loaded() ;
		}
		this.loaded = function()
		{
			this.owner.status = this.owner.xmlDoc.documentElement ? "ok" : "error" ;			
			this.owner.onload(this.owner.owner) ;
		}
		this.getProperty = function(x) { return "no" ; }
  }
	else
	{
		if ( !doc || typeof(doc) == 'string' )	this.xmlDoc = new ActiveXObject("Msxml2.DOMDocument");

		if ( typeof(doc) == 'string' )
		{
			this.xmlDoc.async="false";
			this.xmlDoc.loadXML(doc);
		}
		this.transform = function(node,img) 
		{
			var strHTML = node.transformNode ( cbXslTree.xmlDoc );
			if(window.ampFilter) 
				strHTML = strHTML.replace( window.ampFilter, '&' )  ;
			try {img.insertAdjacentHTML('beforeBegin', strHTML ) ;} catch(xxx){this.div.innerText = strHTML ; }
		}
		// !!!!!!!!!!!
//		this.findNode = function(xpath){	 return this.xml.selectSingleNode( xpath ) ; }
		this.findNode = function(xpath){	 return this.xmlDoc.documentElement.selectSingleNode( xpath ) ; }
		this.getStatus = function()
			{
				if ( this.xmlDoc.readyState < 4 ) return 'loading' ;
				if ( this.xmlDoc.parseError.errorCode == 0) return 'ok' ;
				if ( this.xmlDoc.parseError.errorCode == -1072897514) return 'loading' ;
				this.errorMessage = this.xmlDoc.parseError.reason + ' (' + this.xmlDoc.parseError.errorCode + ') ' + this.xmlDoc.xml ;
				return 'error' ;
			}
		this.badProperty = '' ;
		this.setProperty = function(n,v) { try {this.xmlDoc.setProperty(n,v) ;}catch(xxx){this.badProperty=n;} }
		this.getProperty = function(n) { try { return this.xmlDoc.getProperty(n) ;}catch(xxx){return true ;} }
		this.load = function(url, tree )
		{
			this.owner = tree ;
			if ( !window.arrTrees ) window.arrTrees = new Array() ;
			window.arrTrees.push( this ) ;
			this.xmlDoc.onreadystatechange = this.readyStateChanged ;
			this.xmlDoc.load( url ) ;
		}
		this.readyStateChanged = function()
		{
			var remove = true ;
			for ( i = 0 ; i < window.arrTrees.length ; i++ )
			{				
				if ( window.arrTrees[i] )
				{
					if ( window.arrTrees[i].xmlDoc.readyState == 4 )
					{
						window.arrTrees[i].onload(window.arrTrees[i].owner) ;
						window.arrTrees[i] = null ;
					}
					else
					{
						remove = false ;
					}
				}
			}
			if ( remove ) window.arrTrees = null ;
		}		
	}
	//this.load = function (u) {this.xmlDoc.load(u) ; }
}

function changeProprty(name )
{
	if ( mozilla )
	{
		var e = new Object ;
		e.propertyName = name ;
		e.target = this ;
		onPropertyChanged(e) ;
	}
}

function setProperty(name, value)
{
	eval("this." + name + "='" + value + "'" ) ;
	this.changeProprty(name )
}

function Exist(a)
{
	if ( !a ) return false ;
	a = a.toString();
	return  a.toUpperCase() != 'NO' && a.toUpperCase() != 'FALSE'  && a != '0' ;
}

