Index: extensions/GrumPluginClasses/classes/GPCCategorySelector.class.inc.php
===================================================================
--- extensions/GrumPluginClasses/classes/GPCCategorySelector.class.inc.php	(revision 11529)
+++ extensions/GrumPluginClasses/classes/GPCCategorySelector.class.inc.php	(revision 12215)
@@ -301,4 +301,6 @@
         $row['rank']=implode('.', $row['rank']);
 
+        $row['name']=GPCCore::getUserLanguageDesc($row['name']);
+
         $returned[]=$row;
       }
Index: extensions/GrumPluginClasses/classes/GPCCore.class.inc.php
===================================================================
--- extensions/GrumPluginClasses/classes/GPCCore.class.inc.php	(revision 11529)
+++ extensions/GrumPluginClasses/classes/GPCCore.class.inc.php	(revision 12215)
@@ -3,7 +3,7 @@
 /* -----------------------------------------------------------------------------
   class name     : GPCCore
-  class version  : 1.4.0
-  plugin version : 3.5.0
-  date           : 2011-04-10
+  class version  : 1.4.1
+  plugin version : 3.5.2
+  date           : 2011-09-19
   ------------------------------------------------------------------------------
   author: grum at piwigo.org
@@ -55,4 +55,5 @@
 | 1.4.0   | 2011/04/10 | * Updated for piwigo 2.2
 |         |            |
+| 1.4.1   | 2011/09/19 | * Add [var] and [form_mail] markup interpreter
 |         |            |
 |         |            |
@@ -69,4 +70,6 @@
     - static function unregister
     - static function BBtoHTML
+    - static function VarToHTML
+    - static function FormMailToHTML
     - static function addHeaderCSS
     - static function addHeaderJS
@@ -78,4 +81,5 @@
     - static function formatOctet
     - static function rmDir
+    - static function applyMarkups
    ---------------------------------------------------------------------- */
 
@@ -95,5 +99,20 @@
   static public function init()
   {
+    global $conf;
+
     self::$piwigoSystemPath=dirname(dirname(dirname(dirname(__FILE__))));
+
+    if(isset($conf['gpc.script.minify'])) self::setMinifiedState($conf['gpc.script.minify']);
+
+    if((isset($conf['gpc.markup.bb']) && $conf['gpc.markup.bb']) ||
+       (isset($conf['gpc.markup.var']) && $conf['gpc.markup.var']) ||
+       (isset($conf['gpc.markup.form']) && $conf['gpc.markup.form'])
+      )
+    {
+      add_event_handler('render_category_name', array('GPCCore', 'applyMarkups'), EVENT_HANDLER_PRIORITY_NEUTRAL+5);
+      add_event_handler('render_category_description', array('GPCCore', 'applyMarkups'), EVENT_HANDLER_PRIORITY_NEUTRAL+5, 2);
+      add_event_handler('render_element_description', array('GPCCore', 'applyMarkups'), EVENT_HANDLER_PRIORITY_NEUTRAL+5);
+      add_event_handler('AP_render_content', array('GPCCore', 'applyMarkups'), EVENT_HANDLER_PRIORITY_NEUTRAL+5);
+    }
   }
 
@@ -108,5 +127,5 @@
         Array('name' => "GPCAjax", 'version' => "3.0.0"),
         Array('name' => "GPCCategorySelector", 'version' => "1.0.1"),
-        Array('name' => "GPCCore", 'version' => "1.4.0"),
+        Array('name' => "GPCCore", 'version' => "1.4.1"),
         Array('name' => "GPCCss", 'version' => "3.1.0"),
         Array('name' => "GPCPagesNavigation", 'version' => "2.0.0"),
@@ -362,4 +381,146 @@
 
     return(preg_replace($patterns, $replacements, $text));
+  }
+
+  /**
+   * apply [var] tag
+   *
+   * [var=<name>]
+   * with <name> :
+   *  - USER
+   *  - GALLERY_TITLE
+   *  - NB_PHOTOS
+   *  - CATEGORY
+   *  - TOKEN
+   *  - IP
+   *
+   * @param String $text : text to convert
+   * @return String : processed text
+   */
+  static public function VarToHTML($text)
+  {
+    global $user, $page, $conf;
+
+    $patterns = Array(
+      '/\[var=user\]/im',
+      '/\[var=gallery_title\]/im',
+      '/\[var=nb_photos\]/im',
+      '/\[var=category\]/im',
+      '/\[var=token\]/im',
+      '/\[var=ip\]/im'
+    );
+    $replacements = Array(
+      isset($user['username'])?$user['username']:'',
+      isset($conf['gallery_title'])?$conf['gallery_title']:'',
+      isset($user['nb_total_images'])?$user['nb_total_images']:'',
+      isset($page['category']['name'])?$page['category']['name']:'',
+      get_pwg_token(),
+      $_SERVER['REMOTE_ADDR']
+    );
+
+    return(preg_replace($patterns, $replacements, $text));
+  }
+
+  /**
+   * apply [form_mail] tag
+   *
+   * @param String $text : text to convert
+   * @return String : processed text
+   */
+  static public function FormMailToHTML($text)
+  {
+    global $template;
+
+    $file=GPCCore::getPiwigoSystemPath().'/'.PWG_LOCAL_DIR.'templates/GPCFormMsg.tpl';
+    if(!file_exists($file)) $file=dirname(dirname(__FILE__))."/templates/GPCFormMsg.tpl";
+
+    $template->set_filename('gpc_form', $file);
+
+    $template->assign('token', get_pwg_token() );
+
+    $patterns = Array(
+      '/\[form_mail\]/im'
+    );
+    $replacements = Array(
+      $template->parse('gpc_form', true)
+    );
+
+    if(preg_match($patterns[0], $text)>0)
+    {
+      GPCCore::addHeaderJS('gpc.markup.formMail', GPC_PATH.'js/markup.formMail'.self::$minified.'.js', array('jquery'));
+      return(preg_replace($patterns, $replacements, $text));
+    }
+    return($text);
+  }
+
+  /**
+   * apply [tab], [/tab] and [tabs] tags
+   *
+   * @param String $text : text to convert
+   * @return String : processed text
+   */
+  static public function TabsToHTML($text)
+  {
+    $result=array();
+
+    $tabs='';
+    if(preg_match_all('/\[tab=([^(;\]).]*)(?:;(default))?;([^\].]*)\]/im', $text, $result, PREG_SET_ORDER)>0)
+    {
+      foreach($result as $val)
+      {
+        $tabs.="<li class='gpcTabSeparator'><a id='iGpcTab".$val[1]."' class='".($val[2]=='default'?'gpcTabSelected':'gpcTabNotSelected')."' tabId='#iGpcTabContent".$val[1]."'>".$val[3]."</a></li>";
+      }
+      $tabs="<div id='iGpcTabs'><ul>".$tabs."</ul></div>";
+    }
+    else return($text);
+
+    $patterns = Array(
+      '/\[tabs\]/im',
+      '/\[tab=([^(;\]).]*)(?!;default);.*\]/im',
+      '/\[tab=([^(;\]).]*);default;(.*)\]/im',
+      '/\[\/tab\]/im'
+    );
+    $replacements = Array(
+      $tabs,
+      '<div id="iGpcTabContent\1" class="gpcTabContent" style="display:none;">',
+      '<div id="iGpcTabContent\1" class="gpcTabContent">',
+      '</div>'
+    );
+
+    if(preg_match($patterns[0], $text)>0)
+    {
+      GPCCore::addHeaderJS('gpc.markup.tabs', GPC_PATH.'js/markup.tabs'.self::$minified.'.js', array('jquery'));
+      GPCCore::addHeaderCSS('gpc.markup.tabs', GPC_PATH.'css/gpcTabs.css');
+      return(preg_replace($patterns, $replacements, $text));
+    }
+    return($text);
+  }
+
+  static public function applyMarkups($text)
+  {
+    global $conf;
+
+    if(isset($conf['gpc.markup.form']) && $conf['gpc.markup.form'])
+    {
+      $text=GPCCore::FormMailToHTML($text);
+    }
+
+    if(isset($conf['gpc.markup.tabs']) && $conf['gpc.markup.tabs'])
+    {
+      $text=GPCCore::TabsToHTML($text);
+    }
+
+    if(isset($conf['gpc.markup.var']) && $conf['gpc.markup.var'])
+    {
+      $text=GPCCore::VarToHTML($text);
+    }
+
+    if(isset($conf['gpc.markup.bb']) && $conf['gpc.markup.bb'])
+    {
+      $text=GPCCore::BBtoHTML($text);
+    }
+
+
+    return($text);
   }
 
Index: extensions/GrumPluginClasses/css/gpcTabs.css
===================================================================
--- extensions/GrumPluginClasses/css/gpcTabs.css	(revision 12215)
+++ extensions/GrumPluginClasses/css/gpcTabs.css	(revision 12215)
@@ -0,0 +1,26 @@
+
+div#iGpcTabs ul {
+  padding:0;
+}
+div#iGpcTabs ul li {
+  list-style:none;
+  float:left;
+  margin-right: 10px;
+  padding-right: 10px;
+}
+div#iGpcTabs ul li.gpcSeparator {
+  border-right:1px solid;
+}
+div#iGpcTabs ul li a {
+  font-weight:bold;
+  cursor:pointer;
+}
+div#iGpcTabs ul li a.gpcTabSelected {
+}
+
+div#iGpcTabsContent {
+  position:relative;
+  top:40px;
+  clear:both;
+  padding-bottom: 30px;
+}
Index: extensions/GrumPluginClasses/gpc_ajax.php
===================================================================
--- extensions/GrumPluginClasses/gpc_ajax.php	(revision 11529)
+++ extensions/GrumPluginClasses/gpc_ajax.php	(revision 12215)
@@ -25,4 +25,5 @@
  *  - public.tagSelector.get
  *  - admin.tagSelector.get
+ *  - public.contact.sendMsg
  *
  *
@@ -31,4 +32,5 @@
 
   define('PHPWG_ROOT_PATH',dirname(dirname(dirname(__FILE__))).'/');
+
 
   /*
@@ -43,4 +45,5 @@
   // the common.inc.php file loads all the main.inc.php plugins files
   include_once(PHPWG_ROOT_PATH.'include/common.inc.php' );
+  include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php' );
   include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/CommonPlugin.class.inc.php');
   include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/GPCAjax.class.inc.php');
@@ -98,5 +101,6 @@
            $_REQUEST['ajaxfct']=='public.categorySelector.getList' or
            $_REQUEST['ajaxfct']=='public.tagSelector.get' or
-           $_REQUEST['ajaxfct']=='admin.tagSelector.get'
+           $_REQUEST['ajaxfct']=='admin.tagSelector.get' or
+           $_REQUEST['ajaxfct']=='public.contact.sendMsg'
           )
         ) $_REQUEST['ajaxfct']='';
@@ -192,4 +196,17 @@
         }
 
+        /*
+         * check public.contact.sendMsg
+         */
+        if($_REQUEST['ajaxfct']=="public.contact.sendMsg")
+        {
+          if(!isset($_REQUEST['email'])) $_REQUEST['email']='';
+          if(!isset($_REQUEST['subject'])) $_REQUEST['subject']='';
+          if(!isset($_REQUEST['msg'])) $_REQUEST['msg']='';
+          if(!isset($_REQUEST['token'])) $_REQUEST['token']='';
+
+          if($_REQUEST['token']!=get_pwg_token()) $_REQUEST['ajaxfct']='';
+        }
+
       }
     } //checkRequest()
@@ -225,8 +242,22 @@
           $result=$this->ajax_gpc_both_TagSelectorGet('public', $_REQUEST['letters'], $_REQUEST['filter'], $_REQUEST['maxTags'], $_REQUEST['ignoreCase']);
           break;
+        case 'public.contact.sendMsg':
+          $result=$this->ajax_gpc_public_contactSendMsg($_REQUEST['email'], $_REQUEST['subject'], $_REQUEST['msg']);
+          break;
       }
       GPCAjax::returnResult($result);
     }
 
+
+    /**
+     * check validity of an email address
+     *
+     * @param String $email : email to check
+     * @returned Boolean :
+     */
+    private function emailAdressValid($email)
+    {
+      return(preg_match('#^[_a-z0-9\.\-]*[_a-z0-9\-]+@[_a-z0-9\.\-]+\.[a-z0-9\-]{2,}$#im', $email)>0);
+    }
 
 
@@ -454,4 +485,90 @@
 
 
+    /**
+     *
+     *
+     * @param String $email :
+     * @param String $subject :
+     * @param String $msg :
+     * @param Integer $token :
+     * @return String : json string
+     */
+    private function ajax_gpc_public_contactSendMsg($email, $subject, $msg)
+    {
+      global $user, $conf;
+
+      $returned=array('result' => false, 'msg' => '');
+
+      if($email==null or trim($email)=='')
+      {
+        $returned['msg']=l10n('Email is mandatory');
+        return(json_encode($returned));
+      }
+
+      if(!$this->emailAdressValid($email))
+      {
+        $returned['msg']=l10n('Email is not valid');
+        return(json_encode($returned));
+      }
+
+      if($subject==null or trim($subject)=='')
+      {
+        $returned['msg']=l10n('Subject is mandatory');
+        return(json_encode($returned));
+      }
+
+      if($msg==null or trim($msg)=='')
+      {
+        $returned['msg']=l10n('Message is mandatory');
+        return(json_encode($returned));
+      }
+
+
+
+      $admins=array();
+      $sql="SELECT put.".$conf['user_fields']['username']." AS username,
+                   put.".$conf['user_fields']['email']." AS mail_address
+            FROM ".USERS_TABLE." AS put
+              JOIN ".USER_INFOS_TABLE." AS puit
+                ON puit.user_id =  put.".$conf['user_fields']['id']."
+            WHERE puit.status IN ('webmaster',  'admin')
+              AND ".$conf['user_fields']['email']." IS NOT NULL
+              AND puit.user_id <> ".$user['id']."
+            ORDER BY username;";
+
+      $result = pwg_query($sql);
+      if($result)
+      {
+        while ($row = pwg_db_fetch_assoc($result))
+        {
+          if(!empty($row['mail_address']))
+          {
+            array_push($admins, format_email($row['username'], $row['mail_address']));
+          }
+        }
+      }
+
+      $args=array(
+        'subject' => sprintf(l10n('[%s][Message from %s] %s'), $conf['gallery_title'], $email, $subject),
+        'content' => sprintf("[%s]\n%s\n%s\n--------\n%s", $_SERVER['REMOTE_ADDR'], $email, $subject, stripslashes($msg))
+      );
+
+      $send=pwg_mail(implode(',', $admins), $args);
+
+      if(!$send)
+      {
+        $returned['msg']=l10n('Sorry, an error has occured while sending the message to the webmaster');
+      }
+      else
+      {
+        $returned['result']=true;
+        $returned['msg']=l10n('Your message was sent to the webmaster!');
+      }
+
+      return(json_encode($returned));
+
+    }
+
+
   } //class
 
Index: extensions/GrumPluginClasses/gpc_version.inc.php
===================================================================
--- extensions/GrumPluginClasses/gpc_version.inc.php	(revision 11529)
+++ extensions/GrumPluginClasses/gpc_version.inc.php	(revision 12215)
@@ -15,5 +15,5 @@
   if(!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
 
-  if(!defined('GPC_VERSION')) define('GPC_VERSION',  '3.5.1');
-  if(!defined('GPC_VERSION2')) define('GPC_VERSION2', '03.05.01');
+  if(!defined('GPC_VERSION')) define('GPC_VERSION',  '3.5.2');
+  if(!defined('GPC_VERSION2')) define('GPC_VERSION2', '03.05.02');
 ?>
Index: extensions/GrumPluginClasses/js/markup.formMail.js
===================================================================
--- extensions/GrumPluginClasses/js/markup.formMail.js	(revision 12215)
+++ extensions/GrumPluginClasses/js/markup.formMail.js	(revision 12215)
@@ -0,0 +1,58 @@
+/**
+ * -----------------------------------------------------------------------------
+ * file: markup.formMail.js
+ * file version: 1.0.0
+ * date: 2011-09-20
+ *
+ * JS file provided by the piwigo's plugin "GrumPluginClasses"
+ *
+ * -----------------------------------------------------------------------------
+ * Author     : Grum
+ *   email    : grum@piwigo.com
+ *   website  : http://photos.grum.fr
+ *   PWG user : http://forum.phpwebgallery.net/profile.php?id=3706
+ *
+ *   << May the Little SpaceFrog be with you ! >>
+ * -----------------------------------------------------------------------------
+ */
+$(window).bind('load',
+  function ()
+  {
+
+    $('#iSubmit').bind('click',
+      function ()
+      {
+        var data = {
+              email:$('#iEmail').val(),
+              subject:$('#iSubject').val(),
+              msg:$('#iMessageContent').val(),
+              token:$('#iToken').val(),
+              ajaxfct:"public.contact.sendMsg"
+            };
+
+        $.ajax(
+          {
+            type: "POST",
+            url: "plugins/GrumPluginClasses/gpc_ajax.php",
+            async: true,
+            data: data,
+            success:
+              function(msg)
+              {
+                result=$.parseJSON(msg);
+                if(result.result)
+                {
+                  $('#iGpcFormMail').html(result.msg);
+                }
+                else
+                {
+                  alert(result.msg);
+                }
+              }
+          }
+        );
+      }
+    );
+
+  }
+);
Index: extensions/GrumPluginClasses/js/markup.formMail.min.js
===================================================================
--- extensions/GrumPluginClasses/js/markup.formMail.min.js	(revision 12215)
+++ extensions/GrumPluginClasses/js/markup.formMail.min.js	(revision 12215)
@@ -0,0 +1,2 @@
+/* file: markup.formMail.js - v1.0.0 | minified on 2011/09/20 with http://jscompress.com/ */
+$(window).bind("load",function(){$("#iSubmit").bind("click",function(){var a={email:$("#iEmail").val(),subject:$("#iSubject").val(),msg:$("#iMessageContent").val(),token:$("#iToken").val(),ajaxfct:"public.contact.sendMsg"};$.ajax({type:"POST",url:"plugins/GrumPluginClasses/gpc_ajax.php",async:true,data:a,success:function(a){result=$.parseJSON(a);if(result.result){$("#iGpcFormMail").html(result.msg)}else{alert(result.msg)}}})})})
Index: extensions/GrumPluginClasses/js/markup.tabs.js
===================================================================
--- extensions/GrumPluginClasses/js/markup.tabs.js	(revision 12215)
+++ extensions/GrumPluginClasses/js/markup.tabs.js	(revision 12215)
@@ -0,0 +1,35 @@
+/**
+ * -----------------------------------------------------------------------------
+ * file: markup.tabs.js
+ * file version: 1.0.0
+ * date: 2011-09-21
+ *
+ * JS file provided by the piwigo's plugin "GrumPluginClasses"
+ *
+ * -----------------------------------------------------------------------------
+ * Author     : Grum
+ *   email    : grum@piwigo.com
+ *   website  : http://photos.grum.fr
+ *   PWG user : http://forum.phpwebgallery.net/profile.php?id=3706
+ *
+ *   << May the Little SpaceFrog be with you ! >>
+ * -----------------------------------------------------------------------------
+ */
+ $(window).bind('load',
+  function ()
+  {
+    $('#iGpcTabs a').bind('click',
+      function ()
+      {
+        if($(this).hasClass('gpcTabSelected')) return(false);
+
+        $('#iGpcTabs a.gpcTabSelected').removeClass('gpcTabSelected').addClass('gpcTabNotSelected');
+        $(this).removeClass('gpcTabNotSelected').addClass('gpcTabSelected');
+
+        $('div.gpcTabContent').css('display', 'none');
+
+        $($(this).attr('tabId')).css('display', 'block');
+      }
+    );
+  }
+);
Index: extensions/GrumPluginClasses/js/markup.tabs.min.js
===================================================================
--- extensions/GrumPluginClasses/js/markup.tabs.min.js	(revision 12215)
+++ extensions/GrumPluginClasses/js/markup.tabs.min.js	(revision 12215)
@@ -0,0 +1,2 @@
+/* file: markup.tabs.js - v1.0.0 | minified on 2011/09/21 with http://jscompress.com/ */
+$(window).bind("load",function(){$("#iGpcTabs a").bind("click",function(){if($(this).hasClass("gpcTabSelected"))return false;$("#iGpcTabs a.gpcTabSelected").removeClass("gpcTabSelected").addClass("gpcTabNotSelected");$(this).removeClass("gpcTabNotSelected").addClass("gpcTabSelected");$("div.gpcTabContent").css("display","none");$($(this).attr("tabId")).css("display","block")})})
Index: extensions/GrumPluginClasses/js/ui.inputConsole.js
===================================================================
--- extensions/GrumPluginClasses/js/ui.inputConsole.js	(revision 11529)
+++ extensions/GrumPluginClasses/js/ui.inputConsole.js	(revision 12215)
@@ -2,5 +2,5 @@
  * -----------------------------------------------------------------------------
  * file: ui.inputConsole.js
- * file version: 1.0.0
+ * file version: 1.0.1
  * date: 2010-11-05
  *
@@ -58,5 +58,7 @@
                       historyHeight:60,
                       change:null,
-                      submit:null
+                      submit:null,
+                      submited:null,
+                      focusChanged:null
                     };
 
@@ -76,5 +78,6 @@
                     mouseIsOver:false,
                     historyIsVisible:false,
-                    inputMargins:0
+                    inputMargins:0,
+                    focus:false
                   }
                 );
@@ -153,4 +156,14 @@
                       }
                     ),
+                    historyBackground:$('<div/>',
+                      {
+                        'class':'ui-inputConsole-historyBg'
+                      }
+                    ),
+                    historyListContainer:$('<div/>',
+                      {
+                        'class':'ui-inputConsole-historyListContainer'
+                      }
+                    ),
                     historyList:$('<ul/>')
 
@@ -162,5 +175,7 @@
                     objects.container
                     .append(
-                      objects.historyContainer.append(objects.historyList)
+                      objects.historyContainer
+                        .append(objects.historyBackground)
+                        .append(objects.historyListContainer.append(objects.historyList))
                     )
                     .append(
@@ -334,5 +349,5 @@
         }, // value
 
-      history: function (value)
+      history: function (value, param)
         {
           var objects=this.data('objects');
@@ -343,7 +358,12 @@
             return this.each(function()
               {
-                if(value=='clear')
+                switch(value)
                 {
-                  objects.historyList.html('');
+                  case 'clear':
+                    objects.historyList.html('');
+                    break;
+                  case 'addResult':
+                    privateMethods.updateHistoryResult($(this), param);
+                    break;
                 }
               }
@@ -383,4 +403,23 @@
         }, // isValid
 
+      focus: function (value)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return this.each(function()
+              {
+                privateMethods.setFocus($(this), value);
+              }
+            );
+          }
+          else
+          {
+            // return the selected tags
+            var properties=this.data('properties');
+            return(properties.focus);
+          }
+        }, // isValid
+
       change: function (value)
         {
@@ -436,5 +475,60 @@
             }
           }
-        } // submit
+        }, // submit
+
+      submited: function (value)
+        {
+          if(value!=null && $.isFunction(value))
+          {
+            // set selected value
+            return this.each(function()
+              {
+                privateMethods.setEventSubmited($(this), value);
+              }
+            );
+          }
+          else
+          {
+            // return the selected value
+            var options=this.data('options');
+
+            if(options)
+            {
+              return(options.submited);
+            }
+            else
+            {
+              return(null);
+            }
+          }
+        }, // submited
+
+      focusChanged: function (value)
+        {
+          if(value!=null && $.isFunction(value))
+          {
+            // set selected value
+            return this.each(function()
+              {
+                privateMethods.setEventFocusChanged($(this), value);
+              }
+            );
+          }
+          else
+          {
+            // return the selected value
+            var options=this.data('options');
+
+            if(options)
+            {
+              return(options.focusChanged);
+            }
+            else
+            {
+              return(null);
+            }
+          }
+        }, // focusChanged
+
 
 
@@ -464,4 +558,6 @@
           privateMethods.setEventChange(object, (value.change!=null)?value.change:options.change);
           privateMethods.setEventSubmit(object, (value.submit!=null)?value.submit:options.submit);
+          privateMethods.setEventSubmited(object, (value.submited!=null)?value.submited:options.submited);
+          privateMethods.setEventFocusChanged(object, (value.focusChanged!=null)?value.focusChanged:options.focusChanged);
 
           properties.initialized=true;
@@ -566,17 +662,49 @@
 
 
+      setFocus : function (object, value)
+        {
+          var objects=object.data('objects'),
+              options=object.data('options'),
+              properties=object.data('properties');
+
+          if(value===true||value===false)
+          {
+            if(value)
+            {
+              objects.input.focus();
+            }
+            else
+            {
+              objects.input.blur();
+            }
+
+            properties.focus=value;
+            if(options.focusChanged) object.trigger('inputConsoleFocusChanged', properties.focus);
+          }
+
+          return(properties.focus);
+        },
+
       getFocus : function (object)
         {
-          var objects=object.data('objects');
-
+          var objects=object.data('objects'),
+              options=object.data('options'),
+              properties=object.data('properties');
+
+          properties.focus=true;
           objects.historyContainer.css('display', 'block');
           privateMethods.setObjectsWidth(object);
+          if(options.focusChanged) object.trigger('inputConsoleFocusChanged', properties.focus);
         },
 
       lostFocus : function (object)
         {
-          var objects=object.data('objects');
-
+          var objects=object.data('objects'),
+              options=object.data('options'),
+              properties=object.data('properties');
+
+          properties.focus=false;
           objects.historyContainer.css('display', 'none');
+          if(options.focusChanged) object.trigger('inputConsoleFocusChanged', properties.focus);
         },
 
@@ -601,4 +729,24 @@
         },
 
+      setEventSubmited : function (object, value)
+        {
+          var options=object.data('options');
+
+          options.submited=value;
+          object.unbind('inputConsoleSubmited');
+          if(value) object.bind('inputConsoleSubmited', options.submited);
+          return(options.submited);
+        },
+
+      setEventFocusChanged : function (object, value)
+        {
+          var options=object.data('options');
+
+          options.focusChanged=value;
+          object.unbind('inputConsoleFocusChanged');
+          if(value) object.bind('inputConsoleFocusChanged', options.focusChanged);
+          return(options.focusChanged);
+        },
+
       keyUp : function (object, event)
         {
@@ -612,4 +760,5 @@
             if(options.submit) object.trigger('inputConsoleSubmit', properties.value);
             privateMethods.updateHistory(object, properties.value);
+            if(options.submited) object.trigger('inputConsoleSubmited', properties.value);
             privateMethods.setValue(object, '', true);
           }
@@ -625,5 +774,14 @@
               objects=object.data('objects');
 
-          if(item!='' && item!=null) objects.historyList.append($('<li/>', { html: item }));
+          if(item!='' && item!=null)
+            objects.historyList.append(
+              $('<li/>', { html: '<span class="ui-inputConsole-historyCmd">'+item+'</span>' } )
+                .bind('click', object,
+                  function (event)
+                    {
+                      privateMethods.setValue(event.data, $(this).children('.ui-inputConsole-historyCmd').html(), true);
+                    }
+                )
+            );
 
           while(objects.historyList.children().length>options.historySize)
@@ -632,4 +790,19 @@
           }
           objects.historyContainer.scrollTop(objects.historyList.height());
+        },
+
+      updateHistoryResult : function (object, item)
+        {
+          var options=object.data('options'),
+              objects=object.data('objects');
+
+          if(item!='' && item!=null)
+          {
+            objects.historyList.children(':last').html(
+              objects.historyList.children(':last').html() + "<span class='ui-inputConsole-historyResult'>"+item+"</span>"
+            );
+          }
+
+          objects.historyListContainer.scrollTop(objects.historyList.height());
         },
 
@@ -674,4 +847,2 @@
   }
 )(jQuery);
-
-
Index: extensions/GrumPluginClasses/js/ui.inputConsole.min.js
===================================================================
--- extensions/GrumPluginClasses/js/ui.inputConsole.min.js	(revision 11529)
+++ extensions/GrumPluginClasses/js/ui.inputConsole.min.js	(revision 12215)
@@ -1,125 +1,2 @@
-/* file: ui.inputColorsFB.js - v1.0.0 | minified on 2011/01/27 with http://jscompress.com/ */
-(function($)
-{var publicMethods={init:function(opt)
-{return this.each(function()
-{var $this=$(this),data=$this.data('options'),objects=$this.data('objects'),properties=$this.data('properties'),options={disabled:false,prompt:'>',historySize:8,historyHeight:60,change:null,submit:null};$this.data('options',options);if(!properties)
-{$this.data('properties',{initialized:false,value:'',isValid:true,mouseIsOver:false,historyIsVisible:false,inputMargins:0});properties=$this.data('properties');}
-if(!objects)
-{objects={container:$('<div/>',{'class':'ui-inputConsole',css:{width:'100%'}}).bind('click.inputConsole',function()
-{objects.input.focus();}).bind('mouseenter',function()
-{properties.mouseIsOver=true;}).bind('mouseleave',function()
-{properties.mouseIsOver=false;}),inputContainer:$('<div/>',{'class':'ui-inputConsole-input'}),input:$('<input>',{type:"text",value:''}).bind('focusout.inputConsole',function()
-{privateMethods.lostFocus($this);}).bind('focus.inputConsole',function()
-{privateMethods.getFocus($this);}).bind('keyup.inputConsole',function(event)
-{privateMethods.keyUp($this,event);}),prompt:$('<div/>',{html:options.prompt,'class':'ui-inputConsole-prompt'}),historyContainer:$('<div/>',{'class':'ui-inputConsole-history',css:{display:'none'}}),historyList:$('<ul/>')};$this.html('').append(objects.container.append(objects.historyContainer.append(objects.historyList)).append(objects.inputContainer.append(objects.prompt).append(objects.input))).bind('resize.inputConsole',function()
-{privateMethods.setObjectsWidth($this);});properties.inputMargins=objects.input.outerWidth(true)-objects.input.width();$this.data('objects',objects);}
-privateMethods.setOptions($this,opt);});},destroy:function()
-{return this.each(function()
-{var $this=$(this),objects=$this.data('objects');objects.input.unbind().remove();objects.container.unbind().remove();$this.unbind('.inputConsole').css({width:'',height:''});});},options:function(value)
-{return this.each(function()
-{privateMethods.setOptions($(this),value);});},disabled:function(value)
-{if(value!=null)
-{return this.each(function()
-{privateMethods.setDisabled($(this),value);});}
-else
-{var options=this.data('options');if(options)
-{return(options.disabled);}
-else
-{return('');}}},prompt:function(value)
-{if(value!=null)
-{return this.each(function()
-{privateMethods.setPrompt($(this),value);});}
-else
-{var options=this.data('options');if(options)
-{return(options.prompt);}
-else
-{return('');}}},historySize:function(value)
-{if(value!=null)
-{return this.each(function()
-{privateMethods.setHistorySize($(this),value);});}
-else
-{var options=this.data('options');if(options)
-{return(options.historySize);}
-else
-{return('');}}},historyHeight:function(value)
-{if(value!=null)
-{return this.each(function()
-{privateMethods.setHistoryHeight($(this),value);});}
-else
-{var options=this.data('options');if(options)
-{return(options.historyHeight);}
-else
-{return('');}}},value:function(value)
-{if(value!=null)
-{return this.each(function()
-{privateMethods.setValue($(this),value,true);});}
-else
-{var properties=this.data('properties');return(properties.value);}},history:function(value)
-{var objects=this.data('objects');if(value!=null)
-{return this.each(function()
-{if(value=='clear')
-{objects.historyList.html('');}});}
-else
-{var returned=[];objects.historyList.children().each(function(index,item)
-{returned.push($(item).text());});return(returned);}},isValid:function(value)
-{if(value!=null)
-{return this.each(function()
-{privateMethods.setIsValid($(this),value);});}
-else
-{var properties=this.data('properties');return(properties.isValid);}},change:function(value)
-{if(value!=null&&$.isFunction(value))
-{return this.each(function()
-{privateMethods.setEventChange($(this),value);});}
-else
-{var options=this.data('options');if(options)
-{return(options.change);}
-else
-{return(null);}}},submit:function(value)
-{if(value!=null&&$.isFunction(value))
-{return this.each(function()
-{privateMethods.setEventSubmit($(this),value);});}
-else
-{var options=this.data('options');if(options)
-{return(options.submit);}
-else
-{return(null);}}}};var privateMethods={setOptions:function(object,value)
-{var properties=object.data('properties'),options=object.data('options');if(!$.isPlainObject(value))return(false);properties.initialized=false;privateMethods.setHistoryHeight(object,(value.historyHeight!=null)?value.historyHeight:options.historyHeight);privateMethods.setHistorySize(object,(value.historySize!=null)?value.historySize:options.historySize);privateMethods.setPrompt(object,(value.prompt!=null)?value.prompt:options.prompt);privateMethods.setValue(object,(value.value!=null)?value.value:options.value,true);privateMethods.setDisabled(object,(value.disabled!=null)?value.disabled:options.disabled);privateMethods.setEventChange(object,(value.change!=null)?value.change:options.change);privateMethods.setEventSubmit(object,(value.submit!=null)?value.submit:options.submit);properties.initialized=true;},setPrompt:function(object,value)
-{var objects=object.data('objects'),options=object.data('options'),properties=object.data('properties');if(!properties.initialized||options.prompt!=value)
-{options.prompt=value;objects.prompt.html(options.prompt);privateMethods.setObjectsWidth(object);}
-return(options.prompt);},setHistorySize:function(object,value)
-{var options=object.data('options'),properties=object.data('properties');if(!properties.initialized||options.historySize!=value)
-{options.historySize=value;privateMethods.updateHistory(object,null);}
-return(options.historySize);},setHistoryHeight:function(object,value)
-{var objects=object.data('objects'),options=object.data('options'),properties=object.data('properties');if(!properties.initialized||options.historyHeight!=value)
-{options.historyHeight=value;objects.historyContainer.css({height:options.historyHeight+'px','margin-top':(-options.historyHeight)+'px'});}
-return(options.historyHeight);},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');objects.input.removeClass('ui-error');}
-else
-{objects.container.addClass('ui-error');objects.input.addClass('ui-error');}}
-return(properties.isValid);},setDisabled:function(object,value)
-{var options=object.data('options'),properties=object.data('properties');if((!properties.initialized||options.disabled!=value)&&(value==true||value==false))
-{options.disabled=value;}
-return(options.disabled);},setValue:function(object,value,apply)
-{var options=object.data('options'),properties=object.data('properties'),objects=object.data('objects');properties.value=value;if(apply)objects.input.val(properties.value);if(options.change)object.trigger('inputConsoleChange',properties.value);return(true);},getFocus:function(object)
-{var objects=object.data('objects');objects.historyContainer.css('display','block');privateMethods.setObjectsWidth(object);},lostFocus:function(object)
-{var objects=object.data('objects');objects.historyContainer.css('display','none');},setEventChange:function(object,value)
-{var options=object.data('options');options.change=value;object.unbind('inputConsoleChange');if(value)object.bind('inputConsoleChange',options.change);return(options.change);},setEventSubmit:function(object,value)
-{var options=object.data('options');options.submit=value;object.unbind('inputConsoleSubmit');if(value)object.bind('inputConsoleSubmit',options.submit);return(options.submit);},keyUp:function(object,event)
-{var properties=object.data('properties'),options=object.data('options'),objects=object.data('objects');if(event.keyCode==13&&properties.isValid)
-{if(options.submit)object.trigger('inputConsoleSubmit',properties.value);privateMethods.updateHistory(object,properties.value);privateMethods.setValue(object,'',true);}
-else
-{privateMethods.setValue(object,objects.input.val(),false);}},updateHistory:function(object,item)
-{var options=object.data('options'),objects=object.data('objects');if(item!=''&&item!=null)objects.historyList.append($('<li/>',{html:item}));while(objects.historyList.children().length>options.historySize)
-{objects.historyList.children(':first').remove();}
-objects.historyContainer.scrollTop(objects.historyList.height());},setObjectsWidth:function(object)
-{var objects=object.data('objects')
-properties=object.data('properties');if(objects.inputContainer.width()>0)
-{objects.input.css('width',(objects.inputContainer.innerWidth()-objects.prompt.outerWidth(true)-properties.inputMargins)+'px');objects.historyContainer.css({width:objects.inputContainer.innerWidth()+'px','margin-left':((objects.historyContainer.width()-objects.historyContainer.outerWidth())/2)+'px'});}}};$.fn.inputConsole=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.inputConsole');}}})(jQuery);
+/* file: ui.inputConsole.js - v1.0.1 | minified on 2011/09/21 with http://jscompress.com/ */
+(function(a){var b={init:function(b){return this.each(function(){var d=a(this),e=d.data("options"),f=d.data("objects"),g=d.data("properties"),h={disabled:false,prompt:">",historySize:8,historyHeight:60,change:null,submit:null,submited:null,focusChanged:null};d.data("options",h);if(!g){d.data("properties",{initialized:false,value:"",isValid:true,mouseIsOver:false,historyIsVisible:false,inputMargins:0,focus:false});g=d.data("properties")}if(!f){f={container:a("<div/>",{"class":"ui-inputConsole",css:{width:"100%"}}).bind("click.inputConsole",function(){f.input.focus()}).bind("mouseenter",function(){g.mouseIsOver=true}).bind("mouseleave",function(){g.mouseIsOver=false}),inputContainer:a("<div/>",{"class":"ui-inputConsole-input"}),input:a("<input>",{type:"text",value:""}).bind("focusout.inputConsole",function(){c.lostFocus(d)}).bind("focus.inputConsole",function(){c.getFocus(d)}).bind("keyup.inputConsole",function(a){c.keyUp(d,a)}),prompt:a("<div/>",{html:h.prompt,"class":"ui-inputConsole-prompt"}),historyContainer:a("<div/>",{"class":"ui-inputConsole-history",css:{display:"none"}}),historyBackground:a("<div/>",{"class":"ui-inputConsole-historyBg"}),historyListContainer:a("<div/>",{"class":"ui-inputConsole-historyListContainer"}),historyList:a("<ul/>")};d.html("").append(f.container.append(f.historyContainer.append(f.historyBackground).append(f.historyListContainer.append(f.historyList))).append(f.inputContainer.append(f.prompt).append(f.input))).bind("resize.inputConsole",function(){c.setObjectsWidth(d)});g.inputMargins=f.input.outerWidth(true)-f.input.width();d.data("objects",f)}c.setOptions(d,b)})},destroy:function(){return this.each(function(){var b=a(this),c=b.data("objects");c.input.unbind().remove();c.container.unbind().remove();b.unbind(".inputConsole").css({width:"",height:""})})},options:function(b){return this.each(function(){c.setOptions(a(this),b)})},disabled:function(b){if(b!=null){return this.each(function(){c.setDisabled(a(this),b)})}else{var d=this.data("options");if(d){return d.disabled}else{return""}}},prompt:function(b){if(b!=null){return this.each(function(){c.setPrompt(a(this),b)})}else{var d=this.data("options");if(d){return d.prompt}else{return""}}},historySize:function(b){if(b!=null){return this.each(function(){c.setHistorySize(a(this),b)})}else{var d=this.data("options");if(d){return d.historySize}else{return""}}},historyHeight:function(b){if(b!=null){return this.each(function(){c.setHistoryHeight(a(this),b)})}else{var d=this.data("options");if(d){return d.historyHeight}else{return""}}},value:function(b){if(b!=null){return this.each(function(){c.setValue(a(this),b,true)})}else{var d=this.data("properties");return d.value}},history:function(b,d){var e=this.data("objects");if(b!=null){return this.each(function(){switch(b){case"clear":e.historyList.html("");break;case"addResult":c.updateHistoryResult(a(this),d);break}})}else{var f=[];e.historyList.children().each(function(b,c){f.push(a(c).text())});return f}},isValid:function(b){if(b!=null){return this.each(function(){c.setIsValid(a(this),b)})}else{var d=this.data("properties");return d.isValid}},focus:function(b){if(b!=null){return this.each(function(){c.setFocus(a(this),b)})}else{var d=this.data("properties");return d.focus}},change:function(b){if(b!=null&&a.isFunction(b)){return this.each(function(){c.setEventChange(a(this),b)})}else{var d=this.data("options");if(d){return d.change}else{return null}}},submit:function(b){if(b!=null&&a.isFunction(b)){return this.each(function(){c.setEventSubmit(a(this),b)})}else{var d=this.data("options");if(d){return d.submit}else{return null}}},submited:function(b){if(b!=null&&a.isFunction(b)){return this.each(function(){c.setEventSubmited(a(this),b)})}else{var d=this.data("options");if(d){return d.submited}else{return null}}},focusChanged:function(b){if(b!=null&&a.isFunction(b)){return this.each(function(){c.setEventFocusChanged(a(this),b)})}else{var d=this.data("options");if(d){return d.focusChanged}else{return null}}}};var c={setOptions:function(b,d){var e=b.data("properties"),f=b.data("options");if(!a.isPlainObject(d))return false;e.initialized=false;c.setHistoryHeight(b,d.historyHeight!=null?d.historyHeight:f.historyHeight);c.setHistorySize(b,d.historySize!=null?d.historySize:f.historySize);c.setPrompt(b,d.prompt!=null?d.prompt:f.prompt);c.setValue(b,d.value!=null?d.value:f.value,true);c.setDisabled(b,d.disabled!=null?d.disabled:f.disabled);c.setEventChange(b,d.change!=null?d.change:f.change);c.setEventSubmit(b,d.submit!=null?d.submit:f.submit);c.setEventSubmited(b,d.submited!=null?d.submited:f.submited);c.setEventFocusChanged(b,d.focusChanged!=null?d.focusChanged:f.focusChanged);e.initialized=true},setPrompt:function(a,b){var d=a.data("objects"),e=a.data("options"),f=a.data("properties");if(!f.initialized||e.prompt!=b){e.prompt=b;d.prompt.html(e.prompt);c.setObjectsWidth(a)}return e.prompt},setHistorySize:function(a,b){var d=a.data("options"),e=a.data("properties");if(!e.initialized||d.historySize!=b){d.historySize=b;c.updateHistory(a,null)}return d.historySize},setHistoryHeight:function(a,b){var c=a.data("objects"),d=a.data("options"),e=a.data("properties");if(!e.initialized||d.historyHeight!=b){d.historyHeight=b;c.historyContainer.css({height:d.historyHeight+"px","margin-top":-d.historyHeight+"px"})}return d.historyHeight},setIsValid:function(a,b){var c=a.data("objects"),d=a.data("properties");if(d.isValid!=b){d.isValid=b;if(d.isValid){c.container.removeClass("ui-error");c.input.removeClass("ui-error")}else{c.container.addClass("ui-error");c.input.addClass("ui-error")}}return d.isValid},setDisabled:function(a,b){var c=a.data("options"),d=a.data("properties");if((!d.initialized||c.disabled!=b)&&(b==true||b==false)){c.disabled=b}return c.disabled},setValue:function(a,b,c){var d=a.data("options"),e=a.data("properties"),f=a.data("objects");e.value=b;if(c)f.input.val(e.value);if(d.change)a.trigger("inputConsoleChange",e.value);return true},setFocus:function(a,b){var c=a.data("objects"),d=a.data("options"),e=a.data("properties");if(b===true||b===false){if(b){c.input.focus()}else{c.input.blur()}e.focus=b;if(d.focusChanged)a.trigger("inputConsoleFocusChanged",e.focus)}return e.focus},getFocus:function(a){var b=a.data("objects"),d=a.data("options"),e=a.data("properties");e.focus=true;b.historyContainer.css("display","block");c.setObjectsWidth(a);if(d.focusChanged)a.trigger("inputConsoleFocusChanged",e.focus)},lostFocus:function(a){var b=a.data("objects"),c=a.data("options"),d=a.data("properties");d.focus=false;b.historyContainer.css("display","none");if(c.focusChanged)a.trigger("inputConsoleFocusChanged",d.focus)},setEventChange:function(a,b){var c=a.data("options");c.change=b;a.unbind("inputConsoleChange");if(b)a.bind("inputConsoleChange",c.change);return c.change},setEventSubmit:function(a,b){var c=a.data("options");c.submit=b;a.unbind("inputConsoleSubmit");if(b)a.bind("inputConsoleSubmit",c.submit);return c.submit},setEventSubmited:function(a,b){var c=a.data("options");c.submited=b;a.unbind("inputConsoleSubmited");if(b)a.bind("inputConsoleSubmited",c.submited);return c.submited},setEventFocusChanged:function(a,b){var c=a.data("options");c.focusChanged=b;a.unbind("inputConsoleFocusChanged");if(b)a.bind("inputConsoleFocusChanged",c.focusChanged);return c.focusChanged},keyUp:function(a,b){var d=a.data("properties"),e=a.data("options"),f=a.data("objects");if(b.keyCode==13&&d.isValid){if(e.submit)a.trigger("inputConsoleSubmit",d.value);c.updateHistory(a,d.value);if(e.submited)a.trigger("inputConsoleSubmited",d.value);c.setValue(a,"",true)}else{c.setValue(a,f.input.val(),false)}},updateHistory:function(b,d){var e=b.data("options"),f=b.data("objects");if(d!=""&&d!=null)f.historyList.append(a("<li/>",{html:'<span class="ui-inputConsole-historyCmd">'+d+"</span>"}).bind("click",b,function(b){c.setValue(b.data,a(this).children(".ui-inputConsole-historyCmd").html(),true)}));while(f.historyList.children().length>e.historySize){f.historyList.children(":first").remove()}f.historyContainer.scrollTop(f.historyList.height())},updateHistoryResult:function(a,b){var c=a.data("options"),d=a.data("objects");if(b!=""&&b!=null){d.historyList.children(":last").html(d.historyList.children(":last").html()+"<span class='ui-inputConsole-historyResult'>"+b+"</span>")}d.historyListContainer.scrollTop(d.historyList.height())},setObjectsWidth:function(a){var b=a.data("objects");properties=a.data("properties");if(b.inputContainer.width()>0){b.input.css("width",b.inputContainer.innerWidth()-b.prompt.outerWidth(true)-properties.inputMargins+"px");b.historyContainer.css({width:b.inputContainer.innerWidth()+"px","margin-left":(b.historyContainer.width()-b.historyContainer.outerWidth())/2+"px"})}}};a.fn.inputConsole=function(c){if(b[c]){return b[c].apply(this,Array.prototype.slice.call(arguments,1))}else if(typeof c==="object"||!c){return b.init.apply(this,arguments)}else{a.error("Method "+c+" does not exist on jQuery.inputConsole")}}})(jQuery)
Index: extensions/GrumPluginClasses/js/ui.inputSwitchButton.js
===================================================================
--- extensions/GrumPluginClasses/js/ui.inputSwitchButton.js	(revision 12215)
+++ extensions/GrumPluginClasses/js/ui.inputSwitchButton.js	(revision 12215)
@@ -0,0 +1,393 @@
+/**
+ * -----------------------------------------------------------------------------
+ * file: ui.inputCheckbox.js
+ * file version: 1.0.0
+ * date: 2011-06-18
+ *
+ * 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.phpwebgallery.net/profile.php?id=3706
+ *
+ *   << May the Little SpaceFrog be with you ! >>
+ * -----------------------------------------------------------------------------
+ *
+ *
+ *
+ *
+ * :: HISTORY ::
+ *
+ * | release | date       |
+ * | 1.0.0   | 2011/06/18 | first release
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ * |         |            |
+ *
+ */
+
+
+
+(
+  function($)
+  {
+    /*
+     * plugin 'public' functions
+     */
+    var publicMethods =
+    {
+      init : function (opt)
+        {
+          return this.each(
+            function()
+            {
+              // default values for the plugin
+              var $this=$(this),
+                  data = $this.data('options'),
+                  objects = $this.data('objects'),
+                  properties = $this.data('properties'),
+                  options =
+                    {                      
+                      values:
+                        {
+                          checked:'yes',
+                          unchecked:'no'
+                        },
+                      change:null,
+                      group:''
+                    };
+
+              // if options given, merge it
+              // if(opt) $.extend(options, opt); ==> options are set by setters
+
+              $this
+                .data('options', options)
+                .addClass('ui-inputSwitchButton ui-inputSwitchButton-unchecked');
+
+              if(!properties)
+              {
+                $this.data('properties',
+                  {
+                    initialized:false,
+                    checked:false
+                  }
+                );
+                properties=$this.data('properties');
+              }
+
+
+              $this.bind('click.inputSwitchButton',
+                function (event)
+                {
+                  privateMethods.switchValue($this);
+                }
+              );
+
+              privateMethods.setOptions($this, opt);
+            }
+          );
+        }, // init
+      destroy : function ()
+        {
+          return this.each(
+            function()
+            {
+              // default values for the plugin
+              var properties=this.data('properties');
+              $this.unbind('.inputSwitchButton');
+              this.removeClass('ui-inputSwitchButton');
+            }
+          );
+        }, // destroy
+
+      options: function (value)
+        {
+          return(
+            this.each(
+              function()
+              {
+                privateMethods.setOptions($(this), value);
+              }
+            )
+          );
+        }, // options
+
+      disabled: function (value)
+        {
+          if(value!=null)
+          {
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setDisabled($(this), value);
+                }
+              )
+            );
+          }
+          else
+          {
+            return(privateMethods.getDisabled($(this)));
+          }
+        }, // disabled
+        
+      values: function (values)
+        {
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setValues($(this), value);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options=this.data('options');
+            // return the selected tags
+            return(options.values);
+          }
+      },
+
+      switchValue: function ()
+        {
+          return(
+            this.each(
+              function()
+              {
+                privateMethods.switchValue($(this));
+              }
+            )
+          );
+
+        }, // value
+
+      value: function (value)
+        {
+          var properties=this.data('properties');
+
+          if(value!=null)
+          {
+            // set selected value
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setValue($(this), value, true);
+                }
+              )
+            );
+          }
+          else
+          {
+            var options = this.data('options');
+            
+            return(properties.checked?options.values.checked:options.values.unchecked);
+          }
+
+        }, // value
+
+      group: function (value)
+        {
+          var options=this.data('options');
+
+          if(value!=null)
+          {
+            // set selected group
+            return(
+              this.each(
+                function()
+                {
+                  privateMethods.setGroup($(this), value);
+                }
+              )
+            );
+          }
+          else
+          {
+            return(options.group);
+          }
+
+        }, // group
+
+      change: function (value)
+        {
+          if(value!=null && $.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);
+            }
+          }
+        } // change
+
+    }; // 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.setGroup(object, (value.group!=null)?value.group:options.group, true);
+          privateMethods.setValue(object, (value.value!=null)?value.value:options.value, true);
+          privateMethods.setEventChange(object, (value.change!=null)?value.change:options.change);
+
+          properties.initialized=true;
+        },
+
+      setValues : function (object, value)
+        {
+          var options=object.data('options');
+
+          if(value.checked!=null) options.values.checked=value.checked;
+          if(value.unchecked!=null) options.values.unchecked=value.unchecked;
+
+          return(options.values);
+        }, //setValues
+        
+        
+      setGroup: function (object, value)
+        {
+          var options=object.data('options'),
+              properties=object.data('properties');
+
+          if(options.group!=value)
+          {
+            if(options.group!="")
+            {
+              var listGroup=$(document).data('isbGroup_'+options.group),
+                  p=-1;
+              if(listGroup==null) listGroup=[];
+              p=$.inArray(object.attr('id'), listGroup);
+              if(p>-1) listGroup.splice(p,1);              
+              $(document).data('isbGroup_'+options.group, listGroup);
+            }            
+            options.group=value;
+            listGroup=$(document).data('isbGroup_'+value);
+            if(listGroup==null) listGroup=[];
+            listGroup.push(object.attr('id'));
+            $(document).data('isbGroup_'+value, listGroup);
+          }
+        },        
+        
+      switchValue: function (object)
+        {
+          var options=object.data('options'),
+              properties=object.data('properties');
+
+          if(options.values.checked==properties.checked)
+          {
+            privateMethods.setValue(object, options.values.unchecked, true);
+          }
+          else
+          {
+            privateMethods.setValue(object, options.values.checked, true);
+          }
+        },
+
+      setValue : function (object, value, apply)
+        {
+          var options=object.data('options'),
+              properties=object.data('properties');
+
+          if(options.values.checked==value)
+          {
+            if(options.group!="")
+            {
+              listGroup=$(document).data('isbGroup_'+options.group);
+              if(listGroup==null) listGroup=[];
+              for(i=0;i<listGroup.length;i++)
+              {
+                if(listGroup[i]!=object.attr('id')) $('#'+listGroup[i]).inputSwitchButton('value', options.values.unchecked);
+              }
+            }
+            
+            object
+              .addClass('ui-inputSwitchButton-checked')
+              .removeClass('ui-inputSwitchButton-unchecked');
+            properties.checked=value;
+          }
+          else if(options.values.unchecked==value)
+          {
+            object
+              .addClass('ui-inputSwitchButton-unchecked')
+              .removeClass('ui-inputSwitchButton-checked');
+            properties.checked=value;
+          }
+
+          if(options.change) object.trigger('inputSwitchButtonChange', {checked:value});
+
+          return(true);
+        }, //setValue
+
+      setEventChange : function (object, value)
+        {
+          var options=object.data('options');
+
+          options.change=value;
+          object.unbind('inputSwitchButtonChange');
+          if(value) object.bind('inputSwitchButtonChange', options.change);
+          return(options.change);
+        }
+
+    };
+
+
+    $.fn.inputSwitchButton = 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.inputSwitchButton' );
+      }
+    } // $.fn.inputSwitchButton
+
+  }
+)(jQuery);
+
+
Index: extensions/GrumPluginClasses/js/ui.inputSwitchButton.min.js
===================================================================
--- extensions/GrumPluginClasses/js/ui.inputSwitchButton.min.js	(revision 12215)
+++ extensions/GrumPluginClasses/js/ui.inputSwitchButton.min.js	(revision 12215)
@@ -0,0 +1,2 @@
+/* file: ui.inputSwitchButton.js - v1.0.0 | minified on 2011/09/21 with http://jscompress.com/ */
+(function(a){var b={init:function(b){return this.each(function(){var d=a(this),e=d.data("options"),f=d.data("objects"),g=d.data("properties"),h={values:{checked:"yes",unchecked:"no"},change:null,group:""};d.data("options",h).addClass("ui-inputSwitchButton ui-inputSwitchButton-unchecked");if(!g){d.data("properties",{initialized:false,checked:false});g=d.data("properties")}d.bind("click.inputSwitchButton",function(a){c.switchValue(d)});c.setOptions(d,b)})},destroy:function(){return this.each(function(){var a=this.data("properties");$this.unbind(".inputSwitchButton");this.removeClass("ui-inputSwitchButton")})},options:function(b){return this.each(function(){c.setOptions(a(this),b)})},disabled:function(b){if(b!=null){return this.each(function(){c.setDisabled(a(this),b)})}else{return c.getDisabled(a(this))}},values:function(b){if(value!=null){return this.each(function(){c.setValues(a(this),value)})}else{var d=this.data("options");return d.values}},switchValue:function(){return this.each(function(){c.switchValue(a(this))})},value:function(b){var d=this.data("properties");if(b!=null){return this.each(function(){c.setValue(a(this),b,true)})}else{var e=this.data("options");return d.checked?e.values.checked:e.values.unchecked}},group:function(b){var d=this.data("options");if(b!=null){return this.each(function(){c.setGroup(a(this),b)})}else{return d.group}},change:function(b){if(b!=null&&a.isFunction(b)){return this.each(function(){c.setEventChange(a(this),b)})}else{var d=this.data("options");if(d){return d.change}else{return null}}}};var c={setOptions:function(b,d){var e=b.data("properties"),f=b.data("options");if(!a.isPlainObject(d))return false;e.initialized=false;c.setGroup(b,d.group!=null?d.group:f.group,true);c.setValue(b,d.value!=null?d.value:f.value,true);c.setEventChange(b,d.change!=null?d.change:f.change);e.initialized=true},setValues:function(a,b){var c=a.data("options");if(b.checked!=null)c.values.checked=b.checked;if(b.unchecked!=null)c.values.unchecked=b.unchecked;return c.values},setGroup:function(b,c){var d=b.data("options"),e=b.data("properties");if(d.group!=c){if(d.group!=""){var f=a(document).data("isbGroup_"+d.group),g=-1;if(f==null)f=[];g=a.inArray(b.attr("id"),f);if(g>-1)f.splice(g,1);a(document).data("isbGroup_"+d.group,f)}d.group=c;f=a(document).data("isbGroup_"+c);if(f==null)f=[];f.push(b.attr("id"));a(document).data("isbGroup_"+c,f)}},switchValue:function(a){var b=a.data("options"),d=a.data("properties");if(b.values.checked==d.checked){c.setValue(a,b.values.unchecked,true)}else{c.setValue(a,b.values.checked,true)}},setValue:function(b,c,d){var e=b.data("options"),f=b.data("properties");if(e.values.checked==c){if(e.group!=""){listGroup=a(document).data("isbGroup_"+e.group);if(listGroup==null)listGroup=[];for(i=0;i<listGroup.length;i++){if(listGroup[i]!=b.attr("id"))a("#"+listGroup[i]).inputSwitchButton("value",e.values.unchecked)}}b.addClass("ui-inputSwitchButton-checked").removeClass("ui-inputSwitchButton-unchecked");f.checked=c}else if(e.values.unchecked==c){b.addClass("ui-inputSwitchButton-unchecked").removeClass("ui-inputSwitchButton-checked");f.checked=c}if(e.change)b.trigger("inputSwitchButtonChange",{checked:c});return true},setEventChange:function(a,b){var c=a.data("options");c.change=b;a.unbind("inputSwitchButtonChange");if(b)a.bind("inputSwitchButtonChange",c.change);return c.change}};a.fn.inputSwitchButton=function(c){if(b[c]){return b[c].apply(this,Array.prototype.slice.call(arguments,1))}else if(typeof c==="object"||!c){return b.init.apply(this,arguments)}else{a.error("Method "+c+" does not exist on jQuery.inputSwitchButton")}}})(jQuery)
Index: extensions/GrumPluginClasses/language/fr_FR/plugin.lang.php
===================================================================
--- extensions/GrumPluginClasses/language/fr_FR/plugin.lang.php	(revision 11529)
+++ extensions/GrumPluginClasses/language/fr_FR/plugin.lang.php	(revision 12215)
@@ -16,3 +16,17 @@
 $lang['All the gallery'] = 'Toute la galerie';
 
+
+// gpc 3.5.2
+$lang['Your email']='Adresse email';
+$lang['Subject']='Sujet';
+$lang['Message']='Message';
+$lang['Submit']='Soumettre';
+$lang['Email is mandatory']='L\'adresse email est obligatoire';
+$lang['Email is not valid']='L\'adresse email est invalide';
+$lang['Subject is mandatory']='Le sujet du message est obligatoire';
+$lang['Message is mandatory']='Le message ne peut pas &ecirc;tre vide !';
+$lang['[%s][Message from %s] %s']='[%s][Message de %s] %s';
+$lang['Sorry, an error has occured while sending the message to the webmaster']='D&eacute;sol&eacute;, une erreur est survenue lors de l\'envoi du message au webmaster';
+$lang['Your message was sent to the webmaster!']='Votre message a bien &eacute;t&eacute; transmis !';
+
 ?>
Index: extensions/GrumPluginClasses/main.inc.php
===================================================================
--- extensions/GrumPluginClasses/main.inc.php	(revision 11529)
+++ extensions/GrumPluginClasses/main.inc.php	(revision 12215)
@@ -2,5 +2,5 @@
 /*
 Plugin Name: Grum Plugins Classes.3
-Version: 3.5.1
+Version: 3.5.2
 Description: Collection de classes partagées entre mes plugins (existants, ou à venir) / Partaged classes between my plugins (actuals or futures)
 Plugin URI: http://piwigo.org/ext/extension_view.php?eid=199
@@ -163,4 +163,13 @@
 |         |            |   . Request builder interface don't work
 |         |            |
+| 3.5.2   | 2011/05/15 | * Add function to manage special markup
+|         |            |   . BB like markup [b],[i],[url]...
+|         |            |   . [var=<name>] markup
+|         |            |   . [tabs],[tab=id(;default);<tab title>]
+|         |            |   . [form_mail]
+|         |            |
+|         |            | * mantis bug:2160
+|         |            |   . CategorySelector : extended description are not
+|         |            |     managed
 |         |            |
 |         |            |
@@ -168,6 +177,4 @@
 |         |            |
 |         |            | ===== Don't forget to update the plugin version ! =====
-|         |            |
-|         |            |
 |         |            |
 |         |            |
Index: extensions/GrumPluginClasses/templates/GPCFormMsg.tpl
===================================================================
--- extensions/GrumPluginClasses/templates/GPCFormMsg.tpl	(revision 12215)
+++ extensions/GrumPluginClasses/templates/GPCFormMsg.tpl	(revision 12215)
@@ -0,0 +1,17 @@
+<div id='iGpcFormMail'>
+  <form>
+    <input type="hidden" id="iToken" name="fToken" value="{$token}">
+    <h2>{"Your email"|@translate}</h2>
+    <input type="text" value="" id="iEmail" name="fEmail" >
+
+    <h2>{"Subject"|@translate}</h2>
+    <input type="text" value="" id="iSubject" name="fSubject">
+
+    <h2>{"Message"|@translate}</h2>
+    <textarea value="" id="iMessageContent" name="fMessageContent" rows="10"></textarea>
+
+    <p>
+      <input type="button" value="{'Submit'|translate}" id="iSubmit" name="fSubmit">
+    </p>
+  </form>
+</div>
