Index: extensions/GrumPluginClasses/classes/GPCCore.class.inc.php
===================================================================
--- extensions/GrumPluginClasses/classes/GPCCore.class.inc.php	(revision 16732)
+++ extensions/GrumPluginClasses/classes/GPCCore.class.inc.php	(revision 16733)
@@ -265,9 +265,14 @@
         {
           $config[$key][$key2]=$val2;
+
+          if(isset($conf['plugin.'.$pluginName]) and isset($conf['plugin.'.$pluginName][$key]) and isset($conf['plugin.'.$pluginName][$key][$key2]))
+              $config[$key][$key2]=$conf['plugin.'.$pluginName][$key][$key2];
         }
       }
       else
       {
-        $config[$key] =$val;
+        $config[$key]=$val;
+        if(isset($conf['plugin.'.$pluginName]) and isset($conf['plugin.'.$pluginName][$key]))
+            $config[$key]=$conf['plugin.'.$pluginName][$key];
       }
     }
@@ -856,5 +861,4 @@
           self::addHeaderJS('jquery.ui.widget', 'themes/default/js/ui/jquery.ui.widget.js', array('jquery.ui'));
 
-          self::addHeaderJS('jquery.ui.widget', 'themes/default/js/ui/jquery.ui.widget.js', array('jquery.ui'));
           self::addHeaderJS('jquery.ui.mouse', 'themes/default/js/ui/jquery.ui.mouse.js', array('jquery.ui.widget'));
           self::addHeaderJS('jquery.ui.position', 'themes/default/js/ui/jquery.ui.position.js', array('jquery.ui.mouse'));
@@ -872,4 +876,19 @@
           self::addHeaderJS('gpc.inputPages', GPC_PATH.'js/ui.inputPages.js', array('jquery.ui.dialog'));
           self::addHeaderJS('gpc.dynamicTable', GPC_PATH.'js/ui.dynamicTable.js', array('jquery.ui.dialog'));
+          break;
+        case 'progressArea':
+          self::addHeaderCSS('gpc.progressArea', GPC_PATH.'css/progressArea.css');
+          self::addHeaderCSS('gpc.progressAreaT', sprintf($themeFile, 'progressArea'));
+          self::addHeaderJS('jquery.ui', 'themes/default/js/ui/jquery.ui.core.js', array('jquery'));
+          self::addHeaderJS('jquery.ui.widget', 'themes/default/js/ui/jquery.ui.widget.js', array('jquery.ui'));
+          self::addHeaderJS('jquery.ui.mouse', 'themes/default/js/ui/jquery.ui.mouse.js', array('jquery.ui.widget'));
+          self::addHeaderJS('jquery.ui.position', 'themes/default/js/ui/jquery.ui.position.js', array('jquery.ui.mouse'));
+          self::addHeaderJS('jquery.ui.draggable', 'themes/default/js/ui/jquery.ui.draggable.js', array('jquery.ui.position'));
+          self::addHeaderJS('jquery.ui.sortable', 'themes/default/js/ui/jquery.ui.sortable.js', array('jquery.ui.draggable'));
+          self::addHeaderJS('jquery.ui.dialog', 'themes/default/js/ui/jquery.ui.dialog.js', array('jquery.ui.sortable'));
+          self::addHeaderJS('gpc.progressArea', GPC_PATH.'js/ui.progressArea.js', array('jquery.ui.dialog'));
+          break;
+        case 'timer':
+          self::addHeaderJS('gpc.timer', GPC_PATH.'js/ui.timer.js', array('jquery'));
           break;
       }
Index: extensions/GrumPluginClasses/css/inputFilterBox_clear.css
===================================================================
--- extensions/GrumPluginClasses/css/inputFilterBox_clear.css	(revision 16733)
+++ extensions/GrumPluginClasses/css/inputFilterBox_clear.css	(revision 16733)
@@ -0,0 +1,20 @@
+/* roma css */
+
+
+/* dialog box */
+.ui-inputDialogFilterBox {
+  background:#AAAAAA;
+  border:1px solid #999999;
+}
+.ui-inputDialogFilterBox div.ui-dialog-titlebar {
+  background:#DDDDDD;
+  color:#666666;
+}
+.ui-inputDialogFilterBox div.ui-dialog-buttonpane button {
+  background:none repeat scroll 0 0 #999999;
+  border-color:#DDDDDD #666666 #666666 #DDDDDD;
+  color:#005E89;
+}
+.ui-inputDialogFilterBox div.ui-dialog-buttonpane button:active {
+  border-color:#666666 #DDDDDD #DDDDDD #666666;
+}
Index: extensions/GrumPluginClasses/css/progressArea.css
===================================================================
--- extensions/GrumPluginClasses/css/progressArea.css	(revision 16733)
+++ extensions/GrumPluginClasses/css/progressArea.css	(revision 16733)
@@ -0,0 +1,57 @@
+/* for progressArea plugin */
+
+.ui-progressArea-globalText,
+.ui-progressArea-detailText {
+  display:none;
+}
+
+.ui-progressArea-globalBar,
+.ui-progressArea-detailBar {
+  width:100%;
+  height:14px;
+  display:none;
+}
+
+.ui-progressArea-globalBarProgress,
+.ui-progressArea-detailBarProgress {
+  width:0px;
+  position:absolute;
+}
+
+.ui-progressArea-globalBarText,
+.ui-progressArea-detailBarText {
+  width:0px;
+  text-align:center;
+  position:absolute;
+}
+
+.ui-progressArea-workInProgress {
+  width:100%;
+  height:30px;
+  margin-top:8px;
+}
+
+/* dialog box */
+.ui-dialogProgressArea div.ui-dialog-titlebar {
+  cursor:move;
+}
+.ui-dialogProgressArea div.ui-dialog-buttonpane {
+  text-align:left;
+}
+.ui-dialogProgressArea div.ui-dialog-buttonpane button {
+  border-style:solid;
+  border-width:1px;
+  cursor:pointer;
+  font-weight:bold;
+  margin-top:1px;
+  text-align:center;
+  visibility:hidden;
+}
+.ui-dialogProgressArea div.ui-dialog-content {
+  margin:0px;
+  padding:8px;
+}
+.ui-dialogProgressArea a.ui-dialog-titlebar-close {
+  display:none;
+}
+
Index: extensions/GrumPluginClasses/css/progressArea_clear.css
===================================================================
--- extensions/GrumPluginClasses/css/progressArea_clear.css	(revision 16733)
+++ extensions/GrumPluginClasses/css/progressArea_clear.css	(revision 16733)
@@ -0,0 +1,44 @@
+/* for progressArea plugin */
+
+.ui-progressArea-globalText,
+.ui-progressArea-detailText {
+  padding-top:8px;
+}
+
+.ui-progressArea-globalBar,
+.ui-progressArea-detailBar {
+  border:1px solid #666666;
+}
+
+.ui-progressArea-globalBarText,
+.ui-progressArea-detailBarText {
+  color:#005e89;
+}
+
+.ui-progressArea-globalBarProgress,
+.ui-progressArea-detailBarProgress {
+  background:#f9f9f9;
+}
+
+.ui-progressArea-workInProgress {
+  background:url("./../icons/processing.gif") no-repeat scroll 0 8px transparent;
+}
+
+
+/* dialog box */
+.ui-dialogProgressArea {
+  background:#AAAAAA;
+  border:1px solid #999999;
+}
+.ui-dialogProgressArea div.ui-dialog-titlebar {
+  background:#DDDDDD;
+  color:#666666;
+}
+.ui-dialogProgressArea div.ui-dialog-buttonpane button {
+  background:none repeat scroll 0 0 #999999;
+  border-color:#DDDDDD #666666 #666666 #DDDDDD;
+  color:#005E89;
+}
+.ui-dialogProgressArea div.ui-dialog-buttonpane button:active {
+  border-color:#666666 #DDDDDD #DDDDDD #666666;
+}
Index: extensions/GrumPluginClasses/css/progressArea_roma.css
===================================================================
--- extensions/GrumPluginClasses/css/progressArea_roma.css	(revision 16733)
+++ extensions/GrumPluginClasses/css/progressArea_roma.css	(revision 16733)
@@ -0,0 +1,44 @@
+/* for progressArea plugin */
+
+.ui-progressArea-globalText,
+.ui-progressArea-detailText {
+  padding-top:8px;
+}
+
+.ui-progressArea-globalBar,
+.ui-progressArea-detailBar {
+  border:1px solid #666666;
+}
+
+.ui-progressArea-globalBarText,
+.ui-progressArea-detailBarText {
+  color:#FF7700;
+}
+
+.ui-progressArea-globalBarProgress,
+.ui-progressArea-detailBarProgress {
+  background:#444444;
+}
+
+.ui-progressArea-workInProgress {
+  background:url("./../icons/processing.gif") no-repeat scroll 0 8px transparent;
+}
+
+
+/* dialog box */
+.ui-dialogProgressArea {
+  background:#222222;
+  border:1px solid #666666;
+}
+.ui-dialogProgressArea div.ui-dialog-titlebar {
+  background:#666666;
+  color:#222222;
+}
+.ui-dialogProgressArea div.ui-dialog-buttonpane button {
+  background:none repeat scroll 0 0 #666666;
+  border-color:#999999 #444444 #444444 #999999;
+  color:#222222;
+}
+.ui-dialogProgressArea div.ui-dialog-buttonpane button:active {
+  border-color:#444444 #999999 #999999 #444444;
+}
Index: extensions/GrumPluginClasses/js/ui.inputTreeList.js
===================================================================
--- extensions/GrumPluginClasses/js/ui.inputTreeList.js	(revision 16732)
+++ 	(revision )
@@ -1,1841 +1,0 @@
-/**
- * -----------------------------------------------------------------------------
- * file: ui.inputTreeList.js
- * file version: 1.1.1
- * date: 2012-06-18
- *
- * A jQuery plugin provided by the piwigo's plugin "GrumPluginClasses"
- *
- * -----------------------------------------------------------------------------
- * Author     : Grum
- *   email    : grum@piwigo.com
- *   website  : http://photos.grum.fr
- *
- *   << May the Little SpaceFrog be with you ! >>
- * -----------------------------------------------------------------------------
- *
- * see ui.inputTreeList.help.txt for help about this plugin
- *
- * :: HISTORY ::
- *
- * | release | date       |
- * | 1.0.0   | 2010/10/10 | * first release
- * |         |            |
- * | 1.0.1   | 2010/10/14 | * fix bug on 'value' functions ':none', ':all' and
- * |         |            |   ':invert'
- * |         |            |
- * |         |            | * add 'name' property
- * |         |            |
- * | 1.1.0   | 2011/01/12 | * checkbox moved between +/- button and text
- * |         |            |
- * |         |            | * dropdown list is managed like dropdown list on
- * |         |            |   <select> object (hidden only when object loose
- * |         |            |   focus )
- * |         |            |
- * |         |            | * selected values are dislayed like tags
- * |         |            |
- * |         |            | * add 'isValid' method
- * |         |            |
- * |         |            | * add 'displayPath' property
- * |         |            |
- * | 1.1.1   | 2012-06-18 | * fix bug with jquery 1.7.2
- * |         |            |   . display list now works :)
- * |         |            |
- * |         |            | * improve memory managment
- * |         |            |
- * |         |            | * change name from categorySelector to inputTreeList
- * |         |            |
- * |         |            |
- * |         |            |
- * |         |            |
- * |         |            |
- * |         |            |
- *
- */
-
-
-
-(
-  function($)
-  {
-    /*
-     * plugin 'public' functions
-     */
-    var publicMethods =
-    {
-      init : function (opt)
-        {
-          return this.each(function()
-            {
-              // default values for the plugin
-              var $this=$(this),
-                  timeStamp=new Date(),
-                  data = $this.data('options'),
-                  objects = $this.data('objects'),
-                  properties = $this.data('properties'),
-                  options =
-                    {
-                      autoLoad:true,
-                      galleryRoot:true,
-                      displayStatus:true,
-                      //displayNbPhotos:true,
-                      listMaxWidth:0,
-                      listMaxHeight:0,
-                      levelIndent:16,
-                      iconWidthEC:15,
-                      serverUrl:'',
-                      filter:'accessible',
-                      popup:null,
-                      change:null,
-                      load:null,
-                      multiple:false,
-                      userMode:'public',
-                      popupMode:'click',
-                      displayPath:false,
-                      downArrow:'' //'&dArr;'
-                    };
-
-              // if options given, merge it
-              // if(opt) $.extend(options, opt); ==> options are set by setters
-
-              $this.data('options', options);
-
-
-              if(!properties)
-              {
-                $this.data('properties',
-                  {
-                    objectId:'cs'+Math.ceil(timeStamp.getTime()*Math.random()),
-                    index:-1,
-                    initialized:false,
-                    selectorVisible:false,
-                    categories:[],
-                    labelStatus:['', ''],
-                    mouseOver:false,
-                    isValid:true
-                  }
-                );
-                properties=$this.data('properties');
-              }
-
-              if(!objects)
-              {
-                objects =
-                  {
-                    container:$('<div/>',
-                        {
-                          'class':'ui-inputTreeList',
-                          tabindex:0,
-                          css:{
-                            width:'100%'
-                          }
-                        }
-                    ).bind('click.inputTreeList',
-                        function ()
-                        {
-                          privateMethods.displaySelector($this, !$this.data('properties').selectorVisible);
-                          //$(this).focus(); // if get the focus, it hide the dorp-down list.. ?
-                        }
-                      ),
-                    containerName:$('<div/>',
-                      {
-                        html: '&nbsp;',
-                        'class':'ui-inputTreeList-name'
-                      }
-                    ),
-                    containerList:null,
-                    containerStatus:$('<div/>',
-                      {
-                        'class':'ui-inputTreeList-status',
-                        css: {
-                          'float':'right',
-                          display:(options.displayStatus)?'block':'none'
-                        }
-                      }
-                    ),
-                    containerArrow:$('<div/>',
-                      {
-                        html: '&dArr;',
-                        'class':'ui-inputTreeList-arrow',
-                        css: {
-                          'float':'right',
-                          cursor:'pointer'
-                        }
-                      }
-                    ).bind('mousedown',
-                        function ()
-                        {
-                          $(this).addClass('ui-inputTreeList-arrow-active');
-                        }
-                    ).bind('mouseup',
-                        function ()
-                        {
-                          $(this).removeClass('ui-inputTreeList-arrow-active');
-                        }
-                    ),
-                    listContainer:$('<div/>',
-                        {
-                          html: "",
-                          'class':'ui-inputTreeList-list',
-                          css: {
-                            overflow:"auto",
-                            display:'none',
-                            position:'absolute'
-                          }
-                        }
-                    ),
-                    list:$('<ul/>',
-                      {
-                        css: {
-                          listStyle:'none',
-                          padding:'0px',
-                          margin:'0px'
-                        }
-                      }
-                    )
-                  };
-              }
-
-              $this.data('objects', objects);
-
-              privateMethods.setOptions($this, opt);
-
-
-              if($this.html()!='') privateMethods.setItems($this, $this.html());
-
-              $this
-                .html('')
-                .append(objects.container.append(objects.containerArrow).append(objects.containerStatus).append(objects.containerName))
-                .append(objects.listContainer.append(objects.list));
-
-            }
-          );
-        }, // init
-      destroy : function ()
-        {
-          return this.each(
-            function()
-            {
-              // default values for the plugin
-              var $this=$(this),
-                  objects = $this.data('objects');
-              objects.containerName.unbind().remove();
-              objects.containerList.unbind().remove();
-              objects.containerStatus.unbind().remove();
-              objects.containerArrow.unbind().remove();
-              objects.container.unbind().remove();
-              objects.list.children().unbind();
-              objects.listContainer.remove();
-              $(document).unbind('focusout.'+properties.objectId+' focusin.'+properties.objectId);
-              $this
-                .removeData()
-                .unbind('.inputTreeList')
-                .css(
-                  {
-                    width:'',
-                    height:''
-                  }
-                );
-              delete $this;
-            }
-          );
-        }, // destroy
-
-      options: function (value)
-        {
-          return this.each(function()
-            {
-              privateMethods.setOptions($(this), value);
-            }
-          );
-        }, // autoLoad
-
-      autoLoad: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setAutoLoad($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.autoLoad);
-            }
-            else
-            {
-              return(true);
-            }
-          }
-        }, // autoLoad
-
-      galleryRoot: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setGalleryRoot($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.galleryRoot);
-            }
-            else
-            {
-              return(true);
-            }
-          }
-        }, // autoLoad
-
-
-      listMaxWidth: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setListMaxWidth($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.listMaxWidth);
-            }
-            else
-            {
-              return(0);
-            }
-          }
-        }, // listMaxWidth
-
-      listMaxHeight: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setListMaxHeight($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.listMaxHeight);
-            }
-            else
-            {
-              return(0);
-            }
-          }
-        }, // listMaxHeight
-
-      displayStatus: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setDisplayStatus($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.displayStatus);
-            }
-            else
-            {
-              return(true);
-            }
-          }
-        }, // displayStatus
-
-      levelIndent: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setLevelIndent($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.levelIndent);
-            }
-            else
-            {
-              return(0);
-            }
-          }
-        }, // levelIndent
-
-      serverUrl: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setServerUrl($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.serverUrl);
-            }
-            else
-            {
-              return('');
-            }
-          }
-        }, // serverUrl
-
-      filter: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setFilter($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.filter);
-            }
-            else
-            {
-              return('');
-            }
-          }
-        }, // filter
-
-      collapse: function (value)
-        {
-          return this.each(function()
-            {
-              if(!value) value=':all';
-              privateMethods.setExpandCollapse($(this), value, 'C');
-            }
-          );
-        }, // collapse
-
-      expand: function (value)
-        {
-          return this.each(function()
-            {
-              if(!value) value=':all';
-              privateMethods.setExpandCollapse($(this), value, 'E');
-            }
-          );
-        }, // expand
-
-      iconWidthEC: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setIconWidthEC($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.iconWidthEC);
-            }
-            else
-            {
-              return(0);
-            }
-          }
-        }, // iconWidthEC
-
-      userMode: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setUserMode($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.userMode);
-            }
-            else
-            {
-              return(0);
-            }
-          }
-        }, // userMode
-
-      name: function ()
-        {
-          var options=this.data('options'),
-              properties=this.data('properties');
-
-          if(!options.multiple)
-          {
-            return(properties.categories[properties.index].name);
-          }
-          else
-          {
-            var listNames=[];
-            for(var i=0;i<properties.index.length;i++)
-            {
-              listNames.push(properties.categories[properties.index[i]].name);
-            }
-            return(listNames);
-          }
-        }, // userMode
-
-      popupMode: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setPopupMode($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.popupMode);
-            }
-            else
-            {
-              return(0);
-            }
-          }
-        }, // popupMode
-
-      displayPath: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setDisplayPath($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.displayPath);
-            }
-            else
-            {
-              return(0);
-            }
-          }
-        }, // displayPath
-
-      downArrow: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setDownArrow($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var options = this.data('options');
-
-            if(options)
-            {
-              return(options.downArrow);
-            }
-            else
-            {
-              return('');
-            }
-          }
-        }, // downArrow
-
-      value: function (value)
-        {
-          if(value!=null)
-          {
-            // set selected value
-            return this.each(function()
-              {
-                privateMethods.setValue($(this), value);
-              }
-            );
-          }
-          else
-          {
-            // return the selected value
-            var properties=this.data('properties'),
-                options = this.data('options');
-
-            if(properties && properties.index!=null && !options.multiple && properties.index>-1 && properties.index<properties.categories.length)
-            {
-              return(properties.categories[properties.index].id);
-            }
-            else if(properties && properties.index!=null && options.multiple)
-            {
-              var returned=[];
-              for(var i=0;i<properties.index.length;i++)
-              {
-                if(properties.index[i]>-1 && properties.index[i]<properties.categories.length)
-                  returned.push(properties.categories[properties.index[i]].id);
-              }
-              return(returned);
-            }
-            else
-            {
-              return(null);
-            }
-          }
-        }, // value
-
-      isValid: function (value)
-        {
-          if(value!=null)
-          {
-            return this.each(function()
-              {
-                privateMethods.setIsValid($(this), value);
-              }
-            );
-          }
-          else
-          {
-            var properties=this.data('properties');
-            return(properties.isValid);
-          }
-        }, // isValid
-
-      load: function (value)
-        {
-          /*
-           * two functionnalities :
-           *  - if value is set, use it to set the load event function
-           *  - if no value, loads data from server
-           */
-          if(value && $.isFunction(value))
-          {
-            // set selected value
-            return this.each(function()
-              {
-                privateMethods.setEventLoad($(this), value);
-              }
-            );
-          }
-          else
-          {
-            // loads data from server
-            privateMethods.load(this);
-          }
-        },
-
-      popup: function (value)
-        {
-          if(value && $.isFunction(value))
-          {
-            // set selected value
-            return this.each(function()
-              {
-                privateMethods.setEventPopup($(this), value);
-              }
-            );
-          }
-          else
-          {
-            // return the selected value
-            var options=this.data('options');
-
-            if(options)
-            {
-              return(options.popup);
-            }
-            else
-            {
-              return(null);
-            }
-          }
-        }, // popup
-      change: function (value)
-        {
-          if(value && $.isFunction(value))
-          {
-            // set selected value
-            return this.each(function()
-              {
-                privateMethods.setEventChange($(this), value);
-              }
-            );
-          }
-          else
-          {
-            // return the selected value
-            var options=this.data('options');
-
-            if(options)
-            {
-              return(options.change);
-            }
-            else
-            {
-              return(null);
-            }
-          }
-        }, // popup
-
-      numberOfCategories: function ()
-        {
-          var properties=this.data('properties');
-
-          if(properties)
-          {
-            return(properties.categories.length);
-          }
-          else
-          {
-            return(null);
-          }
-        }, // numberOfCategories
-
-      properties: function (value)
-        {
-          var properties=this.data('properties'),
-              options=this.data('options');
-
-          if(properties && value==':first' && properties.categories.length>0)
-          {
-            return(properties.categories[0]);
-          }
-          else if(properties && properties.index!=null && (value==':selected' || value==null) && properties.categories.length>0)
-          {
-            if(!options.multiple && properties.index>-1 && properties.index<properties.categories.length)
-            {
-              return(properties.categories[properties.index]);
-            }
-            else if(options.multiple)
-            {
-              var returned=[];
-              for(var i=0;i<properties.index.length;i++)
-              {
-                if(properties.index[i]>-1 && properties.index<properties.categories.length)
-                  returned.push(properties.categories[properties.index[i]]);
-              }
-              return(returned);
-            }
-            return(null);
-          }
-          else if(properties && value>-1)
-          {
-            var index=privateMethods.findIndexByValue(this, value);
-            if(index>-1)
-            {
-              return(properties.categories[index]);
-            }
-            return(null);
-          }
-          else
-          {
-            return(null);
-          }
-        }, // numberOfCategories
-    }; // methods
-
-
-    /*
-     * plugin 'private' methods
-     */
-    var privateMethods =
-    {
-      setOptions : function (object, value)
-        {
-          var properties=object.data('properties'),
-              options=object.data('options');
-
-          if(!$.isPlainObject(value)) return(false);
-
-          properties.initialized=false;
-
-          privateMethods.setAutoLoad(object, (value.autoLoad!=null)?value.autoLoad:options.autoLoad);
-          privateMethods.setGalleryRoot(object, (value.galleryRoot!=null)?value.galleryRoot:options.galleryRoot);
-          privateMethods.setDisplayStatus(object, (value.displayStatus!=null)?value.displayStatus:options.displayStatus);
-          privateMethods.setListMaxWidth(object, (value.listMaxWidth!=null)?value.listMaxWidth:options.listMaxWidth);
-          privateMethods.setListMaxHeight(object, (value.listMaxHeight!=null)?value.listMaxHeight:options.listMaxHeight);
-          privateMethods.setLevelIndent(object, (value.levelIndent!=null)?value.levelIndent:options.levelIndent);
-          privateMethods.setIconWidthEC(object, (value.iconWidthEC!=null)?value.iconWidthEC:options.iconWidthEC);
-          privateMethods.setServerUrl(object, (value.serverUrl!=null)?value.serverUrl:options.serverUrl);
-          privateMethods.setFilter(object, (value.filter!=null)?value.filter:options.filter);
-          privateMethods.setUserMode(object, (value.userMode!=null)?value.userMode:options.userMode);
-          privateMethods.setPopupMode(object, (value.popupMode!=null)?value.popupMode:options.popupMode);
-          privateMethods.setDisplayPath(object, (value.displayPath!=null)?value.displayPath:options.displayPath);
-          privateMethods.setDownArrow(object, (value.downArrow!=null)?value.downArrow:options.downArrow);
-          privateMethods.setEventPopup(object, (value.popup!=null)?value.popup:options.popup);
-          privateMethods.setEventChange(object, (value.change!=null)?value.change:options.change);
-          privateMethods.setEventLoad(object, (value.load!=null)?value.load:options.load);
-          privateMethods.setMultiple(object, (value.multiple!=null)?value.multiple:options.multiple); // can be set only at the initialization
-
-          if(options.autoLoad) privateMethods.load(object);
-
-          properties.initialized=true;
-        },
-
-      setIsValid : function (object, value)
-        {
-          var objects=object.data('objects'),
-              properties=object.data('properties');
-
-          if(properties.isValid!=value)
-          {
-            properties.isValid=value;
-            if(properties.isValid)
-            {
-              objects.container.removeClass('ui-error');
-            }
-            else
-            {
-              objects.container.addClass('ui-error');
-            }
-          }
-          return(properties.isValid);
-        },
-
-      setAutoLoad : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties');
-
-          if((!properties.initialized || options.autoLoad!=value) && (value==true || value==false))
-          {
-            options.autoLoad=value;
-          }
-          return(options.autoLoad);
-        },
-
-      setGalleryRoot : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties');
-
-          if((!properties.initialized || options.galleryRoot!=value) && (value==true || value==false))
-          {
-            options.galleryRoot=value;
-            if(options.autoLoad && properties.initialized) privateMethods.load(object);
-          }
-          return(options.galleryRoot);
-        },
-
-      setListMaxWidth : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if((!properties.initialized || options.listMaxWidth!=value) && value>=0)
-          {
-            options.listMaxWidth=value;
-            if(options.listMaxWidth>0)
-            {
-              objects.listContainer.css('max-width', options.listMaxWidth+'px');
-            }
-            else
-            {
-              objects.listContainer.css('max-width', '');
-            }
-          }
-          return(options.listMaxWidth);
-        },
-
-      setListMaxHeight : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if((!properties.initialized || options.listMaxHeight!=value) && value>=0)
-          {
-            options.listMaxHeight=value;
-            if(options.listMaxHeight>0)
-            {
-              objects.listContainer.css('max-height', options.listMaxHeight+'px');
-            }
-            else
-            {
-              objects.listContainer.css('max-height', '');
-            }
-          }
-          return(options.listMaxHeight);
-        },
-
-      setDisplayStatus : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if((!properties.initialized || options.displayStatus!=value) && (value==true || value==false))
-          {
-            options.displayStatus=value;
-            if(options.displayStatus)
-            {
-              object.find('.ui-inputTreeList-status').show();
-            }
-            else
-            {
-              object.find('.ui-inputTreeList-status').hide();
-            }
-          }
-          return(options.displayStatus);
-        },
-
-      setLevelIndent : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if((!properties.initialized || options.levelIndent!=value) && value>=0)
-          {
-            options.levelIndent=value;
-            objects.list.find('.ui-inputTreeList-item').each(
-              function ()
-              {
-                $(this).css('padding-left', (options.iconWidthEC+$(this).attr('level')*options.levelIndent)+'px');
-              }
-            );
-          }
-          return(options.levelIndent);
-        },
-
-      setServerUrl : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties');
-
-          if(!properties.initialized || options.serverUrl!=value)
-          {
-            options.serverUrl=value;
-            if(options.autoLoad && properties.initialized) privateMethods.load(object);
-          }
-          return(options.serverUrl);
-        },
-
-      setFilter : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties');
-
-          if((!properties.initialized || options.filter!=value) && (value=='none' || value=='accessible' || value=='public'))
-          {
-            options.filter=value;
-            if(options.autoLoad && properties.initialized) privateMethods.load(object);
-          }
-          return(options.filter);
-        },
-
-      setIconWidthEC : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if((!properties.initialized || options.iconWidthEC!=value) && value>=0)
-          {
-            options.iconWidthEC=value;
-            objects.list.find('.ui-inputTreeList-item').each(
-              function ()
-              {
-                $(this).css('padding-left', (options.iconWidthEC+$(this).attr('level')*options.levelIndent)+'px');
-              }
-            );
-          }
-          return(options.iconWidthEC);
-        },
-
-      setMultiple : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if((!properties.initialized || options.multiple!=value) && (value==true || value==false))
-          {
-            if(!value)
-            {
-              properties.index=-1;
-              if(objects.containerList!=null)
-              {
-                objects.containerList.remove();
-                objects.containerList=null;
-              }
-            }
-            else
-            {
-              properties.index=[];
-              objects.listContainer.addClass('ui-inputTreeList-multiple');
-              if(objects.containerList==null)
-              {
-                objects.containerList=$('<ul/>',
-                  {
-                    css: {
-                      listStyle:'none',
-                      padding:'0px',
-                      margin:'0px',
-                      overflow:"auto"
-                    },
-                    html:'<li>&nbsp;</li>'
-                  }
-                );
-                objects.containerName.html('').append(objects.containerList);
-              }
-            }
-            options.multiple=value;
-          }
-          return(options.multiple);
-        }, //setMultiple
-
-      setUserMode : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties');
-
-          if((!properties.initialized || options.userMode!=value) && (value=='admin' || value=='public'))
-          {
-            options.userMode=value;
-            if(options.autoLoad && properties.initialized) privateMethods.load(object);
-          }
-          return(options.userMode);
-        }, //setUserMode
-
-      setPopupMode : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if((!properties.initialized || options.popupMode!=value) && (value=='click' || value=='mouseout'))
-          {
-            options.popupMode=value;
-
-            if(value=='mouseout')
-            {
-              objects.listContainer
-                .unbind('mouseleave.inputTreeList')
-                .unbind('mouseenter.inputTreeList')
-                .bind('mouseleave.inputTreeList',
-                  function ()
-                  {
-                    privateMethods.displaySelector(object, false);
-                  }
-                );
-            }
-            else
-            {
-              objects.listContainer
-                .unbind('mouseleave.inputTreeList')
-                .bind('mouseleave.inputTreeList',
-                  function ()
-                  {
-                    properties.mouseOver=false;
-                  }
-                )
-                .bind('mouseenter.inputTreeList',
-                  function ()
-                  {
-                    properties.mouseOver=true;
-                  }
-                );
-              $(document).bind('focusout.'+properties.objectId+' focusin.'+properties.objectId,
-                function (event)
-                {
-                  if(!properties.mouseOver) privateMethods.displaySelector(object, false);
-                }
-              );
-            }
-          }
-          return(options.popupMode);
-        }, //setUserMode
-
-
-      setDisplayPath : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if((!properties.initialized || options.displayPath!=value) && (value==true || value==false))
-          {
-            options.displayPath=value;
-
-          }
-          return(options.userMode);
-        }, //setDisplayPath
-
-      setDownArrow : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if(!properties.initialized || options.downArrow!=value)
-          {
-            options.downArrow=value;
-            objects.containerArrow.html(options.downArrow);
-          }
-          return(options.downArrow);
-        }, //setDownArrow
-
-
-      setItems : function (object, value)
-        {
-          var properties=object.data('properties'),
-              options=object.data('options'),
-              objects=object.data('objects');
-
-          if(value=='' || value==null)
-          {
-            value={
-              status:['',''],
-              categories:[]
-            }
-          }
-          else if($.isArray(value))
-          {
-            value={
-              status:'public',
-              categories:value
-            }
-          }
-          else
-          {
-            try
-            {
-              value=$.parseJSON($.trim(value));
-            }
-            catch (e)
-            {
-              return(false);
-            }
-          }
-
-          properties.labelStatus=value.status;
-          privateMethods.listClear(object);
-          if(value.categories.length>0) privateMethods.listAddItems(object, value.categories, objects.list);
-
-          properties.initialized=false;
-          if(options.multiple)
-          {
-            privateMethods.setValue(object, ':none');
-          }
-          else
-          {
-            privateMethods.setValue(object, ':first');
-          }
-          properties.initialized=true;
-
-          if(options.load) object.trigger('inputTreeListLoad');
-        },
-
-      /**
-       * usage : see notes on the header file
-       *
-       * @param jQuery object : the jQuery object
-       * @param String value : the filter
-       * @param String mode : 'C' for collapse, 'E' for expand
-       */
-      setExpandCollapse : function (object, value, mode)
-        {
-          var objects=object.data('objects');
-
-          /*
-           *  target[1] = ':all' or catId
-           * target[2] = '=' or '<' or '>' or '+'
-           * target[3] = level
-           */
-          re=/^(?:(:all)(?:(=|<|>|\+)(\d+))?)|(?:(\d+)(<|>|\+))?$/i;
-          target=re.exec(value);
-
-          if(target!=null)
-          {
-            switch(mode)
-            {
-              case 'C':
-                applyExpandCollapse=privateMethods.applyCollapse;
-                break;
-              case 'E':
-                applyExpandCollapse=privateMethods.applyExpand;
-                break;
-            }
-
-
-            if(target[1]==':all')
-            {
-              objects.list.find('.ui-inputTreeList-expandable-item, .ui-inputTreeList-collapsable-item').each(applyExpandCollapse);
-            }
-            else if(target[4]!=null)
-            {
-              switch(target[5])
-              {
-                case '+':
-                  objects.list.find('.ui-inputTreeList-expandable-item, .ui-inputTreeList-collapsable-item').each(privateMethods.applyCollapse);
-                  objects.list.find(':has([catId='+target[4]+'])').prev().each(privateMethods.applyExpand);
-                  objects.list.find('[catId='+target[4]+']').each(applyExpandCollapse);
-                  break;
-                case '>':
-                  objects.list.find('[catId='+target[4]+'] + ul').find('.ui-inputTreeList-expandable-item, .ui-inputTreeList-collapsable-item').each(applyExpandCollapse);
-                  objects.list.find('[catId='+target[4]+']').each(applyExpandCollapse);
-                  break;
-                case '<':
-                  objects.list.find(':has([catId='+target[4]+'])').prev().each(applyExpandCollapse);
-                  objects.list.find('[catId='+target[4]+']').each(applyExpandCollapse);
-                  break;
-                default:
-                  objects.list.find('[catId='+target[4]+']').each(applyExpandCollapse);
-                  break;
-              }
-            }
-
-          }
-        }, //setExpandCollapse
-
-      applyExpand : function (index, domElt)
-        {
-          privateMethods.applyExpandCollapse(index, domElt, 'E');
-        }, //applyExpand
-      applyCollapse : function (index, domElt)
-        {
-          privateMethods.applyExpandCollapse(index, domElt, 'C');
-        }, //applyCollapse
-
-      /**
-       * used by the setExpandCollapse function
-       * not aimed to be used directly
-       */
-      applyExpandCollapse : function (index, domElt, mode)
-              {
-                action='';
-                var $domElt=$(domElt);
-
-                if(target.length>2 && target[2]!=null && target[3]!=null)
-                {
-                  switch(target[2])
-                  {
-                    case '=':
-                      if($domElt.attr('level')==target[3]) action=mode;
-                      break;
-                    case '>':
-                      if($domElt.attr('level')>=target[3]) action=mode;
-                      break;
-                    case '<':
-                      if($domElt.attr('level')<=target[3]) action=mode;
-                      break;
-                    case '+':
-                      if((mode=='E' && $domElt.attr('level')<=target[3]) ||
-                         (mode=='C' && $domElt.attr('level')>=target[3]))
-                      {
-                        action=mode;
-                      }
-                      else
-                      {
-                        action=(mode=='C')?'E':'C';
-                      }
-                      break;
-                  }
-                }
-                else action=mode;
-
-                switch(action)
-                {
-                  case 'C':
-                    $domElt
-                      .removeClass('ui-inputTreeList-expandable-item ui-inputTreeList-collapsable-item')
-                      .addClass('ui-inputTreeList-expandable-item')
-                      .next().hide();
-                    break;
-                  case 'E':
-                    $domElt
-                      .removeClass('ui-inputTreeList-expandable-item ui-inputTreeList-collapsable-item')
-                      .addClass('ui-inputTreeList-collapsable-item')
-                      .next().show();
-                    break;
-                }
-
-              }, //applyExpandCollapse
-
-      setValue : function (object, value, trigger)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects'),
-              index=-1;
-
-          re=/^(:invert|:all|:none)(?:(=|<|>)(\d+))$/i;
-          target=re.exec(value);
-          if(target!=null) value=target[1];
-
-          switch(value)
-          {
-            case ':first':
-              if(properties.categories.length>0) index=0;
-              break;
-            case ':last':
-              index=properties.categories.length-1;
-              break;
-            case ':invert':
-              if(!options.multiple) return(false);
-              properties.index=[];
-              objects.list.find('.ui-inputTreeList-item').each(
-                function ()
-                {
-                  var $this=$(this),
-                      apply=true;
-
-                  if(target!=null)
-                  {
-                    switch(target[2])
-                    {
-                      case '=':
-                        apply=($this.attr('level')==target[3]);
-                        break;
-                      case '>':
-                        apply=($this.attr('level')>=target[3]);
-                        break;
-                      case '<':
-                        apply=($this.attr('level')<=target[3]);
-                        break;
-                    }
-                  }
-
-                  if(apply)
-                  {
-                    if($this.hasClass('ui-inputTreeList-selected-item'))
-                    {
-                      $this.removeClass('ui-inputTreeList-selected-item');
-                    }
-                    else
-                    {
-                      $this.addClass('ui-inputTreeList-selected-item');
-                      tmp=privateMethods.findIndexByValue(object, $this.attr('catId'));
-                      if(tmp>-1) properties.index.push(tmp);
-                    }
-                  }
-                }
-              );
-              privateMethods.setValue(object, [], false);
-              return(false);
-              break;
-            case ':none':
-              if(!options.multiple) return(false);
-
-              properties.index=[];
-              objects.list.find('.ui-inputTreeList-selected-item').each(
-                function ()
-                {
-                  var $this=$(this),
-                      apply=true;
-
-                  if(target!=null)
-                  {
-                    switch(target[2])
-                    {
-                      case '=':
-                        apply=($this.attr('level')==target[3]);
-                        break;
-                      case '>':
-                        apply=($this.attr('level')>=target[3]);
-                        break;
-                      case '<':
-                        apply=($this.attr('level')<=target[3]);
-                        break;
-                    }
-                  }
-
-                  if(apply) $this.removeClass('ui-inputTreeList-selected-item');
-                }
-              );
-              privateMethods.setValue(object, [], false);
-              return(false);
-              break;
-            case ':all':
-              if(!options.multiple) return(false);
-              properties.index=[];
-              objects.list.find('.ui-inputTreeList-item').each(
-                function ()
-                {
-                  var $this=$(this),
-                      apply=true;
-
-                  if(target!=null)
-                  {
-                    switch(target[2])
-                    {
-                      case '=':
-                        apply=($this.attr('level')==target[3]);
-                        break;
-                      case '>':
-                        apply=($this.attr('level')>=target[3]);
-                        break;
-                      case '<':
-                        apply=($this.attr('level')<=target[3]);
-                        break;
-                    }
-                  }
-                  if(apply)
-                  {
-                    tmp=privateMethods.findIndexByValue(object, $this.attr('catId'));
-                    if(tmp>-1) properties.index.push(tmp);
-
-                    $this.addClass('ui-inputTreeList-selected-item');
-                  }
-                }
-              );
-              privateMethods.setValue(object, [], false);
-              return(false);
-              break;
-            default:
-              if($.isArray(value) && options.multiple)
-              {
-                index=[];
-                for(var i=0;i<value.length;i++)
-                {
-                  tmp=privateMethods.findIndexByValue(object, value[i]);
-                  if(tmp>-1) index.push(tmp);
-                }
-              }
-              else
-              {
-                index=privateMethods.findIndexByValue(object, value);
-              }
-
-              break;
-          }
-
-          if(!options.multiple && (!properties.initialized || properties.index!=index) && index>-1)
-          {
-
-            objects.list.find('.ui-inputTreeList-selected-item').removeClass('ui-inputTreeList-selected-item');
-            objects.list.find('[catId="'+value+'"]').addClass('ui-inputTreeList-selected-item');
-            title=privateMethods.getParentName(object, objects.list.find('[catId="'+value+'"] div.ui-inputTreeList-name')).replace('&amp;', '&').replace('&gt;', '>').replace('&lt;', '<');
-            properties.index=index;
-            objects.containerName.html(properties.categories[properties.index].name).attr('title', title);
-            objects.containerStatus.html(properties.labelStatus[properties.categories[properties.index].status]);
-            if(trigger && options.change) object.trigger('inputTreeListChange', [properties.categories[properties.index].id]);
-            if(properties.index>-1) return(properties.categories[properties.index].id);
-          }
-          else if(options.multiple)
-          {
-            if(!$.isArray(index))
-            {
-              if(index<0 || index==null) return(-1);
-              index=[index];
-            }
-            tmp=[];
-            for(var i=0;i<index.length;i++)
-            {
-              var item=objects.list.find('[catId="'+properties.categories[index[i]].id+'"]');
-
-              if(item.hasClass('ui-inputTreeList-selected-item'))
-              {
-                item.removeClass('ui-inputTreeList-selected-item');
-
-                tmpIndex=$.inArray(index[i] ,properties.index);
-                if(tmpIndex>-1) properties.index.splice(tmpIndex, 1);
-              }
-              else
-              {
-                item.addClass('ui-inputTreeList-selected-item');
-                properties.index.push(index[i]);
-              }
-              tmp.push(properties.categories[index[i]].id);
-            }
-            objects.containerList.html('');
-            objects.list.find('.ui-inputTreeList-selected-item div.ui-inputTreeList-name').each(
-              function ()
-              {
-                var path='';
-
-                title=privateMethods.getParentName(object, $(this)).replace('&amp;', '&').replace('&gt;', '>').replace('&lt;', '<');
-                if(!options.displayPath)
-                {
-                  path=$(this).html();
-                }
-                else
-                {
-                  path=title;
-                }
-
-                objects.containerList.append(
-                  $('<li/>',
-                    {
-                      html:path,
-                      title:title,
-                      'class':'ui-inputTreeList-selected-cat'
-                    }
-                  ).prepend(
-                      $('<span/>',
-                        {
-                          html:'x'
-                        }
-                       ).bind('click.inputTreeList',
-                          {object:object, value:$(this).parent().parent().attr('catid')},
-                          function (event)
-                          {
-                            event.stopPropagation();
-                            privateMethods.setValue(event.data.object, event.data.value);
-                          }
-                        )
-                      )
-                )
-              }
-            );
-
-            if(objects.containerList.children().length==0) objects.containerList.append('<li>&nbsp;</li>');
-
-            if(trigger && options.change) object.trigger('inputTreeListChange', [tmp]);
-            return(tmp);
-          }
-          return(null);
-        },
-
-      getParentName : function (object, item)
-      {
-        if(item==null || item.length==0) return('');
-        foundItem=item.parent().parent().parent().prev().find('div.ui-inputTreeList-name');
-
-        if(foundItem.length==0) return(item.html());
-
-        return(privateMethods.getParentName(object, foundItem)+' / '+item.html());
-      },
-
-      displaySelector : function (object, value)
-        {
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          if(properties.selectorVisible!=value)
-          {
-            properties.selectorVisible=value;
-
-            if(properties.selectorVisible && properties.categories.length>0)
-            {
-              var index=0;
-              objects.listContainer
-                .css(
-                  {
-                    display:'block',
-                    'min-width':objects.listContainer.parent().css('width')
-                  }
-                );
-
-              if($.isArray(properties.index))
-              {
-                if (properties.index.length>0) index=properties.index[0];
-              }
-              else if(properties.index>-1)
-              {
-                index=properties.index;
-              }
-              objects.listContainer.scrollTop(objects.listContainer.scrollTop()+objects.list.find('[catId="'+properties.categories[index].id+'"]').position().top);
-            }
-            else
-            {
-              objects.listContainer.css('display', 'none');
-            }
-            if(options.popup) object.trigger('inputTreeListPopup', [properties.selectorVisible]);
-          }
-          return(properties.selectorVisible);
-        },
-
-      load : function (object)
-        {
-          // load datas from server through an asynchronous ajax call
-          var options=object.data('options'),
-              objects=object.data('objects');
-
-          if(options.serverUrl=='') return(false);
-
-          $.ajax(
-            {
-              type: "POST",
-              url: options.serverUrl,
-              async: true,
-              data:
-                {
-                  ajaxfct:options.userMode+'.inputTreeList.getList',
-                  filter:options.filter,
-                  galleryRoot:options.galleryRoot?'y':'n',
-                  tree:'y'
-                },
-              success: function(msg)
-                {
-                  privateMethods.setItems(object, msg);
-                },
-              error: function(msg)
-                {
-                  objects.listContainer.html('Error ! '+msg);
-                },
-            }
-         );
-        },
-
-      listClear : function (object)
-        {
-          // clear the categorie list
-          var objects=object.data('objects'),
-              options=object.data('options'),
-              properties=object.data('properties');
-
-          objects.list.children().unbind();
-          objects.list.html('');
-          if(options.multiple)
-          {
-            properties.index=[];
-          }
-          else
-          {
-            properties.index=-1;
-          }
-          properties.categories=[];
-        },
-
-      listAddItems : function (object, listItems, parent)
-        {
-          // add the items to the categorie list
-          var options=object.data('options'),
-              properties=object.data('properties'),
-              objects=object.data('objects');
-
-          var previousLevel=-1;
-          for(var i=0;i<listItems.length;i++)
-          {
-            properties.categories.push(
-              {
-                id:listItems[i].id,
-                level:listItems[i].level,
-                name:listItems[i].name,
-                status:listItems[i].status,
-                childs:listItems[i].childs.length
-              }
-            );
-
-            if(options.displayStatus)
-            {
-              status="<div class='ui-inputTreeList-status'>"+properties.labelStatus[listItems[i].status]+"</div>";
-            }
-            else
-            {
-              status="";
-            }
-
-            var spaceWidth = (options.iconWidthEC+listItems[i].level*options.levelIndent),
-                li=$('<li/>',
-                      {
-                        'class':'ui-inputTreeList-item',
-                        html:"<div>"+status+"<div class='ui-inputTreeList-name'>"+listItems[i].name+"</div></div>",
-                        catId:listItems[i].id,
-                        level:listItems[i].level,
-                        css:{
-                          'padding-left':spaceWidth+'px'
-                        }
-                      }
-                    ).bind('click.inputTreeList',
-                        {object:object, expandArea: spaceWidth, nbchilds:listItems[i].childs.length },
-                        function (event)
-                        {
-                          event.layerX=event.pageX-$(event.currentTarget).offset().left;
-                          event.layerY=event.pageY-$(event.currentTarget).offset().top;
-
-                          if(event.layerX<event.data.expandArea && event.data.nbchilds>0 )
-                          {
-                            if($(this).hasClass('ui-inputTreeList-expandable-item'))
-                            {
-                              $(this)
-                                .removeClass('ui-inputTreeList-expandable-item')
-                                .addClass('ui-inputTreeList-collapsable-item')
-                                .next().show();
-                            }
-                            else
-                            {
-                              $(this)
-                                .removeClass('ui-inputTreeList-collapsable-item')
-                                .addClass('ui-inputTreeList-expandable-item')
-                                .next().hide();
-                            }
-                          }
-                          else
-                          {
-                            privateMethods.setValue(event.data.object, $(this).attr('catId'), true);
-                            if(options.multiple)
-                            {
-                            }
-                            else
-                            {
-                              privateMethods.displaySelector(event.data.object, false);
-                            }
-                          }
-
-                          if(options.multiple) objects.container.focus();
-                        }
-                      );
-            if(listItems[i].childs.length>0)
-            {
-              li.addClass('ui-inputTreeList-collapsable-item').css('background-position', (options.levelIndent*listItems[i].level)+'px 0px');
-            }
-
-            if(options.multiple)
-            {
-              li.children().prepend('<div class="ui-inputTreeList-check"></div>');
-            }
-
-            parent.append(li);
-
-            if(listItems[i].childs.length>0)
-            {
-              var ul=$('<ul/>',
-                        {
-                          'class':'ui-inputTreeList-group',
-                          css: {
-                            listStyle:'none',
-                            padding:'0px',
-                            margin:'0px',
-                            'font-size':(100-listItems[i].level*2)+'%'
-                          }
-                        }
-                      );
-              li.after(ul);
-              privateMethods.listAddItems(object, listItems[i].childs, ul);
-            }
-
-          }
-        },
-
-      findIndexByValue : function (object, value)
-        {
-          /*
-           * search a categorie inside the categories list and return the index
-           * in the array
-           */
-          var properties=object.data('properties');
-
-          for(var i=0;i<properties.categories.length;i++)
-          {
-            if(properties.categories[i].id==value) return(i);
-          }
-          return(-1);
-        },
-
-      setEventPopup : function (object, value)
-        {
-          var options=object.data('options');
-
-          options.popup=value;
-          object.unbind('inputTreeListPopup');
-          if(value) object.bind('inputTreeListPopup', options.popup);
-          return(options.popup);
-        },
-
-      setEventChange : function (object, value)
-        {
-          var options=object.data('options');
-
-          options.change=value;
-          object.unbind('inputTreeListChange');
-          if(value) object.bind('inputTreeListChange', options.change);
-          return(options.change);
-        },
-
-      setEventLoad : function (object, value)
-        {
-          var options=object.data('options');
-
-          options.load=value;
-          object.unbind('inputTreeListLoad');
-          if(value) object.bind('inputTreeListLoad', options.load);
-          return(options.load);
-        }
-    };
-
-
-    $.fn.inputTreeList = function(method)
-    {
-      if(publicMethods[method])
-      {
-        return publicMethods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
-      }
-      else if(typeof method === 'object' || ! method)
-      {
-        return publicMethods.init.apply(this, arguments);
-      }
-      else
-      {
-        $.error( 'Method ' +  method + ' does not exist on jQuery.inputTreeList' );
-      }
-    } // $.fn.inputTreeList
-
-  }
-)(jQuery);
-
-
Index: extensions/GrumPluginClasses/js/ui.progressArea.js
===================================================================
--- extensions/GrumPluginClasses/js/ui.progressArea.js	(revision 16733)
+++ extensions/GrumPluginClasses/js/ui.progressArea.js	(revision 16733)
@@ -0,0 +1,1084 @@
+/**
+ * -----------------------------------------------------------------------------
+ * file: ui.progressArea.js
+ * file version: 1.0.0
+ * date: 2012-07-13
+ *
+ * A jQuery plugin provided by the piwigo's plugin "GrumPluginClasses"
+ *
+ * -----------------------------------------------------------------------------
+ * Author     : Grum
+ *   email    : grum@piwigo.com
+ *   website  : http://photos.grum.fr
+ *   PWG user : http://forum.piwigo.org/profile.php?id=3706
+ *
+ *   << May the Little SpaceFrog be with you ! >>
+ * -----------------------------------------------------------------------------
+ *
+ *
+ *
+ *
+ * :: HISTORY ::
+ *
+ * | release | date       |
+ * | 1.0.0   | 2012/07/13 | first release
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ *
+ */
+
+
+
+(
+  function($)
+  {
+    /*
+     * plugin 'public' functions
+     */
+    var publicMethods =
+    {
+      init : function (opt)
+        {
+          return this.each(function()
+            {
+              // default values for the plugin
+              var $this=$(this),
+                  timeStamp=new Date(),
+                  data = $this.data('options'),
+                  objects = $this.data('objects'),
+                  properties = $this.data('properties'),
+                  options =
+                    {
+                      text:'',
+                      workInProgress:false,
+                      global:{
+                          visible:true,
+                          showPercent:true,
+                          percentDecimal:2,
+                          value:0,
+                          maxValue:100,
+                          text:'',
+                          complete:null,
+                          change:null
+                      },
+                      detail:{
+                          visible:false,
+                          showPercent:true,
+                          percentDecimal:2,
+                          value:0,
+                          maxValue:100,
+                          text:'',
+                          complete:null,
+                          change:null
+                      }
+                    };
+
+              // if options given, merge it
+              // if(opt) $.extend(options, opt); ==> options are set by setters
+
+              $this.data('options', options);
+
+
+              if(!properties)
+              {
+                $this.data('properties',
+                  {
+                    globalTime:{
+                        totalExecution:0,
+                        lastExecution:0,
+                        start:0
+                      },
+                    detailTime:{
+                        totalExecution:0,
+                        lastExecution:0,
+                        start:0
+                      }
+                  }
+                );
+                properties=$this.data('properties');
+              }
+
+              if(!objects)
+              {
+                objects =
+                  {
+                    text:$('<div/>',
+                        {
+                          'class':'ui-progressArea-text'
+                        }
+                      ),
+                    globalText:$('<div/>',
+                        {
+                          'class':'ui-progressArea-globalText'
+                        }
+                      ),
+                    detailText:$('<div/>',
+                        {
+                          'class':'ui-progressArea-detailText'
+                        }
+                      ),
+                    globalBar:$('<div/>',
+                        {
+                          'class':'ui-progressArea-globalBar'
+                        }
+                      ),
+                    detailBar:$('<div/>',
+                        {
+                          class:'ui-progressArea-detailBar'
+                        }
+                      ),
+                    globalBarProgress:$('<div/>',
+                        {
+                          class:'ui-progressArea-globalBarProgress'
+                        }
+                      ),
+                    detailBarProgress:$('<div/>',
+                        {
+                          class:'ui-progressArea-detailBarProgress'
+                        }
+                      ),
+                    globalBarText:$('<div/>',
+                        {
+                          class:'ui-progressArea-globalBarText'
+                        }
+                      ),
+                    detailBarText:$('<div/>',
+                        {
+                          class:'ui-progressArea-detailBarText'
+                        }
+                      ),
+                    workInProgress:$('<div/>',
+                        {
+                          class:'ui-progressArea-workInProgress'
+                        }
+                      )
+                  };
+
+                $this
+                  .html('')
+                  .addClass('ui-progressArea')
+                  .append(objects.text)
+                  .append(objects.globalText)
+                  .append(objects.globalBar
+                            .append(objects.globalBarProgress)
+                            .append(objects.globalBarText)
+                         )
+                  .append(objects.detailText)
+                  .append(objects.detailBar
+                            .append(objects.detailBarProgress)
+                            .append(objects.detailBarText)
+                         )
+                  .append(objects.workInProgress);
+
+                $this.data('objects', objects);
+              }
+
+              privateMethods.setOptions($this, opt);
+            }
+          );
+        }, // init
+      destroy : function ()
+        {
+          return this.each(
+            function()
+            {
+              // default values for the plugin
+              var $this=$(this),
+                  objects = $this.data('objects');
+              objects.text.remove();
+              objects.globalText.remove();
+              objects.detailText.remove();
+              objects.globalBarText.remove();
+              objects.globalBarProgress.remove();
+              objects.globalBar.remove();
+              objects.detailBarText.remove();
+              objects.detailBarProgress.remove();
+              objects.detailBar.remove();
+              objects.workInProgress.remove();
+
+              delete objects.text;
+              delete objects.globalText;
+              delete objects.detailText;
+              delete objects.globalBarText;
+              delete objects.globalBarProgress;
+              delete objects.globalBar;
+              delete objects.detailBarText;
+              delete objects.detailBarProgress;
+              delete objects.detailBar;
+              delete objects.workInProgress;
+
+              $this.removeClass('ui-progressArea');
+            }
+          );
+        }, // destroy
+
+      options: function (value)
+        {
+          return(
+            this.each(
+              function()
+              {
+                privateMethods.setOptions($(this), value);
+              }
+            )
+          );
+        }, // options
+
+      // hide progress information: progress bar and work in progress
+      hideProgress: function ()
+        {
+          return(
+            this.each(
+              function()
+              {
+                privateMethods.hideProgress($(this));
+              }
+            )
+          );
+        }, //hideProgress
+
+       // hide progress information: progress bar and work in progress
+      workInProgress: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setWorkInProgress($(this), value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            return(options.workInProgress);
+          }
+        }, //workInProgress
+
+      // set or return the value for global progress bar text
+      globalText: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setText($(this), 'global', value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            return(options.global.text);
+          }
+        }, // globalText
+
+      // set or return the value for detail progress bar text
+      detailText: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setText($(this), 'detail', value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            return(options.detail.text);
+          }
+        }, // detailText
+
+      // set or return the value for generic text
+      text: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setText($(this), 'generic', value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            return(options.text);
+          }
+        }, // text
+
+
+
+      // set or return the value for global progress bar
+      globalValue: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setGlobalValue($(this), value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            return(options.global.value);
+          }
+        }, // globalValue
+
+      // set or return the value for detail progress bar
+      detailValue: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setDetailValue($(this), value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            return(options.detail.value);
+          }
+        }, // detailValue
+
+
+      // set or return the max value for global progress bar
+      globalMaxValue: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setGlobalMaxValue($(this), value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            return(options.global.maxValue);
+          }
+        }, // globalMaxValue
+
+      // set or return the max value for detail progress bar
+      detailMaxValue: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setDetailMaxValue($(this), value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            return(options.detail.maxValue);
+          }
+        }, // detailMaxValue
+
+
+      // return the execution times
+      executionTime: function (value)
+        {
+          var properties=this.data('properties');
+          switch(value)
+          {
+            case 'resetGlobal':
+              return(
+                this.each(
+                  function()
+                  {
+                    privateMethods.resetTime($(this), 'global');
+                  }
+                )
+              );
+              break;
+            case 'resetDetail':
+              return(
+                this.each(
+                  function()
+                  {
+                    privateMethods.resetTime($(this), 'detail');
+                  }
+                )
+              );
+              break;
+            case 'global':
+              return(
+                {
+                  totalExecution:properties.globalTime.totalExecution,
+                  lastExecution:properties.globalTime.lastExecution
+                }
+              );
+              break;
+            case 'detail':
+              return(
+                {
+                  totalExecution:properties.detailTime.totalExecution,
+                  lastExecution:properties.detailTime.lastExecution
+                }
+              );
+              break;
+            default:
+              return(
+                {
+                  global:
+                    {
+                      totalExecution:properties.globalTime.totalExecution,
+                      lastExecution:properties.globalTime.lastExecution
+                    },
+                  detail:
+                    {
+                      totalExecution:properties.detailTime.totalExecution,
+                      lastExecution:properties.detailTime.lastExecution
+                    }
+                }
+              );
+              break;
+          }
+        } // executionTime
+
+    }; // methods
+
+
+    /*
+     * plugin 'private' methods
+     */
+    var privateMethods =
+    {
+      setOptions : function (object, value)
+        {
+          var properties=object.data('properties'),
+              options=object.data('options');
+
+          if(!$.isPlainObject(value)) return(false);
+
+          properties.initialized=false;
+
+          privateMethods.setText(object, 'generic', (value.text!=null)?value.text:options.text);
+          privateMethods.setWorkInProgress(object, (value.workInProgress!=null)?value.workInProgress:options.workInProgress);
+          privateMethods.setProperties(object, 'global', (value.global!=null)?value.global:options.global);
+          privateMethods.setProperties(object, 'detail', (value.detail!=null)?value.detail:options.detail);
+
+          privateMethods.resetTime(object, 'detail');
+          privateMethods.resetTime(object, 'global');
+
+          privateMethods.updateBar(object, 'global');
+          privateMethods.updateBar(object, 'detail');
+
+          privateMethods.showWorkInProgress(object);
+
+          properties.initialized=true;
+        },
+
+
+      setWorkInProgress: function (object, value)
+        {
+          var options=object.data('options'),
+              properties=object.data('properties'),
+              objects=object.data('objects');
+
+          if(!properties.initialized || value!=options.workInProgress)
+          {
+            options.workInProgress=value;
+            privateMethods.showWorkInProgress(object);
+          }
+          return(options.workInProgress);
+        }, // setWorkInProgress
+
+      setText: function (object, target, value)
+        {
+          var options=object.data('options'),
+              properties=object.data('properties'),
+              objects=object.data('objects');
+
+          switch(target)
+          {
+            case 'global':
+              if(!properties.initialized || value!=options.global.text)
+              {
+                options.global.text=value;
+                objects.globalText.html(options.global.text);
+              }
+
+              return(options.global.text);
+              break;
+            case 'detail':
+              if(!properties.initialized || value!=options.detail.text)
+              {
+                options.detail.text=value;
+                objects.detailText.html(options.detail.text);
+              }
+
+              return(options.detail.text);
+              break;
+            case 'generic':
+              if(!properties.initialized || value!=options.text)
+              {
+                options.text=value;
+                objects.text.html(options.text);
+              }
+
+              return(options.text);
+              break;
+          }
+        }, // setText
+
+
+      /**
+       * set properties for global/detail options
+       *
+       * @param String target: 'global' or 'detail'
+       */
+      setProperties: function (object, target, value)
+        {
+          var options=object.data('options'),
+              properties=object.data('properties'),
+              objects=object.data('objects'),
+              tmpValue={
+                visible:true,
+                value:0,
+                maxValue:100,
+                text:'',
+                complete:null,
+                change:null
+              };
+
+          if(!properties.initialized &&
+              (target=='global' || target=='detail') &&
+              $.isPlainObject(value)
+          )
+          {
+            if(value.visible!=null &&
+                (value.visible==true || value.visible==false)
+              )
+                tmpValue.visible=value.visible;
+
+            if(value.maxValue!=null &&
+               value.maxValue>=0)
+                tmpValue.maxValue=value.maxValue;
+
+            if(value.value!=null &&
+               value.value>=0 &&
+               value.value<=tmpValue.maxValue)
+                tmpValue.value=value.value;
+
+            if(value.text!=null)
+                tmpValue.text=value.text;
+
+            if(value.complete!=null &&
+                $.isFunction(value.complete))
+              tmpValue.complete=value.complete;
+
+            if(value.change!=null &&
+                $.isFunction(value.change))
+              tmpValue.change=value.change;
+
+            if(value.showPercent!=null &&
+                (value.showPercent==true || value.showPercent==false)
+              )
+                tmpValue.showPercent=value.showPercent;
+
+            if(value.percentDecimal!=null &&
+               value.percentDecimal>=0 && value.percentDecimal<=2)
+                tmpValue.percentDecimal=value.percentDecimal;
+          }
+
+          switch(target)
+          {
+            case 'global':
+              options.global.visible=tmpValue.visible;
+              options.global.value=tmpValue.value;
+              options.global.maxValue=tmpValue.maxValue;
+              options.global.text=tmpValue.text;
+              options.global.showPercent=tmpValue.showPercent;
+              options.global.percentDecimal=tmpValue.percentDecimal;
+
+              privateMethods.setEventComplete(object, 'global', tmpValue.complete);
+              privateMethods.setEventChange(object, 'global', tmpValue.change);
+
+              objects.globalText
+                .html(options.global.text)
+                .css('display', options.global.visible?'block':'none');
+              objects.globalBar.css('display', options.global.visible?'block':'none');
+              privateMethods.updateBar(object, 'global');
+              return(options.global);
+              break;
+            case 'detail':
+              options.detail.visible=tmpValue.visible;
+              options.detail.value=tmpValue.value;
+              options.detail.maxValue=tmpValue.maxValue;
+              options.detail.text=tmpValue.text;
+              options.detail.showPercent=tmpValue.showPercent;
+              options.detail.percentDecimal=tmpValue.percentDecimal;
+
+              privateMethods.setEventComplete(object, 'detail', tmpValue.complete);
+              privateMethods.setEventChange(object, 'detail', tmpValue.change);
+
+              objects.detailText
+                .html(options.detail.text)
+                .css('display', options.detail.visible?'block':'none');
+              objects.detailBar.css('display', options.detail.visible?'block':'none');
+              privateMethods.updateBar(object, 'detail');
+              return(options.detail);
+              break;
+            default:
+              return(null);
+              break;
+          }
+        }, // setProperties
+
+
+      setGlobalValue : function (object, value)
+        {
+          var properties=object.data('properties'),
+              options=object.data('options');
+
+          if(value!=options.global.value && value>=0 && value<=options.global.maxValue)
+          {
+
+            options.global.value=value;
+            privateMethods.updateBar(object, 'global');
+
+            properties.globalTime.lastExecution=Date.now()-properties.globalTime.start;
+            properties.globalTime.totalExecution+=properties.globalTime.lastExecution;
+            properties.globalTime.start=Date.now();
+
+            if(options.global.value==options.global.maxValue && options.global.complete!=null)
+            {
+              object.trigger('progressAreaCompleteglobal',
+                {
+                  lastExecution:properties.globalTime.lastExecution,
+                  totalExecution:properties.globalTime.totalExecution
+                }
+              );
+            }
+            else if(options.global.change!=null)
+            {
+              object.trigger('progressAreaChangeglobal',
+                {
+                  currentValue:options.global.value,
+                  lastExecution:properties.globalTime.lastExecution,
+                  totalExecution:properties.globalTime.totalExecution
+                }
+              );
+            }
+          }
+
+          return(options.global.value);
+        }, // setGlobalValue
+
+      setDetailValue : function (object, value)
+        {
+          var properties=object.data('properties'),
+              options=object.data('options');
+
+          if(value!=options.detail.value && value>=0 && value<=options.detail.maxValue)
+          {
+            options.detail.value=value;
+            privateMethods.updateBar(object, 'detail');
+
+            properties.detailTime.lastExecution=Date.now()-properties.detailTime.start;
+            properties.detailTime.totalExecution+=properties.detailTime.lastExecution;
+            properties.detailTime.start=Date.now();
+
+            properties.globalTime.lastExecution+=properties.detailTime.lastExecution;
+            properties.globalTime.totalExecution+=properties.detailTime.lastExecution;
+
+            if(options.detail.value==options.detail.maxValue && options.detail.complete!=null)
+            {
+              object.trigger('progressAreaCompletedetail',
+                {
+                  lastExecution:properties.detailTime.lastExecution,
+                  totalExecution:properties.detailTime.totalExecution,
+                  globalLastExecution:properties.globalTime.lastExecution,
+                  globalTotalExecution:properties.globalTime.totalExecution
+                }
+              );
+            }
+            else if(options.detail.change!=null)
+            {
+              object.trigger('progressAreaChangedetail',
+                {
+                  currentValue:options.detail.value,
+                  lastExecution:properties.detailTime.lastExecution,
+                  totalExecution:properties.detailTime.totalExecution,
+                  globalLastExecution:properties.globalTime.lastExecution,
+                  globalTotalExecution:properties.globalTime.totalExecution
+                }
+              );
+            }
+
+            if(options.detail.value==options.detail.maxValue)
+              privateMethods.resetTime(object, 'detail');
+          }
+
+          return(options.detail.value);
+        }, // setDetailValue
+
+
+      setGlobalMaxValue : function (object, value)
+        {
+          var properties=object.data('properties'),
+              options=object.data('options');
+
+          if(value!=options.global.maxValue && value>=0 && value>=options.global.value)
+          {
+            options.global.maxValue=value;
+            privateMethods.updateBar(object, 'global');
+          }
+
+          return(options.global.maxValue);
+        }, // setGlobalMaxValue
+
+      setDetailMaxValue : function (object, value)
+        {
+          var properties=object.data('properties'),
+              options=object.data('options');
+
+          if(value!=options.detail.maxValue && value>=0 && value>=options.detail.value)
+          {
+            options.detail.maxValue=value;
+            privateMethods.updateBar(object, 'detail');
+          }
+
+          return(options.detail.maxValue);
+        }, // setDetailMaxValue
+
+
+      setEventComplete : function (object, target, value)
+        {
+          var options=object.data('options');
+
+          object.unbind('progressAreaComplete'+target);
+          switch(target)
+          {
+            case 'global':
+              options.global.complete=value;
+              if(value) object.bind('progressAreaCompleteglobal', options.global.complete);
+              return(options.global.complete);
+              break;
+            case 'detail':
+              options.detail.complete=value;
+              if(value) object.bind('progressAreaCompletedetail', options.detail.complete);
+              return(options.detail.complete);
+              break;
+          }
+        }, //setEventComplete
+
+      setEventChange : function (object, target, value)
+        {
+          var options=object.data('options');
+
+          object.unbind('progressAreaChange'+target);
+          switch(target)
+          {
+            case 'global':
+              options.global.change=value;
+              if(value) object.bind('progressAreaChangeglobal', options.global.change);
+              return(options.global.change);
+              break;
+            case 'detail':
+              options.detail.change=value;
+              if(value) object.bind('progressAreaChangedetail', options.detail.change);
+              return(options.detail.change);
+              break;
+          }
+        }, //setEventChange
+
+
+      updateBar : function (object, target)
+        {
+          var objects=object.data('objects'),
+              options=object.data('options'),
+              properties=object.data('properties'),
+              percent=0,
+              targetObj=null,
+              barObj=null,
+              textObj=null,
+              progressObj=null;
+
+          switch(target)
+          {
+            case 'global':
+              targetObj=options.global;
+              barObj=objects.globalBar;
+              textObj=objects.globalBarText;
+              progressObj=objects.globalBarProgress;
+              break;
+            case 'detail':
+              targetObj=options.detail;
+              barObj=objects.detailBar;
+              textObj=objects.detailBarText;
+              progressObj=objects.detailBarProgress;
+              break;
+            default:
+              return(false);
+              break;
+          }
+
+          if(targetObj.maxValue>0)
+            percent=100*targetObj.value/targetObj.maxValue;
+
+          switch(targetObj.percentDecimal)
+          {
+            case 0:
+              percent=Math.round(percent);
+              break;
+            case 1:
+              percent=Math.round(percent*10)/10;
+              break;
+            case 2:
+              percent=Math.round(percent*100)/100;
+              break;
+          }
+
+          progressObj.css(
+            {
+              'width': Math.round(barObj.width()*percent/100)+'px',
+              'height': barObj.innerHeight()
+            }
+          );
+
+          if(targetObj.showPercent)
+            textObj
+              .css('width', barObj.width())
+              .html(percent.toFixed(targetObj.percentDecimal)+'%');
+        }, //updateBar
+
+
+      hideProgress : function (object)
+        {
+          var options=object.data('options'),
+              properties=object.data('properties'),
+              objects=object.data('objects');
+
+          objects.globalBar.css('display', 'none');
+          objects.globalText.css('display', 'none');
+
+          objects.detailBar.css('display', 'none');
+          objects.detailText.css('display', 'none');
+
+          objects.workInProgress.css('display', 'none');
+        }, //hideProgress
+
+      showWorkInProgress : function (object)
+        {
+          var options=object.data('options'),
+              properties=object.data('properties'),
+              objects=object.data('objects');
+
+          if(options.workInProgress)
+          {
+            bgPosition=(object.width()/2-8)+'px 8px';
+
+            objects.workInProgress.css(
+              {
+                'display': 'block',
+                'background-position': bgPosition
+              }
+            );
+          }
+          else
+          {
+            objects.workInProgress.css('display', 'none');
+          }
+        }, //showWorkInProgress
+
+
+      resetTime : function(object, target)
+        {
+          var properties=object.data('properties');
+
+          switch(target)
+          {
+            case 'global':
+              properties.globalTime.totalExecution=0;
+              properties.globalTime.lastExecution=0;
+              properties.globalTime.start=Date.now();
+              break;
+            case 'detail':
+              properties.detailTime.totalExecution=0;
+              properties.detailTime.lastExecution=0;
+              properties.detailTime.start=Date.now();
+              break;
+          }
+        } // resetTime
+
+
+    };
+
+    $.fn.progressArea = function(method)
+    {
+      if(publicMethods[method])
+      {
+        return publicMethods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
+      }
+      else if(typeof method === 'object' || ! method)
+      {
+        return publicMethods.init.apply(this, arguments);
+      }
+      else
+      {
+        $.error( 'Method ' +  method + ' does not exist on jQuery.progressArea' );
+      }
+    } // $.fn.progressArea
+
+  }
+)(jQuery);
+
+/**
+ * open a modal dialog box for progression
+ */
+$.dialogProgressArea = function (opt)
+{
+  var options=
+        {
+          width:350,
+          height:200,
+          title:'Progression',
+          buttons:
+            {
+              ok:'Ok'
+            },
+          text:'',
+          progressBar:
+            {
+              global:null,
+              detail:null
+            },
+         close:null,
+        },
+      objects=
+        {
+          dialogBox:$('<div/>'),
+          pArea:$('<div/>')
+        },
+      properties=
+        {
+          complete:null
+        },
+
+  setOptions = function (opt)
+    {
+      if(opt.width!=null && opt.width>0) options.width=opt.width;
+      if(opt.height!=null && opt.height>0) options.height=opt.height;
+      if(opt.title) options.title=opt.title;
+      if(opt.buttons && opt.buttons.ok) options.buttons.ok=opt.buttons.ok;
+      if(opt.text) options.text=opt.text;
+
+      if($.isPlainObject(opt.progressBar) &&
+         $.isPlainObject(opt.progressBar.global))
+            options.progressBar.global=opt.progressBar.global;
+      if($.isPlainObject(opt.progressBar) &&
+         $.isPlainObject(opt.progressBar.detail))
+            options.progressBar.detail=opt.progressBar.detail;
+
+      if(opt.close && $.isFunction(opt.close)) options.close=opt.close;
+
+      if($.isFunction(options.progressBar.global.complete))
+        properties.complete=options.progressBar.global.complete;
+
+      options.progressBar.global.complete=function (event, value)
+        {
+          objects.pArea.progressArea('hideProgress');
+          if(properties.complete) properties.complete(event, value);
+          objects.dialogBox.parent().find('div.ui-dialog-buttonpane button').css('visibility', 'visible');
+        }
+    },
+
+  initDialog = function ()
+    {
+      var dialogOpt={},
+          dialogButtons={};
+
+      dialogButtons[options.buttons.ok] = function (event)
+        {
+          if(options.close) options.close(event);
+          $(this).dialog('close');
+        };
+
+      dialogOpt=
+          {
+            width:options.width,
+            height:options.height,
+            closeOnEscape:false,
+            closeText:'',
+            dialogClass:'ui-dialogProgressArea',
+            modal:true,
+            resizable:false,
+            title:options.title,
+            buttons:dialogButtons,
+            open: open= function ()
+                    {
+                      objects.pArea
+                        .progressArea(
+                          {
+                            text:options.text,
+                            workInProgress:true,
+                            global:options.progressBar.global,
+                            detail:options.progressBar.detail
+                          }
+                        );
+                    },
+            close: function ()
+                    {
+                      objects.pArea.progressArea('destroy').remove();
+                      $(this).dialog('destroy').remove();
+                    }
+          };
+
+      objects.dialogBox
+        .append(objects.pArea)
+        .dialog(dialogOpt);
+    };
+
+  setOptions(opt);
+  initDialog();
+
+  return(objects.pArea);
+} // $.dialogProgressArea
Index: extensions/GrumPluginClasses/js/ui.timer.js
===================================================================
--- extensions/GrumPluginClasses/js/ui.timer.js	(revision 16733)
+++ extensions/GrumPluginClasses/js/ui.timer.js	(revision 16733)
@@ -0,0 +1,286 @@
+/**
+ * -----------------------------------------------------------------------------
+ * file: ui.timer.js
+ * file version: 1.0.0
+ * date: 2012-07-14
+ *
+ * A jQuery plugin provided by the piwigo's plugin "GrumPluginClasses"
+ *
+ * -----------------------------------------------------------------------------
+ * Author     : Grum
+ *   email    : grum@piwigo.com
+ *   website  : http://www.grum.fr
+ *
+ *   << May the Little SpaceFrog be with you ! >>
+ * -----------------------------------------------------------------------------
+ *
+ *
+ *
+ *
+ * :: HISTORY ::
+ *
+ * | release | date       |
+ * | 1.0.0   | 2012/07/14 | first release
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ *
+ */
+
+
+
+/**
+ * set a timer
+ *
+ * $.timer('id', {properties})
+ *  => set properties for a timer 'id'
+ *
+ * $.timer('id', 'function' [, param])
+ *  => available functions are:
+ *    - start: start the timer
+ *    - stop: stop the timer
+ *    - reset: reset the timer
+ *    - remove: remove the timer
+ *    - count: if no param, returns the current count value otherwise set the count value to param value
+ *    - frequency: if no param, returns the current frequency value otherwise set the frequency value to param value
+ *
+ */
+$.timer = function (id, value1, value2)
+{
+  var returned=null,
+      properties=
+        {
+          timers:{}
+        },
+
+
+    // set timer properties
+    setTimer = function (id, value)
+      {
+        if(properties.timers[id]==null)
+        {
+          properties.timers[id]={
+            autoStart:false,      // if set to true, timer start automaticaly when created
+            frequency:1000,       // frequency of timer, in milliseconds
+            limit:0,              // limit of timer, 0=no limit; timer stop when limit is reached
+            timer:null,           // function callback
+
+            count:0,              // number of times the timer was triggered
+            started:false,        // true if timer is started, otherwise false
+            timerId:null          // timer uid
+          }
+        }
+
+        if($.isPlainObject(value))
+        {
+          if(value.autoStart &&
+            (value.autoStart==true || value.autoStart==false))
+            properties.timers[id].autoStart=value.autoStart;
+
+          if(value.frequency &&
+            Math.round(value.frequency)>0)
+            properties.timers[id].frequency=Math.round(value.frequency);
+
+          if(value.limit &&
+            Math.round(value.limit)>=0)
+            properties.timers[id].limit=Math.round(value.limit);
+
+          if(value.timer &&
+            $.isFunction(value.timer))
+            properties.timers[id].timer=value.timer;
+        }
+
+        if(properties.timers[id].autoStart && !properties.timers[id].started)
+          startTimer(id);
+      },
+
+    /**
+     * start the timer if not started
+     *
+     * @param String Id: timer Id
+     * @return Boolean: true if started, otherwise false
+     */
+    startTimer = function (id)
+      {
+        if(properties.timers[id]==null || properties.timers[id].started) return(false);
+
+        properties.timers[id].timerId=window.setInterval(
+          callFunction,
+          properties.timers[id].frequency,
+          id
+        );
+        properties.timers[id].started=true;
+        return(true);
+      },
+
+    /**
+     * stop the timer if started
+     *
+     * @param String Id: timer Id
+     * @return Boolean: true if stopped, otherwise false
+     */
+    stopTimer = function (id)
+      {
+        if(properties.timers[id]==null && !properties.timers[id].started) return(false);
+
+        properties.timers[id].started=false;
+        window.clearInterval(properties.timers[id].timerId);
+        return(true);
+      },
+
+    /**
+     * reset the timer:
+     *  - count is set to 0
+     *  - timer is reseted for a new cycle
+     *
+     * @param String Id: timer Id
+     * @return Boolean: true if reseted, otherwise false
+     */
+    resetTimer = function (id)
+      {
+        if(properties.timers[id]==null) return(false);
+
+        stopTimer(id);
+        properties.timers[id].count=0;
+        startTimer(id);
+        return(true);
+      },
+
+    /**
+     * remove a timer
+     * timer if stopped is necessary
+     *
+     * @param String Id: timer Id
+     * @return Boolean: true if removed, otherwise false
+     */
+    removeTimer = function (id)
+      {
+        if(properties.timers[id]==null) return(false);
+
+        stopTimer(id);
+        delete properties.timers[id];
+        return(true);
+      },
+
+    /**
+     * set the limit of execution
+     *
+     * @param String Id: timer Id
+     * @param Integer value: limit to apply
+     * @return Boolean: true if limit is set, otherwise false
+     */
+    setLimit = function (id, value)
+      {
+        if(properties.timers[id]==null || Math.round(value)<0) return(false);
+        properties.timers[id].limit=Math.round(value);
+      },
+
+    /**
+     * set the frequency of execution
+     *
+     * @param String Id: timer Id
+     * @param Integer value: frequency (in milliseconds) to apply
+     * @return Boolean: true if frequency is set, otherwise false
+     */
+    setFrequency = function (id, value)
+      {
+        if(properties.timers[id]==null || Math.round(value)<=0) return(false);
+        properties.timers[id].frequency=Math.round(value);
+        stopTimer(id);
+        startTimer(id);
+        return(true);
+      },
+
+    /**
+     * internal callback
+     * called when a timer is triggered; if a callback function is set for the
+     * timer, execute it
+     *
+     *
+     * @param String Id: timer Id
+     * @return Boolean: true if callback is called, otherwise false
+     */
+    callFunction = function (id)
+      {
+        var noLimit=true,
+            last=false;
+
+        if(properties.timers[id]==null) return(false);
+        properties.timers[id].count++;
+
+        noLimit=(properties.timers[id].limit==0);
+        last=(!noLimit && (properties.timers[id].count==properties.timers[id].limit));
+
+        if(last)
+          stopTimer(id);
+
+        if(properties.timers[id].timer)
+          properties.timers[id].timer(
+            properties.timers[id].count,
+            last
+          );
+
+        return(true);
+      },
+
+    // save current data in global stack
+    setData = function ()
+      {
+        $(window).data('timerProperties', properties.timers);
+      },
+
+    // load current data from global stack
+    getData = function ()
+      {
+        var tmp=$(window).data('timerProperties');
+        if(tmp!=null) properties.timers=tmp;
+      }
+
+  getData();
+
+  switch(value1)
+  {
+    case 'start':
+      returned=startTimer(id);
+      break;
+    case 'stop':
+      returned=stopTimer(id);
+      break;
+    case 'reset':
+      returned=resetTimer(id);
+      break;
+    case 'count':
+      getData();
+      if(!properties.timers[id]) return(-1);
+      returned=properties.timers[id].count;
+      break;
+    case 'limit':
+      getData();
+      if(!properties.timers[id]) return(-1);
+
+      if(value2!=null)
+        setLimit(id, value2);
+      returned=properties.timers[id].limit;
+      break;
+    case 'frequency':
+      getData();
+      if(!properties.timers[id]) return(-1);
+
+      if(value2!=null)
+        setFrequency(id, value2);
+      returned=properties.timers[id].frequency;
+      break;
+    case 'remove':
+      returned=removeTimer(id);
+      break;
+    default:
+      setTimer(id, value1);
+      break;
+  }
+
+  setData();
+  return(returned);
+} // $.timer
