diff --git a/VERSIONS b/VERSIONS
index 47c66f0f1f18c0d7b9714645fee49e2813fe9183..a738de1ee59f96a47221c666e5b78bb11d99c04a 100644
--- a/VERSIONS
+++ b/VERSIONS
@@ -1,3 +1,15 @@
+12/10/2015 - v7.3.16
+
+  - ticket #29524 : Mise en conformité CNIL sur les cookies de traçage (Ajout du message de consentement et de la page d'information sur les cookies). 
+
+  - ticket #30163 : Augmentation de la limite d'affichage de 10 à 100 résultats dans la recherche de catalogue OAI
+
+  - ticket #30747 : Correction de la possibilité de déclarer une catégorie d'article comme étant sa propre catégorie parente.
+
+  - ticket #8625 : Ajout de DS et PC dans les formes autorisées à l'indexation.
+
+
+
 29/09/2015 - v7.3.15
 
  - ticket #24867 : PNB, Suppression du bouton "Rendre un exemplaire emprunté" (non applicable)
diff --git a/application/modules/opac/controllers/HelpController.php b/application/modules/opac/controllers/HelpController.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d5819dd78cf0ec8c51cdbab65dad0f4b886a52b
--- /dev/null
+++ b/application/modules/opac/controllers/HelpController.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+class HelpController extends ZendAfi_Controller_Action {
+  public function cookiesAction() {
+    $this->view->ga_warning = "";
+    if (Class_AdminVar::getGoogleAnalyticsId()) {
+      $this->view->ga_warning = $this->view->help_GoogleAnalyticsWarning();
+    }
+  }
+}
diff --git a/application/modules/opac/views/scripts/help/cookies.phtml b/application/modules/opac/views/scripts/help/cookies.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..81fe0d8f1f4199c2d82ca8c25a5b8a59daf740cb
--- /dev/null
+++ b/application/modules/opac/views/scripts/help/cookies.phtml
@@ -0,0 +1,15 @@
+<h3>Qu'est-ce qu'un cookie ?</h3>
+
+<p>Un cookie est un fichier texte déposé, sous réserve de vos choix, sur votre ordinateur lors de la visite d'un site ou de la consultation d'une publicité. Il a pour but de collecter des informations relatives à votre navigation et de vous adresser des services adaptés à votre terminal (ordinateur, mobile ou tablette).
+
+Les cookies sont gérés par votre navigateur internet et seul l’émetteur d’un cookie est susceptible de lire ou de modifier les informations qui y sont contenues.</p>
+
+<h3>A quoi servent les cookies émis sur notre site ?</h3>
+
+<p>Nous utilisons uniquement des cookies visant à faciliter votre navigation. Il s'agit notamment des cookies suivants:
+<ul>
+ <li>les cookies de session utilisés pour le maintient de l'authentification.</li>
+ <li>les cookies permettent d'identifier les services et rubriques que l'utilisateur a visités.</li>
+</ul>
+
+<?php echo $this->ga_warning; ?>
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index 1435c930b9d9c8330e841e08118713149c747e96..119873f436f613b9edd46da0c88f3060e4a61b68 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -162,7 +162,8 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
 
                     'TEXT_REPLACEMENTS' => Class_AdminVar_Meta::newRawText($this->_('Remplacement de textes à la volée. <br/>Ex:<br/>Panier;Sélection<br/>Vous avez %%d paniers;Vous avez %%d sélections')),
                     'URL_COSMOGRAMME' => Class_AdminVar_Meta::newDefault('')->bePrivate(),
-                    'PACK_MOBILE' => Class_AdminVar_Meta::newOnOff($this->_('Activation des fonctions avancées du téléphone'))->bePrivate()],
+                    'PACK_MOBILE' => Class_AdminVar_Meta::newOnOff($this->_('Activation des fonctions avancées du téléphone'))->bePrivate(),
+                    'CNIL_CONSENT_ENABLE' => Class_AdminVar_Meta::newOnOff('Affiche la demande de consentement avant l\'insertion de cookies ou autres traceurs')],
        'catalogue' => [
                        'OAI_SERVER' => Class_AdminVar_Meta::newOnOff($this->_('Activation du serveur OAI: permet le moissonnage des domaines par d\'autres logiciels via OAI'))],
        'newsletter' => [
diff --git a/library/Class/ArticleCategorie.php b/library/Class/ArticleCategorie.php
index 9e1cc7b6957ce2119002412439ec68f2d23cf3a1..edf5dbe37d59d307a47afa328e52622628782968 100644
--- a/library/Class/ArticleCategorie.php
+++ b/library/Class/ArticleCategorie.php
@@ -70,6 +70,8 @@ class Class_ArticleCategorie extends Storm_Model_Abstract {
     $filter = new Zend_Filter_StripTags();
     $this->setLibelle(trim($filter->filter($this->getLibelle())));
 
+    if($this->getId() === $this->getIdCatMere())
+      $this->setIdCatMere(0);
   }
 
 
diff --git a/library/Class/Cnil.php b/library/Class/Cnil.php
new file mode 100644
index 0000000000000000000000000000000000000000..01f3324dbf6d0c3a889d0b9fcfd9d68dff4d58ec
--- /dev/null
+++ b/library/Class/Cnil.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_Cnil {
+  use Trait_Translator, Trait_TimeSource;
+
+  protected static $_cookie_jar;
+
+  protected $_key, $_session;
+
+
+  /** @category testing */
+  public static function setCookieJar($cookie_jar) {
+    static::$_cookie_jar = $cookie_jar;
+  }
+
+
+  public function __construct() {
+    $this->_key = 'cnil' . md5(BASE_URL);
+    $this->_session = new Zend_Session_Namespace($this->_key);
+    if (isset($_COOKIE[$this->_key]))
+      $this->_session->seen = true;
+  }
+
+
+  public function trackConsent() {
+    if ($this->_session->seen)
+      return;
+
+    $this->_session->seen = true;
+    $this->_setConsentCookie();
+    $this->_displayMessage();
+  }
+
+
+  protected function _setConsentCookie() {
+    $this->_getCookieJar()
+         ->setcookie($this->_key,
+                     '1',
+                     $this->getTimeSource()->time() + (3600 * 24 * 30 *13));
+  }
+
+
+  protected function _displayMessage() {
+    $read_more = Class_Url::absolute(['controller' => 'help',
+                                      'action' => 'cookies']);
+    Class_ScriptLoader::getInstance()
+      ->notify($this->_('En poursuivant votre navigation sur ce site, vous acceptez l\'utilisation de cookies. <a href="'.$read_more.'">En savoir plus</a>'),
+               false);
+  }
+
+
+  protected function _getCookieJar() {
+    return static::$_cookie_jar ?
+      static::$_cookie_jar : new Class_Cnil_CookieJar();
+  }
+}
+
+
+
+class Class_Cnil_CookieJar {
+  public function setcookie() {
+    call_user_func_array('setcookie', func_get_args());
+  }
+}
\ No newline at end of file
diff --git a/library/Class/Indexation.php b/library/Class/Indexation.php
index ba12ebfd3a34f62d395d9fe0e5a440abd99a9493..40dd5297d561e200dccea14a48c961b4aa98ecc0 100644
--- a/library/Class/Indexation.php
+++ b/library/Class/Indexation.php
@@ -51,539 +51,542 @@ class Class_Indexation {
 
   function __construct()  {
     // Lire formes rejetées
-    $this->articles=array('L\'','LE ','LA ','LES ','UN ','UNE ');
-    $this->inclu=array('AN','AS','OR','U2','AI','LU','XO','DO','RE','MI','FA','SI','AC','DC','XX','B','C','D','E','F','G','H','I','J','K','M','P','Q','R','S','T','V','W','X','Y','Z','L','YU','UT','LI','OC','PI','ZU','WU','TO','OZ','ZZ','XX');
-    $this->exclu = array('L','LE','LA','LES','UN','UNE','LES','DES','MES','TES','CES');
+    $this->articles = ['L\'','LE ','LA ','LES ','UN ','UNE '];
+
+    $this->inclu =  ['AN','AS','OR','U2','AI','LU','XO','DO','RE','MI','FA','SI','AC','DC','XX','B','C','D','E','F','G','H','I','J','K','M','P','Q','R','S','T','V','W','X','Y','Z','L','YU','UT','LI','OC','PI','ZU','WU','TO','OZ','ZZ','XX', 'PC', 'DS'];
+
+    $this->exclu = ['L','LE','LA','LES','UN','UNE','LES','DES','MES','TES','CES'];
 
     // Pluriels
-    $this->pluriel=array(
-                         array('AIL','AULX'),
-                         array('AVAL','AVALS'),
-                         array('BAIL','BAUX'),
-                         array('BAL','BALS'),
-                         array('BANAL','BANALS'),
-                         array('BANCAL','BANCALS'),
-                         array('BIJOU','BIJOUX'),
-                         array('BLEU','BLEUS'),
-                         array('CAILLOU','CAILLOUX'),
-                         array('CAL','CALS'),
-                         array('CARNAVAL','CARNAVALS'),
-                         array('CEREMONIAL','CEREMONIALS'),
-                         array('CHACAL','CHACALS'),
-                         array('CHORAL','CHORALS'),
-                         array('CHOU','CHOUX'),
-                         array('CORAIL','CORAUX'),
-                         array('DETAIL','DETAILS'),
-                         array('EMAIL','EMAUX'),
-                         array('EMEU','EMEUS'),
-                         array('ETAL','ETALS'),
-                         array('FATAL','FATALS'),
-                         array('FESTIVAL','FESTIVALS'),
-                         array('GEMMAIL','GEMMAUX'),
-                         array('GENOU','GENOUX'),
-                         array('HIBOU','HIBOUX'),
-                         array('JOUJOU','JOUJOUX'),
-                         array('LANDAU','LANDAUX'),
-                         array('NATAL','NATALS'),
-                         array('OEIL','YEUX'),
-                         array('PAL','PALS'),
-                         array('PNEU','PNEUS'),
-                         array('POU','POUX'),
-                         array('RECITAL','RECITALS'),
-                         array('REGAL','REGALS'),
-                         array('SARRAU','SARRAUS'),
-                         array('SOUPIRAIL','SOUPIRAUX'),
-                         array('TONAL','TONALS'),
-                         array('TRAVAIL','TRAVAUX'),
-                         array('VAL','VALS'),
-                         array('VENTAIL','VENTAUX'),
-                         array('VIRGINAL','VIRGINALS'),
-                         array('VITRAIL','VITRAUX'),
-                         array('*EAU','*EAUX'),
-                         array('*AL','*AUX'),
-                         array('*EU','*EUX'),
-                         array('*AU','*AUX')
-    );
-
-    // Init table ascii pour majuscules
-    $this->tableMajTo = str_split(str_repeat( ' ', 42 )
-                                  . '*     0123456789       '
-                                  . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ      '
-                                  . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ      '
-                                  . str_repeat( ' ', 63)
-                                  .'AAAAAAACEEEEIIII NOOOOO  UUUUY  AAAAAAACEEEEIIII NOOOOO  UUUUY Y');
-
-    for($i=0; $i<count($this->tableMajTo); $i++)
-      $this->tableMajFrom[] = chr($i);
-
-
-    $this->tableMajUtf8=array(chr(0xC9) => 'E', 'È' => 'E', 'Ë' => 'E', 'Ê' => 'E','Á' => 'A', 'À' => 'A', 'Ä' => 'A', 'Â' => 'A',
-                              'Å' => 'A', 'Ã' => 'A', 'Æ' => 'E','Ï' => 'I', 'Î' => 'I', 'Ì' => 'I', 'Í' => 'I',
-                              'Ô' => 'O', 'Ö' => 'O', 'Ò' => 'O', 'Ó' => 'O', 'Õ' => 'O', 'Ø' => 'O', 'Œ' => 'OEU',
-                              'Ú' => 'U', 'Ù' => 'U', 'Û' => 'U', 'Ü' => 'U','Ñ' => 'N', 'Ç' => 'S', '¿' => 'E');
-  }
-  // Indexation d'un titre
-  public function codeAlphaTitre($titre)
-  {
-    $titre=$this->alphaMaj($titre);
-    foreach($this->articles as $article)
+    $this->pluriel = [ ['AIL','AULX'],
+                      ['AVAL','AVALS'],
+                      ['BAIL','BAUX'],
+                      ['BAL','BALS'],
+                      ['BANAL','BANALS'],
+                      ['BANCAL','BANCALS'],
+                      ['BIJOU','BIJOUX'],
+                      ['BLEU','BLEUS'],
+                      ['CAILLOU','CAILLOUX'],
+                      ['CAL','CALS'],
+                      ['CARNAVAL','CARNAVALS'],
+                      ['CEREMONIAL','CEREMONIALS'],
+                      ['CHACAL','CHACALS'],
+                      ['CHORAL','CHORALS'],
+                      ['CHOU','CHOUX'],
+                      ['CORAIL','CORAUX'],
+                      ['DETAIL','DETAILS'],
+                      ['EMAIL','EMAUX'],
+                      ['EMEU','EMEUS'],
+                      ['ETAL','ETALS'],
+                      ['FATAL','FATALS'],
+                      ['FESTIVAL','FESTIVALS'],
+                      ['GEMMAIL','GEMMAUX'],
+                      ['GENOU','GENOUX'],
+                      ['HIBOU','HIBOUX'],
+                      ['JOUJOU','JOUJOUX'],
+                      ['LANDAU','LANDAUX'],
+                      ['NATAL','NATALS'],
+                      ['OEIL','YEUX'],
+                      ['PAL','PALS'],
+                      ['PNEU','PNEUS'],
+                      ['POU','POUX'],
+                      ['RECITAL','RECITALS'],
+                      ['REGAL','REGALS'],
+                      ['SARRAU','SARRAUS'],
+                      ['SOUPIRAIL','SOUPIRAUX'],
+                      ['TONAL','TONALS'],
+                      ['TRAVAIL','TRAVAUX'],
+                      ['VAL','VALS'],
+                      ['VENTAIL','VENTAUX'],
+                      ['VIRGINAL','VIRGINALS'],
+                      ['VITRAIL','VITRAUX'],
+                      ['*EAU','*EAUX'],
+                      ['*AL','*AUX'],
+                      ['*EU','*EUX'],
+                      ['*AU','*AUX'],
+                      ['PC','PC'],
+                      ['DS','DS']];
+
+                      // Init table ascii pour majuscules
+                      $this->tableMajTo = str_split(str_repeat( ' ', 42 )
+                                                    . '*     0123456789       '
+                                                    . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ      '
+                                                    . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ      '
+                                                    . str_repeat( ' ', 63)
+                                                    .'AAAAAAACEEEEIIII NOOOOO  UUUUY  AAAAAAACEEEEIIII NOOOOO  UUUUY Y');
+
+                      for($i=0; $i<count($this->tableMajTo); $i++)
+                        $this->tableMajFrom[] = chr($i);
+
+
+                      $this->tableMajUtf8=array(chr(0xC9) => 'E', 'È' => 'E', 'Ë' => 'E', 'Ê' => 'E','Á' => 'A', 'À' => 'A', 'Ä' => 'A', 'Â' => 'A',
+                                                'Å' => 'A', 'Ã' => 'A', 'Æ' => 'E','Ï' => 'I', 'Î' => 'I', 'Ì' => 'I', 'Í' => 'I',
+                                                'Ô' => 'O', 'Ö' => 'O', 'Ò' => 'O', 'Ó' => 'O', 'Õ' => 'O', 'Ø' => 'O', 'Œ' => 'OEU',
+                                                'Ú' => 'U', 'Ù' => 'U', 'Û' => 'U', 'Ü' => 'U','Ñ' => 'N', 'Ç' => 'S', '¿' => 'E');
+                      }
+      // Indexation d'un titre
+      public function codeAlphaTitre($titre)
       {
-        $lg=strlen($article);
-        if(strLeft($titre, $lg)==$article) {$titre=strMid($titre,$lg,256); break;}
+        $titre=$this->alphaMaj($titre);
+        foreach($this->articles as $article)
+          {
+            $lg=strlen($article);
+            if(strLeft($titre, $lg)==$article) {$titre=strMid($titre,$lg,256); break;}
+          }
+        $titre=$this->alphaMaj($titre);
+        return $titre;
       }
-    $titre=$this->alphaMaj($titre);
-    return $titre;
-  }
 
-  // Rend une suite de mots complete pour les formes plurielles
-  public function getExpressionRecherche($mot) {
-    if (!$mot=trim($mot))
-      return false;
+    // Rend une suite de mots complete pour les formes plurielles
+    public function getExpressionRecherche($mot) {
+      if (!$mot=trim($mot))
+        return false;
 
-    // Pluriel
-    $etoile = '';
-    if(strRight($mot,1) == '*') {
-      $etoile='*';
-      $mot=strLeft($mot, strlen($mot)-1);
-    }
+      // Pluriel
+      $etoile = '';
+      if(strRight($mot,1) == '*') {
+        $etoile='*';
+        $mot=strLeft($mot, strlen($mot)-1);
+      }
 
-    $m = $this->getPluriel($mot);
-    $m[2] = $this->phonetix($m[0]);
-    return trim('('.$m[0].$etoile.' '.$m[1].' '.$m[2].')');
-  }
+      $m = $this->getPluriel($mot);
+      $m[2] = $this->phonetix($m[0]);
+      return trim('('.$m[0].$etoile.' '.$m[1].' '.$m[2].')');
+    }
 
 
-  public function alphaMaj( $chaine ) {
-    if (isset(static::$_alpha_maj_cache[$chaine]))
-      return static::$_alpha_maj_cache[$chaine];
+    public function alphaMaj( $chaine ) {
+      if (isset(static::$_alpha_maj_cache[$chaine]))
+        return static::$_alpha_maj_cache[$chaine];
 
-    return static::$_alpha_maj_cache[$chaine] = trim(str_replace($this->tableMajFrom,
-                                                                 $this->tableMajTo,
-                                                                 utf8_decode($chaine)));
-  }
+      return static::$_alpha_maj_cache[$chaine] = trim(str_replace($this->tableMajFrom,
+                                                                   $this->tableMajTo,
+                                                                   utf8_decode($chaine)));
+    }
 
 
-  public function getClefAlpha($type_doc,$titre,$complement_titre,$auteur,$tome,$editeur,$annee)
-  {
-    $clef=$this->getClefOeuvre($titre,$complement_titre,$auteur,$tome).'-';
-    $clef.=substr($this->alphaMaj(str_replace(' ','',$editeur)),0,80).'-';
-    $clef.=$annee.'-';
-    $clef.=$type_doc;
-    $clef=str_replace(' ','',$clef);
-    return $clef;
-  }
+    public function getClefAlpha($type_doc,$titre,$complement_titre,$auteur,$tome,$editeur,$annee)
+    {
+      $clef=$this->getClefOeuvre($titre,$complement_titre,$auteur,$tome).'-';
+      $clef.=substr($this->alphaMaj(str_replace(' ','',$editeur)),0,80).'-';
+      $clef.=$annee.'-';
+      $clef.=$type_doc;
+      $clef=str_replace(' ','',$clef);
+      return $clef;
+    }
 
 // Rend une clef alpha pour les oeuvres
-  public function getClefOeuvre($titre,$complement_titre,$auteur,$tome)
-  {
-    $clef = substr($this->codeAlphaTitre(strtoupper(str_replace(' ','',$titre))),0,80).'-';
-    $clef.=substr($this->codeAlphaTitre(strtoupper(str_replace(' ','',$complement_titre))),0,20).'-';
-    $clef.=substr($this->alphaMaj(str_replace(' ','',$auteur)),0,80).'-';
-    $clef.=$this->alphaMaj($tome);
-    $clef=str_replace(' ','',$clef);
-    return $clef;
-  }
+    public function getClefOeuvre($titre,$complement_titre,$auteur,$tome)
+    {
+      $clef = substr($this->codeAlphaTitre(strtoupper(str_replace(' ','',$titre))),0,80).'-';
+      $clef.=substr($this->codeAlphaTitre(strtoupper(str_replace(' ','',$complement_titre))),0,20).'-';
+      $clef.=substr($this->alphaMaj(str_replace(' ','',$auteur)),0,80).'-';
+      $clef.=$this->alphaMaj($tome);
+      $clef=str_replace(' ','',$clef);
+      return $clef;
+    }
 
 // Decoupe une expression en mots en tenant compte des exclusions / inclusions
-  public function getMots( $chaine )
-  {
-    $new=array();
-    $chaine = str_replace( '.', '', $chaine);
-    $chaine = trim($this->alphaMaj($chaine));
-    $mot = explode( ' ', $chaine );
-    $index = 0;
-    for( $i = 0; $i < count($mot); $i++)
-      {
-        // Retirer mots de 2 lettres sauf les inclus et les nombres
-        if( strLen($mot[$i]) < 3 And intVal($mot[$i])==false)
-          {
-            if( in_array( $mot[$i], $this->inclu) == false) continue;
-            if(strlen($mot[$i])==1) $mot[$i].='00'; // mots d'1 lettre : on double la lettre
-          }
-        // Retirer mots vides
-        if( in_array($mot[$i], $this->exclu ) == true ) continue;
-        // On garde le mot
-        $new[$index++] = $mot[$i];
-      }
-    return $new;
-  }
+    public function getMots( $chaine ) {
+      $new = [];
+      $chaine = str_replace( '.', '', $chaine);
+      $chaine = trim($this->alphaMaj($chaine));
+      $words = explode(' ', $chaine);
 
-// Rend une chaine de mots dedoublonnes et filtres
-  public function getFulltext($data) {
-    if (gettype($data) != 'array')
-      $data=array($data);
+      foreach($words as $word) {
+        if(strlen($word) < 3 && intVal($word) == false) {
+          if(false == in_array($word, $this->inclu))
+            continue;
 
-    $new=' ';
-    foreach($data as $chaine)
-      {
-        $mots=$this->getMots($chaine);
-        foreach($mots as $mot)
-          {
-            $mot=' '.$mot.' ';
-            if(strpos($new,$mot) === false )
-              {
-                $new.=trim($mot).' ';
-                $phonem=' '.$this->phonetix(trim($mot)).' ';
-                if($phonem and strpos($new,$phonem) === false ) $new.=trim($phonem).' ';
-              }
-          }
+          if( 1 == strlen($word))
+            $word .= '00';
+        }
+
+        if(true == in_array($word, $this->exclu))
+          continue;
+
+        $new[] = $word;
       }
-    return trim($new);
-  }
+
+      return $new;
+    }
+
+// Rend une chaine de mots dedoublonnes et filtres
+    public function getFulltext($data) {
+      if (gettype($data) != 'array')
+        $data=array($data);
+
+      $new=' ';
+      foreach($data as $chaine)
+        {
+          $mots=$this->getMots($chaine);
+          foreach($mots as $mot)
+            {
+              $mot=' '.$mot.' ';
+              if(strpos($new,$mot) === false )
+                {
+                  $new.=trim($mot).' ';
+                  $phonem=' '.$this->phonetix(trim($mot)).' ';
+                  if($phonem and strpos($new,$phonem) === false ) $new.=trim($phonem).' ';
+                }
+            }
+        }
+      return trim($new);
+    }
 
 // Rend le mot au singulier et au pluriel
-  public function getPluriel( $mot )
-  {
-    if( strToUpper($mot) != $mot ) $mot=$this->alphaMaj($mot);
-    if(!trim($mot)) return false;
-    foreach($this->pluriel as $regle)
-      {
-        if(strLeft($regle[0],1) != '*')
-          {
-            if($mot==$regle[0] or $mot==$regle[1]) return $regle;
-            else continue;
-          }
-        $regle[0]=str_replace('*','',$regle[0]);
-        $regle[1]=str_replace('*','',$regle[1]);
-        $pattern_singulier = '('.$regle[0].'$)';
-        $pattern_pluriel='('.$regle[1].'$)';
-
-        $pluriel=ereg_replace($pattern_singulier, $regle[1], $mot);
-        $singulier=ereg_replace($pattern_pluriel, $regle[0], $mot);
-        if($singulier != $mot or $pluriel != $mot) break;
-      }
-    // Si inchangé on ajoute le S
-    if($singulier == $pluriel)
-      {
-        if(strRight($mot,1)=='S') {$pluriel=$singulier; $singulier=strLeft($singulier,strlen($singulier)-1);}
-        else $pluriel=$singulier.'S';
-      }
-    return array($singulier,$pluriel);
-  }
+    public function getPluriel( $mot )
+    {
+      if( strToUpper($mot) != $mot ) $mot=$this->alphaMaj($mot);
+      if(!trim($mot)) return false;
+      foreach($this->pluriel as $regle)
+        {
+          if(strLeft($regle[0],1) != '*')
+            {
+              if($mot==$regle[0] or $mot==$regle[1]) return $regle;
+              else continue;
+            }
+          $regle[0]=str_replace('*','',$regle[0]);
+          $regle[1]=str_replace('*','',$regle[1]);
+          $pattern_singulier = '('.$regle[0].'$)';
+          $pattern_pluriel='('.$regle[1].'$)';
+
+          $pluriel=ereg_replace($pattern_singulier, $regle[1], $mot);
+          $singulier=ereg_replace($pattern_pluriel, $regle[0], $mot);
+          if($singulier != $mot or $pluriel != $mot) break;
+        }
+      // Si inchangé on ajoute le S
+      if($singulier == $pluriel)
+        {
+          if(strRight($mot,1)=='S') {$pluriel=$singulier; $singulier=strLeft($singulier,strlen($singulier)-1);}
+          else $pluriel=$singulier.'S';
+        }
+      return array($singulier,$pluriel);
+    }
 
 // ---------------------------------------------------
 // Rend true si mot inclu
 // ---------------------------------------------------
-  public function  isMotInclu($mot)
-  {
-    return in_array($mot, $this->inclu);
-  }
+    public function  isMotInclu($mot)
+    {
+      return in_array($mot, $this->inclu);
+    }
 
 // ---------------------------------------------------
 // Othographe approchante
 // ---------------------------------------------------
-  function phonetix($sIn) {
-    if (strlen($sIn)<4 || is_numeric($sIn))
-      return false;
-    return $this->phonetixCompute($sIn);
-  }
-
-  function phonetixCompute($sIn)  {
-    $sIn = strtr( $sIn, $this->accents);          // minuscules accentuées ou composées en majuscules simples
-    $sIn = strtr( $sIn, $this->min2maj);          // majuscules accentuées ou composées en majuscules simples
-    $sIn = strtoupper( $sIn );              // on passe tout le reste en majuscules
-    $sIn = preg_replace( '`[^A-Z]`', '', $sIn );  // on garde uniquement les lettres de A à Z
-
-    $sBack=$sIn;                  // on sauve le code (utilisé pour les mots très courts)
-
-    $sIn = preg_replace( '`O[O]+`', 'OU', $sIn );   // pré traitement OO... -> OU
-    $sIn = str_replace( 'SAOU', 'SOU', $sIn );  // pré traitement SAOU -> SOU
-    $sIn = str_replace( 'OES', 'OS', $sIn );  // pré traitement OES -> OS
-    $sIn = str_replace( 'CCH', 'K', $sIn );     // pré traitement CCH -> K
-    $sIn = preg_replace( '`CC([IYE])`', 'KS$1', $sIn ); // CCI CCY CCE
-    $sIn = preg_replace( '`(.)\1`', '$1', $sIn );   // supression des répétitions
-
-    // quelques cas particuliers
-    if ($sIn=='CD') return($sIn);
-    if ($sIn=='BD') return($sIn);
-    if ($sIn=='BV') return($sIn);
-    if ($sIn=='TABAC') return('TABA');
-    if ($sIn=='FEU') return('FE');
-    if ($sIn=='FE') return($sIn);
-    if ($sIn=='FER') return($sIn);
-    if ($sIn=='FIEF') return($sIn);
-    if ($sIn=='FJORD') return($sIn);
-    if ($sIn=='GOAL') return('GOL');
-    if ($sIn=='FLEAU') return('FLEO');
-    if ($sIn=='HIER') return('IER');
-    if ($sIn=='HEU') return('E');
-    if ($sIn=='HE') return('E');
-    if ($sIn=='OS') return($sIn);
-    if ($sIn=='RIZ') return('RI');
-    if ($sIn=='RAZ') return('RA');
-
-    // pré-traitements
-    $sIn = preg_replace( '`OIN[GT]$`', 'OIN', $sIn );                 // terminaisons OING -> OIN
-    $sIn = preg_replace( '`E[RS]$`', 'E', $sIn );                     // supression des terminaisons infinitifs et participes pluriels
-    $sIn = preg_replace( '`(C|CH)OEU`', 'KE', $sIn );                   // pré traitement OEU -> EU
-    $sIn = str_replace( 'MOEU', 'ME', $sIn );                     // pré traitement OEU -> EU
-    $sIn = preg_replace( '`OE([UI]+)([BCDFGHJKLMNPQRSTVWXZ])`', 'E$1$2', $sIn );    // pré traitement OEU OEI -> E
-    $sIn = preg_replace( '`^GEN[TS]$`', 'JAN', $sIn );                  // pré traitement GEN -> JAN
-    $sIn = str_replace( 'CUEI', 'KEI', $sIn );                    // pré traitement accueil
-    $sIn = preg_replace( '`([^AEIOUYC])AE([BCDFGHJKLMNPQRSTVWXZ])`', '$1E$2', $sIn );   // pré traitement AE -> E
-    $sIn = preg_replace( '`AE([QS])`', 'E$1', $sIn );                   // pré traitement AE -> E
-    $sIn = preg_replace( '`AIE([BCDFGJKLMNPQRSTVWXZ])`', 'AI$1', $sIn );        // pré-traitement AIE(consonne) -> AI
-    $sIn = str_replace( 'ANIEM', 'ANIM', $sIn );                  // pré traitement NIEM -> NIM
-    $sIn = preg_replace( '`(DRA|TRO|IRO)P$`', '$1', $sIn );               // P terminal muet
-    $sIn = preg_replace( '`(LOM)B$`', '$1', $sIn );                   // B terminal muet
-    $sIn = preg_replace( '`(RON|POR)C$`', '$1', $sIn );                 // C terminal muet
-    $sIn = preg_replace( '`PECT$`', 'PET', $sIn );                    // C terminal muet
-    $sIn = preg_replace( '`ECUL$`', 'CU', $sIn );                     // L terminal muet
-    $sIn = preg_replace( '`(CHA|CA|E)M(P|PS)$`', '$1N', $sIn );             // P ou PS terminal muet
-    $sIn = preg_replace( '`(TAN|RAN)G$`', '$1', $sIn );                 // G terminal muet
-
-
-    // sons YEUX
-    $sIn = preg_replace( '`([^VO])ILAG`', '$1IAJ', $sIn );
-    $sIn = preg_replace( '`([^TRH])UIL(AR|E)(.+)`', '$1UI$2$3', $sIn );
-    $sIn = preg_replace( '`([G])UIL([AEO])`', '$1UI$2', $sIn );
-    $sIn = preg_replace( '`([NSPM])AIL([AEO])`', '$1AI$2', $sIn );
-    $convMIn  = array('DILAI','DILON','DILER','DILEM','RILON','TAILE','GAILET','AILAI','AILAR',
-                      'OUILA','EILAI','EILAR','EILER','EILEM','REILET','EILET','AILOL' );
-    $convMOut = array( 'DIAI', 'DION','DIER', 'DIEM', 'RION', 'TAIE', 'GAIET', 'AIAI', 'AIAR',
-                      'OUIA', 'AIAI', 'AIAR', 'AIER', 'AIEM',  'RAIET', 'EIET', 'AIOL' );
-    $sIn = str_replace( $convMIn, $convMOut, $sIn );
-    $sIn = preg_replace( '`([^AEIOUY])(SC|S)IEM([EA])`', '$1$2IAM$3', $sIn );   // IEM -> IAM
-    $sIn = preg_replace( '`^(SC|S)IEM([EA])`', '$1IAM$2', $sIn );         // IEM -> IAM
-
-    // MP MB -> NP NB
-    $convMIn  = array( 'OMB', 'AMB', 'OMP', 'AMP', 'IMB', 'EMP','GEMB','EMB', 'UMBL','CIEN');
-    $convMOut = array( 'ONB', 'ANB', 'ONP', 'ANP', 'INB', 'ANP','JANB','ANB', 'INBL','SIAN');
-    $sIn = str_replace( $convMIn, $convMOut, $sIn );
-
-    // Sons en K
-    $sIn = preg_replace( '`^ECHO$`', 'EKO', $sIn );   // cas particulier écho
-    $sIn = preg_replace( '`^ECEUR`', 'EKEUR', $sIn );   // cas particulier écœuré
-    // Choléra Chœur mais pas chocolat!
-    $sIn = preg_replace( '`^CH(OG+|OL+|OR+|EU+|ARIS|M+|IRO|ONDR)`', 'K$1', $sIn );        //En début de mot
-    $sIn = preg_replace( '`(YN|RI)CH(OG+|OL+|OC+|OP+|OM+|ARIS|M+|IRO|ONDR)`', '$1K$2', $sIn );  //Ou devant une consonne
-    $sIn = preg_replace( '`CHS`', 'CH', $sIn );
-    $sIn = preg_replace( '`CH(AIQ)`', 'K$1', $sIn );
-    $sIn = preg_replace( '`^ECHO([^UIPY])`', 'EKO$1', $sIn );
-    $sIn = preg_replace( '`ISCH(I|E)`', 'ISK$1', $sIn );
-    $sIn = preg_replace( '`^ICHT`', 'IKT', $sIn );
-    $sIn = preg_replace( '`ORCHID`', 'ORKID', $sIn );
-    $sIn = preg_replace( '`ONCHIO`', 'ONKIO', $sIn );
-    $sIn = preg_replace( '`ACHIA`', 'AKIA', $sIn );     // retouche ACHIA -> AKIA
-    $sIn = preg_replace( '`([^C])ANICH`', '$1ANIK', $sIn ); // ANICH -> ANIK  1/2
-    $sIn = preg_replace( '`OMANIK`', 'OMANICH', $sIn );   // cas particulier  2/2
-    $sIn = preg_replace( '`ACHY([^D])`', 'AKI$1', $sIn );
-    $sIn = preg_replace( '`([AEIOU])C([BDFGJKLMNPQRTVWXZ])`', '$1K$2', $sIn ); // voyelle, C, consonne sauf H
-    $convPrIn  = array('EUCHA','YCHIA','YCHA','YCHO','YCHED','ACHEO','RCHEO','RCHES',
-                       'ECHN','OCHTO','CHORA','CHONDR','CHORE','MACHM','BRONCHO','LICHOS','LICHOC');
-    $convPrOut = array('EKA',  'IKIA', 'IKA', 'IKO',  'IKED','AKEO','RKEO',  'RKES',
-                       'EKN',  'OKTO', 'KORA', 'KONDR' ,'KORE' ,'MAKM', 'BRONKO', 'LIKOS', 'LIKOC');
-    $sIn = str_replace( $convPrIn, $convPrOut, $sIn );
-
-    // Weuh (perfectible)
-    $convPrIn  = array( 'WA','WO', 'WI','WHI','WHY','WHA','WHO');
-    $convPrOut = array( 'OI', 'O','OUI','OUI','OUI','OUA', 'OU');
-    $sIn = str_replace( $convPrIn, $convPrOut, $sIn );
-
-    // Gueu, Gneu, Jeu et quelques autres
-    $convPrIn  = array( 'GNES','GNET','GNER','GNE',  'GI', 'GNI','GNA','GNOU','GNUR','GY','OUGAIN',
-                       'AGEOL', 'AGEOT','GEOLO','GEOM','GEOP','GEOG','GEOS','GEORG','GEOR','NGEOT','UGEOT','GEOT','GEOD','GEOC','GEO','GEA','GE',
-                       'QU', 'Q',  'CY', 'CI', 'CN','ICM','CEAT','CE',
-                       'CR', 'CO', 'CUEI','CU', 'VENCA','CA', 'CS','CLEN','CL', 'CZ', 'CTIQ',
-                       'CTIF','CTIC','CTIS','CTIL','CTIO','CTI', 'CTU', 'CTE','CTO','CTR','CT', 'PH', 'TH',
-                       'OW', 'LH', 'RDL', 'CHLO', 'CHR', 'PTIA');
-    $convPrOut = array( 'NIES','NIET','NIER', 'NE',  'JI',  'NI','NIA','NIOU','NIUR','JI','OUGIN',
-                       'AJOL',  'AJOT','JEOLO','JEOM','JEOP','JEOG','JEOS','JORJ','JEOR','NJOT','UJOT','JEOT','JEOD','JEOC', 'JO','JA' ,'JE',
-                       'K', 'K',  'SI', 'SI', 'KN','IKM', 'SAT','SE',
-                       'KR', 'KO', 'KEI','KU', 'VANSA','KA', 'KS','KLAN','KL', 'KZ', 'KTIK',
-                       'KTIF','KTIS','KTIS','KTIL','KSIO','KTI', 'KTU', 'KTE','KTO','KTR','KT', 'F',  'T',
-                       'OU',  'L',  'RL',  'KLO',  'KR', 'PSIA');
-    $sIn = str_replace( $convPrIn, $convPrOut, $sIn );
-
-    $sIn = preg_replace( '`GU([^RLMBSTPZN])`', 'G$1', $sIn ); // Gueu !
-    $sIn = preg_replace( '`GNO([MLTNRKG])`', 'NIO$1', $sIn ); // GNO ! Tout sauf S pour gnos
-    $sIn = preg_replace( '`GNO([MLTNRKG])`', 'NIO$1', $sIn ); // bis -> gnognotte! Si quelqu'un sait le faire en une seule regexp...
-
-
-    // TI -> SI v2.0
-    $convPrIn  = array( 'BUTIE','BUTIA','BATIA','ANTIEL','RETION','ENTIEL','ENTIAL','ENTIO','ENTIAI','UJETION','ATIEM','PETIEN',
-                       'CETIE','OFETIE','IPETI','LBUTION','BLUTION','LETION','LATION','SATIET');
-    $convPrOut = array( 'BUSIE','BUSIA','BASIA','ANSIEL','RESION','ENSIEL','ENSIAL','ENSIO','ENSIAI','UJESION','ASIAM','PESIEN',
-                       'CESIE','OFESIE','IPESI','LBUSION','BLUSION','LESION','LASION','SASIET');
-    $sIn = str_replace( $convPrIn, $convPrOut, $sIn );
-    $sIn = preg_replace( '`(.+)ANTI(AL|O)`', '$1ANSI$2', $sIn ); // sauf antialcoolique, antialbumine, antialarmer, ...
-    $sIn = preg_replace( '`(.+)INUTI([^V])`', '$1INUSI$2', $sIn ); // sauf inutilité, inutilement, diminutive, ...
-    $sIn = preg_replace( '`([^O])UTIEN`', '$1USIEN', $sIn ); // sauf soutien, ...
-    $sIn = preg_replace( '`([^DE])RATI[E]$`', '$1RASI$2', $sIn ); // sauf xxxxxcratique, ...
-    // TIEN TION -> SIEN SION v3.1
-    $sIn = preg_replace( '`([^SNEU]|KU|KO|RU|LU|BU|TU|AU)T(IEN|ION)`', '$1S$2', $sIn );
-
-
-    // H muet
-    $sIn = preg_replace( '`([^CS])H`', '$1', $sIn );  // H muet
-    $sIn = str_replace( 'ESH', 'ES', $sIn );      // H muet
-    $sIn = str_replace( 'NSH', 'NS', $sIn );      // H muet
-    $sIn = str_replace( 'SH', 'CH', $sIn );       // ou pas!
-
-    // NASALES
-    $convNasIn  = array( 'OMT','IMB', 'IMP','UMD','TIENT','RIENT','DIENT','IEN',
-                        'YMU','YMO','YMA','YME', 'YMI','YMN','YM', 'AHO','FAIM','DAIM','SAIM','EIN','AINS');
-    $convNasOut = array( 'ONT','INB', 'INP','OND','TIANT','RIANT','DIANT', 'IN',
-                        'IMU','IMO','IMA','IME', 'IMI','IMN','IN',  'AO', 'FIN','DIN', 'SIN','AIN','INS');
-    $sIn = str_replace( $convNasIn, $convNasOut, $sIn );
-    // AIN -> IN v2.0
-    $sIn = preg_replace( '`AIN$`', 'IN', $sIn );
-    $sIn = preg_replace( '`AIN([BTDK])`', 'IN$1', $sIn );
-    // UN -> IN
-    $sIn = preg_replace( '`([^O])UND`', '$1IND', $sIn ); // aucun mot français ne commence par UND!
-    $sIn = preg_replace( '`([JTVLFMRPSBD])UN([^IAE])`', '$1IN$2', $sIn );
-    $sIn = preg_replace( '`([JTVLFMRPSBD])UN$`', '$1IN', $sIn );
-    $sIn = preg_replace( '`RFUM$`', 'RFIN', $sIn );
-    $sIn = str_replace( 'LUMB', 'LINB', $sIn );
-    // EN -> AN
-    $sIn = preg_replace( '`([^BCDFGHJKLMNPQRSTVWXZ])EN`', '$1AN', $sIn );
-    $sIn = preg_replace( '`([VTLJMRPDSBFKNG])EN([BRCTDKZSVN])`', '$1AN$2', $sIn ); // deux fois pour les motifs recouvrants malentendu, pendentif, ...
-    $sIn = preg_replace( '`([VTLJMRPDSBFKNG])EN([BRCTDKZSVN])`', '$1AN$2', $sIn ); // si quelqu'un sait faire avec une seule regexp!
-    $sIn = preg_replace( '`^EN([BCDFGHJKLNPQRSTVXZ]|CH|IV|ORG|OB|UI|UA|UY)`', 'AN$1', $sIn );
-    $sIn = preg_replace( '`(^[JRVTH])EN([DRTFGSVJMP])`', '$1AN$2', $sIn );
-    $sIn = preg_replace( '`SEN([ST])`', 'SAN$1', $sIn );
-    $sIn = preg_replace( '`^DESENIV`', 'DESANIV', $sIn );
-    $sIn = preg_replace( '`([^M])EN(UI)`', '$1AN$2', $sIn );
-    $sIn = preg_replace( '`(.+[JTVLFMRPSBD])EN([JLFDSTG])`', '$1AN$2', $sIn );
-    // EI -> AI
-    $sIn = preg_replace( '`([VSBSTNRLPM])E[IY]([ACDFRJLGZ])`', '$1AI$2', $sIn );
-
-    // Histoire d'Ô
-    $convNasIn  = array( 'EAU', 'EU',  'Y', 'EOI', 'JEA','OIEM','OUANJ','OUA','OUENJ');
-    $convNasOut = array(   'O',  'E',  'I',  'OI', 'JA' ,'OIM' ,'OUENJ', 'OI','OUANJ');
-    $sIn = str_replace( $convNasIn, $convNasOut, $sIn );
-    $sIn = preg_replace( '`AU([^E])`', 'O$1', $sIn ); // AU sans E qui suit
-
-    // Les retouches!
-    $sIn = preg_replace( '`^BENJ`', 'BINJ', $sIn );       // retouche BENJ -> BINJ
-    $sIn = str_replace( 'RTIEL', 'RSIEL', $sIn );     // retouche RTIEL -> RSIEL
-    $sIn = str_replace( 'PINK', 'PONK', $sIn );       // retouche PINK -> PONK
-    $sIn = str_replace( 'KIND', 'KOND', $sIn );       // retouche KIND -> KOND
-    $sIn = preg_replace( '`KUM(N|P)`', 'KON$1', $sIn );     // retouche KUMN KUMP
-    $sIn = str_replace( 'LKOU', 'LKO', $sIn );        // retouche LKOU -> LKO
-    $sIn = str_replace( 'EDBE', 'EBE', $sIn );        // retouche EDBE pied-bœuf
-    $sIn = str_replace( 'ARCM', 'ARKM', $sIn );       // retouche SCH -> CH
-    $sIn = str_replace( 'SCH', 'CH', $sIn );          // retouche SCH -> CH
-    $sIn = preg_replace( '`^OINI`', 'ONI', $sIn );        // retouche début OINI -> ONI
-    $sIn = preg_replace( '`([^NDCGRHKO])APT`', '$1AT', $sIn );  // retouche APT -> AT
-    $sIn = preg_replace( '`([L]|KON)PT`', '$1T', $sIn );    // retouche LPT -> LT
-    $sIn = str_replace( 'OTB', 'OB', $sIn );          // retouche OTB -> OB (hautbois)
-    $sIn = str_replace( 'IXA', 'ISA', $sIn );       // retouche IXA -> ISA
-    $sIn = str_replace( 'TG', 'G', $sIn );          // retouche TG -> G
-    $sIn = preg_replace( '`^TZ`', 'TS', $sIn );         // retouche début TZ -> TS
-    $sIn = str_replace( 'PTIE', 'TIE', $sIn );        // retouche PTIE -> TIE
-    $sIn = str_replace( 'GT', 'T', $sIn );          // retouche GT -> T
-    $sIn = str_replace( 'ANKIEM', 'ANKILEM', $sIn );      // retouche tranquillement
-    $sIn = preg_replace( '`(LO|RE)KEMAN`', '$1KAMAN', $sIn ); // KEMAN -> KAMAN
-    $sIn = preg_replace( '`NT(B|M)`', 'N$1', $sIn );      // retouche TB -> B  TM -> M
-    $sIn = str_replace( 'GSU', 'SU', $sIn );          // retouche GS -> SU
-    $sIn = str_replace( 'ESD', 'ED', $sIn );          // retouche ESD -> ED
-    $sIn = str_replace( 'LESKEL', 'LEKEL', $sIn );      // retouche LESQUEL -> LEKEL
-    $sIn = str_replace( 'CK', 'K', $sIn );          // retouche CK -> K
-
-    // Terminaisons
-    $sIn = preg_replace( '`USIL$`', 'USI', $sIn );        // terminaisons USIL -> USI
-    $sIn = preg_replace( '`X$|[TD]S$|[DS]$`', '', $sIn );   // terminaisons TS DS LS X T D S...  v2.0
-    $sIn = preg_replace( '`([^KL]+)T$`', '$1', $sIn );      // sauf KT LT terminal
-    $sIn = preg_replace( '`^[H]`', '', $sIn );          // H pseudo muet en début de mot, je sais, ce n'est pas une terminaison
-    $sBack2=$sIn;                       // on sauve le code (utilisé pour les mots très courts)
-
-    $convPartIn = [
-                   '`TIL$`', // terminaisons TIL -> TI
-                   '`LC$`', // terminaisons LC -> LK
-                   '`L[E]?[S]?$`',      // terminaisons LE LES -> L
-                   '`(.+)N[E]?[S]?$`',    // terminaisons NE NES -> N
-                   '`EZ$`',         // terminaisons EZ -> E
-                   '`OIG$`',        // terminaisons OIG -> OI
-                   '`OUP$`',        // terminaisons OUP -> OU
-                   '`([^R])OM$`',     // terminaisons OM -> ON sauf ROM
-                   '`LOP$`',        // terminaisons LOP -> LO
-                   '`NTANP$`',      // terminaisons NTANP -> NTAN
-                   '`TUN$`',        // terminaisons TUN -> TIN
-                   '`AU$`',         // terminaisons AU -> O
-                   '`EI$`',         // terminaisons EI -> AI
-                   '`R[DG]$`',        // terminaisons RD RG -> R
-                   '`ANC$`',        // terminaisons ANC -> AN
-                   '`KROC$`',       // terminaisons C muet de CROC, ESCROC
-                   '`HOUC$`', // terminaisons C muet de CAOUTCHOUC
-                   '`OMAC$`',       // terminaisons C muet de ESTOMAC (mais pas HAMAC)
-                   '`([J])O([NU])[CG]$`',// terminaisons C et G muet de OUC ONC OUG
-                   '`([^GTR])([AO])NG$`',// terminaisons G muet ANG ONG sauf GANG GONG TANG TONG
-                   '`UC$`',         // terminaisons UC -> UK
-                   '`AING$`',       // terminaisons AING -> IN
-                   '`([EISOARN])C$`',   // terminaisons C -> K
-                   '`([ABD-MO-Z]+)[EH]+$`', // terminaisons E ou H sauf pour C et N
-                   '`EN$`',         // terminaisons EN -> AN (difficile à faire avant sans avoir des soucis) Et encore,
-                   '`(NJ)EN$`',// terminaisons EN -> AN
-                   '`^PAIEM`',      // PAIE -> PAI
-                   '`([^NTB])EF$`',     // F muet en fin de mot
-                   '`(.)\1`'        // supression des répétitions (suite à certains remplacements)
-    ];
-
-    $convPartOut = [
-                    'TI',       // terminaisons TIL -> TI
-                    'LK',         // terminaisons LC -> LK
-                    'L',      // terminaisons LE LES -> L
-                    '$1N',    // terminaisons NE NES -> N
-                    'E',          // terminaisons EZ -> E
-                    'OI',       // terminaisons OIG -> OI
-                    'OU',       // terminaisons OUP -> OU
-                    '$1ON',     // terminaisons OM -> ON sauf ROM
-                    'LO',       // terminaisons LOP -> LO
-                    'NTAN',     // terminaisons NTANP -> NTAN
-                    'TIN',        // terminaisons TUN -> TIN
-                    'O',          // terminaisons AU -> O
-                    'AI',         // terminaisons EI -> AI
-                    'R',        // terminaisons RD RG -> R
-                    'AN',       // terminaisons ANC -> AN
-                    'KRO',        // terminaisons C muet de CROC, ESCROC
-                    'HOU',        // terminaisons C muet de CAOUTCHOUC
-                    'OMA',        // terminaisons C muet de ESTOMAC (mais pas HAMAC)
-                    '$1O$2',// terminaisons C et G muet de OUC ONC OUG
-                    '$1$2N',// terminaisons G muet ANG ONG sauf GANG GONG TANG TONG
-                    'UK',         // terminaisons UC -> UK
-                    'IN',       // terminaisons AING -> IN
-                    '$1K',    // terminaisons C -> K
-                    '$1', // terminaisons E ou H sauf pour C et N
-                    'AN',         // terminaisons EN -> AN (difficile à faire avant sans avoir des soucis) Et encore, c'est pas top!
-                    '$1AN',     // terminaisons EN -> AN
-                    'PAIM',       // PAIE -> PAI
-                    '\1',     // F muet en fin de mot
-                    '$1'        // supression des répétitions (suite à certains remplacements)
-    ];
-
-    $sIn = preg_replace( $convPartIn, $convPartOut, $sIn );
-
-    // cas particuliers, bah au final, je n'en ai qu'un ici
-    $convPartIn  = array( 'FUEL');
-    $convPartOut = array( 'FIOUL');
-    $sIn = str_replace( $convPartIn, $convPartOut, $sIn );
-
-    // Ce sera le seul code retourné à une seule lettre!
-    if ($sIn=='O') return($sIn);
-
-    // seconde chance sur les mots courts qui ont souffert de la simplification
-    if (strlen($sIn)<2)
-      {
-        // Sigles ou abréviations
-        if (preg_match('`[BCDFGHJKLMNPQRSTVWXYZ][BCDFGHJKLMNPQRSTVWXYZ][BCDFGHJKLMNPQRSTVWXYZ][BCDFGHJKLMNPQRSTVWXYZ]*`',$sBack))
-          return($sBack);
-
-        if (preg_match('`[RFMLVSPJDF][AEIOU]`',$sBack))
-          {
-            if (strlen($sBack)==3)
-              return(substr($sBack,0,2));// mots de trois lettres supposés simples
-            if (strlen($sBack)==4)
-              return(substr($sBack,0,3));// mots de quatre lettres supposés simples
-          }
+    function phonetix($sIn) {
+      if (strlen($sIn)<4 || is_numeric($sIn))
+        return false;
+      return $this->phonetixCompute($sIn);
+    }
 
-        if (strlen($sBack2)>1) return $sBack2;
-      }
-    if (strlen($sIn)>1)
-      {
-        return substr($sIn,0,16); // Je limite à 16 caractères mais vous faites comme vous voulez!
-      }
-    else return '';
+    function phonetixCompute($sIn)  {
+      $sIn = strtr( $sIn, $this->accents);          // minuscules accentuées ou composées en majuscules simples
+      $sIn = strtr( $sIn, $this->min2maj);          // majuscules accentuées ou composées en majuscules simples
+      $sIn = strtoupper( $sIn );              // on passe tout le reste en majuscules
+      $sIn = preg_replace( '`[^A-Z]`', '', $sIn );  // on garde uniquement les lettres de A à Z
+
+      $sBack=$sIn;                  // on sauve le code (utilisé pour les mots très courts)
+
+      $sIn = preg_replace( '`O[O]+`', 'OU', $sIn );   // pré traitement OO... -> OU
+      $sIn = str_replace( 'SAOU', 'SOU', $sIn );  // pré traitement SAOU -> SOU
+      $sIn = str_replace( 'OES', 'OS', $sIn );  // pré traitement OES -> OS
+      $sIn = str_replace( 'CCH', 'K', $sIn );     // pré traitement CCH -> K
+      $sIn = preg_replace( '`CC([IYE])`', 'KS$1', $sIn ); // CCI CCY CCE
+      $sIn = preg_replace( '`(.)\1`', '$1', $sIn );   // supression des répétitions
+
+      // quelques cas particuliers
+      if ($sIn=='CD') return($sIn);
+      if ($sIn=='BD') return($sIn);
+      if ($sIn=='BV') return($sIn);
+      if ($sIn=='TABAC') return('TABA');
+      if ($sIn=='FEU') return('FE');
+      if ($sIn=='FE') return($sIn);
+      if ($sIn=='FER') return($sIn);
+      if ($sIn=='FIEF') return($sIn);
+      if ($sIn=='FJORD') return($sIn);
+      if ($sIn=='GOAL') return('GOL');
+      if ($sIn=='FLEAU') return('FLEO');
+      if ($sIn=='HIER') return('IER');
+      if ($sIn=='HEU') return('E');
+      if ($sIn=='HE') return('E');
+      if ($sIn=='OS') return($sIn);
+      if ($sIn=='RIZ') return('RI');
+      if ($sIn=='RAZ') return('RA');
+
+      // pré-traitements
+      $sIn = preg_replace( '`OIN[GT]$`', 'OIN', $sIn );                 // terminaisons OING -> OIN
+      $sIn = preg_replace( '`E[RS]$`', 'E', $sIn );                     // supression des terminaisons infinitifs et participes pluriels
+      $sIn = preg_replace( '`(C|CH)OEU`', 'KE', $sIn );                   // pré traitement OEU -> EU
+      $sIn = str_replace( 'MOEU', 'ME', $sIn );                     // pré traitement OEU -> EU
+      $sIn = preg_replace( '`OE([UI]+)([BCDFGHJKLMNPQRSTVWXZ])`', 'E$1$2', $sIn );    // pré traitement OEU OEI -> E
+      $sIn = preg_replace( '`^GEN[TS]$`', 'JAN', $sIn );                  // pré traitement GEN -> JAN
+      $sIn = str_replace( 'CUEI', 'KEI', $sIn );                    // pré traitement accueil
+      $sIn = preg_replace( '`([^AEIOUYC])AE([BCDFGHJKLMNPQRSTVWXZ])`', '$1E$2', $sIn );   // pré traitement AE -> E
+      $sIn = preg_replace( '`AE([QS])`', 'E$1', $sIn );                   // pré traitement AE -> E
+      $sIn = preg_replace( '`AIE([BCDFGJKLMNPQRSTVWXZ])`', 'AI$1', $sIn );        // pré-traitement AIE(consonne) -> AI
+      $sIn = str_replace( 'ANIEM', 'ANIM', $sIn );                  // pré traitement NIEM -> NIM
+      $sIn = preg_replace( '`(DRA|TRO|IRO)P$`', '$1', $sIn );               // P terminal muet
+      $sIn = preg_replace( '`(LOM)B$`', '$1', $sIn );                   // B terminal muet
+      $sIn = preg_replace( '`(RON|POR)C$`', '$1', $sIn );                 // C terminal muet
+      $sIn = preg_replace( '`PECT$`', 'PET', $sIn );                    // C terminal muet
+      $sIn = preg_replace( '`ECUL$`', 'CU', $sIn );                     // L terminal muet
+      $sIn = preg_replace( '`(CHA|CA|E)M(P|PS)$`', '$1N', $sIn );             // P ou PS terminal muet
+      $sIn = preg_replace( '`(TAN|RAN)G$`', '$1', $sIn );                 // G terminal muet
+
+
+      // sons YEUX
+      $sIn = preg_replace( '`([^VO])ILAG`', '$1IAJ', $sIn );
+      $sIn = preg_replace( '`([^TRH])UIL(AR|E)(.+)`', '$1UI$2$3', $sIn );
+      $sIn = preg_replace( '`([G])UIL([AEO])`', '$1UI$2', $sIn );
+      $sIn = preg_replace( '`([NSPM])AIL([AEO])`', '$1AI$2', $sIn );
+      $convMIn  = array('DILAI','DILON','DILER','DILEM','RILON','TAILE','GAILET','AILAI','AILAR',
+                        'OUILA','EILAI','EILAR','EILER','EILEM','REILET','EILET','AILOL' );
+      $convMOut = array( 'DIAI', 'DION','DIER', 'DIEM', 'RION', 'TAIE', 'GAIET', 'AIAI', 'AIAR',
+                        'OUIA', 'AIAI', 'AIAR', 'AIER', 'AIEM',  'RAIET', 'EIET', 'AIOL' );
+      $sIn = str_replace( $convMIn, $convMOut, $sIn );
+      $sIn = preg_replace( '`([^AEIOUY])(SC|S)IEM([EA])`', '$1$2IAM$3', $sIn );   // IEM -> IAM
+      $sIn = preg_replace( '`^(SC|S)IEM([EA])`', '$1IAM$2', $sIn );         // IEM -> IAM
+
+      // MP MB -> NP NB
+      $convMIn  = array( 'OMB', 'AMB', 'OMP', 'AMP', 'IMB', 'EMP','GEMB','EMB', 'UMBL','CIEN');
+      $convMOut = array( 'ONB', 'ANB', 'ONP', 'ANP', 'INB', 'ANP','JANB','ANB', 'INBL','SIAN');
+      $sIn = str_replace( $convMIn, $convMOut, $sIn );
+
+      // Sons en K
+      $sIn = preg_replace( '`^ECHO$`', 'EKO', $sIn );   // cas particulier écho
+      $sIn = preg_replace( '`^ECEUR`', 'EKEUR', $sIn );   // cas particulier écœuré
+      // Choléra Chœur mais pas chocolat!
+      $sIn = preg_replace( '`^CH(OG+|OL+|OR+|EU+|ARIS|M+|IRO|ONDR)`', 'K$1', $sIn );        //En début de mot
+      $sIn = preg_replace( '`(YN|RI)CH(OG+|OL+|OC+|OP+|OM+|ARIS|M+|IRO|ONDR)`', '$1K$2', $sIn );  //Ou devant une consonne
+      $sIn = preg_replace( '`CHS`', 'CH', $sIn );
+      $sIn = preg_replace( '`CH(AIQ)`', 'K$1', $sIn );
+      $sIn = preg_replace( '`^ECHO([^UIPY])`', 'EKO$1', $sIn );
+      $sIn = preg_replace( '`ISCH(I|E)`', 'ISK$1', $sIn );
+      $sIn = preg_replace( '`^ICHT`', 'IKT', $sIn );
+      $sIn = preg_replace( '`ORCHID`', 'ORKID', $sIn );
+      $sIn = preg_replace( '`ONCHIO`', 'ONKIO', $sIn );
+      $sIn = preg_replace( '`ACHIA`', 'AKIA', $sIn );     // retouche ACHIA -> AKIA
+      $sIn = preg_replace( '`([^C])ANICH`', '$1ANIK', $sIn ); // ANICH -> ANIK  1/2
+      $sIn = preg_replace( '`OMANIK`', 'OMANICH', $sIn );   // cas particulier  2/2
+      $sIn = preg_replace( '`ACHY([^D])`', 'AKI$1', $sIn );
+      $sIn = preg_replace( '`([AEIOU])C([BDFGJKLMNPQRTVWXZ])`', '$1K$2', $sIn ); // voyelle, C, consonne sauf H
+      $convPrIn  = array('EUCHA','YCHIA','YCHA','YCHO','YCHED','ACHEO','RCHEO','RCHES',
+                         'ECHN','OCHTO','CHORA','CHONDR','CHORE','MACHM','BRONCHO','LICHOS','LICHOC');
+      $convPrOut = array('EKA',  'IKIA', 'IKA', 'IKO',  'IKED','AKEO','RKEO',  'RKES',
+                         'EKN',  'OKTO', 'KORA', 'KONDR' ,'KORE' ,'MAKM', 'BRONKO', 'LIKOS', 'LIKOC');
+      $sIn = str_replace( $convPrIn, $convPrOut, $sIn );
+
+      // Weuh (perfectible)
+      $convPrIn  = array( 'WA','WO', 'WI','WHI','WHY','WHA','WHO');
+      $convPrOut = array( 'OI', 'O','OUI','OUI','OUI','OUA', 'OU');
+      $sIn = str_replace( $convPrIn, $convPrOut, $sIn );
+
+      // Gueu, Gneu, Jeu et quelques autres
+      $convPrIn  = array( 'GNES','GNET','GNER','GNE',  'GI', 'GNI','GNA','GNOU','GNUR','GY','OUGAIN',
+                         'AGEOL', 'AGEOT','GEOLO','GEOM','GEOP','GEOG','GEOS','GEORG','GEOR','NGEOT','UGEOT','GEOT','GEOD','GEOC','GEO','GEA','GE',
+                         'QU', 'Q',  'CY', 'CI', 'CN','ICM','CEAT','CE',
+                         'CR', 'CO', 'CUEI','CU', 'VENCA','CA', 'CS','CLEN','CL', 'CZ', 'CTIQ',
+                         'CTIF','CTIC','CTIS','CTIL','CTIO','CTI', 'CTU', 'CTE','CTO','CTR','CT', 'PH', 'TH',
+                         'OW', 'LH', 'RDL', 'CHLO', 'CHR', 'PTIA');
+      $convPrOut = array( 'NIES','NIET','NIER', 'NE',  'JI',  'NI','NIA','NIOU','NIUR','JI','OUGIN',
+                         'AJOL',  'AJOT','JEOLO','JEOM','JEOP','JEOG','JEOS','JORJ','JEOR','NJOT','UJOT','JEOT','JEOD','JEOC', 'JO','JA' ,'JE',
+                         'K', 'K',  'SI', 'SI', 'KN','IKM', 'SAT','SE',
+                         'KR', 'KO', 'KEI','KU', 'VANSA','KA', 'KS','KLAN','KL', 'KZ', 'KTIK',
+                         'KTIF','KTIS','KTIS','KTIL','KSIO','KTI', 'KTU', 'KTE','KTO','KTR','KT', 'F',  'T',
+                         'OU',  'L',  'RL',  'KLO',  'KR', 'PSIA');
+      $sIn = str_replace( $convPrIn, $convPrOut, $sIn );
+
+      $sIn = preg_replace( '`GU([^RLMBSTPZN])`', 'G$1', $sIn ); // Gueu !
+      $sIn = preg_replace( '`GNO([MLTNRKG])`', 'NIO$1', $sIn ); // GNO ! Tout sauf S pour gnos
+      $sIn = preg_replace( '`GNO([MLTNRKG])`', 'NIO$1', $sIn ); // bis -> gnognotte! Si quelqu'un sait le faire en une seule regexp...
+
+
+      // TI -> SI v2.0
+      $convPrIn  = array( 'BUTIE','BUTIA','BATIA','ANTIEL','RETION','ENTIEL','ENTIAL','ENTIO','ENTIAI','UJETION','ATIEM','PETIEN',
+                         'CETIE','OFETIE','IPETI','LBUTION','BLUTION','LETION','LATION','SATIET');
+      $convPrOut = array( 'BUSIE','BUSIA','BASIA','ANSIEL','RESION','ENSIEL','ENSIAL','ENSIO','ENSIAI','UJESION','ASIAM','PESIEN',
+                         'CESIE','OFESIE','IPESI','LBUSION','BLUSION','LESION','LASION','SASIET');
+      $sIn = str_replace( $convPrIn, $convPrOut, $sIn );
+      $sIn = preg_replace( '`(.+)ANTI(AL|O)`', '$1ANSI$2', $sIn ); // sauf antialcoolique, antialbumine, antialarmer, ...
+      $sIn = preg_replace( '`(.+)INUTI([^V])`', '$1INUSI$2', $sIn ); // sauf inutilité, inutilement, diminutive, ...
+      $sIn = preg_replace( '`([^O])UTIEN`', '$1USIEN', $sIn ); // sauf soutien, ...
+      $sIn = preg_replace( '`([^DE])RATI[E]$`', '$1RASI$2', $sIn ); // sauf xxxxxcratique, ...
+      // TIEN TION -> SIEN SION v3.1
+      $sIn = preg_replace( '`([^SNEU]|KU|KO|RU|LU|BU|TU|AU)T(IEN|ION)`', '$1S$2', $sIn );
+
+
+      // H muet
+      $sIn = preg_replace( '`([^CS])H`', '$1', $sIn );  // H muet
+      $sIn = str_replace( 'ESH', 'ES', $sIn );      // H muet
+      $sIn = str_replace( 'NSH', 'NS', $sIn );      // H muet
+      $sIn = str_replace( 'SH', 'CH', $sIn );       // ou pas!
+
+      // NASALES
+      $convNasIn  = array( 'OMT','IMB', 'IMP','UMD','TIENT','RIENT','DIENT','IEN',
+                          'YMU','YMO','YMA','YME', 'YMI','YMN','YM', 'AHO','FAIM','DAIM','SAIM','EIN','AINS');
+      $convNasOut = array( 'ONT','INB', 'INP','OND','TIANT','RIANT','DIANT', 'IN',
+                          'IMU','IMO','IMA','IME', 'IMI','IMN','IN',  'AO', 'FIN','DIN', 'SIN','AIN','INS');
+      $sIn = str_replace( $convNasIn, $convNasOut, $sIn );
+      // AIN -> IN v2.0
+      $sIn = preg_replace( '`AIN$`', 'IN', $sIn );
+      $sIn = preg_replace( '`AIN([BTDK])`', 'IN$1', $sIn );
+      // UN -> IN
+      $sIn = preg_replace( '`([^O])UND`', '$1IND', $sIn ); // aucun mot français ne commence par UND!
+      $sIn = preg_replace( '`([JTVLFMRPSBD])UN([^IAE])`', '$1IN$2', $sIn );
+      $sIn = preg_replace( '`([JTVLFMRPSBD])UN$`', '$1IN', $sIn );
+      $sIn = preg_replace( '`RFUM$`', 'RFIN', $sIn );
+      $sIn = str_replace( 'LUMB', 'LINB', $sIn );
+      // EN -> AN
+      $sIn = preg_replace( '`([^BCDFGHJKLMNPQRSTVWXZ])EN`', '$1AN', $sIn );
+      $sIn = preg_replace( '`([VTLJMRPDSBFKNG])EN([BRCTDKZSVN])`', '$1AN$2', $sIn ); // deux fois pour les motifs recouvrants malentendu, pendentif, ...
+      $sIn = preg_replace( '`([VTLJMRPDSBFKNG])EN([BRCTDKZSVN])`', '$1AN$2', $sIn ); // si quelqu'un sait faire avec une seule regexp!
+      $sIn = preg_replace( '`^EN([BCDFGHJKLNPQRSTVXZ]|CH|IV|ORG|OB|UI|UA|UY)`', 'AN$1', $sIn );
+      $sIn = preg_replace( '`(^[JRVTH])EN([DRTFGSVJMP])`', '$1AN$2', $sIn );
+      $sIn = preg_replace( '`SEN([ST])`', 'SAN$1', $sIn );
+      $sIn = preg_replace( '`^DESENIV`', 'DESANIV', $sIn );
+      $sIn = preg_replace( '`([^M])EN(UI)`', '$1AN$2', $sIn );
+      $sIn = preg_replace( '`(.+[JTVLFMRPSBD])EN([JLFDSTG])`', '$1AN$2', $sIn );
+      // EI -> AI
+      $sIn = preg_replace( '`([VSBSTNRLPM])E[IY]([ACDFRJLGZ])`', '$1AI$2', $sIn );
+
+      // Histoire d'Ô
+      $convNasIn  = array( 'EAU', 'EU',  'Y', 'EOI', 'JEA','OIEM','OUANJ','OUA','OUENJ');
+      $convNasOut = array(   'O',  'E',  'I',  'OI', 'JA' ,'OIM' ,'OUENJ', 'OI','OUANJ');
+      $sIn = str_replace( $convNasIn, $convNasOut, $sIn );
+      $sIn = preg_replace( '`AU([^E])`', 'O$1', $sIn ); // AU sans E qui suit
+
+      // Les retouches!
+      $sIn = preg_replace( '`^BENJ`', 'BINJ', $sIn );       // retouche BENJ -> BINJ
+      $sIn = str_replace( 'RTIEL', 'RSIEL', $sIn );     // retouche RTIEL -> RSIEL
+      $sIn = str_replace( 'PINK', 'PONK', $sIn );       // retouche PINK -> PONK
+      $sIn = str_replace( 'KIND', 'KOND', $sIn );       // retouche KIND -> KOND
+      $sIn = preg_replace( '`KUM(N|P)`', 'KON$1', $sIn );     // retouche KUMN KUMP
+      $sIn = str_replace( 'LKOU', 'LKO', $sIn );        // retouche LKOU -> LKO
+      $sIn = str_replace( 'EDBE', 'EBE', $sIn );        // retouche EDBE pied-bœuf
+      $sIn = str_replace( 'ARCM', 'ARKM', $sIn );       // retouche SCH -> CH
+      $sIn = str_replace( 'SCH', 'CH', $sIn );          // retouche SCH -> CH
+      $sIn = preg_replace( '`^OINI`', 'ONI', $sIn );        // retouche début OINI -> ONI
+      $sIn = preg_replace( '`([^NDCGRHKO])APT`', '$1AT', $sIn );  // retouche APT -> AT
+      $sIn = preg_replace( '`([L]|KON)PT`', '$1T', $sIn );    // retouche LPT -> LT
+      $sIn = str_replace( 'OTB', 'OB', $sIn );          // retouche OTB -> OB (hautbois)
+      $sIn = str_replace( 'IXA', 'ISA', $sIn );       // retouche IXA -> ISA
+      $sIn = str_replace( 'TG', 'G', $sIn );          // retouche TG -> G
+      $sIn = preg_replace( '`^TZ`', 'TS', $sIn );         // retouche début TZ -> TS
+      $sIn = str_replace( 'PTIE', 'TIE', $sIn );        // retouche PTIE -> TIE
+      $sIn = str_replace( 'GT', 'T', $sIn );          // retouche GT -> T
+      $sIn = str_replace( 'ANKIEM', 'ANKILEM', $sIn );      // retouche tranquillement
+      $sIn = preg_replace( '`(LO|RE)KEMAN`', '$1KAMAN', $sIn ); // KEMAN -> KAMAN
+      $sIn = preg_replace( '`NT(B|M)`', 'N$1', $sIn );      // retouche TB -> B  TM -> M
+      $sIn = str_replace( 'GSU', 'SU', $sIn );          // retouche GS -> SU
+      $sIn = str_replace( 'ESD', 'ED', $sIn );          // retouche ESD -> ED
+      $sIn = str_replace( 'LESKEL', 'LEKEL', $sIn );      // retouche LESQUEL -> LEKEL
+      $sIn = str_replace( 'CK', 'K', $sIn );          // retouche CK -> K
+
+      // Terminaisons
+      $sIn = preg_replace( '`USIL$`', 'USI', $sIn );        // terminaisons USIL -> USI
+      $sIn = preg_replace( '`X$|[TD]S$|[DS]$`', '', $sIn );   // terminaisons TS DS LS X T D S...  v2.0
+      $sIn = preg_replace( '`([^KL]+)T$`', '$1', $sIn );      // sauf KT LT terminal
+      $sIn = preg_replace( '`^[H]`', '', $sIn );          // H pseudo muet en début de mot, je sais, ce n'est pas une terminaison
+      $sBack2=$sIn;                       // on sauve le code (utilisé pour les mots très courts)
+
+      $convPartIn = [
+                     '`TIL$`', // terminaisons TIL -> TI
+                     '`LC$`', // terminaisons LC -> LK
+                     '`L[E]?[S]?$`',      // terminaisons LE LES -> L
+                     '`(.+)N[E]?[S]?$`',    // terminaisons NE NES -> N
+                     '`EZ$`',         // terminaisons EZ -> E
+                     '`OIG$`',        // terminaisons OIG -> OI
+                     '`OUP$`',        // terminaisons OUP -> OU
+                     '`([^R])OM$`',     // terminaisons OM -> ON sauf ROM
+                     '`LOP$`',        // terminaisons LOP -> LO
+                     '`NTANP$`',      // terminaisons NTANP -> NTAN
+                     '`TUN$`',        // terminaisons TUN -> TIN
+                     '`AU$`',         // terminaisons AU -> O
+                     '`EI$`',         // terminaisons EI -> AI
+                     '`R[DG]$`',        // terminaisons RD RG -> R
+                     '`ANC$`',        // terminaisons ANC -> AN
+                     '`KROC$`',       // terminaisons C muet de CROC, ESCROC
+                     '`HOUC$`', // terminaisons C muet de CAOUTCHOUC
+                     '`OMAC$`',       // terminaisons C muet de ESTOMAC (mais pas HAMAC)
+                     '`([J])O([NU])[CG]$`',// terminaisons C et G muet de OUC ONC OUG
+                     '`([^GTR])([AO])NG$`',// terminaisons G muet ANG ONG sauf GANG GONG TANG TONG
+                     '`UC$`',         // terminaisons UC -> UK
+                     '`AING$`',       // terminaisons AING -> IN
+                     '`([EISOARN])C$`',   // terminaisons C -> K
+                     '`([ABD-MO-Z]+)[EH]+$`', // terminaisons E ou H sauf pour C et N
+                     '`EN$`',         // terminaisons EN -> AN (difficile à faire avant sans avoir des soucis) Et encore,
+                     '`(NJ)EN$`',// terminaisons EN -> AN
+                     '`^PAIEM`',      // PAIE -> PAI
+                     '`([^NTB])EF$`',     // F muet en fin de mot
+                     '`(.)\1`'        // supression des répétitions (suite à certains remplacements)
+      ];
+
+      $convPartOut = [
+                      'TI',       // terminaisons TIL -> TI
+                      'LK',         // terminaisons LC -> LK
+                      'L',      // terminaisons LE LES -> L
+                      '$1N',    // terminaisons NE NES -> N
+                      'E',          // terminaisons EZ -> E
+                      'OI',       // terminaisons OIG -> OI
+                      'OU',       // terminaisons OUP -> OU
+                      '$1ON',     // terminaisons OM -> ON sauf ROM
+                      'LO',       // terminaisons LOP -> LO
+                      'NTAN',     // terminaisons NTANP -> NTAN
+                      'TIN',        // terminaisons TUN -> TIN
+                      'O',          // terminaisons AU -> O
+                      'AI',         // terminaisons EI -> AI
+                      'R',        // terminaisons RD RG -> R
+                      'AN',       // terminaisons ANC -> AN
+                      'KRO',        // terminaisons C muet de CROC, ESCROC
+                      'HOU',        // terminaisons C muet de CAOUTCHOUC
+                      'OMA',        // terminaisons C muet de ESTOMAC (mais pas HAMAC)
+                      '$1O$2',// terminaisons C et G muet de OUC ONC OUG
+                      '$1$2N',// terminaisons G muet ANG ONG sauf GANG GONG TANG TONG
+                      'UK',         // terminaisons UC -> UK
+                      'IN',       // terminaisons AING -> IN
+                      '$1K',    // terminaisons C -> K
+                      '$1', // terminaisons E ou H sauf pour C et N
+                      'AN',         // terminaisons EN -> AN (difficile à faire avant sans avoir des soucis) Et encore, c'est pas top!
+                      '$1AN',     // terminaisons EN -> AN
+                      'PAIM',       // PAIE -> PAI
+                      '\1',     // F muet en fin de mot
+                      '$1'        // supression des répétitions (suite à certains remplacements)
+      ];
+
+      $sIn = preg_replace( $convPartIn, $convPartOut, $sIn );
+
+      // cas particuliers, bah au final, je n'en ai qu'un ici
+      $convPartIn  = array( 'FUEL');
+      $convPartOut = array( 'FIOUL');
+      $sIn = str_replace( $convPartIn, $convPartOut, $sIn );
+
+      // Ce sera le seul code retourné à une seule lettre!
+      if ($sIn=='O') return($sIn);
+
+      // seconde chance sur les mots courts qui ont souffert de la simplification
+      if (strlen($sIn)<2)
+        {
+          // Sigles ou abréviations
+          if (preg_match('`[BCDFGHJKLMNPQRSTVWXYZ][BCDFGHJKLMNPQRSTVWXYZ][BCDFGHJKLMNPQRSTVWXYZ][BCDFGHJKLMNPQRSTVWXYZ]*`',$sBack))
+            return($sBack);
+
+          if (preg_match('`[RFMLVSPJDF][AEIOU]`',$sBack))
+            {
+              if (strlen($sBack)==3)
+                return(substr($sBack,0,2));// mots de trois lettres supposés simples
+              if (strlen($sBack)==4)
+                return(substr($sBack,0,3));// mots de quatre lettres supposés simples
+            }
+
+          if (strlen($sBack2)>1) return $sBack2;
+        }
+      if (strlen($sIn)>1)
+        {
+          return substr($sIn,0,16); // Je limite à 16 caractères mais vous faites comme vous voulez!
+        }
+      else return '';
+    }
   }
-}
 ?>
\ No newline at end of file
diff --git a/library/Class/MoteurRecherche.php b/library/Class/MoteurRecherche.php
index 0651d991d0599e11241f6524f8a79c1f7d508f5e..a8fdd7dce91fccc1824f4e5979802b24b1153430 100644
--- a/library/Class/MoteurRecherche.php
+++ b/library/Class/MoteurRecherche.php
@@ -440,6 +440,7 @@ class Class_MoteurRecherche {
       $this->limite_facettes;
 
     // Lancer les requetes
+
     $nb = fetchOne($req_comptage);
     if (!$nb && !$this->_shouldExtend()) {
       $this->addStatEchec(2, $criteres_recherche->getCriteres());
diff --git a/library/Class/NoticeOAI.php b/library/Class/NoticeOAI.php
index bffa805efed10870c1c3686014422a77ff92d1a3..04b3c4da4adf336ed527c19cb8697107c998e3fe 100644
--- a/library/Class/NoticeOAI.php
+++ b/library/Class/NoticeOAI.php
@@ -16,14 +16,14 @@
  *
  * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
  * along with BOKEH; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 class TableNoticesOAI extends Zend_Db_Table_Abstract {
     protected $_name = 'oai_notices';
-    
+
     public function insertOrUpdate($data) {
-      $statement = 
+      $statement =
         "INSERT INTO ".$this->_name." (date, id_entrepot, id_oai, alpha_titre, data, recherche) ".
         "VALUES ('".$data['date']."','".$data['id_entrepot']."','".$data['id_oai']."','".$data['alpha_titre']."','".$data['data']."','".$data['recherche']."') ".
         "ON DUPLICATE KEY UPDATE date='".$data['date']."', alpha_titre='".$data['alpha_titre']."', data='".$data['data']."', recherche='".$data['recherche']."';";
@@ -36,11 +36,11 @@ class Class_NoticeOAI extends Storm_Model_Abstract {
   protected $_table_name = 'oai_notices';
   protected $_belongs_to = array('entrepot' => array('model' => 'Class_EntrepotOAI',
                                                      'referenced_in' => 'id_entrepot'));
-  
+
   protected $_table_notices;
   protected $_oai_service;
   protected $_entrepot_oai;
-  protected $_default_attribute_values = array('data' => '', 
+  protected $_default_attribute_values = array('data' => '',
                                                'date' => '');
 
 
@@ -76,7 +76,7 @@ class Class_NoticeOAI extends Storm_Model_Abstract {
       ->setOAIHandler($this->_entrepot_oai->getHandler())
       ->getRecordsFromSet($set);
 
-    $this->saveRecords($records);   
+    $this->saveRecords($records);
     return $this->getOAIService()->getListRecordsResumptionToken();
   }
 
@@ -124,7 +124,7 @@ class Class_NoticeOAI extends Storm_Model_Abstract {
       return null;
 
     $records = $oai_service->getNextRecords();
-    $this->saveRecords($records);   
+    $this->saveRecords($records);
     return $oai_service->getListRecordsResumptionToken();
   }
 
@@ -164,7 +164,7 @@ class Class_NoticeOAI extends Storm_Model_Abstract {
       $this->_indexation = new Class_Indexation();
     return $this->_indexation;
   }
-  
+
   protected function toAlpha($data) {
     return $this->getIndexation()->alphaMaj($data);
   }
@@ -226,8 +226,8 @@ class Class_NoticeOAI extends Storm_Model_Abstract {
 
     $recherche=trim($recherche);
     if(!$recherche)  {
-      $ret["statut"]="erreur"; 
-      $ret["erreur"]=$translate->_("Il n'y aucun mot assez significatif pour la recherche"); 
+      $ret["statut"]="erreur";
+      $ret["erreur"]=$translate->_("Il n'y aucun mot assez significatif pour la recherche");
       return $ret;}
 
     // Constitution des requetes
@@ -258,7 +258,7 @@ class Class_NoticeOAI extends Storm_Model_Abstract {
 //------------------------------------------------------------------------------------------------------
   public function getPageResultat($req, $page = 1)  {
     // Nombre par page
-    $this->nb_par_page=10;
+    $this->nb_par_page=100;
 
     // Calcul de la limite
     $page = (int)$page ? (int)$page : 1;
@@ -310,7 +310,7 @@ class Class_NoticeOAI extends Storm_Model_Abstract {
     return $this->getEntrepot()->isGallica();
   }
 
- 
+
   public function getGallicaArkId() {
     if (!$this->isGallica())
       return '';
diff --git a/library/Class/ScriptLoader.php b/library/Class/ScriptLoader.php
index 038d72ad8c77f2787006275737009d9f8016a16c..d7eb99da60d09b8b021b20f2580095bff15d9db7 100644
--- a/library/Class/ScriptLoader.php
+++ b/library/Class/ScriptLoader.php
@@ -348,14 +348,14 @@ class Class_ScriptLoader {
    * voir [[file:~/public_html/afi-opac3/library/ZendAfi/Controller/Action/Helper/Notify.php::class%20ZendAfi_Controller_Action_Helper_Notify%20extends%20Zend_Controller_Action_Helper_Abstract%20{][ZendAfi_Controller_Action_Helper_Notify]]
    * @return ScriptLoader
    */
-  public function notify($message) {
+  public function notify($message, $autoclose=true, $duration=10, $type='information') {
     return $this
       ->loadNotificationJS()
       ->addJQueryReady(sprintf('showNotification(%s)',
                                json_encode(array('message' => $message,
-                                                 'autoClose' => true,
-                                                 'duration' => 10,
-                                                 'type' => 'information'))));
+                                                 'autoClose' => $autoclose,
+                                                 'duration' => $duration,
+                                                 'type' => $type))));
   }
 
 
diff --git a/library/ZendAfi/Controller/Plugin/CnilConsent.php b/library/ZendAfi/Controller/Plugin/CnilConsent.php
new file mode 100644
index 0000000000000000000000000000000000000000..cf0b78107ad59710d9a4948c5e9cb4ed50b41ab9
--- /dev/null
+++ b/library/ZendAfi/Controller/Plugin/CnilConsent.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+//////////////////////////////////////////////////////////////////////////////////////////
+// OPAC3 : Conformité CNIL (consentement avant l'insertion de cookies)
+//////////////////////////////////////////////////////////////////////////////////////////
+
+class ZendAfi_Controller_Plugin_CnilConsent extends Zend_Controller_Plugin_Abstract {
+  function preDispatch(Zend_Controller_Request_Abstract $request) {
+    if (Class_AdminVar::get('CNIL_CONSENT_ENABLE'))
+      (new Class_Cnil())->trackConsent();
+  }
+}
diff --git a/library/ZendAfi/View/Helper/Help/GoogleAnalyticsWarning.php b/library/ZendAfi/View/Helper/Help/GoogleAnalyticsWarning.php
new file mode 100644
index 0000000000000000000000000000000000000000..0cedc0e9dc0d213ffa4eb4d6409fa7f16b5ab6ad
--- /dev/null
+++ b/library/ZendAfi/View/Helper/Help/GoogleAnalyticsWarning.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ */
+class ZendAfi_View_Helper_Help_GoogleAnalyticsWarning extends Zend_View_Helper_HtmlElement {
+  public function help_googleAnalyticsWarning() {
+    return $this->view->tag('p', $this->view->_('Pour les mesures de trafic dites de “Webanalytics”, 
+notre site utilise les services de Google Analytics. Pour bloquer l’utilisation des données par Google Analytics : https://tools.google.com/dlpage/gaoptout'));
+  }
+}
diff --git a/library/startup.php b/library/startup.php
index f81b87dcdc822eecc20fd539722b7c467dc170e9..83b6cbc10a65528bc3277c659a89af90b5ebdc6d 100644
--- a/library/startup.php
+++ b/library/startup.php
@@ -64,7 +64,7 @@ function defineConstant($name, $value) {
 
 function setupConstants() {
   defineConstant('BOKEH_MAJOR_VERSION','7.3');
-  defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.15');
+  defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.16');
 
   defineConstant('BOKEH_REMOTE_FILES', 'http://git.afi-sa.fr/afi/opacce/');
 
@@ -276,6 +276,7 @@ function newFrontController() {
     ->registerPlugin(new ZendAfi_Controller_Plugin_CustomFields())
     ->registerPlugin(new ZendAfi_Controller_Plugin_Lectura())
     ->registerPlugin(new ZendAfi_Controller_Plugin_InspectorGadget())
+    ->registerPlugin(new ZendAfi_Controller_Plugin_CnilConsent())
     ->setParam('useDefaultControllerAlways', false);
 }
 
diff --git a/tests/application/modules/AbstractControllerTestCase.php b/tests/application/modules/AbstractControllerTestCase.php
index be56c01be3df550de44809ca3e72bbd87c5de396..9ba3b6e540b7921d70b366f91a073653e1a562b7 100644
--- a/tests/application/modules/AbstractControllerTestCase.php
+++ b/tests/application/modules/AbstractControllerTestCase.php
@@ -144,6 +144,10 @@ abstract class AbstractControllerTestCase extends Zend_Test_PHPUnit_ControllerTe
       ->newInstanceWithId('PACK_MOBILE')
       ->setValeur(1);
 
+    $admin_var_loader
+      ->newInstanceWithId('CNIL_CONSENT_ENABLE')
+      ->setValeur(false);
+
     Class_AdminVar::newInstanceWithId('JS_STAT', ['valeur' => '']);
     ZendAfi_Controller_Action_Helper_TrackEvent::setDefaultWebAnalyticsClient(null);
     Storm_Cache::setDefaultZendCache(null);
diff --git a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
index ee6ae1a1fb07a5230013423ea5a265a881597a6b..2242069e81ccfb796b8e3a6fae03d3b36a95cc04 100644
--- a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
+++ b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php
@@ -234,6 +234,12 @@ class AdminIndexControllerAdminVarActionAsAdminPortailTest extends Admin_Abstrac
   public function unexistingVarInDbShouldBeDisplayed() {
     $this->assertXpathContentContains('//td', 'AFFICHER_DISPONIBILITE_SUR_RECHERCHE');
   }
+
+
+  /** @test */
+  public function cnilVarShouldBeDisplay() {
+    $this->assertXPathContentContains('//td', 'CNIL_CONSENT_ENABLE');
+  }
 }
 
 
diff --git a/tests/application/modules/admin/controllers/OaiControllerTest.php b/tests/application/modules/admin/controllers/OaiControllerTest.php
index c411603899df347c605e2b8b10543865a5f83c51..e0ed8bfb0f095db426c0b2ce1622a1b1c6bb16b6 100644
--- a/tests/application/modules/admin/controllers/OaiControllerTest.php
+++ b/tests/application/modules/admin/controllers/OaiControllerTest.php
@@ -16,7 +16,7 @@
  *
  * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
  * along with BOKEH; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 require_once 'AdminAbstractControllerTestCase.php';
 
@@ -94,7 +94,7 @@ class Admin_OaiControllerAddActionTest extends Admin_OaiControllerTestCase  {
     $this->dispatch('/admin/oai/add');
   }
 
-  
+
   /** @test */
   public function pageShouldRenderEntrepotForm() {
     $this->assertXPath('//input[@name="libelle"]');
@@ -110,7 +110,7 @@ class Admin_OaiControllerEditActionTest extends Admin_OaiControllerTestCase  {
     $this->dispatch('/admin/oai/edit/id/4');
   }
 
-  
+
   /** @test */
   public function pageShouldRenderEntrepotForm() {
     $this->assertXPath('//input[@name="libelle"][@value="Gallica"]');
@@ -150,7 +150,7 @@ class Admin_OaiControllerBrowseGallicaActionTest extends Admin_OaiControllerTest
 
 class Admin_OaiControllerImportIsaacAsimovFoundationTest extends Admin_OaiControllerTestCase  {
   protected $_new_album;
-  
+
   public function setUp() {
     parent::setUp();
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_AlbumCategorie')
@@ -211,7 +211,7 @@ class Admin_OaiControllerImportIsaacAsimovFoundationTest extends Admin_OaiContro
 
   /** @test */
   public function idOrigineShouldBeArk() {
-    $this->assertEquals('http://gallica.bnf.fr/ark:/12148/bpt6k86704c', 
+    $this->assertEquals('http://gallica.bnf.fr/ark:/12148/bpt6k86704c',
                         $this->_new_album->getIdOrigine());
   }
 
@@ -263,7 +263,7 @@ class Admin_OaiControllerIndexWithSearchActionTest extends Admin_OaiControllerSe
       ->answers(1)
 
       ->whenCalled('fetchAll')
-      ->with("select id from oai_notices where MATCH(recherche) AGAINST('+(POMME POMMES POM)' IN BOOLEAN MODE) order by alpha_titre LIMIT 0,10",
+      ->with("select id from oai_notices where MATCH(recherche) AGAINST('+(POMME POMMES POM)' IN BOOLEAN MODE) order by alpha_titre LIMIT 0,100",
              false)
       ->answers(array(array('id' => 2)));
 
diff --git a/tests/application/modules/admin/controllers/UsersControllerTest.php b/tests/application/modules/admin/controllers/UsersControllerTest.php
index 8851f4524a09d45cb93fc5d40d9fa0f1bcd8f741..0c892f060deb7f8358e8c62f6fcde8ab0309d38c 100644
--- a/tests/application/modules/admin/controllers/UsersControllerTest.php
+++ b/tests/application/modules/admin/controllers/UsersControllerTest.php
@@ -1,4 +1,4 @@
- <?php
+<?php
 /**
  * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
  *
@@ -16,7 +16,7 @@
  *
  * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
  * along with BOKEH; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 require_once 'AbstractControllerTestCase.php';
 
@@ -68,7 +68,7 @@ abstract class UsersControllerWithMarcusTestCase extends AbstractControllerTestC
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_UserGroup')
       ->whenCalled('findAllBy')
       ->answers([]);
-    
+
     $this->assertTrue($this->marcus->isValid());
     $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');
 
@@ -115,7 +115,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->dispatch('/admin/users/edit/id/10', true);
   }
 
-  
+
   /** @test **/
   public function roleLevelShouldBeSIGBSubscriber() {
     $this->assertXpathContentContains('//tr/td','abonné identifié SIGB');
@@ -127,7 +127,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->assertXPath("//input[@name='username'][@value='mmiller']");
   }
 
-  
+
   /** @test **/
   public function testPasswordIsMysecret() {
     $this->assertXPath("//input[@name='password'][@value='mysecret']");
@@ -145,7 +145,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->assertXPath("//input[@name='prenom'][@value='Marcus']");
   }
 
-  
+
   /** @test **/
   public function testGroupesAreMultimediaAndReferent() {
     $this->assertXPath("//input[@name='id_items'][@value='20-22']",$this->_response->getBody());
@@ -163,7 +163,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->assertNotXPath("//select[@name='role']");
   }
 
-  
+
   /** @test **/
   public function testSelectedBibIsIdOne() {
     $this->assertXPath("//input[@name='bib'][@value='1']", $this->_response->getBody());
@@ -385,7 +385,7 @@ class UsersControllerPostMarcusDataTest extends UsersControllerWithMarcusTestCas
   public function testOrdreabonIsTwo() {
     $this->assertEquals(2, $this->marcus->getOrdreabon());
   }
-  
+
   public function testPseudoIsDave() {
     $this->assertEquals('Dave', $this->marcus->getPseudo());
   }
@@ -419,7 +419,7 @@ class UsersControllerPostMarcusInvalidDataTest extends UsersControllerWithMarcus
     $this->assertAction('edit');
     $this->assertQueryContentContains('span#abonne_erreur', "Vous devez compléter le champ 'Identifiant'");
     $this->assertQueryContentContains('span#abonne_erreur', "Vous devez compléter le champ 'Mot de passe'");
-    $this->assertQueryContentContains('span#abonne_erreur', 
+    $this->assertQueryContentContains('span#abonne_erreur',
                                       "La bibliothèque est obligatoire pour le rôle : administrateur bibliothèque");
   }
 
@@ -503,7 +503,7 @@ class UsersControllerPostValidDataWithCommOpsysTest extends UsersControllerWithM
     $this->_postData();
     $this->assertRedirectTo('/admin/users');
 
-    $this->assertEquals($this->marcus, 
+    $this->assertEquals($this->marcus,
                         $this->user_loader->getFirstAttributeForLastCallOn('save'));
   }
 
@@ -563,13 +563,13 @@ class UsersControllerReferentIndexTest extends UsersControllerWithMarcusTestCase
 
   public function setUp() {
     parent::setUp();
-    $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');    
+    $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');
     $this->user_loader->whenCalled('getIdentity')
       ->answers($user=Class_Users::newInstanceWithId(2)
                 ->setLogin('referent')
                 ->setRoleLevel(ZendAfi_Acl_AdminControllerRoles::MODO_PORTAIL)
                 ->setPseudo('referent'));
-    
+
     $this->addUserToRightsReferent($user);
 
     $this->dispatch('/admin/users', true);
@@ -594,7 +594,7 @@ class UsersControllerReferentIndexTest extends UsersControllerWithMarcusTestCase
     $this->assertNotXPathContentContains("//td",
                                          'Ajouter un utilisateur',$this->_response->getBody());
   }
-  
+
 
 
   /** @test **/
@@ -602,7 +602,7 @@ class UsersControllerReferentIndexTest extends UsersControllerWithMarcusTestCase
     $this->assertNotXPath("//a[contains(@href, '/users/edit')]",
                           $this->_response->getBody());
   }
-  
+
 
 }
 
@@ -614,7 +614,7 @@ class UsersControllerAddPostTest extends UsersControllerWithMarcusTestCase {
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Newsletter')
       ->whenCalled('findAllBy')
       ->answers([]);
-    $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');    
+    $this->user_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');
   }
 
 
@@ -666,7 +666,7 @@ class UsersControllerAddPostTest extends UsersControllerWithMarcusTestCase {
       ->with($this->marcus)
       ->never()
       ->getWrapper()
-      
+
       ->whenCalled('getIdentity')
       ->answers(Class_Users::getLoader()->newInstanceWithId(2)
                 ->setLogin('sysadm')
diff --git a/tests/application/modules/opac/controllers/HelpControllerTest.php b/tests/application/modules/opac/controllers/HelpControllerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..98b8746cf23f535c0a584d786dd5b2c04a128763
--- /dev/null
+++ b/tests/application/modules/opac/controllers/HelpControllerTest.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class HelpControlleriCnilComplianceTest extends AbstractControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $this->dispatch('/help/cookies', true);
+  }
+
+
+  /** @test */
+  public function pageShouldContainTitleQuEstCeQuUnCookie() {
+    $this->assertXPathContentContains('//h3', "Qu'est-ce qu'un cookie ?");
+  }
+
+
+  /** @test */
+  public function pageShouldExplainQuEstCeQuUnCookie() {
+    $this->assertXPathContentContains('//p', 'Un cookie est un fichier texte déposé, sous réserve de vos choix');
+  }
+
+
+  /** @test */
+  public function pageShouldContainTitleAQuoiServentLesCookiesEmisSurNotreSite() {
+    $this->assertXPathContentContains('//h3', "A quoi servent les cookies émis sur notre site ?");
+  }
+
+
+  /** @test */
+  public function pageShouldNotWarnAboutGoogleAnalytic() {
+    $this->assertNotXPathContentContains('//p', 'utilise les services de Google Analytics');
+  }
+}
+
+
+
+class HelpControlleriCnilComplianceWithGoogleAnalyticsTest extends AbstractControllerTestCase {
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture('Class_AdminVar', ['id' =>'JS_STAT',
+                                      'valeur' => "<script> var _gaq = _gaq ||
+                                                  []; _gaq.push(['_setAccount',
+                                                  'UA-41754005-1']);
+                                                  _gaq.push(['_trackPageview']);
+                                                  (function() { var ga =
+                                                  document.createElement('script');
+                                                  ga.type = 'text/javascript'; ga.async =
+                                                  true; ga.src = ('https:' ==
+                                                  document.location.protocol ?
+                                                  'https://ssl' : 'http://www') +
+                                                  '.google-analytics.com/ga.js'; var
+                                                  s =
+                                                  document.getElementsByTagName('script')[
+                                                  0]; s.parentNode.insertBefore(ga, s);
+                                                  })(); </script>"]);
+    $this->dispatch('/help/cookies', true);
+  }
+
+
+  /** @test */
+  public function pageShouldNotWarnAboutGoogleAnalytic() {
+    $this->assertXPathContentContains('//p', 'utilise les services de Google Analytics');
+  }
+}
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/IndexControllerTest.php b/tests/application/modules/opac/controllers/IndexControllerTest.php
index 57119c4ee9e56258d4d10582c418a4e7a687f2d3..e5f0bb5ddf8a5ffe144ae37c900d7a2315d0da2d 100644
--- a/tests/application/modules/opac/controllers/IndexControllerTest.php
+++ b/tests/application/modules/opac/controllers/IndexControllerTest.php
@@ -18,9 +18,9 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
-class IndexControllerSetupDomainTest extends AbstractControllerTestCase {
 
 
+class IndexControllerSetupDomainTest extends AbstractControllerTestCase {
   public function setUp() {
     parent::setUp();
     Class_Adminvar::beVolatile();
@@ -57,8 +57,6 @@ class IndexControllerSetupDomainTest extends AbstractControllerTestCase {
 
 
 class IndexControllerAsInviteTest extends AbstractControllerTestCase {
-
-
   protected function _loginHook($account) {
     $account->ROLE_LEVEL = ZendAfi_Acl_AdminControllerRoles::INVITE;
   }
@@ -126,7 +124,6 @@ class IndexControllerAsAdminWithCssEditorAndNoHeaderCss extends IndexControllerA
 
 
 
-
 class IndexControllerAsAdminWithCSSEditorTest extends IndexControllerAsAdminTestCase {
   public function setUp() {
     parent::setUp();
@@ -149,6 +146,7 @@ class IndexControllerAsAdminWithCSSEditorTest extends IndexControllerAsAdminTest
 }
 
 
+
 class IndexControllerWithInvitedLevelRestrictionForProfilTest extends AbstractControllerTestCase {
   public function setup() {
     parent::setup();
@@ -316,6 +314,122 @@ class IndexControllerRewriteUrlTest extends AbstractControllerTestCase {
 }
 
 
+
+abstract class IndexControllerCnilTrackingTestCase extends AbstractControllerTestCase {
+  protected $_storm_default_to_volatile = true;
+  protected $_expected_message = 'showNotification({"message":"En poursuivant votre navigation sur ce site, vous acceptez l\'utilisation de cookies.","autoClose":false';
+  protected $_cookie_jar;
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture('Class_AdminVar', ['id' => 'CNIL_CONSENT_ENABLE',
+                                      'valeur' => true]);
+
+    Class_Cnil::setTimeSource($this->mock()
+                              ->whenCalled('time')
+                              ->answers(strtotime('2015-09-25 15:45:54')));
+
+    Class_Cnil::setCookieJar($this->_cookie_jar = $this->mock()
+                             ->whenCalled('setcookie')
+                             ->with('cnil' . md5(BASE_URL),
+                                    1,
+                                    1476884754)
+                             ->answers(null));
+  }
+
+
+  public function tearDown() {
+    Class_Cnil::setCookieJar(null);
+    Class_Cnil::setTimeSource(null);
+    parent::tearDown();
+  }
+}
+
+
+
+class IndexControllerCnilTrackingFirstVisitTest
+  extends IndexControllerCnilTrackingTestCase {
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->dispatch('/', true);
+  }
+
+
+  /** @test */
+  public function shouldDisplayCnilMessage() {
+    $this->assertXPath('//script[contains(text(), "En poursuivant votre navigation")][contains(text(), "\/help\/cookies")][contains(text(), "En savoir plus")]', $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function shouldSetCnilCookieFor13Months() {
+    $this->assertTrue($this->_cookie_jar->methodHasBeenCalled('setcookie'));
+  }
+}
+
+
+
+
+class IndexControllerCnilTrackingFirstVisitWithAdminVarDisabledTest
+  extends IndexControllerCnilTrackingTestCase {
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->fixture('Class_AdminVar', ['id' => 'CNIL_CONSENT_ENABLE',
+                                      'valeur' => false]);
+    $this->dispatch('/', true);
+  }
+
+
+  public function shouldDisplayCnilMessage() {
+    $this->assertNotXPathContentContains('//script', $this->_expected_message);
+  }
+}
+
+
+
+
+class IndexControllerCnilTrackingSecondVisitTest
+  extends IndexControllerCnilTrackingTestCase {
+
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('/', true);
+    $this->_response->setBody('');
+    Class_ScriptLoader::resetInstance();
+    $this->dispatch('/', true);
+  }
+
+
+  /** @test */
+  public function shouldNotDisplayCnilMessageOf() {
+    $this->assertNotXPathContentContains('//script', $this->_expected_message);
+  }
+}
+
+
+
+class IndexControllerCnilTrackingExistingCookieVisitTest
+  extends IndexControllerCnilTrackingTestCase {
+
+
+  /** @test */
+  public function shouldNotDisplayCnilMessage() {
+    $_COOKIE['cnil' . md5(BASE_URL)] = 1;
+    $this->dispatch('/', true);
+    $this->assertNotXPathContentContains('//script', $this->_expected_message);
+  }
+}
+
+
+
+
 class IndexControllerInspectorGadgetTest extends AbstractControllerTestCase {
   public function setUp() {
     parent::setUp();
diff --git a/tests/application/modules/opac/controllers/OAIControllerTest.php b/tests/application/modules/opac/controllers/OAIControllerTest.php
index daee27035b6b5c5012d8274bbd3d4588157c970c..84dc8970784095bf14abf6cec740bb4ecf7fe79d 100644
--- a/tests/application/modules/opac/controllers/OAIControllerTest.php
+++ b/tests/application/modules/opac/controllers/OAIControllerTest.php
@@ -207,7 +207,7 @@ class OAIControllerResultatActionTest extends AbstractControllerTestCase  {
       ->answers(1)
 
       ->whenCalled('fetchAll')
-      ->with("select id from oai_notices where MATCH(recherche) AGAINST('+(POMME POMMES POM)' IN BOOLEAN MODE) order by alpha_titre LIMIT 0,10",
+      ->with("select id from oai_notices where MATCH(recherche) AGAINST('+(POMME POMMES POM)' IN BOOLEAN MODE) order by alpha_titre LIMIT 0,100",
              false)
       ->answers(array(array('id' => 2)))
       ->beStrict();
diff --git a/tests/library/Class/ArticleCategorieTest.php b/tests/library/Class/ArticleCategorieTest.php
index 8d06fbf1fed035278c08832b3951e39fe108eca5..577bc2b52af6407d6643433d80b96892cf1140c1 100644
--- a/tests/library/Class/ArticleCategorieTest.php
+++ b/tests/library/Class/ArticleCategorieTest.php
@@ -16,7 +16,7 @@
  *
  * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
  * along with BOKEH; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 class ArticleCategorieTest extends Storm_Test_ModelTestCase {
@@ -82,7 +82,7 @@ class ArticleCategorieTest extends Storm_Test_ModelTestCase {
       ->answers(true)
       ->getWrapper();
 
-    
+
     $this->concert_truffaz = Class_Article::newInstanceWithId(23, ['id_cat' => 34,
                                                                    'titre' => 'Concert Truffaz']);
 
@@ -169,9 +169,9 @@ class ArticleCategorieTest extends Storm_Test_ModelTestCase {
   }
 
 
-  /** 
+  /**
    * Régression vue sur Pontault
-   * @test 
+   * @test
    */
   public function annecyWithCarriageReturnToJSONShouldRemoveCR() {
     $this->annecy->setLibelle("Annecy\n");
@@ -317,11 +317,11 @@ JSON;
 
   /** @test */
   public function findByPathAdulteConcertsJazzShouldAnswerCategorieJazz() {
-    $this->assertEquals($this->cat_concerts_jazz, 
+    $this->assertEquals($this->cat_concerts_jazz,
                         Class_ArticleCategorie::getRoot()->findByPath('/Adulte/Concerts/Jazz'));
   }
 
-  
+
   /** @test **/
   public function profilConcertPathAndCategorieConcertPathShouldBeEquals() {
     $this->assertEquals($this->cat_concerts->getPath(), $this->concert_profil->getPath());
@@ -334,4 +334,27 @@ JSON;
   }
 }
 
-?>
\ No newline at end of file
+
+
+class ArticleCategorieParentTest extends Storm_Test_ModelTestCase {
+  protected $_news;
+
+  public function setUp() {
+    parent::setUp();
+
+    $annecy = $this->fixture('Class_Bib',
+                             ['id' => 1,
+                              'libelle' => 'Annecy']);
+
+    $this->_news = $this->fixture('Class_ArticleCategorie',
+                                  ['id' => 5,
+                                   'id_cat_mere' => 5,
+                                   'libelle' => 'News']);
+  }
+
+
+  /** @test */
+  public function newsParentCategorieShouldBe0() {
+    $this->assertEquals('0', $this->_news->getIdCatMere());
+  }
+}
\ No newline at end of file
diff --git a/tests/library/Class/MoteurRechercheTest.php b/tests/library/Class/MoteurRechercheTest.php
index c24ee55d06413b2dde5c5ae233bcb1b406f299ce..38f505c0c7202635549c9594017b66f620f88c30 100644
--- a/tests/library/Class/MoteurRechercheTest.php
+++ b/tests/library/Class/MoteurRechercheTest.php
@@ -154,6 +154,34 @@ class MoteurRechercheAvanceeTest extends MoteurRechercheTestCase {
              'req_comptage' => $this->countSqlWith("annee >='1960' and annee <='2013' and date_creation >'".$this->expected_date."' and (not matieres like 'PHILOSOPHIE%') and MATCH(facettes) AGAINST('+T1' IN BOOLEAN MODE)"),
              'req_facettes' => $this->facetSqlWith("annee >='1960' and annee <='2013' and date_creation >'".$this->expected_date."' and (not matieres like 'PHILOSOPHIE%') and MATCH(facettes) AGAINST('+T1' IN BOOLEAN MODE)")],
 
+            [['rech_matieres' => 'games',
+              'operateur_matieres' => 'and',
+              'facette' => '',
+              'annee_debut' => '',
+              'annee_fin' => '',
+              'nouveaute' => '',
+              'type_recherche' => '',
+              'pertinence' => true,
+              'tri' => 'alpha_titre'] ,
+             'req_notices' => $this->listSqlWith("(MATCH(matieres) AGAINST('(GAME GAMES GAM)' IN BOOLEAN MODE))",
+                                                 "alpha_titre"),
+             'req_comptage' => $this->countSqlWith("(MATCH(matieres) AGAINST('(GAME GAMES GAM)' IN BOOLEAN MODE))"),
+             'req_facettes' => $this->facetSqlWith("(MATCH(matieres) AGAINST('(GAME GAMES GAM)' IN BOOLEAN MODE))")],
+
+            [['rech_matieres' => 'PC',
+              'operateur_matieres' => 'and',
+              'facette' => '',
+              'annee_debut' => '',
+              'annee_fin' => '',
+              'nouveaute' => '',
+              'type_recherche' => '',
+              'pertinence' => true,
+              'tri' => 'alpha_titre'] ,
+             'req_notices' => $this->listSqlWith("(MATCH(matieres) AGAINST('(PC PC )' IN BOOLEAN MODE))",
+                                                 "alpha_titre"),
+             'req_comptage' => $this->countSqlWith("(MATCH(matieres) AGAINST('(PC PC )' IN BOOLEAN MODE))"),
+             'req_facettes' => $this->facetSqlWith("(MATCH(matieres) AGAINST('(PC PC )' IN BOOLEAN MODE))")],
+
             [['rech_titres' => 'Les nouveaux chiens de garde',
               'operateur_titres' => 'and',
               'rech_editeur' => 'Raisons d\'agir',
diff --git a/tests/library/Trait/UserGroupFixtures.php b/tests/library/Trait/UserGroupFixtures.php
index f5cd82eeed890ca12e5c4cdfd10065bdf5709f21..0d86dcb06056ec77c5efccddfc837ef0723e45d6 100644
--- a/tests/library/Trait/UserGroupFixtures.php
+++ b/tests/library/Trait/UserGroupFixtures.php
@@ -16,7 +16,7 @@
  *
  * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
  * along with BOKEH; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 trait Trait_UserGroupFixtures {
@@ -58,15 +58,10 @@ trait Trait_UserGroupFixtures {
                   ->newInstanceWithId(223)
                   ->setUserId($user->getId())
                   ->setUserGroupId(2222)]);
-    
+
     Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users')
       ->whenCalled('getIdentity')
       ->answers($user);
 
   }
-
-}
-
-?>
-
-
+}
\ No newline at end of file