/*
 * Ever Ezida (c) 2004-2005 
 *
 * L'objet générique se base sur un handler pour lui fournir les données nécessaires.
 * Ce handler doit implémenter les méthodes suivantes :
 *
 *   void onItemSelected (TreeViewItem item,boolean isMultiSelection); isMultiSelection vaut true si le bouton shift et pressé
 *   void removeItemSelected (TreeViewItem item); supprime un élément de la sélection. Uniquement pour les sélections multiples.
 *   TreeViewItem createFolder(TreeViewItem parentItem, String folderName);
 *   TreeViewItem createItem(TreeViewItem parentItem, String itemName, String itemType, Object itemData);
 *   boolean deleteItem(TreeViewItem item);
 *   TreeViewItem renameItem(TreeViewItem item, String itemName);
 *   TreeViewItem moveItem(TreeViewItem item, TreeViewItem newParentItem);
 *   TreeViewItem duplicateItem(TreeViewItem item, TreeViewItem newParentItem);
 *   TreeViewItem [] getChildren(String itemId);
 *   void ondblClick (TreeViewItem item);
 *   TreeViewItem buildItem(String xmlElement);
 * 
 * Les méthodes publiques intéressantes de TreeView :
 *
 *   TreeViewItem setSelectedItem (String itemId)
 *   TreeViewItem getSelectedItem ()
 *   TreeViewItem getItemById (String itemId)
 *   void ensureItemVisible (TreeViewItem item)
 *   void onToggle (String itemId, [boolean forceOpen=false]) 
 *   void refresh ()
 *   void clearAll ()
 *   void setActionHandler (TreeViewHandler actionHandler)
 *   TreeViewHandler getActionHandler()
 *   void setWaitState (boolean setOrUnset)
 *   boolean getWaitState()
 *   void refreshItemChildren(TreeViewItem item, TreeViewItem[] childItems);
 *   void directAddItemChildren(TreeViewItem item, TreeViewItem[] childItems);
 *
 *   TreeViewItem createFolder(TreeViewItem parentItem, String folderName);
 *   TreeViewItem createItem(TreeViewItem parentItem, String itemName, String itemType, Object itemData);
 *   boolean deleteItem(TreeViewItem item);
 *   TreeViewItem renameItem(TreeViewItem item, String itemName);
 *   TreeViewItem moveItem(TreeViewItem item, TreeViewItem newParentItem);
 *   TreeViewItem duplicateItem(TreeViewItem item, TreeViewItem newParentItem);
 *	 TreeViewItem isVirtualItem(TreeViewItem item); 
 *
 * @author tdn
 * @author csi
 *
 * requière system.js, tag.js
 */
 
 
var TREE_VIEW_ICON_ROOT = "icons/css/flora2/tree/";
var VIRTUAL_KEY = "virtual";
var DefaultTreeViewConfig = {
   rootIcon               : TREE_VIEW_ICON_ROOT + 'rooticon.png',
   rootMinusIcon          : TREE_VIEW_ICON_ROOT + 'rootminus.png',
   rootPlusIcon           : TREE_VIEW_ICON_ROOT + 'rootplus.png',
   openRootIcon           : TREE_VIEW_ICON_ROOT + 'openrooticon.png',
   folderIcon             : TREE_VIEW_ICON_ROOT + 'foldericon.png',
   openFolderIcon         : TREE_VIEW_ICON_ROOT + 'openfoldericon.png',
   virtualFolderIcon      : TREE_VIEW_ICON_ROOT + 'virtualfoldericon.png',
   openVirtualFolderIcon  : TREE_VIEW_ICON_ROOT + 'openvirtualfoldericon.png',   
   fileIcon               : TREE_VIEW_ICON_ROOT + 'file.png',
   iIcon                  : TREE_VIEW_ICON_ROOT + 'I.png',
   lIcon                  : TREE_VIEW_ICON_ROOT + 'L.png',
   lMinusIcon             : TREE_VIEW_ICON_ROOT + 'Lminus.png',
   lPlusIcon              : TREE_VIEW_ICON_ROOT + 'Lplus.png',
   tIcon                  : TREE_VIEW_ICON_ROOT + 'T.png',
   tMinusIcon             : TREE_VIEW_ICON_ROOT + 'Tminus.png',
   tPlusIcon              : TREE_VIEW_ICON_ROOT + 'Tplus.png',
   blankIcon              : TREE_VIEW_ICON_ROOT + 'blank.png',
   waitIcon               : TREE_VIEW_ICON_ROOT + 'wait.gif',
   
   blankImg : "<img src=\"" + TREE_VIEW_ICON_ROOT + 'blank.png' + "\" class='treeview-icon' />",
   iImg     : "<img src=\"" + TREE_VIEW_ICON_ROOT + 'I.png' + "\" class='treeview-icon' />"
};
var LargeTreeViewConfig = {
   rootIcon               : TREE_VIEW_ICON_ROOT + 'rooticon.png',
   rootMinusIcon          : TREE_VIEW_ICON_ROOT + 'rootminus.png',
   rootPlusIcon           : TREE_VIEW_ICON_ROOT + 'rootplus.png',
   openRootIcon           : TREE_VIEW_ICON_ROOT + 'openrooticon.png',
   folderIcon             : TREE_VIEW_ICON_ROOT + 'foldericon.png',
   openFolderIcon         : TREE_VIEW_ICON_ROOT + 'openfoldericon.png',
   virtualFolderIcon      : TREE_VIEW_ICON_ROOT + 'virtualfoldericon.png',
   openVirtualFolderIcon  : TREE_VIEW_ICON_ROOT + 'openvirtualfoldericon.png',   
   fileIcon               : TREE_VIEW_ICON_ROOT + 'file.png',
   iIcon                  : TREE_VIEW_ICON_ROOT + 'large/I.png',
   lIcon                  : TREE_VIEW_ICON_ROOT + 'large/L.png',
   lMinusIcon             : TREE_VIEW_ICON_ROOT + 'large/Lminus.png',
   lPlusIcon              : TREE_VIEW_ICON_ROOT + 'large/Lplus.png',
   tIcon                  : TREE_VIEW_ICON_ROOT + 'large/T.png',
   tMinusIcon             : TREE_VIEW_ICON_ROOT + 'large/Tminus.png',
   tPlusIcon              : TREE_VIEW_ICON_ROOT + 'large/Tplus.png',
   blankIcon              : TREE_VIEW_ICON_ROOT + 'blank.png',
   waitIcon               : TREE_VIEW_ICON_ROOT + 'wait.gif',
   
   blankImg : "<img src=\"" + TREE_VIEW_ICON_ROOT + 'blank.png' + "\" class='treeview-icon' />",
   iImg     : "<img src=\"" + TREE_VIEW_ICON_ROOT + 'large/I.png' + "\" class='treeview-icon' />"
};

var TreeViewConfig = DefaultTreeViewConfig;

// Renvoie l'objet TreeView s'il existe déjà en cache
// Renvoie null sinon
function TreeView_getCacheInstance(objectId)
{
   var obj = sysGetAttribute(objectId + ".TreeView");
   return obj;
}

// Mémorise en cache l'objet
function TreeView_setCacheInstance(objectId, treeView)
{
   sysSetAttribute(objectId + ".TreeView", treeView);
}

// -----------------------------------------------------------------------------
// objId     : id unique de l'objet, servant à stocker les infos en cache
//             mais doit être aussi le nom de l'attribut du document créée
//             dynamiquement pour stocke une référence à l'objet
// doc       : document de la page
// rootDivId : id de l'élément racine
// acceptMultiSelection: si true il est possible d'effectué des sélections 
//                      multiple. valeur par default:false
// -----------------------------------------------------------------------------
function TreeView(objId, rootDivId,acceptMultiSelection)
{
   this.objId = objId;
   this.rootDivId = rootDivId;
   this.visibleItems = new Array();
   this.allItems = new Array();
   this.rootItem = null;
   this.selectedItem = null;
   if(acceptMultiSelection==null)
      this.acceptMultiSelection=false;
   else
      this.acceptMultiSelection=acceptMultiSelection;
   this.acceptDirSelection=true;
   
   // par défaut, la window est le courante
   this.updateWindow(window);
   
   // Pour changer l'aspect de l'ihm, changer l'objet config.
   this.config = DefaultTreeViewConfig;
   
   //Pour avoir le clic droit
   this.rightClick=false;
}

/**
 * Permet de refuser la selection des répertoires.
 * par défaut le repertoire sont accepté
 */
TreeView.prototype.setAcceptDirSelection = function (isDirSelectionAccept)
{
   this.acceptDirSelection=isDirSelectionAccept;
}

/**
 * @return true si la sélection des répertoires est possible
 */
TreeView.prototype.isDirSelectionAccepted = function ()
{
   return this.acceptDirSelection;
}

// Met à jour le document contenant la treeview : à faire lorsque l'objet
// est récupéré du cache
TreeView.prototype.updateWindow = function (newWindow)
{
   this.doc = newWindow.document; 
   // Vérifie si la variable GLOBALE existe déjà
   var treeObj = eval("newWindow." + this.objId);
   if ( treeObj )
   {
      alert("TreeView : La variable globale dynamqiue" + this.objId + 
         " existe déjà dans la fenêtre " + newWindow.name );
   }
   else
   {
      eval("newWindow." + this.objId + " = this" );
   }
}


//Réinitialisation des éléments du TV
TreeView.prototype.clearAll = function ()
{
   this.visibleItems = null;
   this.allItems = null;
   this.selectedItem = null;
   this.rootItem = null;
   this.visibleItems = new Array();
   this.allItems = new Array();    
}
//Efface tous les éléments de l'arbre situés sous la racine
TreeView.prototype.reset = function ()
{
   this.rootDiv.innerHTML="";
   var rootItem = this.rootItem;
   this.clearAll();
   this.addItem(null, rootItem.id, rootItem.name, rootItem.isFolder, rootItem.type, rootItem.data);
}

// right click actif ou non
TreeView.prototype.isRightClickable = function ()
{
return this.rightClick;
}

// active le rightclick
TreeView.prototype.setRightClick = function (isRight)
{
this.rightClick=isRight;
}


// Sélection de l'item + éventuellement action
TreeView.prototype.onItemClick = function (event,itemId) 
{
   var item = this.allItems[itemId]; 
   if (!this.isVirtualItem(item))
   {
   var isMultiSelection=(event.ctrlKey==1 && this.acceptMultiSelection);
   if ( item != null )
   {
      
      if(item.isFolder && !this.isDirSelectionAccepted())
      {
         //On ignore le click si l'item sélectionné est un dossier et 
         //si la sélection des dossiers n'est pas permise
         //Si l'on est en mode multiselection, on selection tous les items
         //fils
         if(!this.acceptMultiSelection)
         {
            return;
         }
         if(!isMultiSelection)
         {
            this.clearSelection();
         }
         //La selection des fichiers via la selection du répertoire
         //n'est possible que si l'on accept la sélection multiple
         //et pas la selection de répertoire
         this.selectAllItems(item,isMultiSelection);
         return;
      }
   
      if(event!=null)
      if(isMultiSelection)
      {
         if(this.isSelected(item.id))
         {
            //la déselection de l'élément n'est réalisé que si l'handler
            //implément removeItemSelected.
            if(this.actionHandler.removeItemSelected)
            {
               this._removeFromSelection(item);
               this.actionHandler.removeItemSelected(item);
            }
            return;
         }
      }
      this._setLastSelectedItemElement(item,isMultiSelection);
      
      // Appelle le gestionnaire pour l'action de sélection
      if ( this.actionHandler.onItemSelected )
      {
      this.actionHandler.onItemSelected(item,isMultiSelection);
      }
   }
   }
}


// POsitionne l'item envoyé, sans autre action
TreeView.prototype.goToItem = function (itemId) 
{
   var item = null;
   if(itemId!=null)
       item = this.allItems[itemId];
   if ( item != null || itemId==null)
   {
       // Change le style du nouveau label sélectionné
       var location=this.doc.location.toString().split("#");
       if(location) this.doc.location.href=location[0]+"#label_" + item.id;
   }
   return item;
}

/**
 * Permet la selection de tout les items fils directement contenu sous
 * folder. Si isMultiSelection vaut true, la selection est ajouté à
 * la sélection courante.
 * @param folder: répertoire contenant les items.
 * @param isMultiSelection: n'est utilisé que si folder n'est pas un folder. On 
 * l'ajoute à la selection
 */
TreeView.prototype.selectAllItems = function (folder,isMultiSelection)
{
   if(!folder.isFolder)
   {
      this.setSelectedItem(folder,isMultiSelection);
   }
   else
   {
      if(folder.hasChildren())
      {
         var items = folder.getChildren();
         for(var i=0;i<items.length;i++)
         {
            var item = items[i]
            if(!item.isFolder)
               this.setSelectedItem(item.id,true);
         }
      }
   }
}


// Sélection de l'item + éventuellement action
TreeView.prototype.onItemdblClick = function (itemId) 
{
   var item =this.allItems[itemId];
   if ( item != null )
   {
      if (!this.isVirtualItem(item))
      {
      //On ignore le click si l'item sélectionné est un dossier et 
      //si la sélection des dossiers n'est pas permise
      if(item.isFolder && !this.isDirSelectionAccepted())
      {
         return;
      }
      this._setLastSelectedItemElement(item,false);
      // Appelle le gestionnaire pour l'action de sélection
      if ( this.actionHandler.ondblClick )
      {
         this.actionHandler.ondblClick (item);
      }
      }
   }  
}

// POsitionne l'item sélectionné, sans autre action
TreeView.prototype.setSelectedItem = function (itemId,isMultiSelection) 
{
   if(isMultiSelection==null)
      isMultiSelection=false;
   var item = null;
   if(itemId!=null)
       item = this.allItems[itemId];
   
   if ( item != null || itemId==null)
   {
   if (!this.isVirtualItem(item))
   {
      this._setLastSelectedItemElement(item,isMultiSelection);
      // Appelle le gestionnaire pour l'action de sélection
      if ( this.actionHandler.onItemSelected )
      {
      this.actionHandler.onItemSelected(item,isMultiSelection);
      }
   }
   }
   return item;
}

// Plier/Déplier pour un dossier
// - forceOpen : force l'ouverture si true
TreeView.prototype.onToggle = function (itemId, forceOpen) 
{

   this.setWaitState(true);
 
   
   var vElt = this.doc.getElementById(itemId);
   if ( vElt == null ) return;
   var itemType = vElt.getAttribute("ezItemType");
   if ( itemType == "dir" )
   {
      //alert("item à plier/déplier : " + itemId);
      var htmlDirContent = this.doc.getElementById("dirContent_" + itemId); 
      if ( htmlDirContent != null ) 
      {
         var item = this.allItems[itemId];
         // Mémorise l'état de l'item
         if ( item == null )
            alert("[onToggle] Item non trouvé dans les données : " + itemId);
         else
            item.toggle(forceOpen); // dans toggle, on va rechercher les enfants si nécessaires
         // Switch l'affichage du dossier
         if ( item.isOpened ) 
            this._showFolderOpen(htmlDirContent, item);
         else 
            this._showFolderClose(htmlDirContent, item);
      }
      else
      {
         alert("dirContent non trouvé pour dossier " + itemId);
      }
   } 
   else  // itempType == "item"
   {
      alert("Ne peut pas plier/déplier autre chose qu'un dossier");
   }
   this.setWaitState(false);
   
}

// Rafraîchit l'affichage du dossier pour le montrer ouvert
TreeView.prototype._showFolderOpen = function (htmlDirContent, item)
{
   if ( htmlDirContent )
   {
      // Sous IE, un dossier vide ouvert provoque une ligne blanche
      // on masque donc son contenu
      if ( isMSIE )
      {
         if ( item.hasChildren() )
            htmlDirContent.style.display = 'block';
         else
            htmlDirContent.style.display = 'none';
      }
      else
      {
         htmlDirContent.style.display = 'block';
      }
      this._refreshFolderIcons(item);
      // Mémorise comme item visible
      this.visibleItems[item.id] = 1;
   }
   else
   {
      alert("_showFolderOpen : htmlDirContent=null pour item.name=" + item.name);
   }
}
// Rafraîchit l'affichage du dossier pour le montrer fermé
TreeView.prototype._showFolderClose = function (htmlDirContent, item)
{
   htmlDirContent.style.display = 'none';
   this._refreshFolderIcons(item);
   // Enlève de la liste des items visibles
   this.visibleItems[item.id] = 0;
}

// Renvoie l'item sélectionné, null s'il n'y en a pas
TreeView.prototype.getSelectedItem = function ()
{
   if(this.selectedItem!=null)
   {
      if(this.selectedItem.length>0)
         return this.selectedItem[0];
      return null;
   }
   return null;
}

// Renvoie tous les items sélectionnés
TreeView.prototype.getSelectedItems = function ()
{
   return this.selectedItem;
}

/**
 * Déselectionne tout les éléments
 */
TreeView.prototype.clearSelection = function ()
{
   if(this.selectedItem==null)
      return; //rien n'est sélectionné
   for(var i=0;i<this.selectedItem.length;i++)
      {
         var curSel=this.selectedItem[i];
         if ( curSel != null )
         {
            var htmlLabel = this.doc.getElementById("label_" + curSel.id);
            if ( htmlLabel != null ) // se peut que l'imte n'existe plus suite à suppression
            {
               HTMLUtil_setStyleClass(htmlLabel, "treeview-item");
            }
            curSel.isSelected = false;
         }
      }
      this.selectedItem=new Array();
}

// Mémorise le dernier élément graphique sélectionné
TreeView.prototype._setLastSelectedItemElement = function (item,isMultiSelection)
{
   if(item==null)
      return;
   // Change le style de l'ancien label sélectionné
   if(!isMultiSelection && this.selectedItem!=null)
   {
      this.clearSelection();
   }
   if(item!=null)
   {
       // Change le style du nouveau label sélectionné
       var htmlLabel = this.doc.getElementById("label_" + item.id);
       if(htmlLabel!=null)
       {
         HTMLUtil_setStyleClass(htmlLabel, "treeview-item-selected");
       }
       // Mémorise l'état dans l'item
       item.isSelected = true;
   }
   // Mémorise dans l'arbre
   if(this.selectedItem==null)
      this.selectedItem=new Array();
   if(isMultiSelection)
   {
      this.selectedItem[this.selectedItem.length] = item;
   }
   else
   {
      this.selectedItem[0] = item;
   }
}

// Change le style du texte lorsque la souris passe dessus
TreeView.prototype.mouseOver = function (htmlLabel)
{
   HTMLUtil_setStyleClass(htmlLabel, "treeview-item-over");
}
// Change le style du texte lorsque la souris passe dessus
TreeView.prototype.mouseOverVirtual = function (htmlLabel)
{
   HTMLUtil_setStyleClass(htmlLabel, "treeview-item-virtual-over");
}


// Remet le style lorsque la souris sort, mais ne le fait pas
// si on sort de l'élément sélectionné
TreeView.prototype.mouseOut = function (htmlLabel)
{
   var itemId = htmlLabel.getAttribute("ezParentId");
   var className = "treeview-item";
   if ( this.isSelected(itemId) )
   {
      className = "treeview-item-selected";
   }
   HTMLUtil_setStyleClass(htmlLabel, className);
}

// Remet le style lorsque la souris sort, mais ne le fait pas
// si on sort de l'élément sélectionné
TreeView.prototype.mouseOutVirtual = function (htmlLabel)
{
   var itemId = htmlLabel.getAttribute("ezParentId");
   var className = "treeview-item-virtual";
   HTMLUtil_setStyleClass(htmlLabel, className);
}

/**
 * @param itemId Id de l'élément recherché
 * @return true si l'element d'id itemId est sélectionné
 */
TreeView.prototype.isSelected = function(itemId)
{
   if(this.selectedItem==null || this.selectedItem.length==0)
      return false;
   for(var i=0;i<this.selectedItem.length;i++)
   {
      if(this.selectedItem[i]!=null && this.selectedItem[i].id == itemId)
         return true;
   }
   return false;
}

/**
 * Selection l'item
 * @param item, TreeViewItem devant être sélectionné
 */
TreeView.prototype.setSelected = function(item)
{
   this._setLastSelectedItemElement(item,false);
}

// Affiche tous les items de type 'dir' qui doivent être visibles
// Les éléments HTML doivent avoir été créés avant
TreeView.prototype._ensureVisible = function ()
{
   var hash = this.visibleItems;
   for (var itemId in hash)
   {
      if ( hash[itemId] == 1 )
      {
         var item = this.allItems[itemId];
         if ( item != null )
         {
            item.toggle(true);  // force l'ouverture et chargement des enfants
            var htmlDirContent = this.doc.getElementById("dirContent_" + itemId); 
            this._showFolderOpen(htmlDirContent, item);
         }
      }
   }
}
// S'assure qu'un item est visible dans l'arbre en dépliant tous
// ses ancêtres (qui doivent avoir été générés déjà)
TreeView.prototype.ensureItemVisible = function (item)
{
   // elem div de l'item
   var itemId = item.id;
   var htmlElem = this.doc.getElementById(itemId);
   if ( htmlElem == null) 
   {
      alert("ensureItemVisible : pas de HTML pour id=" + itemId);
      return;
   }
   var parentItemId = htmlElem.getAttribute("ezParentItem");
   while ( parentItemId && (parentItemId != "") )
   {
      // elem div du contenu du parent
      var htmlParentContent = this.doc.getElementById("dirContent_"+parentItemId);
      var parentItem = this.allItems[parentItemId];
      // Rend visible le parent
      this._showFolderOpen (htmlParentContent, parentItem);
      
      // elem div du parent lui-même
      var htmlParent = this.doc.getElementById(parentItemId);
      parentItemId = htmlParent.getAttribute("ezParentItem");
   }
}

// Regénère tout l'arbre
// Veille à ce que le dernier item sélectionné soit visible
TreeView.prototype.refresh = function ()
{

   this.setWaitState(true);

   this.generate();

   // Les dossiers visibles doivent l'être
   this._ensureVisible();

   // Les éléments sélectionnés doivent l'être
   var elts = this.selectedItem;
   if ( elts != null )
   {
      for(var i=0;i<elts.length;i++)
      {
         var elt = elts[i];
         this.ensureItemVisible(elt);
         this.setSelectedItem(elt.id);
      }
   }
   
   this.setWaitState(false);
   
}

// Rafraichissement de tout l'arbre en effaçant la div initiale
// Veille à ce que le dernier item sélectionné soit visible
TreeView.prototype._eraseAndRefresh = function ()
{

   this.setWaitState(true);
   this.rootDiv.innerHTML="";
   this.generate();

   // Les dossiers visibles doivent l'être
   this._ensureVisible();

   // Les éléments sélectionnés doivent l'être
   var elts = this.selectedItem;
   if ( elts != null )
   {
      for(var i=0;i<elts.length;i++)
      {
         var elt = elts[i];
         this.ensureItemVisible(elt);
         this.setSelectedItem(elt.id);
      }
   }
   
   this.setWaitState(false);
   
}


// Ajoute un item, sans rafraîchir l'arbre
// Il faut ajoute la racine manuellement avec l'id ROOT
// Renvoie item
TreeView.prototype.addItem = function (parentId, id, name, isFolder, type, data)
{
   var newItem = new TreeViewItem(id, name, isFolder, type, data);
   this.addItemObjet(parentId,newItem);
   return newItem;
}

/**
 * Ajoute un item, sans rafraîchir l'arbre
 * Il faut ajoute la racine manuellement avec l'id ROOT
 * @param parentID {String} identifiant du noeud devant contenir le newItem, si null new item est root
 * @param newItem {TreeViewItem} Objet de type treeView
 * @return {TreeViewItem} Renvoie newItem
 */
TreeView.prototype.addItemObjet = function (parentId, newItem)
{
   newItem.tree = this;
   if ( parentId != null)
   {
      var parentItem = this.allItems[parentId];
      if ( parentItem == null )
      {
         alert("Parent d'id=" + parentId + " introuvable");
         return;
      }
      parentItem.addItem(newItem);
   }
   else // Ajout de la racine
   {
      this.rootItem = newItem;
      // On est censé avoir chargé les enfants directs de la racine au démarrage
      newItem.fillChecked = true;
      this.allItems[newItem.id] = newItem;
   }
   return newItem;
}

// Regénère toute la vue de l'arbre
TreeView.prototype.generate = function()
{

   // La racine
   var rootDiv = this.doc.getElementById(this.rootDivId);
   this.rootDiv = rootDiv;
   
   var rootContent = this._generateOneFolder(this.rootDiv, this.rootItem);

   // Récursif : génère les enfants de rootItem
   this._generateChildItem(rootContent, this.rootItem);
   
   // Mémorise qu'on a déjà généré la racine
   var htmlElem = this.doc.getElementById(this.rootItem.id);
   htmlElem.setAttribute("ezIsChildrenGenerated", "true");
}

// Génère dans <destDiv> un seul dossier à partir de <item>
TreeView.prototype._generateOneFolder = function(destDiv, item)
{
   
   var itemId = item.id;
   // Div pour le dossier lui-même
   var folderDiv = this.doc.createElement("div");
   HTMLUtil_setStyleClass (folderDiv, "treeview-dir");
   folderDiv.setAttribute("id", itemId);
   folderDiv.setAttribute("ezItemType", "dir");
   folderDiv.setAttribute("ezAction", "todo");
   if ( item.parent != null )
   {
      folderDiv.setAttribute("ezParentItem", item.parent.id);
   }

   destDiv.appendChild(folderDiv);
   
   return this._updateOneFolderView(item, folderDiv);

}

// Met à jour div d'un dossier dont la div existe déjà
// - folderDiv : optionnel
TreeView.prototype._updateOneFolderView = function(item, folderDiv)
{
   //pru:15/03/07 : Folder.innerHtml transform &#x27; en '
   var itemId = XMLUtil_replaceEntities(item.id);
   
   // Div pour le dossier lui-même
   if ( folderDiv == null )
      folderDiv = this.doc.getElementById(itemId);
   if ( folderDiv == null )
   {
      alert("DIV pour le dossier introuvable :\n" + item.toString());
      return;
   }
   // Les icônes d'entête :
   var folderHTML = this._getFrontText(item);
   
   // L'icone toggle
   var imgName = "toggle_" + itemId;
   var img = new Tag("img").att("class", "treeview-icon");
   // icone différent pour root, et si l'item est le dernier enfant
   var toogleIcon;
   if ( item.parent == null ) // root
   {
      toogleIcon = this.config.rootPlusIcon;
   }
   else
   {
   if(!item.data || item.data.isParent==null)
   {
    if ( item.isLastChild() )
         toogleIcon = this.config.lPlusIcon;
      else
         toogleIcon = this.config.tPlusIcon;
   }
   else
   {      
   if(item.data.isParent==true)
   {
      if ( item.isLastChild() )
         toogleIcon = this.config.lPlusIcon;
      else
         toogleIcon = this.config.tPlusIcon;
   }
   else 
   {
         if ( item.isLastChild() )
         toogleIcon = this.config.lIcon;
      else
         toogleIcon = this.config.tIcon;
   }
   }
   }   
   img.att("src", toogleIcon).att("name", imgName).att("id", imgName);

   if(!item.data || item.data.isParent==null || item.data.isParent==true)img.att("onclick", this.objId + ".onToggle('" + itemId +"')");
   img.att("title", mess_treeview_open_folder);
   folderHTML += img.text();

   // L'icone dossier : peut être spécifique ou standard
   imgName = "chip_" + itemId;
   img = new Tag("img").att("class", "treeview-icon");
   var folderIcon = item.getIcon();
   // csi Gestion des groupes de réponses
   if (item.type)
   {
      if (this.isVirtualItem(item))
      {
            folderIcon = this.config.virtualFolderIcon;
      }
   }
   if ( ! folderIcon ) folderIcon = this.config.folderIcon;

   img.att("src", folderIcon).att("name", imgName).att("id", imgName);
   // Clic sur label du dossier = clic sur icone = sélection
   if(!item.data || item.data.isParent==null || item.data.isParent==true) 
   {
   img.att("onclick", this.objId + ".onItemClick(event,'" + itemId +"')");
   img.att("ondblclick", this.objId + ".onItemdblClick('" + itemId +"')");
   }
   img.att("title", mess_treeview_select_item);

   if ( item.parent != null ) // root
   {
    var folderLabel = item.getLabel();
   	if (folderLabel) img.att("title", folderLabel);
   }
   folderHTML += img.text();

   // Gestion code HTML complémentaire au début
   var prefix = item.getPrefix();
   if ( ! prefix ) prefix = "";
   folderHTML += prefix;

   // Div pour le label du dossier : positionner suivant qu'il est sélectionné ou pas
   var labelDiv = new Tag("span");
   
   var className = "treeview-dir";
   if (this.isVirtualItem(item))   var className = "treeview-dir-virtual";
   
   if ( item.isSelected )
      className = "treeview-item-selected";
   labelDiv.att("class", className).att("id", "label_" + itemId);
   labelDiv.att("ezParentId", itemId);
   if (!this.isVirtualItem(item))
   labelDiv.att("onmouseover", this.objId + ".mouseOver(this)");
   else labelDiv.att("onmouseover", this.objId + ".mouseOverVirtual(this)");
   if (!this.isVirtualItem(item))
   labelDiv.att("onmouseout", this.objId + ".mouseOut(this)");
   else labelDiv.att("onmouseout", this.objId + ".mouseOutVirtual(this)");
   // Clic sur label du dossier = clic sur icone = sélection
   labelDiv.att("onclick", this.objId + ".onItemClick(event,'" + itemId +"')");
   labelDiv.att("ondblclick", this.objId + ".onItemdblClick('" + itemId +"')");
   if(this.isRightClickable()) labelDiv.att("onmousedown", "RIGHT_CLICK_CONTEXT.initialize('rightClickContainer',event, ['COPY','RENAME','MOVE']);");
   labelDiv.setBody("&nbsp;" + item.name);
   labelDiv.att("title", mess_treeview_select_item + " : " + item.name);


   folderHTML += labelDiv.text();

   // Icone attente juste pour la racine
   if ( item.parent  == null )
   {
      imgName = "wait_" + itemId;
      img = new Tag("img").att("class", "treeview-icon").att("style", "display:none");
      img.att("src", this.config.waitIcon).att("name", imgName).att("id", imgName);
      folderHTML += img.text();
   }

   // Div regroupant le contenu du dossier
   var dirContent = new Tag("div");
   dirContent.att("class", "treeview-dir-content");
   dirContent.att("id", "dirContent_" + itemId);
   // Div visible ou non en fonction de l'état ouvert ou non
   var dirContentStyle = "display:none";
   //if ( item.isOpened )
   //   dirContentStyle = "display:block";
   dirContent.att("style", dirContentStyle);
   dirContent.att("nowrap", null);
   
   
   // Gestion code HTML complémentaire à la fin
   var suffix = item.getSuffix();
   if ( ! suffix ) suffix = "";
   
   folderDiv.innerHTML = folderHTML + suffix + dirContent.text();
   
   return this.doc.getElementById("dirContent_" + itemId);
}


// Génère dans <destDiv> un seul item simple à partir de <item>
TreeView.prototype._generateOneItem = function(destDiv, item)
{
   var itemId = XMLUtil_replaceEntities(item.id);

   // Div pour l'item
   var itemDiv = this.doc.createElement("div");
   if (!this.isVirtualItem(item)) HTMLUtil_setStyleClass (itemDiv, "treeview-item");
   else HTMLUtil_setStyleClass (itemDiv, "treeview-item-virtual");
   itemDiv.setAttribute("id", itemId);
   itemDiv.setAttribute("ezItemType", "item");
   //itemDiv.setAttribute("onclick", this.objId + ".onClick(this.id)");
   itemDiv.setAttribute("ezAction", "todo");
   if ( item.parent != null )
   {
      itemDiv.setAttribute("ezParentItem", item.parent.id);
   }

   destDiv.appendChild(itemDiv);

   // Les icônes d'entête :
   var itemHTML = this._getFrontText(item);

   // L'icône de lien avec le dossier parent, appelé par commodité toogle aussi
   var img = new Tag("img");
   var imgName = "toggle_" + itemId;
   img.att("name", imgName).att("id", imgName).att("class", "treeview-icon");
   var icon;
   if ( item.parent.children.length == item.rank ) // L
      icon = this.config.lIcon;
   else // T
      icon = this.config.tIcon;
   img.att("src", icon);
   
   itemHTML += img.text();
   
   // L'icône de l'item
   img = new Tag("img");
   imgName = "chip_" + itemId;
   var itemIcon = item.getIcon();
   if ( ! itemIcon ) itemIcon = this.config.fileIcon;
   img.att("src", itemIcon);
   img.att("name", imgName).att("id", imgName).att("class", "treeview-icon");
   // Clic sur label du dossier = clic sur icone = sélection
   img.att("onclick", this.objId + ".onItemClick(event,'" + itemId +"')");
   img.att("ondblclick", this.objId + ".onItemdblClick('" + itemId +"')");
   // csi 230207 Ajout d'un infobulle spécifique pour les items
   if ( item.data )
   {
      if ( item.data.getLabel )
      {
         var itemTip = item.data.getLabel();
         img.att("title", itemTip);
      }
      else
      {
         img.att("title", mess_treeview_select_item);
      }
   }
   else
   {
      img.att("title", mess_treeview_select_item);
   }
   
   itemHTML += img.text();
   
   // Gestion code HTML complémentaire au début
   var prefix = item.getPrefix();
   if ( ! prefix ) prefix = "";
   itemHTML += prefix;
   
   // Div pour le label
   var labelDiv = new Tag("span");
   if (!this.isVirtualItem(item)) labelDiv.att("class", "treeview-item");
   else labelDiv.att("class", "treeview-item-virtual");
   labelDiv.att("id", "label_" + itemId);
   labelDiv.att("ezParentId", itemId);
   labelDiv.att("onmouseover", this.objId + ".mouseOver(this)");
   labelDiv.att("onmouseout", this.objId + ".mouseOut(this)");
   // Clic sur label du dossier = clic sur icone = sélection
   labelDiv.att("onclick", this.objId + ".onItemClick(event,'" + itemId +"')");
   labelDiv.att("ondblclick", this.objId + ".onItemdblClick('" + itemId +"')");
   if(this.isRightClickable()) labelDiv.att("onmousedown", "RIGHT_CLICK_CONTEXT.initialize('rightClickContainer',event, ['COPY','RENAME','MOVE']);");
   labelDiv.setBody("&nbsp;" + item.name);
   labelDiv.att("title", mess_treeview_select_item + " : " + item.name);
        
   itemHTML += labelDiv.text();
   
   // Gestion code HTML complémentaire à la fin
   var suffix = item.getSuffix();
   if ( ! suffix ) suffix = "";
   
   itemDiv.innerHTML = itemHTML + suffix;

   return itemDiv;
}

// Renvoie le code HTML qui doit précéder l'affichage de l'item
TreeView.prototype._getFrontText = function(item)
{
   // Si l'item n'a pas de parent, aucun code n'est nécessaire
   var ret = "";
   var par = item.parent;
   if ( par != null )
   {
      // Si le parent est le dernier enfant de son parent ou est la racine,
      // ajouter un blanc
      if ( par.isLastChild()  )
      {
         ret = this.config.blankImg;
      }
      else
      {
         // Jointure verticale avec le prochain frère du parent
         ret = this.config.iImg;
      }
      // calcule récursivement le texte déduit des parents
      ret = this._getFrontText(par) + ret;
   }
   return ret;
}

// Génère la vue des enfants de l'item donné
// parentDirContent : peut être null, on le déduit alors de parentItem.id
// parentItem : l'item parent
TreeView.prototype._generateChildItem = function(parentDirContent, parentItem)
{
   if ( parentDirContent == null )
      parentDirContent = this.doc.getElementById("dirContent_" + parentItem.id);
   if ( parentDirContent == null )
   {
      alert("_generateChildItem : élement html parent non trouvé pour dossier " + parentItem.id);
      return;
   }
   
   if ( parentItem.hasChildren() )
   {
      var children = parentItem.children;
      for (var i=0; i<children.length; i++)
      {
         var item = children[i];
         if ( item.isFolder )
         {
            this._generateOneFolder(parentDirContent, item);
         }
         else
         {
            this._generateOneItem(parentDirContent, item);
         }
      }
   }
   // Méorise qu'on a déjà généré les enfants
   var parentDiv = this.doc.getElementById(parentItem.id);
   parentDiv.setAttribute("ezIsChildrenGenerated", "true");
   
}
// Un item a été ajouté précédemment, on doit générer sa vue
// Si l'item existe déjà dans la vue, ne fait rien
// parentItem : l'item parent
// childItem  : l'item nouvellement ajouté
TreeView.prototype._addChildItemView = function(parentItem, childItem)
{
   var htmlParentContent = this.doc.getElementById("dirContent_" + parentItem.id);
   if ( htmlParentContent == null )
   {
      alert("_addChildItemView : élément html parent non trouvé pour dossier\n" + parentItem.toString());
      return;
   }
   var htmlChild = this.doc.getElementById(childItem.id);
   if ( htmlChild != null )
   {
     //alert("_addChildItemView : l'élément existe déjà dans la vue\n" + childItem.toString());
      return;
   }
   // Refresh le dossier parent
   htmlParentContent = this._updateOneFolderView(parentItem);
   this._generateChildItem(htmlParentContent, parentItem);
   this._ensureVisible();
   
   // S'assure que l'item devient visible
   this.ensureItemVisible(childItem);
   // ne le sélectionne pas pour que l'utilisateur puisse ajouter plusieurs item
   // au dossier courant
   // this.onItemClick(childItem.id);
}


//
TreeView.prototype.setActionHandler = function (actionHandler)
{
   this.actionHandler = actionHandler;
}

TreeView.prototype.getActionHandler = function ()
{
   return this.actionHandler;
}

// Affiche l'icône adéquat pour le toogle et l'item dossier
TreeView.prototype._refreshFolderIcons = function (item)
{
   var itemId = item.id;
   var imgIcon = this.doc.images["chip_" + itemId];
   var imgToggle = this.doc.images["toggle_" + itemId];
   // Icones dépend si root et si dernier enfant
   var itemIcon;
   var toogleIcon;
   // Expérimentla : Si le dossier est vide et que son contenu a déjà été vérifié,
   // n'affiche pas de toogle
   // var showToogle = ( !item.hasChildren() );
   var showToogle = true;
   if ( item.parent == null ) // root
   {
      if ( item.isOpened )
      {
         itemIcon = item.getOpenedIcon();
         if ( ! itemIcon ) itemIcon = this.config.openRootIcon;
         toogleIcon = this.config.rootMinusIcon;
      }
      else
      {
         itemIcon = item.getIcon();
         if ( ! itemIcon ) itemIcon = this.config.rootIcon;
         toogleIcon = this.config.rootPlusIcon;
      }
   }
   else
   {
      if ( item.isOpened )
      {
         itemIcon = item.getOpenedIcon();
         // csi Gestion des groupes de réponses   
    	 if (item.type)
		 {
		    if (this.isVirtualItem(item))
		    {
   		          itemIcon = this.config.openVirtualFolderIcon;
		    }
		 }
         if ( ! itemIcon ) 
            itemIcon = this.config.openFolderIcon;		 
                        
         if ( item.isLastChild() )
         {
            if ( showToogle )
               toogleIcon = this.config.lMinusIcon;
            else
               toogleIcon = this.config.lIcon;
         }
         else
         {
            if ( showToogle )
               toogleIcon = this.config.tMinusIcon;
            else
               toogleIcon = this.config.tIcon;
         }
      }
      else
      {
         itemIcon = item.getIcon();
         // csi Gestion des groupes de réponses
         if (item.type)
		 {
		    if (this.isVirtualItem(item))
		    {
 		          itemIcon = this.config.virtualFolderIcon;
		    }
		 }
         if ( ! itemIcon ) itemIcon = this.config.folderIcon;		 
         if ( item.isLastChild() )
         {
            if ( showToogle )
               toogleIcon = this.config.lPlusIcon;
            else
               toogleIcon = this.config.lIcon;
         }
         else
         {
            if ( showToogle )
               toogleIcon = this.config.tPlusIcon;
            else
               toogleIcon = this.config.tIcon;
         }
      }
   }
   if(imgIcon!=null) imgIcon.setAttribute("src", itemIcon);
   if(imgToggle!=null) imgToggle.setAttribute("src", toogleIcon);
}

// Affiche l'icône adéquat pour l'item 
TreeView.prototype._refreshItemIcons = function (item)
{
   var itemId = item.id;
   var imgIcon = this.doc.images["toggle_" + itemId];
   var itemIcon;
   if ( item.isLastChild() )
      itemIcon = this.config.lIcon;
   else
      itemIcon = this.config.tIcon;
   imgIcon.setAttribute("src", itemIcon);
}

// 
TreeView.prototype._updateItemView = function (item)
{
   var itemId = item.id;
   var labelElem = this.doc.getElementById("label_" + itemId);
   if ( labelElem )
   {
      labelElem.innerHTML = "&nbsp;" + item.name;
   }
}
// Remet l'état d'atttente à zéro, masquant l'icône sablier
TreeView.prototype.reinitWaitState = function ()
{
   this.countWaitState = 1;
   this.setWaitState(false);
}

// Indique une attente à l'utilisateur
TreeView.prototype.setWaitState = function (wait)
{
   if ( this.countWaitState == null )
      this.countWaitState = 0;
   
   var statusMsg = "";
   var iconDisplay = "";
   var cursor = "default";
   if ( wait )
   {
      this.countWaitState ++;
   }
   else
   {
      if ( this.countWaitState > 0 )
         this.countWaitState --;
   }
   if ( this.countWaitState > 0 )
   {
      //statusMsg = mess_treeview_wait;
      cursor = "wait"; 
      iconDisplay = "inline";
      this.waitState = true;
   }
   else
   {
      iconDisplay = "none";
      this.waitState = false;
   }
   // Affiche ou masque l'icône d'attente
   var waitIcon = this.doc.images["wait_" + this.rootItem.id];
   

   if ( waitIcon )
      waitIcon.style.display = iconDisplay;
      
   this.doc.body.style.cursor = cursor; 
      
   window.status = statusMsg;
   
}

// Renvoie true si l'objet est en traitement
TreeView.prototype.getWaitState = function()
{
   return this.waitState;
}


// Demande l'ajout d'un dossier au handler de mise à jour
// Renvoie le nouvel item ajouté, null si échec
TreeView.prototype.createFolder = function(parentItem, folderName)
{
  if (!this.isVirtualItem(parentItem))
   {
   this.setWaitState(true);

   // Déplie et charge si nécessaire les enfants du parent d'abord
   this.onToggle(parentItem.id, true);
   
   // Ajoute dans la base
   var newItem = this.actionHandler.createFolder(parentItem, folderName);
   // Si ok, ajout dans la structure et la vue
   if ( newItem != null )
   {
      // Ajoute à la structure
      parentItem.addItem(newItem);
      // Ajoute à la vue
      this._addChildItemView (parentItem, newItem);
   }
   
   this.setWaitState(false);
   }
   else
   {
   alert(sysGetMessage("tree.operation.not.allowed"));
   }
   return newItem;
}

// Demande l'ajout d'un item fichier
// Renvoie le nouvel item ajouté, null si échec
TreeView.prototype.createItem = function(parentItem, itemName, itemType, itemData)
{
   if (!this.isVirtualItem(parentItem))
   {
   this.setWaitState(true);

   // Déplie et charge si nécessaire les enfants du parent d'abord
   this.onToggle(parentItem.id, true);
   
   // Ajoute dans la base
   var newItem = this.actionHandler.createItem(parentItem, itemName, itemType, itemData);
   // Si ok, ajout dans la structure et la vue
   if ( newItem != null )
   {
      // Ajoute à la structure
      parentItem.addItem(newItem);
      // Ajoute à la vue
      this._addChildItemView (parentItem, newItem);
   }
   
   this.setWaitState(false);
   }
   else
   {
   alert(sysGetMessage("tree.operation.not.allowed"));
   }
   return newItem;
}

// Demande l'ajout de plusieurs item fichiers
// Renvoie les nouvels item ajoutés, null si échec
TreeView.prototype.createItems = function(parentItem, itemIds)
{
   this.setWaitState(true);

   // Déplie et charge si nécessaire les enfants du parent d'abord
   this.onToggle(parentItem.id, true);
   
   // Ajoute dans la base
   var newItems = this.actionHandler.createItems(parentItem, itemIds);
   // Si ok, ajout dans la structure et la vue
   if ( newItems != null )
   {
      for (var i=0; i<newItems.length; i++)
      {
         var newItem = newItems[i];
         this.addItemObjet(parentItem.id,newItem);
      }
      // Un seul refresh
      this._eraseAndRefresh();

    
   }
   
   this.setWaitState(false);
   
   return newItems;
}


// Supprime l'item
// Renvoie true si ok, false sinon
//deleteSimple passe la main à un action handler différent de deleteItem(exemple thesaurus : suppression de la relation)
TreeView.prototype.deleteItem = function(item, deleteSimple)
{
   var success=false;
   if (!this.isVirtualItem(item))
   {
   
   this.setWaitState(true);

   // Ajoute dans la base
   if(!deleteSimple) success=this.actionHandler.deleteItem(item);
   else 
   {
   var parentItem=this.getNoVirtualParentItem(item);
   if(parentItem)
   {
   success=this.actionHandler.deleteSimple(item, parentItem.id);
   }
   }
   // Si ok, suppression de la structure et de la vue
   if ( success )
   {
      this._deleteItemFromView(item);
      // Refresh le dossier parent
      var parentItem = item.parent;
      var htmlParentContent = this._updateOneFolderView(parentItem);
      
   // enlève le cache de la vue : le dossier doit être régénéré
   	var parentDiv = this.doc.getElementById(parentItem.id);
   	if(parentDiv!=null) parentDiv.setAttribute("ezIsChildrenGenerated", "false");
   	
   
      this._generateChildItem(htmlParentContent, parentItem);
      
      this._ensureVisible();         
   }
   
   this.setWaitState(false);
   }
   else
   {
   alert(sysGetMessage("tree.operation.not.allowed"));
   }
   return success;
}

// Enlève l'item de la structure et du document HTML sans action sur la base
// Le refresh du parent n'est pas appelé
TreeView.prototype._deleteItemFromView = function(item)
{
   // Supprime de la structure récursivement
   item.removeFromParent(true);
   this._removeFromSelection(item);
   // Supprime exclusivement le code HTML de cet item
   var itemDiv = this.doc.getElementById(item.id);
   itemDiv.parentNode.removeChild(itemDiv);
}

/**
 * Supprime un élément de la selection s'il est sélectionné.
 * Sinon ne fait rien
 * @param item a supprimer de la sélection
 */
TreeView.prototype._removeFromSelection = function(item)
{
   if(this.selectedItem!=null && this.selectedItem.length!=0)
   {
      var newSelection=new Array();
      var itemId = item.id;
      for(var i=0;i<this.selectedItem.length;i++)
      {
         if(this.selectedItem[i].id != itemId)
            newSelection[newSelection.length]=this.selectedItem[i];
      }
      this.selectedItem=newSelection;
   }
}

// Renomme l'item
TreeView.prototype.renameItem = function(item, itemName)
{
   if (!this.isVirtualItem(item))
   {
   this.setWaitState(true);

   // Ajoute dans la base
   var updatedItem = this.actionHandler.renameItem(item, itemName);
   // Si ok, mise à jour
   if ( updatedItem )
   {
      item.copyData(updatedItem);
      this._updateItemView(updatedItem);
   }
   
   this.setWaitState(false);
   }
   else
   {
   alert(sysGetMessage("tree.operation.not.allowed"));
   }
   return updatedItem;
}

// Renvoie un item par son id
TreeView.prototype.getItemById = function(itemId)
{
   return this.allItems[itemId];
}

// Déplace l'item
// Renvoie item ou null si échec
TreeView.prototype.moveItem = function(item, newParentItem)
{
   var ret = null;
   if (!this.isVirtualItem(item))
   {
   this.setWaitState(true);

   // Déplie le parent d'abord pour charger les enfants
   newParentItem.toggle(true);
   
   // Déplace dans la base
   var movedItem = this.actionHandler.moveItem(item, newParentItem);
   if ( movedItem )
   {
      // csi 030506 Retourne le nouvel item
      // ret = item;
      ret = movedItem;
      
      var prevParentItem = item.parent;
      
      // Enlève juste du parent en conservant les enfants
      item.removeFromParent(false);

      // Refresh l'ancien dossier parent
      var htmlParentContent = this._updateOneFolderView(prevParentItem);
      this._generateChildItem(htmlParentContent, prevParentItem);
      // Refresh car le nouveau parent peut avoir été masqué par le refresh de l'ancien parent
      // (si le nouveau parent est 2 niveaux en dessous de l'ancien parent au moins)
      this._ensureVisible();
      
      // csi 030506 Traitement du nouvel item
      // Ajoute à la structure du nouveau parent
      // newParentItem.addItem(item);
      newParentItem.addItem(movedItem);
      // Ajoute à la vue
      // this._addChildItemView (newParentItem, item);
      this._addChildItemView (newParentItem, movedItem);
   }
   
   this.setWaitState(false);
   }
   else
   {
   alert(sysGetMessage("tree.operation.not.allowed"));
   }
   return ret;
}



// Insert l'item item au dessus de l'item de référence refItem
// si up == true l'item est insérée au dessus
//sinon l'item est insérée en dessous
// Renvoie item ou null si échec
TreeView.prototype.insertItem = function(item, refItem,parentFolder, up)
{
   var ret = null;
   if (!this.isVirtualItem(item))
   {
   this.setWaitState(true);

   
   var parentItem= this.getNoVirtualParentItem(item);
   
   // Déplace dans la base
   this.actionHandler.insertItem(item, refItem, parentItem, up);
   if ( parentItem && parentItem.id!="ROOT")
   {
      // On suprrime tous les items sous le parent
      newItemsSorted=parentItem.tree.actionHandler.getChildren(parentItem.id);
      
      for (var i=0; i<newItemsSorted.length; i++)
         {
         this._deleteItemFromView(this.getItemById(newItemsSorted[i].id));
         }
     //on recrée tous les items dans le bon ordre fourni par getChildren          
	this.refreshItemChildren(parentItem,newItemsSorted);
	this.setSelectedItem(item.id);
   }
   this.setWaitState(false);
   }
   else
   {
   alert(sysGetMessage("tree.operation.not.allowed"));
   }
   return ret;
}

// Suppression de l'item de son emplacement d'origine
// Création dans l'item pèresi le toggle a déjà été fait
// Renvoie item ou null si échec
TreeView.prototype.moveItemWithParentCheckToggle = function(item, newParentItem)
{
   var ret = null;
   if (!this.isVirtualItem(item))
   {
   this.setWaitState(true);

   // Déplie le parent d'abord pour charger les enfants
   newParentItem.toggle(true);
   
   // Déplace dans la base
   var movedItem = this.actionHandler.moveItem(item, newParentItem);
   if ( movedItem )
   {
      ret = movedItem;
      
      var prevParentItem = item.parent;
      
      // Enlève juste du parent en conservant les enfants
      item.removeFromParent(false);

      // Refresh l'ancien dossier parent
      var htmlParentContent = this._updateOneFolderView(prevParentItem);
      this._generateChildItem(htmlParentContent, prevParentItem);
      // Refresh car le nouveau parent peut avoir été masqué par le refresh de l'ancien parent
      // (si le nouveau parent est 2 niveaux en dessous de l'ancien parent au moins)
      this._ensureVisible();
      // Avant d'ajouter l'élément, on vérifie qu'il n'existe pas déjà
      var addThisItem = true;
      if ( newParentItem.children != null ) 
      {
         for (var i=0; i<newParentItem.children.length; i++)
         {
            var curItem = newParentItem.children[i];
            if (curItem.id == movedItem.id)
            {
               addThisItem = false;
               break;
            }
         }
      }
      if (addThisItem)
      {      
	     newParentItem.addItem(movedItem);
	     this._addChildItemView (newParentItem, movedItem);
      }
   }
   
   this.setWaitState(false);
   }
   else
   {
   alert(sysGetMessage("tree.operation.not.allowed"));
   }
   return ret;
}

// Duplique l'item
// Renvoie item ou null si échec
TreeView.prototype.duplicateItem = function(item, newParentItem)
{
   if (!this.isVirtualItem(item))
   {
   this.setWaitState(true);

   // Duplique dans la base
   var newItem = this.actionHandler.duplicateItem(item, newParentItem);
   if ( newItem )
   {
      // Ajoute à la structure du parent 
      newParentItem.addItem(newItem);
      // Ajoute à la vue
      this._addChildItemView (newParentItem, newItem);
   }
   
   this.setWaitState(false);
   }
   else
   {
   alert(sysGetMessage("tree.operation.not.allowed"));
   }
   return newItem;
}
//teste si l'item est de type virtual ou non en fonction de VIRTUAL_KEY
// - item : l'item à tester
TreeView.prototype.isVirtualItem = function(item)
{
	if (item!=null && item.type) 
	{
	return item.type == VIRTUAL_KEY;
	}
	else return false;
}

TreeView.prototype.getNoVirtualParentItem = function(item)
{
	if(item.parent!=null)
	{
	if(!this.isVirtualItem(item.parent)) return item.parent;
	else return this.getNoVirtualParentItem(item.parent);
	}
	else return item;
}

// Met à jour l'affichage des enfants de l'item
// - item : l'item à rafraîchir
// - childItems : la liste des enfants à comparer avec celle existant dans la vue
// Si un enfant n'existe pas dans la vue, mais dans childItems, le crée
// Si un enfant existe déjà dans vue, mais pas dans childItems, le supprime
TreeView.prototype.refreshItemChildren = function(item, childItems)
{
   if ( !item || !childItems ) return;
   
   var hasUpdate = false;
   
   // 1) Repère d'abord les items osbolète de la vue à enelver tout de suite
   // ex : toto se trouve encore à la racine alors qu'il a été déplacé
   // ailleurs, mais dans une arborescence non encore chargée
   var parentItemsToRefresh = new Array();
   for (var i=0; i<childItems.length; i++)
   {
      var childItem = childItems[i];
      var viewChildItem = this.allItems[childItem.id];
      if ( ! viewChildItem ) 
      {
         // L'item n'existe pas dans la vue, ok on ne fait rien pour le moment
      }
      else
      {
         // il existe, s'il n'est pas attaché au bon parent, on le supprime
         if (viewChildItem.parent.id != item.id )
         {
            this._deleteItemFromView(viewChildItem);
            // mémorise le parent pour refresh
            parentItemsToRefresh[viewChildItem.parent.id] = viewChildItem.parent;
            hasUpdate = true;
         }
      }
   }
   // 2) Déplie ensuite pour charger les enfants si ce n'est pas fait
   this.onToggle(item.id, true);
   
   // 3) Teste ses nouveaux enfants à ajouter
   var childItemsById = new Array(); // remplit ce hashing au passage
   for (var i=0; i<childItems.length; i++)
   {
      var childItem = childItems[i];
      childItemsById[childItem.id] = childItem;
      var viewChildItem = this.allItems[childItem.id];
      if ( ! viewChildItem )
      {
         // L'item n'existe pas dans la vue, ok on le crée
         // Ajoute à la structure
         item.addItem(childItem);
         // Ajoute à la vue
         this._addChildItemView (item, childItem);
         hasUpdate = true;
      }
   }
   // 4) Teste les enfants à supprimer
   // Attention, _deleteItemFromView met à jour item.children, il faut donc faire
   // 2 passages 
   if ( item.hasChildren() )
   {
      var itemsToDelete = new Array();
      // a) mémoriser les enfants à supprimer
      for (var i=0; i<item.children.length; i++)
      {
         var viewChildItem = item.children[i];
         if ( childItemsById[viewChildItem.id] == null )
         {
            itemsToDelete[itemsToDelete.length] = viewChildItem;
         }
      }
      // b) les supprimer
      for (var i=0; i<itemsToDelete.length; i++)
      {
         this._deleteItemFromView(itemsToDelete[i]);
         hasUpdate = true;
      }
   }
   if ( hasUpdate )
   {
      // Refresh des parents des items obsolètes enlevés
      for (var parentId in parentItemsToRefresh)
      {
         var parentItem = parentItemsToRefresh[parentId];
         if ( parentItem )
         {
            var htmlParentContent = this._updateOneFolderView(parentItem);
            this._generateChildItem(htmlParentContent, parentItem);
         }
      }
      // Rend visible les nouveaux ajouts
      this._ensureVisible();

      // Refresh l'item courant en dernier pour éviter message : element html introuvable
      var htmlParentContent = this._updateOneFolderView(item);
      this._generateChildItem(htmlParentContent, item);
      this.onToggle(item.id, true);
   }
}

// Ajoute directement des items à l'arbre sans impacter la base
// - item : l'item parent
// - childItems : la liste des enfants à ajouter
TreeView.prototype.directAddItemChildren = function(item, childItems)
{
   if ( !item || !childItems ) return;
   
   var hasUpdate = false;
   
   for (var i=0; i<childItems.length; i++)
   {
      var childItem = childItems[i];
      var viewChildItem = this.allItems[childItem.id];
      if ( ! viewChildItem ) // L'item n'existe pas déjà dans la vue
      {
         // Ajoute à la structure
         item.addItem(childItem);
         // Ajoute à la vue
         this._addChildItemView (item, childItem);
         hasUpdate = true;
      }
   }
   /*
   if ( hasUpdate )
   {
      var htmlParentContent = this._updateOneFolderView(parentItem);
      this._generateChildItem(htmlParentContent, parentItem);
      this._ensureVisible();
   }
   */
}

// Initialisation du treeView par chargement d'un fichier
// Décode le document au format :
// <items>
// 	<item id="ROOT" isFolder="true" name="Documents" type="ROOT">
//   <item id="xx" name="yy" type="zz" isFolder="false" objectId="source:table:clé">
// ...
// </item>
// </item>
//</items>
/**
 * Cette méthode permet de construire un arbre à partir d'un document xml.
 * Les noeuds construit sont standard, il peuvent être personnalisé par
 * un treeHandler implémentant la méthode buildItem si ce dernier est configuré 
 * dans l'arbre avant l'appel
 * à cette méthode.
 */
TreeView.prototype.initFromXml = function(dataActionUrl,xmlDoc)
{	

	var ret = null;
	if(!xmlDoc) xmlDoc=this.loadXml(dataActionUrl);
	var collecItem=xmlDoc.getElementsByTagName("item");
   if(collecItem.length>0)
   {
   	var item=collecItem[0];
      var rootNode = new Array();
      rootNode[0]=item;
   	this.addChildItem(rootNode,null);
   }
   return ret;
}

/*
 * Contruction récursive d'un sous arbre complet 
 * @param {Array} Tableau d'élément xml, chaque élément est un fils de noeud d'id parentItemId
 * @param {String} parentItemId id du noeud contenant le sous arbre, si null tabItem ne doit contenir qu'un seul
 * élément, l'élément root. 
 * Ajout d'un fils à partir d'un élémént xml
 * fonction récursive sur les autres éléments
 */
TreeView.prototype.addChildItem = function(tabItem,parentItemId)
{
	if(tabItem)
	{
      if(parentItemId==null && tabItem.length>1)
      {
         return;
      }
      for (var i=0; i< tabItem.length; i++)
      {
         var item = tabItem[i];
         var newItem = null;
         if(this.actionHandler!=null && typeof(this.actionHandler.buildItem)!="undefined")
         {
            newItem=this.actionHandler.buildItem(item);
            this.addItemObjet(parentItemId, newItem);
         }
         else
         {            
            var id = item.getAttribute("id");
            var name = item.getAttribute("name");
            var type = item.getAttribute("type");
            var isFolder = ("true" == item.getAttribute("isFolder"));
            var objectId = item.getAttribute("objectId");
            var source = item.getAttribute("source");   
            newItem=this.addItem(parentItemId, id, name, isFolder, type, objectId);
         }
         if(item.hasChildNodes()) 
            this.addChildItem(XMLUtil_getChildElements(item),newItem.id);
      }
	}
}

// Chargement d'un fichier XML
TreeView.prototype.loadXml = function(action){
   
   var xmlDoc = XMLUtil_LoadXML(action);
   if ( xmlDoc == null  )
   {
      // Message d'erreur géré au niveau de fct XMLUtil_LoadXML()
      return null;
   }
   var errMsg = XMLUtil_IsError(xmlDoc);
   if ( errMsg )
   {
      alert(errMsg);
      return null;
   }
   return xmlDoc;
}

// ------------------------------------------------------------
// Item de l'arbre
// - data : peut être n'importe quelle objet qu'on associe à l'item
//   L'objet data peut fournir un préfix et un suffix HTML pour 
//   la génération du code de l'item. Il peut aussi fournir
//   les chemins des icônes. Si ce n'est pas fait, on utilise les
//   icônes par défaut.
//   Cet objet peut également implémenter moveUp() et moveDown() 
//   pour être prévenu lorsqu'un item se déplace dans l'arbre à l'aide 
//   de TreeViewItem.moveUp ou TreeViewItem.moveDown.
// ------------------------------------------------------------
function TreeViewItem(id, name, isFolder, type, data)
{
   this.id    = id;
   this.name  = name;
   this.isFolder = isFolder;
   this.type  = type;
   this.level = 0;
   this.rank  = 0;
   this.children = null;
   this.parent = null;
   this.tree = null;
   this.isOpened = false;
   this.isSelected = false;
   this.fillChecked = false; // indique si on a déjà vérifié le remplissage d'un dossier
   this.data = data;
}

/**
 * L'item monte dans l'arbre mais reste dans son niveau.
 * Son rang est décrémenté
 */
TreeViewItem.prototype.moveUp = function ()
{
   if(this.parent == null)
   {
      return;
   }
   var index = this.rank-1;
   if(index<=0)
   {
       return;
   }
   var nodeToMove = this.tree.doc.getElementById(this.id);
   var parentNode = nodeToMove.parentNode;
   parentNode.removeChild(nodeToMove);
   //move item
   var currentItem = this.parent.children[index];
   var childItem = this.parent.children[index-1];
   this.parent.children[index] = childItem;
   childItem.rank+=1;
   this.parent.children[index-1] =currentItem;
   currentItem.rank-=1;
   //move node
   var childNode = this.tree.doc.getElementById(childItem.id);
   parentNode.insertBefore(nodeToMove,childNode);
   
   //listeners
   if(currentItem.data.moveUp) {
       currentItem.data.moveUp();
   }
   // csi 230307 inutile et dangeureux
   /*
   if(childItem.moveDown) {
       childItem.moveDown();
   }
   */
}

/**
 * L'item descend dans l'arbre, il reste dans son niveau
 * son rang est incrémenté
 */
TreeViewItem.prototype.moveDown = function ()
{
   if(this.parent == null || this.parent.children==null )
   {
      return;
   }
   var index = this.rank-1;
   if(index+1 >= this.parent.children.length)
   {
       return;
   }
   var nodeToMove = this.tree.doc.getElementById(this.id);
   var parentNode = nodeToMove.parentNode;
   parentNode.removeChild(nodeToMove);
   //move item
   var currentItem = this.parent.children[index]; //equivalent à this
   var childItem = this.parent.children[index+1];
   this.parent.children[index] = childItem;
   childItem.rank-=1;
   this.parent.children[index+1] =currentItem;
   currentItem.rank+=1;
   if(index+2<this.parent.children.length)
   {
       //move node
       var childItem4Insert = this.parent.children[index+2];
       var childNode = this.tree.doc.getElementById(childItem4Insert.id);
       parentNode.insertBefore(nodeToMove,childNode);
   } else
   {
       parentNode.appendChild(nodeToMove);
   }
   //listeners
   if(currentItem.data.moveDown) {
       currentItem.data.moveDown();
   }
   // csi 230307 inutile et dangeureux
   /*
   if(childItem.moveUp) {
       childItem.moveUp();
   }
   */
}

// Fournit le code HTML ajouté après le libellé de l'item dans l'arbre
// Renvoie null si pas de suffix utilisé
TreeViewItem.prototype.getSuffix = function ()
{
   if ( this.data ) if ( this.data.getSuffix )
      return this.data.getSuffix();
   return null;
}

// Fournit le code HTML ajouté avant le libellé de l'item dans l'arbre
// Renvoie null si pas de préfix utilisé
TreeViewItem.prototype.getPrefix = function ()
{
   if ( this.data ) if ( this.data.getPrefix )
      return this.data.getPrefix();
   return null;
}

// Renvoie le chemin de l'image à utiliser pour afficher l'item
// Renvoie null si pas d'image spécifique, l'image par défaut est alors
// utilisée.
// Dans le cas d'un dossier, renvoie l'image du dossier fermé
TreeViewItem.prototype.getIcon = function ()
{
   if ( this.data ) if ( this.data.getIcon )
      return this.data.getIcon();
   return null;
}

// Renvoie le chemin de l'image à utiliser pour afficher un item
// de type dossier ouvert
// Renvoie null si pas d'image spécifique, l'image par défaut est alors
// utilisée.
TreeViewItem.prototype.getOpenedIcon = function ()
{
   if ( this.data ) if ( this.data.getOpenedIcon )
      return this.data.getOpenedIcon();
   return null;
}

// rkh : Renvoie le label du niveau 
// Renvoie null si pas de label spécifique, le label par défaut est alors
// utilisée.
TreeViewItem.prototype.getLabel = function ()
{
   var ret = null;
   if ( this.data ) if ( this.data.getLabel )
      ret =  this.data.getLabel();
   // csi Gestion des groupes de réponses
   if (this.type)
   {
      if (ret == null)
      {      
	     if (this.type == VIRTUAL_KEY)
	        ret = mess_treeview_virtual_folder;
      }
   }

   return ret;
}

// Recopie les données name,type,data de item dans this
TreeViewItem.prototype.copyData = function (item)
{
   this.name = item.name;
   this.type = item.type
   this.data = item.data;
}

// Ajoute à la liste des enfants (données seulement, pas de refresh)
// Renvoie item
TreeViewItem.prototype.addItem = function (item)
{
   if ( this.children == null )
   {
      this.children = new Array();
   }
   this.isFolder = true;
   this.children[this.children.length] = item;
   item.rank = this.children.length;
   item.parent = this;
   item.level = this.level+1;
   item.tree = this.tree;
   // Met à jour le hashing général
   this.tree.allItems[item.id] = item;

   return item;
}
// 
TreeViewItem.prototype.hasChildren = function ()
{
   return ((this.children != null) && (this.children.length >0));
}

//return all the children
TreeViewItem.prototype.getChildren = function ()
{
   return this.children;
}

//supprime tous les noeuds fils de cette item
TreeViewItem.prototype.removeAllChildrens = function ()
{
   var nbChilds=0;
   
   if(this.children!=null)
     nbChilds=this.children.length;
     
   for(var i=0;i<nbChilds;i++)
   {
      var item = this.children[0];
      this.tree.deleteItem(item);
   }
   this.children=null;
   this.fillChecked = false;
   this.isOpened = false;
   var htmlElem = this.tree.doc.getElementById(this.id);
   htmlElem.setAttribute("ezIsChildrenGenerated", "false");
}

// Change l'état open/close d'un dossier
// Si on n'a pas encore chargé les enfants, le fait
// - forceOpen : après le toggle, si true, force l'état isOpend = true;
TreeViewItem.prototype.toggle = function (forceOpen)
{

   this.tree.setWaitState(true);
   var needRefresh = false;

   if ( ! this.fillChecked )
   {
      if ( this.tree.actionHandler == null )
      {
         alert("Aucun handler de mise à jour défini !");
         return;
      }

      // csi Gestion des groupes de réponses
      var items = null;
      if (this.type)
      {
         if (this.type == "virtual")
            items = this.tree.actionHandler.getChildrenForVirtualFolder(this.id);
         else
            items = this.tree.actionHandler.getChildren(this.id);         
      }
      else      
         items = this.tree.actionHandler.getChildren(this.id);
         
      if ( items != null )
      {

         for (var i=0; i<items.length; i++)
         {
            this.addItem(items[i]);
         }
      }
      else
      {
         needRefresh = true;
      }
   }
   this.fillChecked = true;
   // Est-il nécessaire de générer la vue des enfants ?
   var htmlElem = this.tree.doc.getElementById(this.id);
   if ( htmlElem != null )
   {
      if ( htmlElem.getAttribute("ezIsChildrenGenerated") != "true" )
      {
         this.tree._generateChildItem(null, this);
         htmlElem.setAttribute("ezIsChildrenGenerated", "true");
      }
   }
   if ( forceOpen )
      this.isOpened = true;
   else
      this.isOpened = ! this.isOpened;

   // refresh les icones
   this.tree._refreshFolderIcons(this);

   this.tree.setWaitState(false);
}
// Renvoie true si l'item est le dernier des enfants de son parent
TreeViewItem.prototype.isLastChild = function ()
{
   if ( this.parent == null ) return true;
   if ( this.parent.children.length == this.rank ) return true;
   return false;
}      

// Renvoie la forme
TreeViewItem.prototype.toString = function ()
{
   var ret = "TreeViewItem (id=" + this.id + ")";
   ret += "\n\tname     : " + this.name;
   ret += "\n\tdossier  : " + this.isFolder;
   ret += "\n\ttype     : " + this.type;
   ret += "\n\tdata     : " + this.data;
   if ( this.children )
      ret += "\n\tchildren : " + this.children.length ;
   return ret;
}      

// Supprime définitivement un item de l'arbre des données
// avec tous ses descendants récursivement si recurse=true
TreeViewItem.prototype.removeFromParent = function(recurse, isNotAncestor)
{
   if ( recurse && this.hasChildren() )
   {
      for (var i=0; i<this.children.length; i++)
      {
         var child = this.children[i];
         child.removeFromParent(true, true);
      }
   }
   // Enlève du tableau des enfants du parent, le fait que pour l'ancêtre de départ
   // Pour les descendants, c'est inutile
   if ( ! isNotAncestor )
   {
      var par = this.parent;
      par.children.splice(this.rank-1,1);
      // Il faut renuméroter les enfants du parent
      if ( par.hasChildren() )
      {
         for (var i=0; i<par.children.length; i++)
         {
            var child = par.children[i];
            child.rank = i+1;
         }
      }
   }
   // Enlève du hashing général
   this.tree.allItems[this.id] = null;
}

// Renvoie le nom complet de l'item en partant de la racine
TreeViewItem.prototype.getFullName = function()
{
   var ret;
   if ( this.parent != null )
   {
      ret = this.parent.getFullName() + "/" + this.name;
   }
   else
   {
      ret = this.name;
   }
   return ret;
}




