From bc05896cab7cd61d0ae8e0d003b7c07406e9e03a Mon Sep 17 00:00:00 2001
From: gloas <gloas@afi-sa.fr>
Date: Mon, 18 May 2020 16:45:04 +0200
Subject: [PATCH] wip on theme store

---
 library/Class/Bib.php                         |  12 +
 library/Class/CodifAnnexe.php                 |   4 +-
 library/Class/Notice/Facette.php              |  22 ++
 library/Class/TypeDoc.php                     |   1 +
 library/ZendAfi/Form/Decorator/Custom.php     |   2 +-
 library/ZendAfi/Form/Decorator/HtmlTag.php    |  31 +++
 .../ZendAfi/Form/Decorator/HtmlTagWrapper.php |   2 +-
 library/ZendAfi/Form/Decorator/Link.php       |   2 +-
 library/ZendAfi/Form/Element/Custom.php       |   2 +-
 .../ZendAfi/Form/Element/NegativeCaptcha.php  |   1 +
 library/ZendAfi/View/Helper/Facettes.php      |  81 +++++--
 library/ZendAfi/View/Helper/FormButton.php    |  63 +++++
 library/ZendAfi/View/Helper/RenderForm.php    |   7 +-
 .../Intonation/Assets/css/intonation.css      |  31 ++-
 .../Library/Criteria/FromSelection.php        |  99 ++++++++
 .../Intonation/Library/Record/Items.php       |   4 +-
 .../templates/Intonation/Library/Settings.php |  62 +++++
 .../Library/View/Wrapper/Abstract.php         |   5 +
 .../Intonation/Library/View/Wrapper/Card.php  |   1 +
 .../Intonation/Library/View/Wrapper/Item.php  |   5 +-
 .../Intonation/Library/View/Wrapper/Loan.php  |  17 +-
 .../Library/View/Wrapper/Record.php           |  31 +++
 .../View/Wrapper/RecordInSelection.php        |   1 +
 .../Library/View/Wrapper/RecordToSelect.php   |   3 +-
 .../Library/View/Wrapper/Review.php           |   1 +
 .../Library/View/Wrapper/Selection.php        |   1 +
 .../RichContent/AddRecordsToSelection.php     |  10 +-
 .../User/RichContent/FollowASearch.php        |   2 +
 .../Library/Widget/Carousel/Record/View.php   |  29 +--
 .../Intonation/Library/Widget/Login/View.php  |  35 +--
 .../Intonation/Library/Widget/Nav/View.php    |  12 +-
 .../Library/Widget/Search/Definition.php      |   7 +-
 .../Intonation/Library/Widget/Search/Form.php |  40 +++-
 .../Intonation/Library/Widget/Search/View.php | 209 ++++++++++++----
 library/templates/Intonation/Template.php     |  84 ++++---
 .../Intonation/View/CardifyHorizontal.php     |   5 +-
 .../View/CardifyOnlyDescription.php           |   4 +-
 .../Intonation/View/CardifyWithOverlay.php    |   3 +-
 .../templates/Intonation/View/Jumbotron.php   |  34 +--
 .../Intonation/View/LibrariesWidget.php       |   6 +-
 library/templates/Intonation/View/Opac.php    |   2 +
 .../templates/Intonation/View/RenderForm.php  |  46 +++-
 .../Intonation/View/RenderInlineForm.php      |   2 +-
 .../Intonation/View/Search/Facets.php         | 224 +++---------------
 .../Intonation/View/Search/HtmlCriteria.php   |   2 +-
 .../Intonation/View/Search/Result.php         | 133 +++++------
 .../Intonation/View/Search/TextCriteria.php   |   6 +
 .../templates/Muscle/Assets/css/muscle.css    |  18 +-
 .../Polygone/Assets/css/polygone.css          |   1 +
 .../Assets/css/terredumilieu.css              |   3 +-
 public/opac/css/core.css                      |  12 +
 .../ZendAfi/View/Helper/FacettesTest.php      |   6 +-
 .../SearchResult/SearchResultTest.php         |   4 +-
 .../Templates/MuscleTemplateTest.php          |  23 ++
 tests/scenarios/Templates/TemplatesTest.php   |  10 +-
 55 files changed, 988 insertions(+), 475 deletions(-)
 create mode 100644 library/ZendAfi/Form/Decorator/HtmlTag.php
 create mode 100644 library/ZendAfi/View/Helper/FormButton.php
 create mode 100644 library/templates/Intonation/Library/Criteria/FromSelection.php

diff --git a/library/Class/Bib.php b/library/Class/Bib.php
index 57a69a360f1..423d7268209 100644
--- a/library/Class/Bib.php
+++ b/library/Class/Bib.php
@@ -187,6 +187,18 @@ class BibLoader extends Storm_Model_Loader {
       ->select('hasLongitude')
       ->getArrayCopy();
   }
+
+
+  public function getMultiOptionsFacets() {
+    $libraries = Class_Bib::findAllBy(['visibilite' => Class_Bib::V_DATA,
+                                       'order' => 'libelle']);
+
+    $facets = [];
+    foreach($libraries as $library)
+      $facets [Class_Bib::CODE_FACETTE . $library->getId()] = $library->getLibelle();
+
+    return $facets;
+  }
 }
 
 
diff --git a/library/Class/CodifAnnexe.php b/library/Class/CodifAnnexe.php
index 61aed280e3c..a7c32abe86b 100644
--- a/library/Class/CodifAnnexe.php
+++ b/library/Class/CodifAnnexe.php
@@ -49,12 +49,14 @@ class CodifAnnexeLoader extends Storm_Model_Loader {
 
   public function getMultiOptionsFacets() {
     $multi_options = $this->getMultiOptions();
-    $multi_options_facets = ['' => $this->_('tous')];
+    $multi_options_facets = [];
     foreach ($multi_options as $id => $label) {
       if (!$annexe = Class_CodifAnnexe::find($id))
         continue;
       $multi_options_facets[$annexe->getFacetCode()] = $annexe->getLabel();
     }
+    asort($multi_options_facets);
+    array_unshift($multi_options_facets, $this->_('tous'));
     return $multi_options_facets;
   }
 }
diff --git a/library/Class/Notice/Facette.php b/library/Class/Notice/Facette.php
index b2d62118d34..d08a3734ed4 100644
--- a/library/Class/Notice/Facette.php
+++ b/library/Class/Notice/Facette.php
@@ -143,4 +143,26 @@ class Class_Notice_Facette {
     return Class_CodifThesaurus::CODE_FACETTE === substr($this->_cle, 0, 1)
       && strlen($this->getValue()) === Class_CodifThesaurus::ID_KEY_LENGTH;
   }
+
+
+  public function isDocTypeFacet() {
+    return Class_CodifTypeDoc::CODE_FACETTE == $this->getGroupCodeFromKey();
+  }
+
+
+  public function isAnnexeFacet() {
+    return Class_CodifAnnexe::CODE_FACETTE == $this->getGroupCodeFromKey();
+  }
+
+
+  public function isLibraryFacet() {
+    return Class_Bib::CODE_FACETTE == $this->getGroupCodeFromKey();
+  }
+
+
+  public function isDomainFacet() {
+    $code = $this->getGroupCodeFromKey();
+    return Class_Catalogue::CODE_FACETTE == $code
+      || Class_CodifThesaurus::CODE_FACETTE == $this->_cle[0];
+  }
 }
\ No newline at end of file
diff --git a/library/Class/TypeDoc.php b/library/Class/TypeDoc.php
index 08639e57d5a..944cec2e306 100644
--- a/library/Class/TypeDoc.php
+++ b/library/Class/TypeDoc.php
@@ -214,6 +214,7 @@ class TypeDocLoader extends Storm_Model_Loader  {
         continue;
       $multi_options_facets[$doctype->getFacetCode()] = $doctype->getLabel();
     }
+
     return $multi_options_facets;
   }
 
diff --git a/library/ZendAfi/Form/Decorator/Custom.php b/library/ZendAfi/Form/Decorator/Custom.php
index 8024fa02ee9..2d7defdd209 100644
--- a/library/ZendAfi/Form/Decorator/Custom.php
+++ b/library/ZendAfi/Form/Decorator/Custom.php
@@ -20,7 +20,7 @@
  */
 
 
-class ZendAfi_Form_Decorator_Custom extends Zend_Form_Decorator_HtmlTag {
+class ZendAfi_Form_Decorator_Custom extends ZendAfi_Form_Decorator_HtmlTag {
 
   public function render($content) {
     $view = $this->_element->getView();
diff --git a/library/ZendAfi/Form/Decorator/HtmlTag.php b/library/ZendAfi/Form/Decorator/HtmlTag.php
new file mode 100644
index 00000000000..d2ea543d1b5
--- /dev/null
+++ b/library/ZendAfi/Form/Decorator/HtmlTag.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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_Form_Decorator_HtmlTag extends Zend_Form_Decorator_HtmlTag {
+
+
+  public function render($content) {
+    return $this->_element->getView()->tag($this->getTag(),
+                                           $content,
+                                           $this->getOptions());
+  }
+}
diff --git a/library/ZendAfi/Form/Decorator/HtmlTagWrapper.php b/library/ZendAfi/Form/Decorator/HtmlTagWrapper.php
index 42d7608b001..8d2bc08147d 100644
--- a/library/ZendAfi/Form/Decorator/HtmlTagWrapper.php
+++ b/library/ZendAfi/Form/Decorator/HtmlTagWrapper.php
@@ -20,5 +20,5 @@
  */
 
 
-class ZendAfi_Form_Decorator_HtmlTagWrapper extends Zend_Form_Decorator_HtmlTag {
+class ZendAfi_Form_Decorator_HtmlTagWrapper extends ZendAfi_Form_Decorator_HtmlTag {
 }
diff --git a/library/ZendAfi/Form/Decorator/Link.php b/library/ZendAfi/Form/Decorator/Link.php
index a577337d52a..c9755ef0cfa 100644
--- a/library/ZendAfi/Form/Decorator/Link.php
+++ b/library/ZendAfi/Form/Decorator/Link.php
@@ -20,7 +20,7 @@
  */
 
 
-class ZendAfi_Form_Decorator_Link extends Zend_Form_Decorator_HtmlTag {
+class ZendAfi_Form_Decorator_Link extends ZendAfi_Form_Decorator_HtmlTag {
   /**
    * @param  string $content
    * @return string
diff --git a/library/ZendAfi/Form/Element/Custom.php b/library/ZendAfi/Form/Element/Custom.php
index 2affce5b688..8cffb3f07eb 100644
--- a/library/ZendAfi/Form/Element/Custom.php
+++ b/library/ZendAfi/Form/Element/Custom.php
@@ -28,7 +28,7 @@ class ZendAfi_Form_Element_Custom extends Zend_Form_Element_Xhtml {
     foreach ($this->_decorators as $name => $value)
       $this->removeDecorator($name);
 
-    $this->_decorators['Custom'] = (new ZendAfi_Form_Decorator_Custom());
+    $this->_decorators['Custom'] = (new ZendAfi_Form_Decorator_Custom())->setOptions(['class' => $this->getAttrib('class')]);
   }
 
 
diff --git a/library/ZendAfi/Form/Element/NegativeCaptcha.php b/library/ZendAfi/Form/Element/NegativeCaptcha.php
index 894a0c1b5d1..bd3a8ec1f24 100644
--- a/library/ZendAfi/Form/Element/NegativeCaptcha.php
+++ b/library/ZendAfi/Form/Element/NegativeCaptcha.php
@@ -25,6 +25,7 @@ class ZendAfi_Form_Element_NegativeCaptcha extends Zend_Form_Element_Text {
     parent::__construct($spec, $options);
 
     $this->setAttribs(['data-spambots' => 'true',
+                       'class' => 'd-none',
                        'autocomplete' => 'off']);
 
     $this->addDecorator(new ZendAfi_Form_Decorator_NegativeCaptcha());
diff --git a/library/ZendAfi/View/Helper/Facettes.php b/library/ZendAfi/View/Helper/Facettes.php
index 4dbe316d5be..61e7b5e0fa4 100644
--- a/library/ZendAfi/View/Helper/Facettes.php
+++ b/library/ZendAfi/View/Helper/Facettes.php
@@ -20,12 +20,15 @@
  */
 
 class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
+
   const MULTI_FACET_KEY = 'multifacet_';
 
+
   protected
     $_criteres,
     $_current,
-    $_preferences;
+    $_preferences,
+    $_wrapper_class = 'facette';
 
 
   public function facettes($facets, $preferences, $criteres)  {
@@ -35,15 +38,28 @@ class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
     $this->_criteres = $criteres;
     $this->_preferences = $preferences;
 
+    $this->_addToggleScript();
+
+    return $this->_tag('div',
+                       $this->_HTMLfacets($criteres, $facets),
+                       ['class' => $this->_wrapper_class]);
+  }
+
+
+  protected function _addToggleScript() {
     Class_ScriptLoader::getInstance()
-      ->addJQueryReady('$(".facette .msg>a").click(function(e){ '
-                                                   .'e.preventDefault();'
-                                                   .'var anchor = $(this);'
-                                                   .'anchor.next("ul").find("li").hide().appendTo(anchor.parent().closest("ul")).slideToggle();'
-                                                   .'anchor.parent().remove();'
-                                                   .'})');
-
-    $url = array_merge($criteres->getUrlRetourListe(),
+      ->addJQueryReady(sprintf('$(".%s .msg>a").click(function(e){ '
+                               .'e.preventDefault();'
+                               .'var anchor = $(this);'
+                               .'anchor.next("ul").find("li").hide().appendTo(anchor.parent().closest("ul")).slideToggle();'
+                               .'anchor.parent().remove();'
+                               .'})',
+                               $this->_wrapper_class));
+  }
+
+
+  protected function _HTMLfacets($criteria, $facets) {
+    $url = array_merge($criteria->getUrlRetourListe(),
                        ['controller' => 'recherche',
                         'action' => 'simple',
                         'page' => null]);
@@ -52,9 +68,7 @@ class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
     $separator = preg_match('/;/', $codes) ? ';' : '';
     $codes = 'T' . $separator . $codes;
 
-    return $this->_tag('div',
-                       $this->listeFacettesHTML($codes, $facets, $url),
-                       ['class' => 'facette']);
+    return $this->listeFacettesHTML($codes, $facets, $url);
   }
 
 
@@ -82,8 +96,10 @@ class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
     $button = $this->view->tag('button', $this->_('Affiner'), $attribs);
 
     return $this->_tag('form',
-                       $button . $this->_tag('ul', $html) . $button,
-                       ['method' => 'POST', 'action' => $this->view->url($url)]);
+                       $button . $this->_tag('ul', $html, ['class' => 'facettes_unordered_list']) . $button,
+                       ['method' => 'POST',
+                        'action' => $this->view->url($url),
+                        'class' => 'form_facets']);
   }
 
 
@@ -103,7 +119,8 @@ class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
       $html .= $this->_renderFollowingFacets($type, $following_facets, $url);
 
     return $this->_tag('ul', $html,
-                       ['style' => 'list-style:none']);
+                       ['style' => 'list-style:none',
+                        'class' => 'facets_group']);
   }
 
 
@@ -114,11 +131,7 @@ class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
 
     return $this
       ->_tag('li',
-             $this->_tag('a',
-                         $this->_('Afficher plus de facettes...'),
-                         ['href' => '#',
-                          'title' => $this->_('Afficher plus de facettes "%s"',
-                                              Class_Codification::getInstance()->getNomChamp($code))])
+             $this->_moreFacetsAnchor($code)
              . $this->_tag('ul', $html,
                            ['id' => $type . '_msg',
                             'style' => 'display:none']),
@@ -126,6 +139,15 @@ class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
   }
 
 
+  protected function _moreFacetsAnchor($code) {
+    return $this->_tag('a',
+                       $this->_('Afficher plus de facettes...'),
+                       ['href' => '#',
+                        'title' => $this->_('Afficher plus de facettes "%s"',
+                                            Class_Codification::getInstance()->getNomChamp($code))]);
+  }
+
+
   public function renderLIFacette($url, $code, $count) {
     $url['facette'] = $code;
     return $this->renderLi($url, $code, $count);
@@ -135,13 +157,20 @@ class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
   protected function renderLi($url, $code, $count) {
     $label = Class_Codification::getInstance()->getLibelleFacette($code);
     $title = Class_Codification::getInstance()->getNomChamp($code);
+
     return $this
       ->_tag('li',
-             $this->renderCheckbox($code)
-             . $this->renderLabel($url, $label, $title)
-             . $this->renderCount($count)
-             . $this->renderRemove($code, $label),
-             ['class' => 'facette' . ($this->isActive($code) ? ' selected' : '')]);
+             $this->_renderFacetHTML($code, $url, $label, $title, $count),
+             ['class' => 'facet_item facette' . ($this->isActive($code) ? ' selected' : '')]);
+  }
+
+
+  protected function _renderFacetHTML($code, $url, $label, $title, $count) {
+    return
+      $this->renderCheckbox($code)
+      . $this->renderLabel($url, $label, $title, $code)
+      . $this->renderCount($count)
+      . $this->renderRemove($code, $label);
   }
 
 
@@ -154,7 +183,7 @@ class ZendAfi_View_Helper_Facettes extends ZendAfi_View_Helper_BaseHelper {
   }
 
 
-  protected function renderLabel($url, $label, $title) {
+  protected function renderLabel($url, $label, $title, $code) {
     return $this->view->tagAnchor($this->view->url($url, null, true),
                                   $label,
                                   ['class' => 'facette',
diff --git a/library/ZendAfi/View/Helper/FormButton.php b/library/ZendAfi/View/Helper/FormButton.php
new file mode 100644
index 00000000000..d81c0e5caf2
--- /dev/null
+++ b/library/ZendAfi/View/Helper/FormButton.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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_FormButton extends Zend_View_Helper_FormButton {
+    public function formButton($name, $value = null, $attribs = null) {
+        $info    = $this->_getInfo($name, $value, $attribs);
+        extract($info); // name, id, value, attribs, options, listsep, disable
+
+        // Get content
+        $content = '';
+        if (isset($attribs['content'])) {
+            $content = $attribs['content'];
+            unset($attribs['content']);
+        } else {
+            $content = $value;
+        }
+
+        // Ensure type is sane
+        $type = 'button';
+        if (isset($attribs['type'])) {
+            $attribs['type'] = strtolower($attribs['type']);
+            if (in_array($attribs['type'], array('submit', 'reset', 'button'))) {
+                $type = $attribs['type'];
+            }
+            unset($attribs['type']);
+        }
+
+        // build the element
+        if ($disable) {
+            $attribs['disabled'] = 'disabled';
+        }
+
+        $content = ($escape) ? $this->view->escape($content) : $content;
+
+        return $this->view->tag('button',
+                                $content,
+                                array_merge(
+                                            ['name' => $name,
+                                             'id' => $id,
+                                             'type' => $type,
+                                             'value' => $value],
+                                            $attribs));
+    }
+}
diff --git a/library/ZendAfi/View/Helper/RenderForm.php b/library/ZendAfi/View/Helper/RenderForm.php
index 6a2eb216944..989176dc441 100644
--- a/library/ZendAfi/View/Helper/RenderForm.php
+++ b/library/ZendAfi/View/Helper/RenderForm.php
@@ -30,7 +30,7 @@ class ZendAfi_View_Helper_RenderForm extends ZendAfi_View_Helper_BaseHelper {
       return;
 
     if (($class = $form->getAttrib('class')) === null)
-      $class = 'form';
+      $class = $this->_getDefaultFormClass();
 
     $form->setAttrib('class', trim($class));
 
@@ -45,6 +45,11 @@ class ZendAfi_View_Helper_RenderForm extends ZendAfi_View_Helper_BaseHelper {
   }
 
 
+  protected function _getDefaultFormClass() {
+    return 'form';
+  }
+
+
   public function decorateElement($element) {
     $new_decorators = [];
     foreach ($element->getDecorators() as $name => $decorator) {
diff --git a/library/templates/Intonation/Assets/css/intonation.css b/library/templates/Intonation/Assets/css/intonation.css
index 218712f4ecd..969dfa2e6a3 100644
--- a/library/templates/Intonation/Assets/css/intonation.css
+++ b/library/templates/Intonation/Assets/css/intonation.css
@@ -22,6 +22,10 @@
     --front-card-footer-background: linear-gradient(rgba(255, 255, 255, 0), white);
 }
 
+html {
+    scroll-behavior: smooth;
+}
+
 body {
     color: var(--front-text);
     background-color: var(--front-background);
@@ -437,7 +441,7 @@ label[data-name=note] ~ div label.multi-element-label + br {
 }
 
 .jumbotron .nav-link div {
-    font-size: 10px;
+    font-size: 9px;
 }
 
 .modal_image {
@@ -780,8 +784,8 @@ dl.row {
     flex-direction: column;
 }
 
-form.form input[type="checkbox"] {
-    margin-left: 0;
+form.form .multi-element-label {
+    margin-left: 2rem;
 }
 
 form.form .dropdown-menu input[type="checkbox"] {
@@ -810,3 +814,24 @@ input[id^="select_record"] + * {
 .menu_buttons .button_text {
     display: none !important;
 }
+
+.card-img-overlay .add_to_selection_link::before {
+    display: block;
+    position: absolute;
+    font-size: 2em;
+    font-weight: bold;
+    font-family: "Font Awesome 5 Free";
+    content: "\f00c";
+    width: 100%;
+    bottom: 0;
+    height: 1em;
+    text-align: right;
+}
+
+.added_to_selection .card-img-overlay {
+    opacity: 1;
+}
+
+.added_to_selection .card-img-overlay .add_to_selection_link::before {
+    color: var(--success);
+}
\ No newline at end of file
diff --git a/library/templates/Intonation/Library/Criteria/FromSelection.php b/library/templates/Intonation/Library/Criteria/FromSelection.php
new file mode 100644
index 00000000000..cd9fa9e0328
--- /dev/null
+++ b/library/templates/Intonation/Library/Criteria/FromSelection.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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 Intonation_Library_Criteria_FromSelection {
+
+  protected
+    $_selection,
+    $_criteria,
+    $_engine;
+
+
+  public function setSelection($selection) {
+    $this->_selection = $selection;
+    return $this;
+  }
+
+
+  public function setCriteria($criteria) {
+    $this->_criteria = $criteria;
+    return $this;
+  }
+
+
+  public function setEngine($engine) {
+    $this->_engine = $engine;
+    return $this;
+  }
+
+
+  public function generate() {
+    $records = $this->_selection->getNoticesAsArray();
+
+    if (empty($records)) {
+      $this->_engine->visitLimit(20);
+      return $this->_criteria
+        ->setParams(['multifacets' => 'T1',
+                     'no_extension' => '1',
+                     'tri' => 'annee desc']);
+    }
+
+    $this->_engine->visitLimit(50);
+
+    return $this->_criteria->setParams(['no_extension' => '1',
+                                        'multifacets' => $this->_findFacets($records),
+                                        'tri' => 'annee desc']);
+  }
+
+
+  protected function _findFacets($records) {
+    $facets = '';
+
+    $this->_facets_to_save = $this->_getFacetsToSave();
+
+    foreach ($records as $record)
+      $facets = $this->_extractFacets($record, $facets);
+
+    return $facets;
+  }
+
+
+  protected function _extractFacets($record, $facets) {
+    $_facets_from_record = Class_Notice_Facette::parseFacettesFromNoticeField($record->getFacettes());
+
+    $_facets_to_add = [];
+    foreach($_facets_from_record as $_facet_from_record) {
+      if (in_array($_facet_from_record->getCodeRubrique(), $this->_facets_to_save))
+          $_facets_to_add [] = $_facet_from_record->getCle();
+    }
+
+    return Class_Notice_Facettes::mergeFacettes(implode('-', $_facets_to_add),
+                                                $facets);
+  }
+
+
+  protected function _getFacetsToSave() {
+    return [Class_CodifGenre::CODE_FACETTE,
+            Class_CodifCentreInteret::CODE_FACETTE,
+            Class_CodifAuteur::CODE_FACETTE];
+  }
+}
diff --git a/library/templates/Intonation/Library/Record/Items.php b/library/templates/Intonation/Library/Record/Items.php
index 27bec1fe896..ecb0ef5955e 100644
--- a/library/templates/Intonation/Library/Record/Items.php
+++ b/library/templates/Intonation/Library/Record/Items.php
@@ -49,7 +49,9 @@ class Intonation_Library_Record_Items {
 
     $session = Zend_Registry::get('session');
 
-    $cond = ['id_notice' => $ids];
+    $cond = ['id_notice' => $ids,
+             'order' => 'id desc'];
+
     if ($lib_ids = $session->id_bib)
       $cond['id_bib'] = $lib_ids;
 
diff --git a/library/templates/Intonation/Library/Settings.php b/library/templates/Intonation/Library/Settings.php
index b440885f7d1..0d61c18d5af 100644
--- a/library/templates/Intonation/Library/Settings.php
+++ b/library/templates/Intonation/Library/Settings.php
@@ -152,6 +152,67 @@ class Intonation_Library_Settings extends Intonation_System_Abstract {
                                                   'span class search_axe_input' => 'col-12 col-sm-7',
                                                   'p class opened' => 'text-white bg-success p-1 rounded d-inline-block',
                                                   'p class closed' => 'text-white bg-danger p-1 rounded d-inline-block',
+                                                  'div class search_dropdown_menu' => 'dropdown-menu-right',
+
+                                                  'ul class result_tools_nav' => 'list-unstyled row no-gutters',
+                                                  'li class result_count_results' => 'order-1',
+                                                  'li class result_list_mod' => 'order-2',
+                                                  'li class result_wall_mod' => 'order-3',
+                                                  'li class result_page_size' => 'order-4',
+                                                  'li class result_search_order' => 'order-5',
+                                                  'li class result_pager' => 'order-6',
+                                                  'li class result_select_records' => 'order-7',
+                                                  'li class result_follow_search' => 'order-8',
+                                                  'li class result_more_actions' => 'order-9',
+
+                                                  'div class search_title_col' => 'col-12 order-1',
+                                                  'div class search_criteria_col' => 'col-12 order-2',
+
+                                                  'div class search_tools_col' => 'col-12 border-top border-bottom pt-2 mb-2 order-3',
+                                                  'div class search_domain_browser_col' => 'col-12 pt-2 mb-2 order-4',
+                                                  'div class search_facets_col' => 'col-12 col-md-3 order-5',
+                                                  'div class search_records_col' => 'col-12 col-md-9 order-6',
+                                                  'div class search_no_records_col' => 'col-12 order-6',
+                                                  'div class search_tools_col_2' => 'col-12 border-top border-bottom pt-2 mb-2 order-7',
+
+                                                  'form class login' => 'row no-gutters',
+
+                                                  'div class wrapper_zendafi_form_login_username' => 'col-12 order-1',
+                                                  'div class wrapper_zendafi_form_login_password' => 'col-12 order-2',
+                                                  'input id login' => 'order-3 my-3',
+                                                  'div class wrapper_zendafi_form_login_lostpass' => 'col-12 order-4',
+                                                  'div class wrapper_zendafi_form_login_pre_registration_link' => 'col-12 order-5 my-3',
+
+                                                  'div class wrapper_zendafi_form_custommultifacetsdoctype' => 'order-1',
+                                                  'div class wrapper_zendafi_form_custommultifacetsdomains' => 'order-2',
+                                                  'div class wrapper_zendafi_form_custommultifacetsannexe' => 'order-3',
+                                                  'div class wrapper_zendafi_form_custommultifacetsbib' => 'order-3',
+                                                  'div class wrapper_zendafi_form_expressionrecherche' => 'order-4',
+                                                  'div class custom_reset_search' => 'order-5',
+                                                  'div class custom_advanced_search' => 'order-6',
+                                                  'button class search_submit_button' => 'order-7',
+                                                  'a class active_criteria' => 'btn btn-warning btn-sm mt-2 mr-2 text-dark text-left',
+                                                  'button class reset_search' => 'btn btn-sm btn-warning border-dark',
+
+                                                  'div class multi_facets_submit' => 'btn btn-sm btn-primary mb-2',
+                                                  'li class facette_titre' => 'list-unstyled list-group-item py-1 px-1 my-1',
+                                                  'ul class facettes_unordered_list' => 'list-group no_border mb-2',
+                                                  'form class form_facets' => 'mb-2',
+                                                  'ul class facets_group' => 'list-unstyled list-group facets',
+                                                  'li class facet_item' => 'list-group-item py-1 pr-1 d-flex justify-content-between align-items-center',
+
+                                                  'li class jumbotron_nav_link' => 'p-1 p-xl-0',
+                                                  'div class jumbotron_nav_tab_text' => 'd-none d-xl-block',
+                                                  'div class jumbotron_previous' => 'order-1 col-3 col-md-1',
+                                                  'div class jumbotron_next' => 'order-3 order-md-4 order-lg-5 col-3 col-md-1',
+                                                  'div class jumbotron_thumbnail' => 'order-2 col-6 col-sm-5 col-md-4 col-lg-2 col-xl-2',
+                                                  'div class jumbotron_title' => 'order-4 order-md-3 col-10 col-md-5 col-lg-5 col-xl-5 ml-md-1 px-md-3',
+                                                  'div class jumbotron_actions' => 'order-5 order-lg-4 mh-100 no-gutters col-10 col-md-9 col-lg-2',
+                                                  'div class jumbotron_nav' => 'order-6 col-10 col-xl-9 mt-2',
+                                                  'div class bokeh_jumbotron' => 'jumbotron jumbotron-fluid w-100 py-3 mb-3',
+                                                  'div class jumbotron_content'  => 'col-10 col-xl-9',
+                                                  'div class jumbotron_rich_content' => 'col-12 border-bottom border-primary mb-3 pb-3',
+                                                  'a class active_item' => 'active',
                           ],
 
                           'icons_map_doc_types' => [],
@@ -222,6 +283,7 @@ class Intonation_Library_Settings extends Intonation_System_Abstract {
                                                 'refresh' => 'class fas fa-sync-alt',
                                                 'lock' => 'class fas fa-unlock-alt',
                                                 'eye' => 'class far fa-eye',
+                                                'attention' => 'class fas fa-exclamation-triangle',
 
                                                 'email' => 'class fas fa-at',
                                                 'phone' => 'class fas fa-phone',
diff --git a/library/templates/Intonation/Library/View/Wrapper/Abstract.php b/library/templates/Intonation/Library/View/Wrapper/Abstract.php
index c176f20a230..075eeeefec5 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Abstract.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Abstract.php
@@ -57,6 +57,11 @@ abstract class Intonation_Library_View_Wrapper_Abstract {
   }
 
 
+  public function getAnchor() {
+    return '';
+  }
+
+
   public function setContext($instance) {
     $this->_context = $instance;
     $this->_context_params = $instance->getLinkToAllParams();
diff --git a/library/templates/Intonation/Library/View/Wrapper/Card.php b/library/templates/Intonation/Library/View/Wrapper/Card.php
index 0ec53684584..458ce294fa7 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Card.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Card.php
@@ -33,6 +33,7 @@ class Intonation_Library_View_Wrapper_Card extends Intonation_Library_View_Wrapp
       return [];
 
     return [new Intonation_Library_Link(['Text' => $this->_('Supprimer'),
+                                         'Class' => 'text-danger',
                                          'Title' => $this->_('Ne plus gérer la carte de %s',
                                                              $this->getMainTitle()),
                                          'Image' => Class_Template::current()->getIco($this->_view,
diff --git a/library/templates/Intonation/Library/View/Wrapper/Item.php b/library/templates/Intonation/Library/View/Wrapper/Item.php
index 7b607fb5aa5..39694718a27 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Item.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Item.php
@@ -127,8 +127,9 @@ class Intonation_Library_View_Wrapper_Item extends Intonation_Library_View_Wrapp
                                                              'cote',
                                                              'library'))
                 ->setText($this->_model->getCote())
-                ->setTitle($this->_('Cote de l\'exemplaire "%s"',
-                                    $this->_model->getCodeBarres()))),
+                ->setTitle($this->_('Cote de l\'exemplaire %s : %s',
+                                    $this->_model->getCodeBarres(),
+                                    $this->_model->getCote()))),
 
                ((new Intonation_Library_Badge)
                 ->setTag('span')
diff --git a/library/templates/Intonation/Library/View/Wrapper/Loan.php b/library/templates/Intonation/Library/View/Wrapper/Loan.php
index f983645a98d..27c433e6694 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Loan.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Loan.php
@@ -116,7 +116,18 @@ class Intonation_Library_View_Wrapper_Loan extends Intonation_Library_View_Wrapp
     $issue_date = $this->_model->getIssueDate();
     $return_date = $this->_model->getDateRetour();
 
+    $late = $this->_model->isLate();
+
     $badges = [
+               ((new Intonation_Library_Badge)
+                ->setTag('span')
+                ->setClass('danger')
+                ->setImage(Class_Template::current()->getIco($this->_view,
+                                                             'attention',
+                                                             'utils'))
+                ->setText($late ? $this->_('En retard') : '')
+                ->setTitle($this->_('Vous devez rendre le document %s dès que possible.', $this->_model->getTitre()))),
+
                ((new Intonation_Library_Badge)
                 ->setTag('span')
                 ->setClass('info')
@@ -128,14 +139,16 @@ class Intonation_Library_View_Wrapper_Loan extends Intonation_Library_View_Wrapp
 
                ((new Intonation_Library_Badge)
                 ->setTag('span')
-                ->setClass(($this->_model->isLate()
+                ->setClass(($late
                             ? 'danger'
                             : 'success'))
                 ->setImage(Class_Template::current()->getIco($this->_view,
                                                             'return-date',
                                                              'library'))
                 ->setText($return_date)
-                ->setTitle($this->_('Date de retour : %s', $return_date))),
+                ->setTitle($this->_('Rendre le document %s avant le %s',
+                                    $this->_model->getTitre(),
+                                    $return_date))),
 
                ((new Intonation_Library_Badge)
                 ->setTag('span')
diff --git a/library/templates/Intonation/Library/View/Wrapper/Record.php b/library/templates/Intonation/Library/View/Wrapper/Record.php
index 982626559f0..49034e04d64 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Record.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Record.php
@@ -29,6 +29,7 @@ class Intonation_Library_View_Wrapper_Record extends Intonation_Library_View_Wra
     $_badges,
     $_allow_XSL = false;
 
+
   public function getMainTitle() {
     return $this->_main_title ?
       $this->_main_title :
@@ -539,6 +540,36 @@ class Intonation_Library_View_Wrapper_Record extends Intonation_Library_View_Wra
     $this->_allow_XSL = $bool;
     return $this;
   }
+
+
+  public function getAnchor() {
+    $id = $this->_model->getId();
+
+    if (Intonation_Library_View_Wrapper_RecordCache::hasAnchorCache($id))
+      return '';
+
+    Intonation_Library_View_Wrapper_RecordCache::setAnchorCache($id);
+
+    return $this->_view->div(['class' => 'scroll_anchor',
+                              'id' => $id]);
+  }
+}
+
+
+
+
+class Intonation_Library_View_Wrapper_RecordCache {
+  protected static $_anchor_cache = [];
+
+
+  public static function hasAnchorCache($id) {
+    return isset(static::$_anchor_cache[$id]);
+  }
+
+
+  public static function setAnchorCache($id) {
+    static::$_anchor_cache[$id] = true;
+  }
 }
 
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/RecordInSelection.php b/library/templates/Intonation/Library/View/Wrapper/RecordInSelection.php
index 053e2822ea9..6e8897c41ea 100644
--- a/library/templates/Intonation/Library/View/Wrapper/RecordInSelection.php
+++ b/library/templates/Intonation/Library/View/Wrapper/RecordInSelection.php
@@ -35,6 +35,7 @@ class Intonation_Library_View_Wrapper_RecordInSelection extends Intonation_Libra
                                                                      'action' => 'supprimer-de-la-selection',
                                                                      'selection_id' => $this->_selection->getId(),
                                                                      'record_id' => $this->_model->getId()]),
+                                         'Class' => 'text-danger',
                                          'Text' => $this->_('Supprimer'),
                                          'Popup' => true,
                                          'Title' => $this->_('Supprimer le document %s de la sélection %s',
diff --git a/library/templates/Intonation/Library/View/Wrapper/RecordToSelect.php b/library/templates/Intonation/Library/View/Wrapper/RecordToSelect.php
index a786df0eb97..15293fd1dfa 100644
--- a/library/templates/Intonation/Library/View/Wrapper/RecordToSelect.php
+++ b/library/templates/Intonation/Library/View/Wrapper/RecordToSelect.php
@@ -48,7 +48,8 @@ class Intonation_Library_View_Wrapper_RecordToSelect extends Intonation_Library_
                               'id' => $this->_selection->getId()]);
 
     return new Intonation_Library_Link(['Text' => $this->_('Ajouter'),
-                                        'Attribs' => ['onclick' => sprintf('$.ajax(\'%s\');$(this).closest(\'.card\').addClass(\'border border-success\').slideUp(1000);',
+                                        'Class' => 'add_to_selection_link position-relative',
+                                        'Attribs' => ['onclick' => sprintf('$.ajax(\'%s\');$(this).closest(\'.card\').addClass(\'border border-success disabled added_to_selection\')',
                                                                            $url),
                                                       'style' => 'cursor: pointer;'],
                                         'Title' => $this->_('Ajouter le document %s à la sélection %s',
diff --git a/library/templates/Intonation/Library/View/Wrapper/Review.php b/library/templates/Intonation/Library/View/Wrapper/Review.php
index 5f42330fea0..407b4c47233 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Review.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Review.php
@@ -182,6 +182,7 @@ class Intonation_Library_View_Wrapper_Review extends Intonation_Library_View_Wra
                                                'Image' => Class_Template::current()->getIco($this->_view,
                                                                                             'delete',
                                                                                             'utils'),
+                                               'Class' => 'text-danger',
                                                'Text' => $this->_('Supprimer'),
                                                'Title' => $this->_('Supprimer l\'avis %s',
                                                                    $this->_model->getEntete()),
diff --git a/library/templates/Intonation/Library/View/Wrapper/Selection.php b/library/templates/Intonation/Library/View/Wrapper/Selection.php
index 680e8025585..a24374a618a 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Selection.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Selection.php
@@ -191,6 +191,7 @@ class Intonation_Library_View_Wrapper_Selection extends Intonation_Library_View_
                         new Intonation_Library_Link(['Url' => $this->_view->url(['controller' => 'abonne',
                                                                                  'action' => 'supprimer-la-selection',
                                                                                  'selection_id' => $this->_model->getId()]),
+                                                     'Class' => 'text-danger',
                                                      'Text' => $this->_('Supprimer'),
                                                      'Right' => $this->_model->isMine(),
                                                      'Title' => $this->_('Supprimer la sélection %s',
diff --git a/library/templates/Intonation/Library/View/Wrapper/User/RichContent/AddRecordsToSelection.php b/library/templates/Intonation/Library/View/Wrapper/User/RichContent/AddRecordsToSelection.php
index 05b20540f8a..427af31fe08 100644
--- a/library/templates/Intonation/Library/View/Wrapper/User/RichContent/AddRecordsToSelection.php
+++ b/library/templates/Intonation/Library/View/Wrapper/User/RichContent/AddRecordsToSelection.php
@@ -47,7 +47,15 @@ class Intonation_Library_View_Wrapper_User_RichContent_AddRecordsToSelection ext
   public function getContent() {
     $engine = Class_MoteurRecherche::getInstance();
 
-    $result = $engine->lancerRecherche($this->_criteria);
+    if ($this->_criteria->isEmpty())
+      $this->_criteria = (new Intonation_Library_Criteria_FromSelection)
+        ->setSelection($this->_selection)
+        ->setCriteria($this->_criteria)
+        ->setEngine($engine)
+        ->generate();
+
+    $result = $engine
+      ->lancerRecherche($this->_criteria);
 
     $all_records = $result->fetchRecords();
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/User/RichContent/FollowASearch.php b/library/templates/Intonation/Library/View/Wrapper/User/RichContent/FollowASearch.php
index 38f53e5af49..799782509a3 100644
--- a/library/templates/Intonation/Library/View/Wrapper/User/RichContent/FollowASearch.php
+++ b/library/templates/Intonation/Library/View/Wrapper/User/RichContent/FollowASearch.php
@@ -51,6 +51,8 @@ class Intonation_Library_View_Wrapper_User_RichContent_FollowASearch extends Int
                    ['label' => $this->_('Rechercher'),
                     'id' => 'expressionRecherche' . uniqid(),
                     'class' => 'expressionRecherche',
+                    'required' => true,
+                    'allowEmpty' => false,
                     'placeholder' => $this->_('Un titre, un auteur')])
 
       ->addElement('select', 'type_doc',
diff --git a/library/templates/Intonation/Library/Widget/Carousel/Record/View.php b/library/templates/Intonation/Library/Widget/Carousel/Record/View.php
index 8e2827c568d..9a79c92b49e 100644
--- a/library/templates/Intonation/Library/Widget/Carousel/Record/View.php
+++ b/library/templates/Intonation/Library/Widget/Carousel/Record/View.php
@@ -23,39 +23,36 @@
 class Intonation_Library_Widget_Carousel_Record_View extends Intonation_Library_Widget_Carousel_View {
 
   protected function _findElements() {
-    $settings = $this->_settings;
-    $order = $settings->getOrder();
-    $sql_rand = '';
+    $order = $this->_settings->getOrder();
 
-    $selection_id = $settings->getIdPanier();
+    if ('selection' == $order && ( ! $this->_settings->getIdPanier()))
+      $order = Class_CriteresRecherche::SORT_PUBLICATION_DESC;
 
-    if ('selection' == $order && !$selection_id)
-      $sql_rand = 'annee desc';
-
-    if (Class_CriteresRecherche::SORT_RANDOM == $order)
-      $sql_rand = 'annee desc';
+    if ($random = Class_CriteresRecherche::SORT_RANDOM == $order)
+      $order = '';
 
     $order = array_filter(['url_image="no"',
                            'url_image=""',
-                           $sql_rand]);
+                           $order]);
 
     $params = ['limit' => 100,
                'order' => new Zend_Db_Expr(implode(', ', $order))];
 
-    $records = $this->_findRecords($settings, $params);
+    $records = $this->_findRecords($params);
 
-    if ($sql_rand)
+    if ($random)
       shuffle($records);
 
     return $records;
   }
 
 
-  protected function _findRecords($settings, $params) {
-    $selection_id = $settings->getIdPanier();
-    $order = $settings->getOrder();
+  protected function _findRecords($params) {
+    $selection_id = $this->_settings->getIdPanier();
+    $order = $this->_settings->getOrder();
+    $domain_id = $this->_settings->getIdCatalogue();
 
-    if ($domain = Class_Catalogue::find($settings->getIdCatalogue())) {
+    if ($domain_id && ( $domain = Class_Catalogue::find($domain_id))) {
       ($where = $domain->asWhere())
         ? ($params ['where'] = $where)
         : '';
diff --git a/library/templates/Intonation/Library/Widget/Login/View.php b/library/templates/Intonation/Library/Widget/Login/View.php
index 8bda83b0eaf..2e28ff3a87d 100644
--- a/library/templates/Intonation/Library/Widget/Login/View.php
+++ b/library/templates/Intonation/Library/Widget/Login/View.php
@@ -123,44 +123,11 @@ abstract class IntonationLoginRenderAbstract {
   }
 
 
-  protected function _getLinks() {
-    return [];
-    $html = [$this->_view->tagAnchor(['controller' => 'auth',
-                                      'action' => 'lostpass'],
-                                     $this->_settings->getLienMotDePasseOublie(),
-                                     ['label' => $this->_settings->getLienMotDePasseOublie()])];
-
-    if (Class_AdminVar::isEnregUtilAllowed() && ($create_account = $this->_settings->getLienCreerCompte()))
-      $html [] = $this->_view->tagAnchor(['controller' => 'auth',
-                                          'action' => 'register'],
-                                         $create_account,
-                                         ['label' => $create_account]);
-
-    if($pre_registration = $this->_settings->getPreRegistration())
-      $html [] = $this->_view->tagAnchor(['controller' => 'auth',
-                                          'action' => 'pre-registration'],
-                                         $pre_registration,
-                                         ['label' => $pre_registration]);
-
-    return array_filter($html);
-  }
-
-
   abstract function renderLogged();
 
 
   public function renderLogin() {
     $form = $this->_getLoginForm();
-    $links = $this->_getLinks();
-
-    $html = array_map(function($link)
-                      {
-                        return $this->_view->tag('li', $link, $this->_getLinkAttribs());
-                      }, $links);
-
-    $html = $this->_view->tag('ul',
-                              implode($html),
-                              $this->_getLinksListAttribs());
 
     $show_password_button =
       $this->_view->tag('div',
@@ -178,7 +145,7 @@ abstract class IntonationLoginRenderAbstract {
     Class_ScriptLoader::getInstance()
       ->addJQueryReady($show_password_js);
 
-    return $this->_renderForm($form) . $html;
+    return $this->_renderForm($form);
   }
 
 
diff --git a/library/templates/Intonation/Library/Widget/Nav/View.php b/library/templates/Intonation/Library/Widget/Nav/View.php
index 826b96c2851..b30fda991f6 100644
--- a/library/templates/Intonation/Library/Widget/Nav/View.php
+++ b/library/templates/Intonation/Library/Widget/Nav/View.php
@@ -158,6 +158,16 @@ class Intonation_Library_Widget_Nav_View extends Zendafi_View_Helper_Accueil_Bas
   }
 
 
+  protected function _getActiveClass($entry) {
+   $classes = [];
+
+   if (Class_CompareUrl::isInRequestUrl($entry->getLink()))
+     $classes [] = 'active_item';
+
+   return implode(' ', $classes);
+  }
+
+
   protected function _getLayout() {
     return '';
   }
@@ -201,6 +211,6 @@ class Intonation_Library_Widget_Nav_View extends Zendafi_View_Helper_Accueil_Bas
     return $this->view->tagAnchor($instance->getLink(),
                                   $label,
                                   ['title' => $this->_('Accéder à "%s"', $instance->getLabel()),
-                                   'class' => 'nav-link']);
+                                   'class' => 'nav-link ' . $this->_getActiveClass($instance)]);
   }
 }
diff --git a/library/templates/Intonation/Library/Widget/Search/Definition.php b/library/templates/Intonation/Library/Widget/Search/Definition.php
index 7a5cdb798fb..cfa1134fb62 100644
--- a/library/templates/Intonation/Library/Widget/Search/Definition.php
+++ b/library/templates/Intonation/Library/Widget/Search/Definition.php
@@ -29,8 +29,13 @@ class Intonation_Library_Widget_Search_Definition extends Class_Systeme_ModulesA
                                         [Class_Template::current()->withNameSpace('FormStyle') => 'inline',
                                          'search_button' => $this->_('Rechercher'),
                                          'placeholder' => $this->_('Titre, auteur…'),
+                                         'library_selection_label' => $this->_('Bibliothèque'),
+                                         'annexe_selection_label' => $this->_('Site'),
+                                         'doc_type_selection_label' => $this->_('Type'),
+                                         'domain_selection_label' => $this->_('Domaine'),
                                          'type_doc' => '',
-                                         'facets_in_session' => 1
+                                         'facets_in_session' => 1,
+                                         'always_new_search' => 0
                                         ]);
   }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/Library/Widget/Search/Form.php b/library/templates/Intonation/Library/Widget/Search/Form.php
index 10a2255b80a..3e699ce9545 100644
--- a/library/templates/Intonation/Library/Widget/Search/Form.php
+++ b/library/templates/Intonation/Library/Widget/Search/Form.php
@@ -24,11 +24,43 @@ class Intonation_Library_Widget_Search_Form extends ZendAfi_Form_Configuration_W
   public function init() {
     parent::init();
 
+    Class_ScriptLoader::getInstance()
+      ->addJQueryReady('checkBoxToggleVisibilityForElement("#facets_in_session", $("#always_new_search").closest("tr"), false);');
+
     $this
       ->addElement('checkbox',
                    'facets_in_session',
                    ['label' => $this->_('Persistance des facettes en session'),
-                    'multiOptions' => [1,0]]);
+                    'multiOptions' => [1,0]])
+
+      ->addElement('checkbox',
+                   'always_new_search',
+                   ['label' => $this->_('Toujours réinitialiser cette boite de recherche'),
+                    'multiOptions' => [1,0]])
+
+      ->addElement('text',
+                   'annexe_selection_label',
+                   ['label' => $this->_('Libellé du sélecteur de sites'),
+                    'value' => $this->_('Site'),
+                    'placeholder' => $this->_('Site')])
+
+      ->addElement('text',
+                   'library_selection_label',
+                   ['label' => $this->_('Libellé du sélecteur de bibliothèques'),
+                    'value' => $this->_('Bibliothèque'),
+                    'placeholder' => $this->_('Bibliothèque')])
+
+      ->addElement('text',
+                   'doc_type_selection_label',
+                   ['label' => $this->_('Libellé du sélecteur de types de documents'),
+                    'value' => $this->_('Type de document'),
+                    'placeholder' => $this->_('Type de document')])
+
+      ->addElement('text',
+                   'domain_selection_label',
+                   ['label' => $this->_('Libellé du sélecteur de domaines'),
+                    'value' => $this->_('Domaine de recherche'),
+                    'placeholder' => $this->_('Domaine de recherche')]);
 
   }
 
@@ -37,6 +69,12 @@ class Intonation_Library_Widget_Search_Form extends ZendAfi_Form_Configuration_W
     parent::populate($datas);
     $this->removeElement('largeur');
     $this->addToSelectionGroup(['facets_in_session']);
+    $this->addToSelectionGroup(['always_new_search']);
+    $this->addToDisplaySettingsGroup(['library_selection_label',
+                                      'annexe_selection_label',
+                                      'doc_type_selection_label',
+                                      'domain_selection_label']);
+
     return $this;
   }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/Library/Widget/Search/View.php b/library/templates/Intonation/Library/Widget/Search/View.php
index d2978a9e73b..170c465b6d4 100644
--- a/library/templates/Intonation/Library/Widget/Search/View.php
+++ b/library/templates/Intonation/Library/Widget/Search/View.php
@@ -22,6 +22,9 @@
 
 class Intonation_Library_Widget_Search_View extends ZendAfi_View_Helper_Accueil_Base {
 
+  protected $_criteria;
+
+
   public function getHtml() {
     $this->titre = $this->_settings->getTitre();
     $this->contenu = $this->_getHTML();
@@ -57,12 +60,20 @@ class Intonation_Library_Widget_Search_View extends ZendAfi_View_Helper_Accueil_
 
 
   protected function _renderHeadScriptsOn($script_loader) {
-    $id = '#expressionRecherche' . $this->_settings->getIdForHtml();
+    $id = '#expressionRecherche' . $this->id_module;
+
+    $criteria = $this->view->search_result
+        ? $this->view->search_result->getCriteresRecherche()
+        : new Class_CriteresRecherche();
+
+    $url = $criteria->getUrlCriteresWithoutFacettes();
 
     $script_loader
+      ->addJQueryReady('var form = $("' . $id . '").closest("form"); form.find("select").on("change", function() {form.attr("action", "' . $this->view->url($url, null, true) . '");});')
       ->addJQueryReady('$("' . $id . '").attr("x-webkit-speech","x-webkit-speech")')
       ->addOPACPluginScript('search_autocomplete/search_autocomplete')
-      ->addJQueryReady('$("' . $id . '").search_autocomplete('. $this->getAutocompleteOptions()  .')');
+      ->addJQueryReady('$("' . $id . '").search_autocomplete('. $this->getAutocompleteOptions()  .')')
+      ->addJQueryReady('$("' . $id . '").on("click", function() { var dropdown = $(this).closest("form").find(".has_facet").closest("form").find(".dropdown-menu"); dropdown.toggleClass("show"); });');
 
     return $this;
   }
@@ -83,14 +94,21 @@ class Intonation_Library_Widget_Search_View extends ZendAfi_View_Helper_Accueil_
 
 
 abstract class IntonationSearchRenderAbstract {
-  use Trait_Translator;
+  use
+    Trait_SearchCriteriaVisitor,
+    Trait_Translator;
 
   protected
     $_view,
+    $_criteria,
     $_settings,
     $_user,
     $_id,
-    $_has_facet;
+    $_has_facet,
+    $_domain_facet,
+    $_annexe_facet,
+    $_doc_type_facet,
+    $_library_facet;
 
 
   public function __construct($view, $settings, $user) {
@@ -98,34 +116,66 @@ abstract class IntonationSearchRenderAbstract {
     $this->_settings = $settings;
     $this->_user = $user;
     $this->_id = $settings->getIdForHtml();
+    $this->_criteria = (new Class_CriteresRecherche());
 
-    if ($this->_view->search_result)
+    if ($this->_settings->getAlwaysNewSearch() && ( ! $this->_settings->getFacetsInSession()))
       return $this;
 
+    if ($this->_view->search_result) {
+      $this->_criteria = $this->_view->search_result->getCriteresRecherche();
+      $this->_criteria->acceptVisitor($this);
+      return $this;
+    }
+
     if (!$last_search = Zend_Registry::get('session')->last_search)
       return $this;
 
     if ( ! $this->_settings->getFacetsInSession())
       return $this;
 
-    $criteria = (new Class_CriteresRecherche())
-      ->setParams($last_search);
+    $this->_criteria
+      ->setParams($last_search)
+      ->acceptVisitor($this);
 
     $engine = Class_MoteurRecherche::getInstance();
-    $this->_view->search_result = new Class_MoteurRecherche_Result($engine->visitSearchSettings($criteria),
-                                                                   $criteria);
-
+    $this->_view->search_result = new Class_MoteurRecherche_Result($engine->visitSearchSettings($this->_criteria),
+                                                                   $this->_criteria);
     return $this;
   }
 
 
+  public function visitFacette($facette) {
+    return $this->_extractFacetForForm(new Class_Notice_Facette($facette));
+  }
+
+
+  public function visitMultiFacet($facette) {
+    return $this->_extractFacetForForm(new Class_Notice_Facette($facette));
+  }
+
+
+  protected function _extractFacetForForm($facet) {
+    if ($facet->isAnnexeFacet())
+      return $this->_annexe_facet = $facet->getCle();
+
+    if ($facet->isDocTypeFacet())
+      return $this->_doc_type_facet = $facet->getCle();
+
+    if ($facet->isLibraryFacet())
+      return $this->_library_facet = $facet->getCle();
+
+    if ($facet->isDomainFacet())
+      return $this->_domain_facet = $facet->getCle();
+  }
+
+
   protected function _getMainForm() {
     $action_url = ['module' => 'opac',
                    'controller' => 'recherche',
                    'action' => 'simple'];
 
     $action_url = $this->_view->search_result
-      ? $this->_view->search_result->getCriteresRecherche()->getUrlCriteresWithFacettes()
+      ? $this->_criteria->getUrlCriteresWithFacettes()
       : $action_url;
 
     if (isset($action_url['expressionRecherche']))
@@ -143,7 +193,7 @@ abstract class IntonationSearchRenderAbstract {
       ->setAction($action_url);
 
     if ($doc_type)
-      $form ->addElement('hidden', 'type_doc');
+      $form->addElement('hidden', 'type_doc');
 
     $form
       ->addElement('hidden',
@@ -153,14 +203,15 @@ abstract class IntonationSearchRenderAbstract {
       ->addElement('search',
                    'expressionRecherche',
                    ['label' => $this->_settings->getMessage(),
-                    'value' => $this->_view->search_result ? $this->_view->search_result->getCriteresRecherche()->getExpressionRecherche() : '',
-                    'id' => 'expressionRecherche' . $this->_settings->getIdForHtml(),
+                    'value' => $this->_view->search_result ? $this->_criteria->getExpressionRecherche() : '',
+                    'id' => 'expressionRecherche' . $this->_id,
                     'class' => 'expressionRecherche',
                     'placeholder' => $this->_settings->getPlaceholder()])
 
       ->addElement('custom',
                    'custom_reset_search',
-                   ['render' => function($view)
+                   ['class' => 'custom_reset_search',
+                    'render' => function($view)
                     {
                       if (!$this->_has_facet)
                         return '';
@@ -172,14 +223,15 @@ abstract class IntonationSearchRenderAbstract {
                                                         'utils'),
                                                ['Title' => $this->_('Réinitialiser la recherche'),
                                                 'type' => 'button',
-                                                'class' => 'btn btn-sm btn-warning border-dark',
-                                                'onclick' => 'var form = $(this).closest(\'form\'); form.attr(\'action\', \'' . $this->_view->url(['controller' => 'recherche',                                                                                          'action' => 'simple'], null, true) .  '\'); form.find(\'.expressionRecherche\').attr(\'value\', \'\'); $(this).hide(); form.parent().find(\'.criteres_recherche\').hide(); form.parent().find(\'.has_facet\').removeClass(\'bg-warning\'); $.ajax(\'' . $this->_view->url(['controller' => 'recherche',                                                                                          'action' => 'clear-last-search-session'], null, true) . '\');']);
+                                                'class' => 'button_reset_search',
+                                                'onclick' => 'var form = $(this).closest(\'form\'); form.attr(\'action\', \'' . $this->_view->url(['controller' => 'recherche',                                                                                          'action' => 'simple'], null, true) .  '\'); form.find(\'input\').attr(\'value\', \'\'); form.find(\'select option\').prop(\'selected\', false); $(this).hide(); form.parent().find(\'.criteres_recherche\').hide(); form.parent().find(\'.has_facet\').removeClass(\'bg-warning\').removeClass(\'has_facet\'); $.ajax(\'' . $this->_view->url(['controller' => 'recherche',                                                                                          'action' => 'clear-last-search-session'], null, true) . '\');']);
                     }])
 
       ->addElement('button',
                    uniqid() . 'search_submit',
                    ['content' => $this->_renderPicto($this->_settings->getSearchButton()),
                     'title' => $this->_('Rechercher'),
+                    'class' => 'search_submit_button',
                     'type' => 'submit',
                     'escape' => false,
                     'onclick' => '$(this).parents(\'form\').submit()',
@@ -214,19 +266,47 @@ abstract class IntonationSearchRenderAbstract {
     $form = new ZendAfi_Form;
     $elements = [];
 
-    if ($this->_settings->getSelectDoc())
+    if ($this->_settings->getSelectDoc()) {
+      $options = Class_TypeDoc::getMultiOptionsFacets();
+      $label = $this->_settings->getDocTypeSelectionLabel();
       $elements [] =
         $form->createElement('select',
                              'custom_multifacets_doctype',
-                             ['label' => $this->_('Type de document'),
-                              'multiOptions' => Class_TypeDoc::getMultiOptionsFacets()]);
+                             ['label' => $label,
+                              'title' => $this->_('Filtrer le résultat de recherche par %s',
+                                                  strtolower($label)),
+                              'value' => $this->_doc_type_facet,
+                              'multiOptions' => $options]);
+    }
+
+    if ($this->_settings->getSelectAnnexe()) {
+      $options = Class_CodifAnnexe::getMultiOptionsFacets();
+      asort($options, SORT_NATURAL | SORT_FLAG_CASE);
+      $label = $this->_settings->getAnnexeSelectionLabel();
 
-    if ($this->_settings->getSelectAnnexe())
       $elements [] =
         $form->createElement('select',
                              'custom_multifacets_annexe',
-                             ['label' => $this->_('Site'),
-                              'multiOptions' => Class_CodifAnnexe::getMultiOptionsFacets()]);
+                             ['label' => $label,
+                              'title' => $this->_('Filtrer le résultat de recherche par %s',
+                                                  strtolower($label)),
+                              'value' => $this->_annexe_facet,
+                              'multiOptions' => $options]);
+    }
+
+    if ($this->_settings->getSelectBib()) {
+      $options = Class_Bib::getMultiOptionsFacets();
+      asort($options, SORT_NATURAL | SORT_FLAG_CASE);
+      $label = $this->_settings->getLibrarySelectionLabel();
+      $elements [] =
+        $form->createElement('select',
+                             'custom_multifacets_bib',
+                             ['label' => $label,
+                              'title' => $this->_('Filtrer le résultat de recherche par %s',
+                                                  strtolower($label)),
+                              'value' => $this->_library_facet,
+                              'multiOptions' => $options]);
+    }
 
     $elements = $this->_createDomainElement($elements);
     return $elements;
@@ -243,14 +323,20 @@ abstract class IntonationSearchRenderAbstract {
     if (!$multi_options = Class_Catalogue::getMultiOptionsFacets($domain_ids))
       return $elements;
 
+    asort($multi_options, SORT_NATURAL | SORT_FLAG_CASE);
+
     $form = $this->_getMainForm();
 
-    $elements [] = (Class_Systeme_ModulesAccueil_RechercheSimple::DOMAIN_SELECT_SELECT
-        == $this->_settings->getDomainSelectStyle())
-      ? $form->createElement('select',
-                             'custom_multifacets_domains',
-                             ['label' => $this->_('Domaine de recherche'),
-                              'multiOptions' => $multi_options])
+    $elements [] = $this->_createDomainElementStyle($form, $multi_options);
+
+    return $elements;
+  }
+
+
+  protected function _createDomainElementStyle($form, $multi_options) {
+    return (Class_Systeme_ModulesAccueil_RechercheSimple::DOMAIN_SELECT_SELECT
+     == $this->_settings->getDomainSelectStyle())
+      ? $this->_createSelectDomainElementStyle($form, $multi_options)
       : $form->createElement('multiCheckbox',
                              'custom_multifacets_domains',
                              ['label' => $this->_('Restreindre à'),
@@ -259,8 +345,18 @@ abstract class IntonationSearchRenderAbstract {
                                                             count($multi_options) -1,
                                                             true)
                              ]);
+  }
 
-    return $elements;
+
+  protected function _createSelectDomainElementStyle($form, $multi_options) {
+    $label = $this->_settings->getDomainSelectionLabel();
+    return $form->createElement('select',
+                                'custom_multifacets_domains',
+                                ['label' => $label,
+                                 'title' => $this->_('Filtrer le résultat de recherche par %s',
+                                                     strtolower($label)),
+                                 'value' => $this->_domain_facet,
+                                 'multiOptions' => $multi_options]);
   }
 
 
@@ -270,10 +366,7 @@ abstract class IntonationSearchRenderAbstract {
 
 
   protected function _renderAdvancedSearch() {
-    return $this->_view->tag('div',
-                             '',
-                             ['class' => 'dropdown-divider'])
-      . $this->_view->tagAnchor($this->_view->url(['controller' => 'recherche',
+    return $this->_view->tagAnchor($this->_view->url(['controller' => 'recherche',
                                                    'action' => 'avancee',
                                                    'statut' => 'reset'],
                                                   null,
@@ -290,15 +383,18 @@ abstract class IntonationSearchRenderAbstract {
                                            'Attribs' => ['onclick' => '',
                                                          'class' => 'dropdown-toggle ' . $this->_has_facet]]);
 
-    return $this->_view->renderDropdown($html, $button, '', 'dropdown-menu-right');
+    return $this->_view->renderDropdown($html, $button, '', 'search_dropdown_menu');
   }
 
 
   protected function _renderCriteria($html) {
+    if ($this->_settings->getAlwaysNewSearch() && ( ! $this->_settings->getFacetsInSession()))
+      return $html;
+
     if (!$this->_view->search_result)
       return $html;
 
-    if (!$criteria = $this->_view->search_HtmlCriteria($this->_view->search_result->getCriteresRecherche()))
+    if (!$criteria = $this->_view->search_HtmlCriteria($this->_criteria))
       return $html;
 
     $this->_has_facet = 'has_facet bg-warning';
@@ -331,7 +427,11 @@ class IntonationSearchRenderDefault extends IntonationSearchRenderAbstract {
     $html .= $criteria;
 
     return $this->_isAdvancedSearchEnabled()
-      ? $html . $this->_renderAdvancedSearch()
+      ? ($html
+         . $this->_view->tag('div',
+                             '',
+                             ['class' => 'dropdown-divider'])
+         . $this->_renderAdvancedSearch())
       : $html;
   }
 }
@@ -348,11 +448,18 @@ class IntonationSearchRenderToggle extends IntonationSearchRenderDefault {
 
 class IntonationSearchRenderInline extends IntonationSearchRenderAbstract {
   public function render() {
+    $form = $this->_getMainForm();
+
     $optional_form_elements = [];
 
     foreach($this->_getOptionalElements() as $element) {
-      $element = (new Intonation_View_RenderForm)->decorateElement($element);
-      $optional_form_elements [] = $element->render($this->_view);
+      $label = $element->getLabel();
+      $element->setLabel('');
+      $multi_options = $element->getAttrib('options');
+      unset($multi_options['']);
+      array_unshift($multi_options, $label);
+      $element->setAttrib('options', $multi_options);
+      $form->addElement($element);
     }
 
     $advanced_search = $this->_isAdvancedSearchEnabled()
@@ -361,19 +468,31 @@ class IntonationSearchRenderInline extends IntonationSearchRenderAbstract {
 
     $criteria = $this->_renderCriteria('');
 
-    $form = $this->_getMainForm();
+    $html = array_filter([$criteria,
+                          $advanced_search]);
+
+    $html = implode($this->_view->tag('div',
+                                      '',
+                                      ['class' => 'dropdown-divider']),
+                    $html);
 
-    if ($html = $criteria . implode($optional_form_elements) . $advanced_search)
+    if ($html)
       $form->addElement('custom',
                         'custom_advanced_search',
-                        ['render' => function($view) use ($html)
-                     {
-                       return $this->_renderToggle($html);
-                     }]);
+                        ['class' => 'custom_advanced_search',
+                         'render' => function($view) use ($html)
+                          {
+                            return $this->_renderToggle($html);
+                          }]);
 
     $form->addUniqDisplayGroup('search_group');
     $form->populate($this->_settings->toArray());
 
     return $this->_view->renderInlineForm($form);
   }
+
+
+  protected function _createDomainElementStyle($form, $multi_options) {
+    return $this->_createSelectDomainElementStyle($form, $multi_options);
+  }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/Template.php b/library/templates/Intonation/Template.php
index 6fc373bf1f5..8be15ce3888 100644
--- a/library/templates/Intonation/Template.php
+++ b/library/templates/Intonation/Template.php
@@ -63,8 +63,8 @@ class Intonation_Template extends Class_Template {
   }
 
 
-  public function hydrate($attribs, $name) {
-    $cache_key = $name . print_r($attribs, true);
+  public function hydrate($attribs, $tag) {
+    $cache_key = $tag . print_r($attribs, true);
 
     if (isset($this->_hydrate_cache[$cache_key]))
       return $this->_hydrate_cache[$cache_key];
@@ -72,55 +72,65 @@ class Intonation_Template extends Class_Template {
     if('opac' != Class_Url::getModuleName())
       return $this->_hydrate_cache[$cache_key] = $attribs;
 
-    $existing_classes = isset($attribs['class'])
-      ? $attribs['class']
-      : '';
-
-    $hydrated_classes =  $this->_getHydrateClasses($attribs, $name);
-
-    $attribs['class'] = trim(implode(' ',
-                                     [$existing_classes,
-                                      $hydrated_classes]));
+    $attribs['class'] =
+      $this->_getHydrateClasses($tag,
+                                $attribs,
+                                (isset($attribs['class'])
+                                 ? $attribs['class']
+                                 : ''));
 
     return $this->_hydrate_cache[$cache_key] = $attribs;
   }
 
 
-  protected function _getHydrateClasses($attribs, $name) {
-    if(!is_array($attribs))
-      return '';
+  protected function _getHydrateClasses($tag, $attribs, $classes) {
+    if (!$this->_hydrating_mapping_cache)
+      $this->_hydrating_mapping_cache = $this->getSettings()->getHydratingMapping();
+
+    $attribs = (array) $attribs;
+    $matches = [];
+    foreach($attribs as $attrib_key => $attrib_value)
+      $matches = $this->_findHydrateClasses($tag, $attrib_key, $attrib_value, $matches);
+
+    if (!$matches)
+      $matches = (isset($this->_hydrating_mapping_cache[$tag])
+                  ? $this->_hydrating_mapping_cache[$tag]
+                  : '');
 
-    $hydrating_mapping = null;
+    return $this->_mergeClasses($classes, $matches);
+  }
 
-    if ($this->_hydrating_mapping_cache)
-      $hydrating_mapping = $this->_hydrating_mapping_cache;
 
-    if (!$hydrating_mapping)
-      $this->_hydrating_mapping_cache = $hydrating_mapping = $this->getSettings()->getHydratingMapping();
+  protected function _findHydrateClasses($tag, $attrib_key, $attrib_value, $matches) {
+    $dry_key = implode(' ', [$tag, $attrib_key, $attrib_value]);
 
-    foreach($attribs as $key => $value) {
-      $dry_key = implode(' ', [$name, $key, $value]);
-      if(isset($hydrating_mapping[$dry_key]))
-        return $hydrating_mapping[$dry_key];
+    if(isset($this->_hydrating_mapping_cache[$dry_key])) {
+      $matches [] = $this->_hydrating_mapping_cache[$dry_key];
+      return $matches;
     }
 
-    $matches = [];
-    foreach($attribs as $key => $value) {
-      if($values = array_filter(explode(' ', $value))) {
-        foreach($values as $value) {
-          $dry_key = implode(' ', [$name, $key, $value]);
-          if(isset($hydrating_mapping[$dry_key]))
-            $matches [] = $hydrating_mapping[$dry_key];
-        }
-      }
+    $attrib_values = array_filter(explode(' ', $attrib_value));
+
+    if (empty($attrib_values))
+      return $matches;
+
+    foreach($attrib_values as $attrib_value_part) {
+      $dry_key = implode(' ', [$tag, $attrib_key, $attrib_value_part]);
+      if(isset($this->_hydrating_mapping_cache[$dry_key]))
+        $matches [] = $this->_hydrating_mapping_cache[$dry_key];
     }
 
-    if ($matches)
-      return implode(' ', array_filter($matches));
+    return $matches;
+  }
+
+
+  protected function _mergeClasses($classes, $matches) {
+    $classes = (array) $classes;
+    $matches = (array) $matches;
 
-    return isset($hydrating_mapping[$name])
-      ? $hydrating_mapping[$name]
-      : '';
+    $merged = implode(' ', $classes) . ' ' . implode(' ', $matches);
+    $merged = array_unique(array_filter(explode(' ', $merged)));
+    return implode(' ', $merged);
   }
 
 
diff --git a/library/templates/Intonation/View/CardifyHorizontal.php b/library/templates/Intonation/View/CardifyHorizontal.php
index bc721d5cce6..22e93c17a2e 100644
--- a/library/templates/Intonation/View/CardifyHorizontal.php
+++ b/library/templates/Intonation/View/CardifyHorizontal.php
@@ -44,8 +44,9 @@ class Intonation_View_CardifyHorizontal extends Intonation_View_CardHelper {
                                  'class' => 'card-text']);
 
     return $this->_tag('div',
-                       $this->view->grid($this->view->div(['class' => 'col-3 p-3'],
-                                                          $img)
+                       $this->view->grid($element->getAnchor()
+                                         . $this->view->div(['class' => 'col-3 p-3'],
+                                                            $img)
 
                                          . $this->view->div(['class' => 'col col-sm-6 py-3'],
                                                             implode($content))
diff --git a/library/templates/Intonation/View/CardifyOnlyDescription.php b/library/templates/Intonation/View/CardifyOnlyDescription.php
index 658e93fe8f3..5c7be9befbf 100644
--- a/library/templates/Intonation/View/CardifyOnlyDescription.php
+++ b/library/templates/Intonation/View/CardifyOnlyDescription.php
@@ -63,7 +63,9 @@ class Intonation_View_CardifyOnlyDescription extends Intonation_View_CardHelper
                              ['class' => 'card-footer d-flex flex-row justify-content-between ' . $hide_text_button_class . ' card_footer_' . $element_class]);
 
     return $this->_tag('div',
-                       $img . implode($html),
+                       $element->getAnchor()
+                       . $img
+                       . implode($html),
                        ['class' => 'card card_' . $element_class]);
   }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/View/CardifyWithOverlay.php b/library/templates/Intonation/View/CardifyWithOverlay.php
index a663b8597eb..27034c1f46c 100644
--- a/library/templates/Intonation/View/CardifyWithOverlay.php
+++ b/library/templates/Intonation/View/CardifyWithOverlay.php
@@ -23,7 +23,8 @@
 class Intonation_View_CardifyWithOverlay extends ZendAfi_View_Helper_BaseHelper {
   public function cardifyWithOverlay($element) {
     return $this->_tag('div',
-                       $this->_cardWithPicture($element),
+                       $element->getAnchor()
+                       . $this->_cardWithPicture($element),
                        ['class' => 'card text-center']);
   }
 
diff --git a/library/templates/Intonation/View/Jumbotron.php b/library/templates/Intonation/View/Jumbotron.php
index 5d18d821e30..c06fd03c744 100644
--- a/library/templates/Intonation/View/Jumbotron.php
+++ b/library/templates/Intonation/View/Jumbotron.php
@@ -44,17 +44,18 @@ class Intonation_View_Jumbotron extends ZendAfi_View_Helper_BaseHelper {
     $html = [];
 
     if ($previous = $this->_getPreviousItem())
-      $html [] = $this->_div(['class' => 'order-1 col-3 col-md-1'], $this->_div(['class' => 'd-table h-100 w-100 text-center'], $previous));
+      $html [] = $this->_div(['class' => 'jumbotron_previous'],
+                             $this->_div(['class' => 'd-table h-100 w-100 text-center'], $previous));
 
     if ($next = $this->_getNextItem())
-      $html [] = $this->_div(['class' => 'order-3 order-md-4 order-lg-5 col-3 col-md-1'], $this->_div(['class' => 'd-table h-100 w-100 text-center'], $next));
+      $html [] = $this->_div(['class' => 'jumbotron_next'], $this->_div(['class' => 'd-table h-100 w-100 text-center'], $next));
 
-    $html [] = $this->_div(['class' => 'order-2 col-6 col-sm-5 col-md-4 col-lg-2 col-xl-2'], $this->_thumbnail());
-    $html [] = $this->_div(['class' => 'order-4 order-md-3 col-10 col-md-5 col-lg-5 col-xl-5 ml-md-1 px-md-3'], $this->_header());
-    $html [] = $this->_div(['class' => 'order-5 order-lg-4 mh-100 no-gutters col-10 col-md-9 col-lg-2'], $this->_getRowActions());
-    $html [] = $this->_div(['class' => 'order-6 col-10 col-xl-9 mt-2'], $this->_nav());
+    $html [] = $this->_div(['class' => 'jumbotron_thumbnail'], $this->_thumbnail());
+    $html [] = $this->_div(['class' => 'jumbotron_title'], $this->_header());
+    $html [] = $this->_div(['class' => 'jumbotron_actions'], $this->_getRowActions());
+    $html [] = $this->_div(['class' => 'jumbotron_nav'], $this->_nav());
 
-    return $this->_div(['class' => 'jumbotron jumbotron-fluid w-100 py-3 mb-3'],
+    return $this->_div(['class' => 'bokeh_jumbotron'],
                        $this->view->grid(implode($html),
                                          [],
                                          ['class' => 'justify-content-center']));
@@ -140,7 +141,7 @@ class Intonation_View_Jumbotron extends ZendAfi_View_Helper_BaseHelper {
 
     return $this->_tag('ul',
                        implode($html),
-                       ['class' => 'nav nav-tabs nav-fill']);
+                       ['class' => 'nav nav-tabs nav-fill row no-gutters']);
   }
 
 
@@ -148,7 +149,7 @@ class Intonation_View_Jumbotron extends ZendAfi_View_Helper_BaseHelper {
     $id = $item->getModel()->getId();
 
     $params = ['title' => $item->getDBNavTitle(),
-               'class' => 'jumbotron_nav_link nav-link ' . $item->getClass() . ' ' . $item->getActiveClass()];
+               'class' => 'jumbotron_nav_link nav-link p-1 p-xl-0 ' . $item->getClass() . ' ' . $item->getActiveClass()];
 
     if ($item->isAjax() || (!$item->getContent()))
       $params ['class'] .= ' text-black-50 disabled';
@@ -157,9 +158,11 @@ class Intonation_View_Jumbotron extends ZendAfi_View_Helper_BaseHelper {
       $this->_tag('li',
                   $this->view->tagAnchor(array_merge($item->getNavUrl(),
                                                      ['id' => $id]),
-                                         Class_Template::current()->getIco($this->view, $item->getDBNavIco()). $this->view->div(['class' => 'button_text d-none d-xl-block text-truncate'], $item->getDBTitle()),
+                                         Class_Template::current()->getIco($this->view, $item->getDBNavIco()). $this->view->div(['class' => 'button_text jumbotron_nav_tab_text'], $item->getDBTitle()),
                                          $params),
-                  ['class' => 'nav-item']);
+                  ['class' => sprintf('nav-item wrapper_%s wrapper_%s',
+                                      $item->getClass(),
+                                      $item->getActiveClass())]);
   }
 
 
@@ -175,7 +178,7 @@ class Intonation_View_Jumbotron extends ZendAfi_View_Helper_BaseHelper {
                       },
                       $sections);
 
-    $html = $this->_div(['class' => 'col-10 col-xl-9'], implode($html));
+    $html = $this->_div(['class' => 'jumbotron_content'], $this->view->grid(implode($html)));
 
     return $this->view->grid($html,
                              [],
@@ -190,7 +193,7 @@ class Intonation_View_Jumbotron extends ZendAfi_View_Helper_BaseHelper {
     if (!$element->isVisible() && !$element->isAjax())
       return '';
 
-    $classes = 'border-bottom border-primary mb-3 pb-3 rich_content';
+    $classes = 'rich_content jumbotron_rich_content ';
 
     if ($element->isAjax())
       $classes .= ' d-none';
@@ -199,7 +202,10 @@ class Intonation_View_Jumbotron extends ZendAfi_View_Helper_BaseHelper {
       $classes .= ' hidde_content';
 
     if ($class = $element->getClass())
-      $classes .= ' ' . $class;
+      $classes .= sprintf(' %s wrapper_%s wrapper_%s',
+                          $class,
+                          $class,
+                          $element->getActiveClass());
 
     return
       $this->_div(['class' => $classes],
diff --git a/library/templates/Intonation/View/LibrariesWidget.php b/library/templates/Intonation/View/LibrariesWidget.php
index ff917a3dbfa..a7661b178c0 100644
--- a/library/templates/Intonation/View/LibrariesWidget.php
+++ b/library/templates/Intonation/View/LibrariesWidget.php
@@ -37,6 +37,8 @@ class Intonation_View_LibrariesWidget extends ZendAfi_View_Helper_BaseHelper {
       $settings = Class_Template::current()->getWidgetSettingsWrapper($temp_settings);
     }
 
+    $this->_libraries = null;
+    $this->_filter_settings = null;
     $this->_settings = $settings;
 
     return $this->view->div(['class' => 'ajax_content'],
@@ -84,13 +86,15 @@ class Intonation_View_LibrariesWidget extends ZendAfi_View_Helper_BaseHelper {
 
 
   protected function _findAllLibraries() {
+    $order = $this->_settings->getOrder();
+
     if (! $id_libraries = $this->_settings->getLibraries())
       return Class_Bib::findAllBy(['order' => 'libelle']);
 
     if (! $id_libraries = array_filter(explode(';', $id_libraries)))
       return Class_Bib::findAllBy(['order' => 'libelle']);
 
-    $order = ($this->_settings->getOrder() == Class_Systeme_ModulesAccueil_Library::ORDER_ALPHA)
+    $order = ($order == Class_Systeme_ModulesAccueil_Library::ORDER_ALPHA)
       ? 'libelle'
       : 'FIELD (id_site, ' . implode($id_libraries, ',') . ')';
 
diff --git a/library/templates/Intonation/View/Opac.php b/library/templates/Intonation/View/Opac.php
index ba390d02cd3..6f701bd074e 100644
--- a/library/templates/Intonation/View/Opac.php
+++ b/library/templates/Intonation/View/Opac.php
@@ -122,6 +122,8 @@ class Intonation_View_Opac extends ZendAfi_View_Helper_BaseHelper {
                                 URL_ADMIN_CSS,
                                 USERFILESURL));
 
+    $script_loader->addJQueryReady('if (window.location.hash) return false; var search_input = $(".search_title_col").closest("body").find(".boite.rech_simple"); var anchors = $("li + li a.active.jumbotron_nav_link"); anchors += search_input; if (!anchors.length) return false; $("html, body").scrollTop(anchors.offset().top);');
+
     if($this->_template->getJquery())
       $head_scripts->loadJQuery();
 
diff --git a/library/templates/Intonation/View/RenderForm.php b/library/templates/Intonation/View/RenderForm.php
index aa018a6120c..0075ebfaed2 100644
--- a/library/templates/Intonation/View/RenderForm.php
+++ b/library/templates/Intonation/View/RenderForm.php
@@ -21,6 +21,26 @@
 
 
 class Intonation_View_RenderForm extends ZendAfi_View_Helper_RenderForm {
+
+  protected $_form;
+
+
+  public function renderForm($form, $buttons = []) {
+    $this->_form = $form;
+    $elements = $form->getElements();
+
+    foreach($elements as $element)
+      $element->setAttrib('class', $this->_getElementClass($element));
+
+    return parent::renderForm($form, $buttons);
+  }
+
+
+  protected function _getDefaultFormClass() {
+    return 'form row';
+  }
+
+
   protected function _getFieldsetDecorator($form) {
     $decorators = ['FormElements'];
     return $decorators;
@@ -41,7 +61,7 @@ class Intonation_View_RenderForm extends ZendAfi_View_Helper_RenderForm {
 
     $newDecorators[] = ['HtmlTagWrapper',
                         ['tag' => 'div',
-                         'class' => 'form-group container-fluid no-gutters py-1']];
+                         'class' => 'form-group container-fluid no-gutters py-1 ' . $this->_getWrapperElementClass($element)]];
 
     return $newDecorators;
   }
@@ -50,7 +70,8 @@ class Intonation_View_RenderForm extends ZendAfi_View_Helper_RenderForm {
   protected function _linkForTableRendering($element, $decorator, $newDecorators, $name) {
     $decorator->setOptions([]);
     $newDecorators[$name] = $decorator;
-    $newDecorators[] = ['HtmlTag', ['tag' => 'div']];
+    $newDecorators[] = ['HtmlTag', ['tag' => 'div',
+                                    'class' => $this->_getWrapperElementClass($element)]];
 
     return $newDecorators;
 
@@ -66,10 +87,29 @@ class Intonation_View_RenderForm extends ZendAfi_View_Helper_RenderForm {
 
   protected function _multipleselectionForTableRendering($element, $decorator, $newDecorators, $name) {
     $newDecorators[] = [['input_data' => 'HtmlTag'],
-                        ['tag' => 'div']];
+                        ['tag' => 'div',
+                         'class' => $this->_getWrapperElementClass($element)]];
 
     $decorator->setOption('tag', 'div');
     $newDecorators[$name] = $decorator;
     return $newDecorators;
   }
+
+
+  protected function _getElementClass($element) {
+    $old_classes = $element->getAttrib('class');
+    $class = str_replace(' ', '_', $old_classes);
+    $key = $class ? $class : $element->getId();
+    return sprintf('%s %s_%s',
+                   $old_classes,
+                   strtolower(get_class($this->_form)),
+                   strtolower($key));
+  }
+
+
+  protected function _getWrapperElementClass($element) {
+    return strtolower(sprintf('wrapper_%s_%s',
+                              get_class($this->_form),
+                              preg_replace("/[^a-zA-Z]/", '', $element->getId())));
+  }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/View/RenderInlineForm.php b/library/templates/Intonation/View/RenderInlineForm.php
index 2a3fdeefc28..80bb631c49b 100644
--- a/library/templates/Intonation/View/RenderInlineForm.php
+++ b/library/templates/Intonation/View/RenderInlineForm.php
@@ -57,7 +57,7 @@ class Intonation_View_RenderInlineForm extends Intonation_View_RenderForm {
 
     $newDecorators[] = ['HtmlTagWrapper',
                         ['tag' => 'div',
-                         'class' => 'col row form-group no-gutters']];
+                         'class' => 'col row form-group no-gutters ' . $this->_getWrapperElementClass($element)]];
 
 
     return $newDecorators;
diff --git a/library/templates/Intonation/View/Search/Facets.php b/library/templates/Intonation/View/Search/Facets.php
index c746e7724a9..bb0a565612c 100644
--- a/library/templates/Intonation/View/Search/Facets.php
+++ b/library/templates/Intonation/View/Search/Facets.php
@@ -19,13 +19,11 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
-class Intonation_View_Search_Facets extends ZendAfi_View_Helper_BaseHelper {
-  const MULTI_FACET_KEY = 'multifacet_';
+class Intonation_View_Search_Facets extends ZendAfi_View_Helper_Facettes {
 
   protected
-    $_criteres,
-    $_current,
-    $_preferences;
+    $_all_facets,
+    $_wrapper_class = 'facets';
 
 
   public function search_Facets($facets, $preferences, $criteres)  {
@@ -39,142 +37,37 @@ class Intonation_View_Search_Facets extends ZendAfi_View_Helper_BaseHelper {
 
     $this->_preferences = $preferences;
 
-    Class_ScriptLoader::getInstance()
-      ->addJQueryReady('$(".facets .msg>a").click(function(e){ '
-                                                   .'e.preventDefault();'
-                                                   .'var anchor = $(this);'
-                                                   .'anchor.next("ul").find("li").hide().appendTo(anchor.parent().closest("ul")).slideToggle();'
-                                                   .'anchor.parent().remove();'
-                                                   .'})');
+    $this->_addToggleScript();
 
-    $url = array_merge($criteres->getUrlRetourListe(),
-                       ['controller' => 'recherche',
-                        'action' => 'simple',
-                        'page' => null]);
-
-    $codes = $this->_preferences['facettes_codes'];
-    $separator = preg_match('/;/', $codes) ? ';' : '';
-    $codes = 'T' . $separator . $codes;
-
-    return $this->listeFacettesHTML($codes, $facets, $url);
-  }
-
-
-  public function listeFacettesHTML($codes, $facets, $url) {
-    $html = '';
-    $rubriques = Class_Notice_RubriqueFacette::parseRubriquesFromPreferenceField($codes);
-
-    foreach($rubriques as $rubrique) {
-      $type = $rubrique->getCode();
-      if (!isset($facets[$type]))
-        continue;
-
-
-      $html .= $this->_tag('li',
-                           $this->_tag('b', $rubrique->getLibelle())
-                           . $this->listeFacettesElementHTML($type, $facets[$type], $url),
-                           ['class' => 'facette_titre list-group-item py-1 px-1 my-1']);
-    }
-
-    $attribs = ['class' => 'multi_facets_submit bouton btn btn-sm btn-primary mb-2',
-                'type' => 'submit',
-                'name' => 'multi_facets_submit'];
-
-    if (!$this->_preferences['multi_facettes'])
-      $attribs = array_merge($attribs, ['style' => 'display: none;']);
-
-    $button = $this->view->tag('button', $this->_('Affiner'), $attribs);
-
-    return $this->_tag('form',
-                       $button
-                       . $this->_tag('ul', $html, ['class' => 'list-group no_border mb-2'])
-                       . $button,
-                       ['method' => 'POST',
-                        'class' => 'mb-2',
-                        'action' => $this->view->url($url)]);
-  }
-
-
-  public function listeFacettesElementHTML($type, $values, $url) {
-    $count = (new Class_AdminVar_UnleashedFacets())->isUnleashed($type)
-      ? count($values)
-      : $this->_preferences['facettes_nombre'];
-
-    $first_facets = array_splice($values, 0, $count);
-    $following_facets = array_splice($values, 0, $count);
-
-    $html = '';
-    foreach($first_facets as $code => $count)
-      $html .= $this->renderLIFacette($url, $code, $count);
-
-    if ($following_facets)
-      $html .= $this->_renderFollowingFacets($type, $following_facets, $url);
-
-    return $this->_tag('ul', $html,
-                       ['class' => 'list-group facets',
-                        'style' => 'list-style:none']);
+    return $this->_HTMLfacets($criteres, $facets);
   }
 
 
-  protected function _renderFollowingFacets($type, $facets, $url) {
-    $html = [];
-    foreach($facets as $code => $count)
-      $html [] = $this->renderLIFacette($url, $code, $count);
-
-    return $this
-      ->_tag('li',
-             $this->view->tagAction(new Intonation_Library_Link(['Url' => '#',
-                                                                 'Text' => $this->_('Afficher plus de facettes'),
-                                                                 'Image' => (Class_Template::current()
-                                                                             ->getIco($this->view,
-                                                                                      'add',
-                                                                                      'utils')),
-                                                                 'Class' => 'text_small text_no_transform',
-                                                                 'Title' => $this->_('Afficher plus de facettes "%s"',
-                                                                                     Class_Codification::getInstance()->getNomChamp($code))]))
-             . $this->_tag('ul', implode($html),
-                           ['id' => $type . '_msg',
-                            'style' => 'display:none']),
-             ['class' => 'msg ']);
+  protected function _moreFacetsAnchor($code) {
+    return $this->view->tagAction(new Intonation_Library_Link(['Url' => '#',
+                                                               'Text' => $this->_('Afficher plus de facettes'),
+                                                               'Image' => (Class_Template::current()
+                                                                           ->getIco($this->view,
+                                                                                    'add',
+                                                                                    'utils')),
+                                                               'Class' => 'text_small text_no_transform',
+                                                               'Title' => $this->_('Afficher plus de facettes "%s"',
+                                                                                   Class_Codification::getInstance()->getNomChamp($code))]));
   }
 
 
-  public function renderLIFacette($url, $code, $count) {
-    $url['facette'] = $code;
-    return $this->renderLi($url, $code, $count);
-  }
-
-
-  protected function renderLi($url, $code, $count) {
-    $label = Class_Codification::getInstance()->getLibelleFacette($code);
-    $title = Class_Codification::getInstance()->getNomChamp($code);
-
-    return $this
-      ->_tag('li',
-             $this->renderCheckbox($code)
-             . $this->renderLabel($url, $label, $title, $code)
-             . $this->renderCount($count),
-             ['class' => 'list-group-item py-1 pr-1 d-flex justify-content-between align-items-center facette' . ($this->isActive($code) ? ' selected' : '')]);
-  }
-
-
-  protected function renderCheckbox($code) {
-    $attribs = ['checked' => $this->isActive($code)];
-    if (!$this->_preferences['multi_facettes'])
-      $attribs['style'] = 'display: none;';
-
-    return $this->view->formCheckbox($this->multiFacetNameFor($code), null, $attribs);
+  protected function _renderFacetHTML($code, $url, $label, $title, $count) {
+    return
+      $this->renderCheckbox($code)
+      . $this->renderLabel($url, $label, $title, $code)
+      . $this->renderCount($count);
   }
 
 
   protected function renderLabel($url, $label, $title, $code) {
-    if ($this->isActive($code))
-      return $this->renderRemove($code, $label);
-
-    return $this->view->tagAnchor($this->view->url($url, null, true),
-                                  $label,
-                                  ['class' => 'facette',
-                                   'title' => $this->view->_('Affiner le résultat par %s: %s', lcfirst($title), $label)]);
+    return $this->isActive($code)
+      ? $this->renderRemove($code, $label)
+      : parent::renderLabel($url, $label, $title, $code);
   }
 
 
@@ -196,62 +89,6 @@ class Intonation_View_Search_Facets extends ZendAfi_View_Helper_BaseHelper {
   }
 
 
-  protected function isActive($code) {
-    return in_array($code, $this->_criteres->getFacettes()) || $this->isMultiActive($code);
-  }
-
-
-  protected function isMultiActive($code) {
-    return in_array($code, $this->_criteres->getMultiFacets());
-  }
-
-
-  protected function multiFacetNameFor($code) {
-    return static::MULTI_FACET_KEY . $code;
-  }
-
-
-  public static function multifacetCodeFromKey($key) {
-    return static::isMultiFacetKey($key) ? substr($key, strlen(static::MULTI_FACET_KEY)) : null;
-  }
-
-
-  public static function isMultiFacetKey($key) {
-    return static::MULTI_FACET_KEY == substr($key, 0, strlen(static::MULTI_FACET_KEY));
-  }
-
-
-  public static function extractMultiFacets($datas) {
-    if (!$datas)
-      return null;
-
-    $facets = [];
-    foreach($datas as $k => $v)
-      $facets = static::_extractMultiFacet($facets, $k, $v);
-
-    return $facets;
-  }
-
-
-  protected static function _extractMultiFacet($facets, $k, $v) {
-    if ($facet = static::multifacetCodeFromKey($k)) {
-      $facets[$facet] = $v;
-      return $facets;
-    }
-
-    if (0 !== strpos($k, 'custom_multifacets_'))
-      return $facets;
-
-    if (!is_array($v)) {
-      $facets[$v] = 1;
-      return $facets;
-    }
-
-    return array_merge($facets,
-                       array_combine($v, array_fill(0, count($v), 1)));
-  }
-
-
   protected function _orderFacetsByAlpha($facets) {
     $ordered = [];
     foreach ($facets as $key => $facets_list)
@@ -274,4 +111,19 @@ class Intonation_View_Search_Facets extends ZendAfi_View_Helper_BaseHelper {
 
     return $sorted;
   }
+
+
+  protected function isActive($code) {
+    $this->_all_facets = $this->_all_facets
+      ? $this->_all_facets
+      : array_unique(array_merge($this->_criteres->getFacettes(),
+                                 $this->_criteres->getMultiFacets()));
+
+    return in_array($code, $this->_all_facets);
+  }
+
+
+  protected function isMultiActive($code) {
+    return $this->isActive($code);
+  }
 }
\ No newline at end of file
diff --git a/library/templates/Intonation/View/Search/HtmlCriteria.php b/library/templates/Intonation/View/Search/HtmlCriteria.php
index cefa3acbdd4..501954c806d 100644
--- a/library/templates/Intonation/View/Search/HtmlCriteria.php
+++ b/library/templates/Intonation/View/Search/HtmlCriteria.php
@@ -60,7 +60,7 @@ class Intonation_View_Search_HtmlCriteria extends ZendAfi_View_Helper_TagCritere
                                                'Text' => $label,
                                                'Title' => $this->_('Retirer le critère: %s', $label),
                                                'InlineText' => 1,
-                                               'Class' => 'btn btn-warning btn-sm mb-2 mr-2 text-dark text-left']));
+                                               'Class' => 'active_criteria']));
   }
 
 
diff --git a/library/templates/Intonation/View/Search/Result.php b/library/templates/Intonation/View/Search/Result.php
index 2d3e2987ae1..3c71ee73cc9 100644
--- a/library/templates/Intonation/View/Search/Result.php
+++ b/library/templates/Intonation/View/Search/Result.php
@@ -35,10 +35,10 @@ class Intonation_View_Search_Result extends ZendAfi_View_Helper_BaseHelper {
 
     $records_html = Class_Systeme_ModulesAppli::LISTE_FORMAT_MUR == $search->getCriteresRecherche()->getFormat()
       ? $this->view->renderWall(new Storm_Collection($records),
-                                       function($wrapped)
-                                       {
-                                         return $this->view->cardify($wrapped);
-                                       })
+                                function($wrapped)
+                                {
+                                  return $this->view->cardify($wrapped);
+                                })
       : $this->view->renderList(new Storm_Collection($records),
                                 function($wrapped)
                                 {
@@ -59,53 +59,56 @@ class Intonation_View_Search_Result extends ZendAfi_View_Helper_BaseHelper {
 
     $title_from_url = $this->view->titre ? $this->view->titre : '*';
 
-    $this->view->titre = $this->_('Résultats pour %s %s',
-                                  strtolower(($criteria->getExpressionRecherche()
-                                   ? $criteria->getExpressionRecherche()
-                                              : $title_from_url)),
-                                  strtolower($text_criteria));
+    $title = $this->_('Résultats pour %s %s',
+                      strtolower(($criteria->getExpressionRecherche()
+                                  ? $criteria->getExpressionRecherche()
+                                  : $title_from_url)),
+                      strtolower($text_criteria));
+
+    $this->view->titre = strip_tags($title);
 
     $domain_browser = ((($domain_browser = $this->view->Search_DomainBrowser($criteria))
-                        ? $this->_div(['class' => 'col-12 pt-2 mb-2 search_domain_browser'],
+                        ? $this->_div(['class' => 'search_domain_browser search_domain_browser_col'],
                                       $this->view->renderExpandable($domain_browser,
                                                                     $this->_tag('span', '', ['class' => 'navbar-toggler-icon'])
                                                                     . $this->_tag('span', $this->_('Arborescence'), ['class' => 'ml-3 text-dark'])))
                         : ''));
 
     $tools = $records
-      ? $this->_div(['class' => 'col-12 border-top border-bottom pt-2 mb-2 search_tools'],
-                    $this->_renderTools($search, $criteria))
+      ? $this->_renderTools($search, $criteria)
       : '';
 
     $result = $records
-      ? $this->_div(['class' => 'col-12 col-md-9'],
+      ? $this->_div(['class' => 'search_records_col'],
                     $this->view->div(['class' => 'ml-md-3'],
                                      $records_html))
-      : $this->_div(['class' => 'col-12'],
+      : $this->_div(['class' => 'search_no_records_col'],
                     $this->_tag('p',
                                 $this->_('Aucun résultat'),
                                 ['class' => 'text-center text-info p-3 m-3']));
 
-    $html = [$this->_div(['class' => 'col-12'],
-                          $this->_tag('h1',
-                                      $this->view->titre,
-                          ['class' => 'content_title'])),
+    $html = [$this->_div(['class' => 'search_title_col'],
+                         $this->_tag('h1',
+                                     $title,
+                                     ['class' => 'content_title'])),
 
-             $this->_div(['class' => 'col-12'],
+             $this->_div(['class' => 'search_criteria_col'],
                          $html_criteria),
 
-             $tools,
+             $this->_div(['class' => 'search_tools_col'],
+                         $tools),
 
              $domain_browser,
 
              ($records
-              ? $this->_div(['class' => 'col-12 col-md-3'],
+              ? $this->_div(['class' => 'search_facets_col'],
                             $facets)
               : ''),
 
              $result,
 
-             $tools];
+             $this->_div(['class' => 'search_tools_col_2'],
+                         $tools)];
 
     return $this->view->grid(implode($html), ['class' => 'search_result'], ['class' => 'justify-content-center']);
   }
@@ -114,27 +117,27 @@ class Intonation_View_Search_Result extends ZendAfi_View_Helper_BaseHelper {
   protected function _renderTools($search, $criteria) {
     $wall_active = Class_Systeme_ModulesAppli::LISTE_FORMAT_MUR == $search->getCriteresRecherche()->getFormat();
 
-    $tools = [$this->_tag( 'span' ,
-                          $this->_( '%d résultats' , $search->getRecordsCount()),
-                          ['class' => 'btn btn-sm btn-info']),
+    $tools = ['result_count_results' => $this->_tag( 'span' ,
+                                                    $this->_( '%d résultats' , $search->getRecordsCount()),
+                                                    ['class' => 'btn btn-sm btn-info']),
 
-              $this->view->tagAction(new Intonation_Library_Link(['Url' => $this->view->url(['liste_format' => Class_Systeme_ModulesAppli::LISTE_FORMAT_LIST]),
-                                                                  'Image' => Class_Template::current()->getIco($this->view, 'list', 'utils'),
-                                                                  'Text' => $this->_tag('span', $this->_('Liste')),
-                                                                  'Title' => $this->_('Afficher le résultat de recherche en mode liste'),
-                                                                  'InlineText' => 1,
-                                                                  'Attribs' => ['class' => 'btn-sm list_format' . ($wall_active ? '' : ' active')]])),
+              'result_list_mod' => $this->view->tagAction(new Intonation_Library_Link(['Url' => $this->view->url(['liste_format' => Class_Systeme_ModulesAppli::LISTE_FORMAT_LIST]),
+                                                                                       'Image' => Class_Template::current()->getIco($this->view, 'list', 'utils'),
+                                                                                       'Text' => $this->_tag('span', $this->_('Liste')),
+                                                                                       'Title' => $this->_('Afficher le résultat de recherche en mode liste'),
+                                                                                       'InlineText' => 1,
+                                                                                       'Attribs' => ['class' => 'btn-sm list_format' . ($wall_active ? '' : ' active')]])),
 
-              $this->view->tagAction(new Intonation_Library_Link(['Url' => $this->view->url(['liste_format' => Class_Systeme_ModulesAppli::LISTE_FORMAT_MUR]),
-                                                                  'Image' => Class_Template::current()->getIco($this->view, 'wall', 'utils'),
-                                                                  'Text' => $this->_tag('span', $this->_('Mur')),
-                                                                  'Title' => $this->_('Afficher le résultat de recherche en mode mur'),
-                                                                  'InlineText' => 1,
-                                                                  'Attribs' => ['class' => 'btn-sm list_format' . ($wall_active ? ' active' : '')]])),
+              'result_wall_mod' => $this->view->tagAction(new Intonation_Library_Link(['Url' => $this->view->url(['liste_format' => Class_Systeme_ModulesAppli::LISTE_FORMAT_MUR]),
+                                                                                       'Image' => Class_Template::current()->getIco($this->view, 'wall', 'utils'),
+                                                                                       'Text' => $this->_tag('span', $this->_('Mur')),
+                                                                                       'Title' => $this->_('Afficher le résultat de recherche en mode mur'),
+                                                                                       'InlineText' => 1,
+                                                                                       'Attribs' => ['class' => 'btn-sm list_format' . ($wall_active ? ' active' : '')]])),
 
-              $this->view->search_PageSize($criteria),
+              'result_page_size' => $this->view->search_PageSize($criteria),
 
-              $this->view->search_Order($criteria)];
+              'result_search_order' => $this->view->search_Order($criteria)];
 
     $tools = array_merge($tools,
                          $this->_pager($search, $criteria));
@@ -147,26 +150,24 @@ class Intonation_View_Search_Result extends ZendAfi_View_Helper_BaseHelper {
       ->setModel($bookmarked_search)
       ->setView($this->view);
 
-    $tools [] = $this->view->getHelper('TagSelectRecord')->tagRecordSelectionCount($criteria);
+    $tools ['result_select_records'] = $this->view->getHelper('TagSelectRecord')->tagRecordSelectionCount($criteria);
 
-    $tools [] = $this->view->tagAction($search_wrapper
-                                       ->getActions()[0]
-                                       ->setAttribs(['data-popup' => true,
-                                                     'class' => 'btn btn-sm btn-primary text-white']));
+    $tools ['result_follow_search'] = $this->view->tagAction($search_wrapper
+                                                             ->getActions()[0]
+                                                             ->setAttribs(['data-popup' => true,
+                                                                           'class' => 'btn btn-sm btn-primary text-white']));
 
-    $tools [] = $search_wrapper->getMoreActions();
+    $tools ['result_more_actions'] = $search_wrapper->getMoreActions();
 
-    $tools = array_map(function($tool)
-                       {
-                         return $this->_tag('li',
-                                            $tool,
-                                            ['class' => 'nav-item mb-2']);
-                       },
-                       $tools);
+    $html_tools = [];
+    foreach ($tools as $class => $html)
+      $html_tools []  = $this->_tag('li',
+                                    $html,
+                                    ['class' => $class . ' nav-item mb-2']);
 
     return $this->_tag('ul',
-                       implode($tools),
-                       ['class' => 'nav nav-fill nav-pills']);
+                       implode($html_tools),
+                       ['class' => 'result_tools_nav nav nav-fill nav-pills']);
   }
 
 
@@ -177,19 +178,19 @@ class Intonation_View_Search_Result extends ZendAfi_View_Helper_BaseHelper {
 
     $count_pages = ceil( $count_result / $page_size );
 
-    return [$this->view->div([],
-                             implode([$this->view->tagAnchor(['page' => $current_page -1],
-                                                             $this->_tag('i','',['class' => 'fas fa-chevron-left m-0']),
-                                                             ['title' => $this->_('page précedente'),
-                                                              'class' => 'btn btn-sm btn-secondary']),
+    return ['result_pager' => $this->view->div([],
+                                               implode([$this->view->tagAnchor(['page' => $current_page -1],
+                                                                               $this->_tag('i','',['class' => 'fas fa-chevron-left m-0']),
+                                                                               ['title' => $this->_('page précedente'),
+                                                                                'class' => 'btn btn-sm btn-secondary']),
 
-                                      $this->_tag( 'span' ,
-                                                  $this->_('Page %d / %d' , $current_page, $count_pages),
-                                                  ['class' => 'btn btn-sm']),
+                                                        $this->_tag( 'span' ,
+                                                                    $this->_('Page %d / %d' , $current_page, $count_pages),
+                                                                    ['class' => 'btn btn-sm']),
 
-                                      $this->view->tagAnchor(['page' => $current_page +1],
-                                                             $this->_tag('i','',['class' => 'fas fa-chevron-right m-0']),
-                                                             ['title' => $this->_('page suivante'),
-                                                              'class' => 'btn btn-sm btn-secondary'])]))];
+                                                        $this->view->tagAnchor(['page' => $current_page +1],
+                                                                               $this->_tag('i','',['class' => 'fas fa-chevron-right m-0']),
+                                                                               ['title' => $this->_('page suivante'),
+                                                                                'class' => 'btn btn-sm btn-secondary'])]))];
   }
 }
diff --git a/library/templates/Intonation/View/Search/TextCriteria.php b/library/templates/Intonation/View/Search/TextCriteria.php
index cb3f577af19..3cd8c74f1d6 100644
--- a/library/templates/Intonation/View/Search/TextCriteria.php
+++ b/library/templates/Intonation/View/Search/TextCriteria.php
@@ -61,4 +61,10 @@ class Intonation_View_Search_TextCriteria extends Intonation_View_Search_HtmlCri
     $this->_html .= ', ' . $text;
     return $this;
   }
+
+
+  public function visitExpression($expression) {
+    if ($this->_current_facettes)
+      $this->_html .= $this->_('restreint à ');
+  }
 }
\ No newline at end of file
diff --git a/library/templates/Muscle/Assets/css/muscle.css b/library/templates/Muscle/Assets/css/muscle.css
index c01a854be84..db41d9c78b4 100644
--- a/library/templates/Muscle/Assets/css/muscle.css
+++ b/library/templates/Muscle/Assets/css/muscle.css
@@ -106,6 +106,7 @@ col-form-label-sm,
 }
 
 .rech_simple.widget input,
+.rech_simple.widget select,
 .rech_simple.widget button {
     background-color: var(--muscle-white);
     line-height: 1em;
@@ -118,10 +119,6 @@ col-form-label-sm,
     font-size: 1.5em;
 }
 
-.rech_simple .dropdown-menu {
-    left: -33% !important;
-}
-
 .rech_simple.widget button[type='submit'] {
     background-color: var(--muscle-red);
     color: var(--muscle-white);
@@ -199,8 +196,10 @@ footer .nav-link {
     color: var(--muscle-black);
 }
 
+.widget.login form,
 .widget.login form * {
     text-align: left;
+    justify-content: flex-start;
 }
 
 .widget.login .text-secondary {
@@ -218,21 +217,10 @@ footer .nav-link {
     padding: 5px 15px;
 }
 
-.widget.login #login {
-    position: absolute;
-    bottom: 4em;
-    left: 0.875em;
-}
-
 .widget.login a {
     font-weight: bold;
 }
 
-.widget.login a[href*="/auth/lostpass"] {
-    display: block;
-    margin-top: 4em;
-}
-
 .widget.login form {
     max-width: 200px;
 }
diff --git a/library/templates/Polygone/Assets/css/polygone.css b/library/templates/Polygone/Assets/css/polygone.css
index dd841405d39..885b8618243 100644
--- a/library/templates/Polygone/Assets/css/polygone.css
+++ b/library/templates/Polygone/Assets/css/polygone.css
@@ -92,6 +92,7 @@ col-form-label-sm,
 }
 
 .rech_simple.widget input,
+.rech_simple.widget select,
 .rech_simple.widget button {
     line-height: 1em;
     vertical-align: middle;
diff --git a/library/templates/TerreDuMilieu/Assets/css/terredumilieu.css b/library/templates/TerreDuMilieu/Assets/css/terredumilieu.css
index d001c68304a..2529fc01788 100644
--- a/library/templates/TerreDuMilieu/Assets/css/terredumilieu.css
+++ b/library/templates/TerreDuMilieu/Assets/css/terredumilieu.css
@@ -62,7 +62,8 @@ header {
 }
 
 .tdm_search_widget .expressionRecherche,
-.tdm_search_widget button {
+.tdm_search_widget button,
+.tdm_search_widget select {
     height: 2.5em;
 }
 
diff --git a/public/opac/css/core.css b/public/opac/css/core.css
index 0a0b4159c6b..228a07d36ea 100644
--- a/public/opac/css/core.css
+++ b/public/opac/css/core.css
@@ -197,4 +197,16 @@ ol.breadcrumb li {
 
 input.form-control[type=text] {
     display:unset;
+}
+
+label.required:after {
+    content:'*';
+    margin: 0 5px;
+}
+
+.scroll_anchor {
+    height: 0 !important;
+    width: 0 !important;
+    padding: 0 !important;
+    margin: 0 !important;
 }
\ No newline at end of file
diff --git a/tests/library/ZendAfi/View/Helper/FacettesTest.php b/tests/library/ZendAfi/View/Helper/FacettesTest.php
index 5d1867a38b2..bca48ae262e 100644
--- a/tests/library/ZendAfi/View/Helper/FacettesTest.php
+++ b/tests/library/ZendAfi/View/Helper/FacettesTest.php
@@ -230,14 +230,14 @@ class ZendAfi_View_Helper_MultiFacettesTest extends ZendAfi_View_Helper_Facettes
   /** @test */
   public function checkboxForFacetT1houldBePresent() {
       $this->assertXPath($this->_html,
-                         '//li[@class="facette"]//input[@name="multifacet_T1"][@value="1"]', $this->_html);
+                         '//li[@class="facet_item facette"]//input[@name="multifacet_T1"][@value="1"]', $this->_html);
   }
 
 
   /** @test */
   public function hiddenCheckboxForFacetT1houldBePresent() {
       $this->assertXPath($this->_html,
-                         '//li[@class="facette"]//input[@name="multifacet_T1"][@value="0"][@type="hidden"]', $this->_html);
+                         '//li[@class="facet_item facette"]//input[@name="multifacet_T1"][@value="0"][@type="hidden"]', $this->_html);
   }
 }
 
@@ -275,6 +275,6 @@ class ZendAfi_View_Helper_DisabledMultiFacettesTest extends ZendAfi_View_Helper_
   /** @test */
   public function checkboxForFacetT1houldBeHidden() {
       $this->assertXPath($this->_html,
-                         '//li[@class="facette"]//input[@style="display: none;"][@name="multifacet_T1"][@value="1"]', $this->_html);
+                         '//li[@class="facet_item facette"]//input[@style="display: none;"][@name="multifacet_T1"][@value="1"]', $this->_html);
   }
 }
\ No newline at end of file
diff --git a/tests/scenarios/SearchResult/SearchResultTest.php b/tests/scenarios/SearchResult/SearchResultTest.php
index acef0f0526b..ceed2a8a01e 100644
--- a/tests/scenarios/SearchResult/SearchResultTest.php
+++ b/tests/scenarios/SearchResult/SearchResultTest.php
@@ -232,13 +232,13 @@ class SearchResultFilterFromProfilOnDomainTest extends AbstractControllerTestCas
 
   /** @test */
   public function shouldDisplayNonFilteredAuthor() {
-    $this->assertXPath('//li[@class="facette"]//input[@name="multifacet_A332"]');
+    $this->assertXPath('//li[@class="facet_item facette"]//input[@name="multifacet_A332"]');
   }
 
 
   /** @test */
   public function facetShouldNotDisplyFilteredDocTypeBook() {
-    $this->assertNotXpath('//li[@class="facette"]//input[@name="multifacet_T1"]');
+    $this->assertNotXpath('//li[@class="facet_item facette"]//input[@name="multifacet_T1"]');
   }
 }
 
diff --git a/tests/scenarios/Templates/MuscleTemplateTest.php b/tests/scenarios/Templates/MuscleTemplateTest.php
index 5650b031c80..95cb20e091a 100644
--- a/tests/scenarios/Templates/MuscleTemplateTest.php
+++ b/tests/scenarios/Templates/MuscleTemplateTest.php
@@ -523,4 +523,27 @@ class MuscleTemplateSearchResultWithDomainBrowserTest extends MuscleTemplateTest
   public function domainBrowserShouldContainsRomans() {
     $this->assertXPathContentContains('//div[contains(@class, "tree_view_current")]', 'Romans');
   }
+}
+
+
+class MuscleTemplateNotLoggedTest extends MuscleTemplateTestCase {
+
+
+  public function setUp() {
+    parent::setUp();
+    ZendAfi_Auth::getInstance()->clearIdentity();
+    $this->dispatch('/');
+  }
+
+
+  /** @test */
+  public function inputUsernameShouldBeHydratedWithOrder3() {
+    $this->assertXPath('//form/div[@class="form-group container-fluid no-gutters py-1 wrapper_zendafi_form_login_username col-12 order-1"]//input[@id="username"][@class= "zendafi_form_login_username form-control form-control-sm"]');
+  }
+
+
+  /** @test */
+  public function inputLoginShouldBeHydratedWithOrder3() {
+    $this->assertXPath('//form/input[@id="login"][@class= "zendafi_form_login_login btn btn-sm btn-primary order-3 my-3"]');
+  }
 }
\ No newline at end of file
diff --git a/tests/scenarios/Templates/TemplatesTest.php b/tests/scenarios/Templates/TemplatesTest.php
index 28187e49fb5..008db272d38 100644
--- a/tests/scenarios/Templates/TemplatesTest.php
+++ b/tests/scenarios/Templates/TemplatesTest.php
@@ -1229,6 +1229,10 @@ class TemplatesIntonationHydratingtest extends ModelTestCase {
               'attribs' => []],
              'form-control form-control-sm custom-select custom-select-sm'],
 
+            [['element' => 'select',
+              'attribs' => ['class' => 'an other class']],
+             'an other class form-control form-control-sm custom-select custom-select-sm'],
+
             [['element' => 'button',
               'attribs' => ['class' => 'btn search-button']],
              'btn search-button']
@@ -3620,7 +3624,11 @@ class TemplatesIntonationDispatchAbonneAjouterASelectionTest extends TemplatesIn
                 $this->fixture('Class_Notice',
                                ['id' => 99])];
 
-    Class_MoteurRecherche::setInstance($this->mock()
+    $engine = $this->mock();
+    Class_MoteurRecherche::setInstance($engine
+                                       ->whenCalled('visitLimit')
+                                       ->answers($engine)
+
                                        ->whenCalled('lancerRecherche')
                                        ->answers($this->mock()
                                                  ->whenCalled('fetchRecords')
-- 
GitLab