From 86391b6f97f8446065761bbeecc72c926302a379 Mon Sep 17 00:00:00 2001
From: Chris Hallberg <crhallberg@gmail.com>
Date: Fri, 21 Apr 2017 12:30:30 -0400
Subject: [PATCH] Breakup Recommendation/SideFacets into smaller templates
 (#892)

---
 .../templates/Recommend/SideFacets.phtml      | 246 +++---------------
 .../SideFacets/checkbox-filters.phtml         |  12 +
 .../Recommend/SideFacets/cluster-list.phtml   |  63 +++++
 .../Recommend/SideFacets/filter-list.phtml    |  27 ++
 .../SideFacets/hierarchical-facet.phtml       |  28 ++
 .../Recommend/SideFacets/range-slider.phtml   |  71 +++++
 6 files changed, 235 insertions(+), 212 deletions(-)
 create mode 100644 themes/bootstrap3/templates/Recommend/SideFacets/checkbox-filters.phtml
 create mode 100644 themes/bootstrap3/templates/Recommend/SideFacets/cluster-list.phtml
 create mode 100644 themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml
 create mode 100644 themes/bootstrap3/templates/Recommend/SideFacets/hierarchical-facet.phtml
 create mode 100644 themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml

diff --git a/themes/bootstrap3/templates/Recommend/SideFacets.phtml b/themes/bootstrap3/templates/Recommend/SideFacets.phtml
index 97bce3b5cee..7116b00d252 100644
--- a/themes/bootstrap3/templates/Recommend/SideFacets.phtml
+++ b/themes/bootstrap3/templates/Recommend/SideFacets.phtml
@@ -7,238 +7,60 @@
 <? if ($results->getResultTotal() > 0): ?>
   <h4><?=$this->transEsc(isset($this->overrideSideFacetCaption) ? $this->overrideSideFacetCaption : 'Narrow Search')?></h4>
 <? endif; ?>
-<? $checkboxFilters = $results->getParams()->getCheckboxFacets(); if (count($checkboxFilters) > 0): ?>
-<?
-  $html = '';
-  $shown = 0;
-  foreach ($checkboxFilters as $current) {
-    $html .= '<label class="checkbox';
-    if($results->getResultTotal() < 1 && !$current['selected'] && !$current['alwaysVisible']) {
-      $html .= ' hidden';
-    } else {
-      $shown ++;
+<? $checkboxFilters = $results->getParams()->getCheckboxFacets(); ?>
+<? $checkboxesShown = false; ?>
+<? if (count($checkboxFilters) > 0):
+    foreach ($checkboxFilters as $current) {
+      if ($results->getResultTotal() > 0 || $current['selected'] || $current['alwaysVisible']) {
+        $checkboxesShown = true;
+        break;
+      }
     }
-    $html .= '"><input type="checkbox" name="filter[]" value="'.$this->escapeHtmlAttr($current['filter']).'"
-      '. ($current['selected'] ? 'checked="checked"' : '') .' id="'.$this->escapeHtmlAttr(str_replace(' ', '', $current['desc'])).'"
-      onclick="document.location.href=\''.($current['selected'] ? $results->getUrlQuery()->removeFilter($current['filter']) : $results->getUrlQuery()->addFilter($current['filter'])).'\';" />'.$this->transEsc($current['desc']).'</label>';
-  }
-?>
-  <div class="checkboxFilter<?if($shown == 0):?> hidden<? endif; ?>"><?=$html ?></div>
+  ?>
+  <?if ($checkboxesShown):?>
+    <div class="checkboxFilter">
+      <?=$this->context($this)->renderInContext('Recommend/SideFacets/checkbox-filters.phtml', ['checkboxFilters' => $checkboxFilters, 'results' => $results]); ?>
+    </div>
+  <? endif; ?>
 <? endif; ?>
 <? $extraFilters = isset($this->extraSideFacetFilters) ? $this->extraSideFacetFilters : []; ?>
 <? $collapsedFacets = $this->recommend->getCollapsedFacets() ?>
-<? $hierarchicalFacetSortOptions = $this->recommend->getHierarchicalFacetSortOptions() ?>
-<? $hierarchicalFacets = $this->recommend->getHierarchicalFacets() ?>
-<? $filterList = array_merge($results->getParams()->getFilterList(true), $extraFilters); if (!empty($filterList)): ?>
-  <div class="list-group filters">
-    <div class="list-group-item title"><?=$this->transEsc('Remove Filters')?></div>
-    <? foreach ($filterList as $field => $filters): ?>
-      <? foreach ($filters as $i => $filter): ?>
-        <?
-          $index = isset($filter['field']) ? array_search($filter['field'], $collapsedFacets) : false;
-          if ($index !== false) {
-              unset($collapsedFacets[$index]); // Open if we have a match
-          }
-          if (isset($filter['specialType']) && $filter['specialType'] == 'keyword') {
-            $removeLink = $this->currentPath().$results->getUrlQuery()->replaceTerm($filter['value'], '');
-          } else {
-            $removeLink = $this->currentPath().$results->getUrlQuery()->removeFacet($filter['field'], $filter['value'], $filter['operator']);
-          }
-          if ($filter['displayText'] == '[* TO *]') {
-            $filter['displayText'] = $this->translate('filter_wildcard');
-          }
-        ?>
-        <a class="list-group-item active" href="<?=$removeLink?>" title="<?=$this->transEsc('clear_tag_filter') ?>">
-          <span class="pull-right flip"><i class="fa fa-times" aria-hidden="true"></i></span>
-          <? if ($filter['operator'] == 'NOT') echo $this->transEsc('NOT') . ' '; if ($filter['operator'] == 'OR' && $i > 0) echo $this->transEsc('OR') . ' '; ?><?=$this->transEsc($field)?>: <?=$this->escapeHtml($filter['displayText'])?>
-        </a>
-      <? endforeach; ?>
-    <? endforeach; ?>
-  </div>
+<? $filterList = array_merge($results->getParams()->getFilterList(true), $extraFilters); ?>
+<? if (!empty($filterList)): ?>
+  <?=$this->context($this)->renderInContext('Recommend/SideFacets/filter-list.phtml', [
+    'collapsedFacets' => $collapsedFacets,
+    'extraFilters' => $extraFilters,
+    'filterList' => $filterList,
+  ]); ?>
 <? endif; ?>
 <?= isset($this->sideFacetExtraControls) ? $this->sideFacetExtraControls : '' ?>
 <? $sideFacetSet = $this->recommend->getFacetSet(); $rangeFacets = $this->recommend->getAllRangeFacets(); ?>
+<? $hierarchicalFacets = $this->recommend->getHierarchicalFacets() ?>
+<? $hierarchicalFacetSortOptions = $this->recommend->getHierarchicalFacetSortOptions() ?>
 <? if (!empty($sideFacetSet) && $results->getResultTotal() > 0): ?>
   <? foreach ($sideFacetSet as $title => $cluster): ?>
-    <? $allowExclude = $this->recommend->excludeAllowed($title); ?>
-    <? $facets_before_more = $this->recommend->getShowMoreSetting($title); ?>
-    <? $showMoreInLightbox = $this->recommend->getShowInLightboxSetting($title); ?>
     <div class="list-group facet" id="side-panel-<?=$this->escapeHtmlAttr($title) ?>">
       <div class="list-group-item title<? if(in_array($title, $collapsedFacets)): ?> collapsed<? endif ?>" data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" >
         <?=$this->transEsc($cluster['label'])?>
       </div>
       <div id="side-collapse-<?=$this->escapeHtmlAttr($title) ?>" class="collapse<? if(!in_array($title, $collapsedFacets)): ?> in<? endif ?>">
         <? if (isset($rangeFacets[$title])): ?>
-          <div class="list-group-item">
-            <form name="<?=$this->escapeHtmlAttr($title)?>Filter" id="<?=$this->escapeHtmlAttr($title)?>Filter">
-              <?=$results->getUrlQuery()->asHiddenFields(['page' => "/./", 'filter' => "/^{$title}:.*/"])?>
-              <input type="hidden" name="<?=$this->escapeHtmlAttr($rangeFacets[$title]['type'])?>range[]" value="<?=$this->escapeHtmlAttr($title)?>"/>
-              <div class="row">
-                <? $extraInputAttribs = ($rangeFacets[$title]['type'] == 'date') ? 'maxlength="4" ' : ''; ?>
-                <div class="col-sm-6">
-                  <label for="<?=$this->escapeHtmlAttr($title)?>from">
-                    <?=$this->transEsc('date_from')?>:
-                  </label>
-                  <input type="text" class="form-control" name="<?=$this->escapeHtmlAttr($title)?>from" id="<?=$this->escapeHtmlAttr($title)?>from" value="<?=isset($rangeFacets[$title]['values'][0])?$this->escapeHtmlAttr($rangeFacets[$title]['values'][0]):''?>" <?=$extraInputAttribs?>/>
-                </div>
-                <div class="col-sm-6">
-                  <label for="<?=$this->escapeHtmlAttr($title)?>to">
-                    <?=$this->transEsc('date_to')?>:
-                  </label>
-                  <input type="text" class="form-control" name="<?=$this->escapeHtmlAttr($title)?>to" id="<?=$this->escapeHtmlAttr($title)?>to" value="<?=isset($rangeFacets[$title]['values'][1])?$this->escapeHtmlAttr($rangeFacets[$title]['values'][1]):''?>" <?=$extraInputAttribs?>/>
-                </div>
-              </div>
-              <? if ($rangeFacets[$title]['type'] == 'date'): ?>
-                <div class="slider-container"><input type="text" class="hidden" id="<?=$this->escapeHtmlAttr($title)?><?=$this->escapeHtml($rangeFacets[$title]['type'])?>Slider"/></div>
-              <? endif; ?>
-              <input class="btn btn-default" type="submit" value="<?=$this->transEsc('Set')?>"/>
-            </form>
-          </div>
-          <? if ($rangeFacets[$title]['type'] == 'date'): ?>
-            <? $this->headScript()->appendFile('vendor/bootstrap-slider.min.js'); ?>
-            <? $this->headLink()->appendStylesheet('vendor/bootstrap-slider.min.css'); ?>
-            <?
-              $min = !empty($rangeFacets[$title]['values'][0]) ? min($rangeFacets[$title]['values'][0], 1400) : 1400;
-              $future = date('Y', time()+31536000);
-              $max = !empty($rangeFacets[$title]['values'][1]) ? max($future, $rangeFacets[$title]['values'][1]) : $future;
-              $low  = !empty($rangeFacets[$title]['values'][0]) ? $rangeFacets[$title]['values'][0] : $min;
-              $high = !empty($rangeFacets[$title]['values'][1]) ? $rangeFacets[$title]['values'][1] : $max;
-              $reversed = $this->layout()->rtl ? 'true' : 'false';
-              $script = <<<JS
-$(document).ready(function() {
-  var fillTexts = function() {
-  var v = {$this->escapeHtmlAttr($title)}dateSlider.getValue();
-  $('#{$this->escapeHtmlAttr($title)}from').val(v[0]);
-  $('#{$this->escapeHtmlAttr($title)}to').val(v[1]);
-  };
-  var {$this->escapeHtmlAttr($title)}dateSlider = $('#{$this->escapeHtmlAttr($title)}dateSlider')
-  .slider({
-    'min':{$min},
-    'max':{$max},
-    'handle':"square",
-    'tooltip':"hide",
-    'value':[{$low},{$high}],
-    'reversed': {$reversed}
-  })
-  .on('change', fillTexts)
-  .data('slider');
-});
-
-$('#{$this->escapeHtmlAttr($title)}from, #{$this->escapeHtmlAttr($title)}to').change(function () {
-  var fromValue = Number($('#{$this->escapeHtmlAttr($title)}from').val());
-  var toValue = Number($('#{$this->escapeHtmlAttr($title)}to').val());
-  $('#{$this->escapeHtmlAttr($title)}dateSlider').slider(
-    'setValue',
-    [
-      isNaN(fromValue) || fromValue <= 0 ? {$min} : fromValue,
-      isNaN(toValue) || toValue <= 0 ? {$max} : toValue
-    ],
-    true
-  );
-});
-JS;
-            ?>
-            <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?>
-          <? endif; ?>
+          <?=$this->context($this)->renderInContext('Recommend/SideFacets/range-slider.phtml', ['title' => $title, 'facet' => $rangeFacets[$title]]); ?>
         <? else: ?>
           <? if (in_array($title, $hierarchicalFacets)): ?>
-            <? $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); ?>
-            <? $sort = isset($hierarchicalFacetSortOptions[$title]) ? $hierarchicalFacetSortOptions[$title] : ''; ?>
-            <? if (!in_array($title, $collapsedFacets)): ?>
-              <?
-              $script = <<<JS
-$(document).ready(function() {
-  initFacetTree($('#facet_{$this->escapeHtml($title)}'), true);
-});
-JS;
-              ?>
-              <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?>
-            <? else: ?>
-              <?
-              $script = <<<JS
-$('#side-collapse-{$this->escapeHtmlAttr($title)}').on('show.bs.collapse', function() {
-  initFacetTree($('#facet_{$this->escapeHtml($title)}'), true);
-});
-JS;
-              ?>
-              <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?>
-            <? endif; ?>
-            <li id="facet_<?=$this->escapeHtml($title)?>" class="jstree-facet"
-                  data-facet="<?=$this->escapeHtmlAttr($title)?>"
-                  data-path="<?=$this->currentPath()?>"
-                  data-exclude="<?=$allowExclude?>"
-                  data-operator="<?=$this->recommend->getFacetOperator($title)?>"
-                  data-exclude-title="<?=$this->transEsc('exclude_facet')?>"
-                  data-sort="<?=isset($hierarchicalFacetSortOptions[$title]) ? $hierarchicalFacetSortOptions[$title] : ''?>">
-            </li>
+            <?=$this->context($this)->renderInContext('Recommend/SideFacets/hierarchical-facet.phtml', ['title' => $title, 'sortOptions' => $hierarchicalFacetSortOptions[$title]]); ?>
             <noscript>
           <? endif; ?>
-          <? foreach ($cluster['list'] as $i=>$thisFacet): ?>
-              <?
-                if(strlen($thisFacet['displayText']) == 0) {
-                  $thisFacet['displayText'] = "-";
-                }
-              ?>
-              <? $moreClass = 'narrowGroupHidden-'.$this->escapeHtmlAttr($title).' hidden'; ?>
-            <? if ($i == $facets_before_more): ?>
-              <? $idAndClass = 'id="more-narrowGroupHidden-'.$this->escapeHtmlAttr($title).'" class="list-group-item narrow-toggle"'; ?>
-              <? if ($facetLightbox = $options->getFacetListAction()): ?>
-                <? $moreUrl = $this->url($facetLightbox) . $results->getUrlQuery()->getParams().'&amp;facet='.$title.'&amp;facetop='.$thisFacet['operator'].'&amp;facetexclude='.($allowExclude ? 1 : 0); ?>
-              <? else: ?>
-                <? $moreUrl = '#'; ?>
-              <? endif; ?>
-              <? if (($showMoreInLightbox && $showMoreInLightbox !== 'more') && $facetLightbox): ?>
-                <a <?=$idAndClass ?> data-lightbox href="<?=$moreUrl ?>" rel="nofollow"><?=$this->transEsc('more')?> ...</a>
-                <? break; ?>
-              <? else: ?>
-                <a <?=$idAndClass ?> href="<?=$moreUrl ?>" onclick="return moreFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($title) ?>')" rel="nofollow"><?=$this->transEsc('more')?> ...</a>
-              <? endif; ?>
-            <? endif; ?>
-            <? if ($thisFacet['isApplied']): ?>
-              <a class="list-group-item active<? if ($i >= $facets_before_more): ?><?=$moreClass ?><?endif ?><? if ($thisFacet['operator'] == 'OR'): ?> facetOR applied<? endif ?>" href="<?=$this->currentPath().$results->getUrlQuery()->removeFacet($title, $thisFacet['value'], $thisFacet['operator']) ?>" title="<?=$this->transEsc('applied_filter') ?>">
-                <? if ($thisFacet['operator'] == 'OR'): ?>
-                  <i class="fa fa-check-square-o" aria-hidden="true"></i>
-                <? else: ?>
-                  <span class="pull-right flip"><i class="fa fa-check" aria-hidden="true"></i></span>
-                <? endif; ?>
-                <?=$this->escapeHtml($thisFacet['displayText'])?>
-              </a>
-            <? else: ?>
-              <? $addURL = $this->currentPath().$results->getUrlQuery()->addFacet($title, $thisFacet['value'], $thisFacet['operator']); ?>
-              <? if ($allowExclude): ?>
-                <div class="list-group-item facet<?=$thisFacet['operator'] ?><? if ($i >= $facets_before_more): ?> <?=$moreClass ?><?endif ?>">
-              <? else: ?>
-                <a href="<?=$addURL ?>" class="list-group-item facet<?=$thisFacet['operator'] ?><? if ($i >= $facets_before_more): ?> <?=$moreClass ?><?endif ?>">
-              <? endif; ?>
-              <span class="badge">
-                <?=$this->localizedNumber($thisFacet['count'])?>
-                <? if ($allowExclude): ?>
-                  <a href="<?=$this->currentPath().$results->getUrlQuery()->addFacet($title, $thisFacet['value'], 'NOT') ?>" title="<?=$this->transEsc('exclude_facet') ?>"><i class="fa fa-times" aria-hidden="true"></i></a>
-                <? endif; ?>
-              </span>
-              <? if ($allowExclude): ?>
-                <a href="<?=$addURL ?>">
-              <? endif; ?>
-              <? if($thisFacet['operator'] == 'OR'): ?>
-                <i class="fa fa-square-o" aria-hidden="true"></i>
-              <? endif; ?>
-              <?=$this->escapeHtml($thisFacet['displayText'])?>
-              <? if ($allowExclude): ?>
-                  </a>
-                </div>
-              <? else: ?>
-                </a>
-              <? endif; ?>
-            <? endif; ?>
-          <? endforeach; ?>
-          <? if ($showMoreInLightbox === 'more' && $facetLightbox = $options->getFacetListAction()): ?>
-            <? $moreUrl = $this->url($facetLightbox) . $results->getUrlQuery()->getParams().'&amp;facet='.$title.'&amp;facetop='.$thisFacet['operator'].'&amp;facetexclude='.($allowExclude ? 1 : 0); ?>
-            <a class="list-group-item narrow-toggle <?=$moreClass ?>" data-lightbox href="<?=$moreUrl ?>" rel="nofollow"><?=$this->transEsc('see all')?> ...</a>
+          <? $contextVars = [
+            'options' => $options,
+            'allowExclude' => $this->recommend->excludeAllowed($title),
+            'facets_before_more' => $this->recommend->getShowMoreSetting($title),
+            'showMoreInLightbox' => $this->recommend->getShowInLightboxSetting($title)
+          ]; ?>
+          <?=$this->context($this)->renderInContext('Recommend/SideFacets/cluster-list.phtml', array_merge($contextVars, ['title' => $title, 'cluster' => $cluster])); ?>
+          <? if (in_array($title, $hierarchicalFacets)): ?>
+            </noscript>
           <? endif; ?>
-          <? if ($i >= $facets_before_more): ?><a class="list-group-item narrow-toggle <?=$moreClass ?>" href="#" onclick="return lessFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($title) ?>')"><?=$this->transEsc('less')?> ...</a><? endif; ?>
-        <? endif; ?>
-        <? if (in_array($title, $hierarchicalFacets)): ?>
-          </noscript>
         <? endif; ?>
       </div>
     </div>
diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/checkbox-filters.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/checkbox-filters.phtml
new file mode 100644
index 00000000000..7d4a4f74584
--- /dev/null
+++ b/themes/bootstrap3/templates/Recommend/SideFacets/checkbox-filters.phtml
@@ -0,0 +1,12 @@
+<? foreach ($checkboxFilters as $current): ?>
+    <label class="checkbox<? if(!($results->getResultTotal() > 0 || $current['selected'] || $current['alwaysVisible'])): ?> hidden<? endif; ?>">
+      <input type="checkbox" name="filter[]"
+        value="<?=$this->escapeHtmlAttr($current['filter']) ?>"
+        <?=$current['selected'] ? ' checked="checked"' : '' ?>
+        id="<?=$this->escapeHtmlAttr(str_replace(' ', '', $current['desc'])) ?>"
+        onclick="document.location.href='<?=$current['selected'] ? $results->getUrlQuery()->removeFilter($current['filter']) : $results->getUrlQuery()->addFilter($current['filter']);?>'" />
+      <?=$this->transEsc($current['desc']) ?>
+    </label>
+  <?
+  endforeach;
+?>
diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/cluster-list.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/cluster-list.phtml
new file mode 100644
index 00000000000..7571097cd3c
--- /dev/null
+++ b/themes/bootstrap3/templates/Recommend/SideFacets/cluster-list.phtml
@@ -0,0 +1,63 @@
+<? $idAndClass = 'id="more-narrowGroupHidden-'.$this->escapeHtmlAttr($this->title).'" class="list-group-item narrow-toggle"'; ?>
+<? $moreClass = 'narrowGroupHidden-'.$this->escapeHtmlAttr($this->title).' hidden'; ?>
+<? foreach ($this->cluster['list'] as $i => $thisFacet): ?>
+  <?
+    if(empty($thisFacet['displayText'])) {
+      $thisFacet['displayText'] = "-";
+    }
+  ?>
+  <? if ($i == $this->facets_before_more): ?>
+    <? if ($facetLightbox = $this->options->getFacetListAction()): ?>
+      <? $moreUrl = $this->url($facetLightbox) . $results->getUrlQuery()->getParams() . '&amp;facet=' . $this->title . '&amp;facetop=' . $thisFacet['operator'] . '&amp;facetexclude=' . ($this->allowExclude ? 1 : 0); ?>
+    <? else: ?>
+      <? $moreUrl = '#'; ?>
+    <? endif; ?>
+    <? if (($this->showMoreInLightbox && $this->showMoreInLightbox !== 'more') && $facetLightbox): ?>
+      <a <?=$idAndClass ?> data-lightbox href="<?=$moreUrl ?>" rel="nofollow"><?=$this->transEsc('more')?> ...</a>
+      <? break; ?>
+    <? else: ?>
+      <a <?=$idAndClass ?> href="<?=$moreUrl ?>" onclick="return moreFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($this->title) ?>')" rel="nofollow"><?=$this->transEsc('more')?> ...</a>
+    <? endif; ?>
+  <? endif; ?>
+  <? if ($thisFacet['isApplied']): ?>
+    <a class="list-group-item active<? if ($i >= $this->facets_before_more): ?><?=$moreClass ?><?endif ?><? if ($thisFacet['operator'] == 'OR'): ?> facetOR applied<? endif ?>" href="<?=$this->currentPath().$results->getUrlQuery()->removeFacet($this->title, $thisFacet['value'], $thisFacet['operator']) ?>" title="<?=$this->transEsc('applied_filter') ?>">
+      <? if ($thisFacet['operator'] == 'OR'): ?>
+        <i class="fa fa-check-square-o" aria-hidden="true"></i>
+      <? else: ?>
+        <span class="pull-right flip"><i class="fa fa-check" aria-hidden="true"></i></span>
+      <? endif; ?>
+      <?=$this->escapeHtml($thisFacet['displayText'])?>
+    </a>
+  <? else: ?>
+    <? $addURL = $this->currentPath().$results->getUrlQuery()->addFacet($this->title, $thisFacet['value'], $thisFacet['operator']); ?>
+    <? if ($this->allowExclude): ?>
+      <div class="list-group-item facet<?=$thisFacet['operator'] ?><? if ($i >= $this->facets_before_more): ?> <?=$moreClass ?><?endif ?>">
+    <? else: ?>
+      <a href="<?=$addURL ?>" class="list-group-item facet<?=$thisFacet['operator'] ?><? if ($i >= $this->facets_before_more): ?> <?=$moreClass ?><?endif ?>">
+    <? endif; ?>
+    <span class="badge">
+      <?=$this->localizedNumber($thisFacet['count'])?>
+      <? if ($this->allowExclude): ?>
+        <a href="<?=$this->currentPath().$results->getUrlQuery()->addFacet($this->title, $thisFacet['value'], 'NOT') ?>" title="<?=$this->transEsc('exclude_facet') ?>"><i class="fa fa-times" aria-hidden="true"></i></a>
+      <? endif; ?>
+    </span>
+    <? if ($this->allowExclude): ?>
+      <a href="<?=$addURL ?>">
+    <? endif; ?>
+    <? if($thisFacet['operator'] == 'OR'): ?>
+      <i class="fa fa-square-o" aria-hidden="true"></i>
+    <? endif; ?>
+    <?=$this->escapeHtml($thisFacet['displayText'])?>
+    <? if ($this->allowExclude): ?>
+        </a>
+      </div>
+    <? else: ?>
+      </a>
+    <? endif; ?>
+  <? endif; ?>
+<? endforeach; ?>
+<? if ($this->showMoreInLightbox === 'more' && $facetLightbox = $options->getFacetListAction()): ?>
+  <? $moreUrl = $this->url($facetLightbox) . $results->getUrlQuery()->getParams().'&amp;facet='.$this->title.'&amp;facetop='.$thisFacet['operator'].'&amp;facetexclude='.($this->allowExclude ? 1 : 0); ?>
+  <a class="list-group-item narrow-toggle <?=$moreClass ?>" data-lightbox href="<?=$moreUrl ?>" rel="nofollow"><?=$this->transEsc('see all')?> ...</a>
+<? endif; ?>
+<? if ($i >= $this->facets_before_more): ?><a class="list-group-item narrow-toggle <?=$moreClass ?>" href="#" onclick="return lessFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($this->title) ?>')"><?=$this->transEsc('less')?> ...</a><? endif; ?>
diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml
new file mode 100644
index 00000000000..37e8f81b209
--- /dev/null
+++ b/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml
@@ -0,0 +1,27 @@
+<div class="list-group filters">
+  <div class="list-group-item title"><?=$this->transEsc('Remove Filters')?></div>
+  <? foreach ($filterList as $field => $filters): ?>
+    <? foreach ($filters as $i => $filter): ?>
+      <?
+        $index = isset($filter['field']) ? array_search($filter['field'], $collapsedFacets) : false;
+        if ($index !== false) {
+          unset($collapsedFacets[$index]); // Open if we have a match
+        }
+        if (isset($filter['specialType']) && $filter['specialType'] == 'keyword') {
+          $removeLink = $this->currentPath() . $results->getUrlQuery()->replaceTerm($filter['value'], '');
+        } else {
+          $removeLink = $this->currentPath() . $results->getUrlQuery()->removeFacet($filter['field'], $filter['value'], $filter['operator']);
+        }
+        if ($filter['displayText'] == '[* TO *]') {
+          $filter['displayText'] = $this->translate('filter_wildcard');
+        }
+      ?>
+      <a class="list-group-item active" href="<?=$removeLink ?>" title="<?=$this->transEsc('clear_tag_filter') ?>">
+        <span class="pull-right flip"><i class="fa fa-times" aria-hidden="true"></i></span>
+        <? if ($filter['operator'] == 'NOT'): ?><?=$this->transEsc('NOT') ?><? endif; ?>
+        <? if ($filter['operator'] == 'OR' && $i > 0): ?><?=$this->transEsc('OR') ?><? endif; ?>
+        <?=$this->transEsc($field) ?>: <?=$this->escapeHtml($filter['displayText']) ?>
+      </a>
+    <? endforeach; ?>
+  <? endforeach; ?>
+</div>
diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/hierarchical-facet.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/hierarchical-facet.phtml
new file mode 100644
index 00000000000..83c50e22aca
--- /dev/null
+++ b/themes/bootstrap3/templates/Recommend/SideFacets/hierarchical-facet.phtml
@@ -0,0 +1,28 @@
+<? $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); ?>
+<? if (!in_array($this->title, $collapsedFacets)): ?>
+  <?
+    $script = <<<JS
+$(document).ready(function() {
+  initFacetTree($('#facet_{$this->escapeHtml($this->title)}'), true);
+});
+JS;
+  ?>
+  <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?>
+<? else: ?>
+  <?
+  $script = <<<JS
+$('#side-collapse-{$this->escapeHtmlAttr($this->title)}').on('show.bs.collapse', function() {
+  initFacetTree($('#facet_{$this->escapeHtml($this->title)}'), true);
+});
+JS;
+  ?>
+  <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?>
+<? endif; ?>
+<li id="facet_<?=$this->escapeHtml($this->title)?>" class="jstree-facet"
+    data-facet="<?=$this->escapeHtmlAttr($this->title)?>"
+    data-path="<?=$this->currentPath()?>"
+    data-exclude="<?=$allowExclude?>"
+    data-operator="<?=$this->recommend->getFacetOperator($this->title)?>"
+    data-exclude-title="<?=$this->transEsc('exclude_facet')?>"
+    data-sort="<?=isset($this->sortOptions) ? $this->sortOptions : ''?>">
+</li>
diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml
new file mode 100644
index 00000000000..1df6d7e3975
--- /dev/null
+++ b/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml
@@ -0,0 +1,71 @@
+<div class="list-group-item">
+  <form name="<?=$this->escapeHtmlAttr($this->title)?>Filter" id="<?=$this->escapeHtmlAttr($this->title)?>Filter">
+    <?=$results->getUrlQuery()->asHiddenFields(['page' => "/./", 'filter' => "/^{$this->title}:.*/"])?>
+    <input type="hidden" name="<?=$this->escapeHtmlAttr($this->facet['type'])?>range[]" value="<?=$this->escapeHtmlAttr($this->title)?>"/>
+    <div class="row">
+      <? $extraInputAttribs = ($this->facet['type'] == 'date') ? 'maxlength="4" ' : ''; ?>
+      <div class="col-sm-6">
+        <label for="<?=$this->escapeHtmlAttr($this->title)?>from">
+          <?=$this->transEsc('date_from')?>:
+        </label>
+        <input type="text" class="form-control" name="<?=$this->escapeHtmlAttr($this->title)?>from" id="<?=$this->escapeHtmlAttr($this->title)?>from" value="<?=isset($this->facet['values'][0])?$this->escapeHtmlAttr($this->facet['values'][0]):''?>" <?=$extraInputAttribs?>/>
+      </div>
+      <div class="col-sm-6">
+        <label for="<?=$this->escapeHtmlAttr($this->title)?>to">
+          <?=$this->transEsc('date_to')?>:
+        </label>
+        <input type="text" class="form-control" name="<?=$this->escapeHtmlAttr($this->title)?>to" id="<?=$this->escapeHtmlAttr($this->title)?>to" value="<?=isset($this->facet['values'][1])?$this->escapeHtmlAttr($this->facet['values'][1]):''?>" <?=$extraInputAttribs?>/>
+      </div>
+    </div>
+    <? if ($this->facet['type'] == 'date'): ?>
+      <div class="slider-container"><input type="text" class="hidden" id="<?=$this->escapeHtmlAttr($this->title)?><?=$this->escapeHtml($this->facet['type'])?>Slider"/></div>
+    <? endif; ?>
+    <input class="btn btn-default" type="submit" value="<?=$this->transEsc('Set')?>"/>
+  </form>
+</div>
+<? if ($this->facet['type'] == 'date'): ?>
+  <? $this->headScript()->appendFile('vendor/bootstrap-slider.min.js'); ?>
+  <? $this->headLink()->appendStylesheet('vendor/bootstrap-slider.min.css'); ?>
+  <?
+    $min = !empty($this->facet['values'][0]) ? min($this->facet['values'][0], 1400) : 1400;
+    $future = date('Y', time()+31536000); // next year
+    $max = !empty($this->facet['values'][1]) ? max($future, $this->facet['values'][1]) : $future;
+    $low  = !empty($this->facet['values'][0]) ? $this->facet['values'][0] : $min;
+    $high = !empty($this->facet['values'][1]) ? $this->facet['values'][1] : $max;
+    $reversed = $this->layout()->rtl ? 'true' : 'false';
+    $script = <<<JS
+$(document).ready(function() {
+  var fillTexts = function() {
+    var v = {$this->escapeHtmlAttr($this->title)}dateSlider.getValue();
+    $('#{$this->escapeHtmlAttr($this->title)}from').val(v[0]);
+    $('#{$this->escapeHtmlAttr($this->title)}to').val(v[1]);
+  };
+  var {$this->escapeHtmlAttr($this->title)}dateSlider = $('#{$this->escapeHtmlAttr($this->title)}dateSlider')
+    .slider({
+      'min':{$min},
+      'max':{$max},
+      'handle':"square",
+      'tooltip':"hide",
+      'value':[{$low},{$high}],
+      'reversed': {$reversed}
+    })
+    .on('change', fillTexts)
+    .data('slider');
+});
+
+$('#{$this->escapeHtmlAttr($this->title)}from, #{$this->escapeHtmlAttr($this->title)}to').change(function () {
+  var fromValue = Number($('#{$this->escapeHtmlAttr($this->title)}from').val());
+  var toValue = Number($('#{$this->escapeHtmlAttr($this->title)}to').val());
+  $('#{$this->escapeHtmlAttr($this->title)}dateSlider').slider(
+    'setValue',
+    [
+      isNaN(fromValue) || fromValue <= 0 ? {$min} : fromValue,
+      isNaN(toValue) || toValue <= 0 ? {$max} : toValue
+    ],
+    true
+  );
+});
+JS;
+  ?>
+  <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?>
+<? endif; ?>
-- 
GitLab