  // Constructor
  function Treeview(properties){
    function property_default(pname, def) { 
      return typeof properties[pname] == "undefined" ? def : properties[pname];
    };  
    
    // Public properties
    this.m_Label       = property_default('Label','');
    this.m_Indentation = property_default('Indentation',Treeview.Indentation);  
    this.m_Collapse    = property_default('Collapse', false);
    this.m_Image       = property_default('Image', Treeview.ImgageFolder);
    this.m_bySort      = property_default('bySort', true);
    this.m_sortReverse = property_default('sortReverse', false);
    this.m_Visible     = property_default('Visible', true);
    this.m_Selected    = property_default('Selected', false);
    this.m_sTag        = property_default('Tag', '');
    
    this.onclick  = null;
    
    // Private property
    this.m_Parent          = property_default('Parent', null);
    this.m_Level           = this.m_Parent != null ? (this.m_Parent.m_Level + 1) : 0;
    this.m_NodesCount      = 0;    
    this.m_element         = null;
    this.m_elementCollapse = null;
    this.m_oLabel          = null;
    this.m_oHeader         = null;
    this.m_oContainer      = null;
    this.m_idTimer;
    this.m_oCellHeader     = null;
    this.m_oTreeContainer  = null;  // Use only if we have a header

    this.m_oEvent = new cEvent();
    this.m_oBrowser = new cBrowser();
    this.m_Nodes = new Array();

    this.m_Key = 0;
    Treeview.UniqueKey = ++Treeview.UniqueKey;
    this.m_Key = Treeview.UniqueKey;    
    this.m_Id = 'treeview_container_' + this.m_Key;
    this.is_ie8 = this.m_oBrowser.m_type['msie'] && this.m_oBrowser.m_type.version > 7.0;
    this.is_ie = this.m_oBrowser.m_type['msie'];
    
    var m_oSelf = this;


    // #######################################################
    //                       Methods
    // #######################################################  

    // -----------------------------------------------------           
    // Private method
    // -----------------------------------------------------               
    this._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" && parent != null) {
		    parent.appendChild(el);
	    }
	    return el;
    };   
    // -----------------------------------------------------          
    this._unit = function(value){
      if(value.toString().indexOf('%') != -1){
        return value;
      }
      return value + 'px';        
    };
    // -----------------------------------------------------              
    this._mouseover = function(event){
      with(this.m_oLabel){
//        style.textDecoration = 'underline';
        style.backgroundColor = Treeview.bckgColorHover;
      }
    }
    // -----------------------------------------------------              
    this._mouseout = function(event){
      with(this.m_oLabel){
        style.textDecoration = 'none';
        style.backgroundColor = this.m_Selected ? Treeview.bckgColorSelected : '';
      }
    }
    // -----------------------------------------------------                      
    this._onscroll = function(event){
      this.m_oHeader.style.top = this._unit(this.m_oContainer.scrollTop);
      
    };   
    // -----------------------------------------------------                  
    this._mouseup = function(event){
     
      if(Treeview.oNodeSelected != null){
        Treeview.oNodeSelected.m_Selected = false;
        Treeview.oNodeSelected.m_oLabel.style.backgroundColor = '';
      }        
      this.m_Selected = true;
      Treeview.oNodeSelected = this;
      
      with(this.m_oLabel){
        style.backgroundColor = Treeview.bckgColorSelected;
      }
      // We expand the node     
      this._expandChild();  
      this._setHeaderSize();      
    }
    // --------------------------------------------------------------------------          
    this._create = function(){
      var parentElement = this.m_Parent != null ? this.m_Parent.m_element : null;
      var container = this._createElement('div',parentElement);
      this.m_element = container;
      var nodecontainer = this._createElement('div',container);
      var table         = this._createElement('table',nodecontainer);
      var tbody         = this._createElement('tbody',table);
      var tr            = this._createElement('tr',tbody);
      var td            = this._createElement('td',tr);
      var imgCollapse   = this._createElement('img',td);
      this.m_elementCollapse = imgCollapse;
      var td            = this._createElement('td',tr);
      var imgLabel      = this._createElement('img',td);
      
      var label         = this._createElement('td',tr);      
      
      with(container){
        id = this.m_Id;   
        className = 'treeview-nodes-container';
        var marginLeft  = parentElement == null ? 0 : this.m_Indentation;
        if(this.m_Parent != null) marginLeft =  this.m_Parent.m_Visible ? marginLeft : 0;
        style.marginLeft = this._unit(marginLeft);
      }
      
      with(nodecontainer){
        style.position = 'relative';        
        className = 'treeview-node-container';
        style.display = this.m_Visible ? 'block' : 'none';
      }
      
      with(table){
        cellSpacing = 0;
        cellPadding = 0;
      }
      
      with(tr){
        className = 'treeview-node-container-row';  
      }
      
      with(imgCollapse){
        className =  'treeview-img-collapse';
        src =  this._getImageCollapse();
        border= this._unit(0);  
        style.cursor = 'pointer';              
        onclick = this.m_oEvent.bindEvent(this._toggle, this);        
      }

      with(imgLabel){
        className = 'treeview-img-label';
        src =  this.m_Image;
        border= this._unit(0);
      }

      this.m_oLabel = label;
      with(label){
        className = 'treeview-label';   
        innerHTML = this.m_Label;
        style.cursor = 'pointer';
        style.backgroundColor = this.m_Selected ? Treeview.bckgColorSelected : '';
        onmouseover = this.m_oEvent.bindEvent(this._mouseover, this);          
        onmouseout  = this.m_oEvent.bindEvent(this._mouseout, this);    
        onmouseup   = this.m_oEvent.bindEvent(this._mouseup, this);  
        onclick     = this.onclick;
      }

      if(Treeview.oNodeSelected != null && this.m_Selected){
        Treeview.oNodeSelected.m_Selected = false;
        Treeview.oNodeSelected.m_oLabel.style.backgroundColor = '';
      }        
      if(this.m_Selected) Treeview.oNodeSelected = this;
      
    };
    // --------------------------------------------------------------------------      
    this._getRootNode = function(){
      if(this.m_Parent != null){
        return this.m_Parent._getRootNode();
      }
      return this;
    };
    // --------------------------------------------------------------------------      
    this._toggle = function(event){
      if(this.is_ie) Treeview.oNodeSelected.m_idTimer = window.setInterval('Treeview.timer()',10);  // base time is 10 msec
      for(var ii = 0; ii < this.m_NodesCount; ii++){
        this.m_Nodes[ii].m_element.style.display = this.m_Collapse ? 'block' : 'none';
      }
      this.m_Collapse = this.m_Collapse == false ? true : false;
      this.m_elementCollapse.src = this._getImageCollapse();   
      this._setHeaderSize();      
    };
    // --------------------------------------------------------------------------      
    // Collapse all children node children of children of children etc..
    this._collapse = function(){
      for(var ii = 0; ii < this.m_NodesCount; ii++){
        this.m_Nodes[ii].m_element.style.display = this.m_Collapse ? 'none' : 'block';
        this.m_Nodes[ii]._collapse();
      }
      this.m_elementCollapse.src = this._getImageCollapse();   
    };
    // --------------------------------------------------------------------------          
    // Expand only the direct children and not the children of children
    this._expandChild = function(){    
      this.m_Collapse = false;
      for(var ii = 0; ii < this.m_NodesCount; ii++){
        this.m_Nodes[ii].m_element.style.display = 'block';
      }
      this.m_elementCollapse.src = this._getImageCollapse(); 
    };
    // --------------------------------------------------------------------------              
    // Expand parent node
    this._expandParent = function(){    
      if(!this.m_Parent) return;
      for(var ii = 0; ii < this.m_Parent.m_NodesCount; ii++){
        this.m_Parent.m_Nodes[ii].m_element.style.display = 'block';
      }
      this.m_Parent.m_Collapse = false;
      this.m_Parent.m_elementCollapse.src = this.m_Parent._getImageCollapse(); 
      this.m_Parent._expandParent();
    };
    // --------------------------------------------------------------------------      
    this._getImageCollapse = function(){
      var img = this.m_Collapse == true ? Treeview.ImgageCollapse : Treeview.ImgageUnCollapse;
      if(this.m_NodesCount == 0) img = Treeview.ImgageEmpty;
      return img;
    };
    //------------------------------------------------------------------------------  
    this._getSortFunction = function(sProperty) {
      return function(oObject1, oObject2){
        if(oObject1[sProperty] == oObject2[sProperty]) {
          return 0
        }
        else {
          return oObject1[sProperty] < oObject2[sProperty] ? -1 : 1
        }
      } 
    };
    //------------------------------------------------------------------------------      
    this._setHeaderSize = function(){
      if(!Treeview.displayHeader) return;
      var oCell = this._getRootNode().m_oCellHeader;
      if(oCell){
        oCell.style.width = this._unit(this._getRootNode().m_oTreeContainer.offsetWidth);
      }
    }
    
    //------------------------------------------------------------------------------  
    // Build the treeview
    //------------------------------------------------------------------------------  
    this._show = function(){
      this._create();
      if(this.m_bySort){ 
        this.m_Nodes.sort(this._getSortFunction('m_Label'));
        if(this.m_sortReverse){
          this.m_Nodes.reverse();
        }
      }
      for(var ii = 0; ii < this.m_NodesCount; ii++){
        this.m_Nodes[ii]._show();
      }      
    };
    
    // ############################################################################################################### 
    //                                              Public method
    // ###############################################################################################################
    // Add a node and return the node reference
    this.addNode=function(Label, Collapse, Image){
      // New Node
      var oNode = new Treeview({Label    : Label,
                                Collapse : Collapse,
                                Image    : Image,
                                Parent   : this
                               });
      this.m_Nodes[this.m_NodesCount++] = oNode;
      return oNode;
    };
    // --------------------------------------------------------------------------      
    // Search a node and return the reference. <> null if found or null if not found
    this.getNode = function(sTag, Caller){
      Caller = typeof(Caller) == 'undefined' ? null : Caller;
      // Est-ce moi ?
      if(sTag == this.m_sTag){
        return this;
      } 
      // On cherche chez les enfants - on descend dans les branches
      for(var ii = 0; ii < this.m_NodesCount; ii++){
        if(this.m_Nodes[ii] != Caller){
          var oNode = this.m_Nodes[ii].getNode(sTag,this);
          if(oNode != null) return oNode;
        }
      }
      // On cherche chez les parents - on remonte dans les branches
      if(this.m_Parent != null){
        if(this.m_Parent != Caller){
          return this.m_Parent.getNode(sTag,this);  
        }
      }
      return null;
    };
    // --------------------------------------------------------------------------      
    this.selectNode = function(){
      Treeview.oNodeSelected = this;
      this.m_Selected = true;
    }    
    //------------------------------------------------------------------------------  
    // Display the treeview
    //------------------------------------------------------------------------------  
    this.show = function(){
      this._show();
      this._collapse();
      if(Treeview.oNodeSelected){
        Treeview.oNodeSelected._expandParent();
        Treeview.oNodeSelected._mouseup(null);  // to hight light the node selected
      }
      else{
        this._expandParent();      
        this.oNodeSelected._mouseup(null);      // to hight light the node selected
      }
      var container = this._createElement('div');
      this.m_oContainer = container;
      
      // do we want a header ?
      if(Treeview.displayHeader){
        var oHeader = this._createElement('table');
        var oBody   = this._createElement('tbody',oHeader);
        var oRow   = this._createElement('tr',oBody);
        
        var oCell   = this._createElement('td',oRow);
        this.m_oCellHeader = oCell;  // to resize the header;
        with(oCell){
          className = 'treeview-header-cell';
          innerHTML = (!this.is_ie8 && this.is_ie) ? '&nbsp;' : '';  // to see the border 
          style.width = this._unit(0);
        }

        var oCell   = this._createElement('td',oRow);
        with(oCell){
          className = 'treeview-header-cell';
          innerHTML = (!this.is_ie8 && this.is_ie) ? '&nbsp;' : '';  // to see the border 
          style.width = this._unit('100%');
        }

        this.m_oHeader = oHeader;
        with(oHeader){
          cellSpacing = 0;
          cellPadding = 0;
          className = 'treeview-header';
          style.overflow = 'hidden';
          style.position = 'relative';                // It's important for IE
          style.width  = '100%';
        }

        // Just to have the good width to resize the header correctly.  Its an astuce     
        var oTreeContainer = this._createElement('table');
        this.m_oTreeContainer = oTreeContainer;
        with(oTreeContainer){
          cellSpacing = 0;
          cellPadding = 0;
          borderCollapse = 'collapse';
          
        }
        var oTbody = this._createElement('tbody',oTreeContainer);
        var oTr = this._createElement('tr',oTbody);
        var oTd = this._createElement('td',oTr);
        oTd.appendChild(this.m_element);          
      }

      with(container){
        className = 'treeview-container';
        style.overflow = 'auto';
        style.position = 'relative';                // It's important for IE
        style.width  = '100%';
        style.height = '100%';
        if(Treeview.displayHeader){
          onscroll = this.m_oEvent.bindEvent(this._onscroll, this);            
          appendChild(oHeader);
          appendChild(oTreeContainer);
        }
        else{
          appendChild(this.m_element);          
        }
      }

      with(document.getElementById(Treeview.IdContainer)){
        appendChild(container);
      }

      if(Treeview.displayHeader){      
        this.m_oCellHeader. style.width = this._unit(this.m_oTreeContainer.offsetWidth);        
      }
      
    };
  }
  // --------------------------------------------------------------------------       
  // Static property with default value
  Treeview.UniqueKey = 0;
  Treeview.Instance = null;
  Treeview.oNodeSelected = null;

  //------------------------------------------------------------------------------  
  // Private function
  //------------------------------------------------------------------------------  
  Treeview.timer = function() {  
    Treeview.oNodeSelected._getRootNode()._onscroll(null);
    window.clearInterval(Treeview.oNodeSelected.m_idTimer); 
  }

  //------------------------------------------------------------------------------  
  // Public function
  //------------------------------------------------------------------------------  
  //------------------------------------------------------------------------------  
  // Setup of treeview
  //------------------------------------------------------------------------------  
  
  Treeview.setup = function (params) {
    function param_default(pname, def) { 
      if (typeof params[pname] == "undefined") { 
        Treeview[pname] = def; 
      } 
      else{
        Treeview[pname] = params[pname]; 
      }
    };  
    
    param_default("RootName", 'Root');
    param_default("IdContainer", null);
    param_default("Indentation", 10);
    param_default("bckgColorHover"   , '#D4DCFC');
    param_default("bckgColorSelected", '#FFCC99');
    param_default("ImgageCollapse"  ,'components/treeview/skin/default/images/plus.png');
    param_default("ImgageUnCollapse",'components/treeview/skin/default/images/minus.png');
    param_default("ImgageEmpty"     ,'components/treeview/skin/default/images/leaf.gif');
    param_default("ImgageRoot"      ,'components/treeview/skin/default/images/root.png');
    param_default("ImgageFolder"    ,'components/treeview/skin/default/images/folder.png');
    param_default("displayHeader"   ,true);
  
    if(Treeview.IdContainer == null){
      alert("Treeview.Setup:\n  Nothing to setup (no fields found).  Please check your code");  
    }
    
    
//    Treeview.Instance = new Treeview({Label : Treeview.RootName,
//                                      Image : Treeview.ImgageRoot,
//                                      Visible : true,
//                                      Collapse :false
//                                     });    
  };

  