From 3d43a93b57bec421856260de7bf5d85578155de7 Mon Sep 17 00:00:00 2001
From: Claas Kazzer <kazzer@ub.uni-leipzig.de>
Date: Mon, 1 Oct 2018 14:58:19 +0200
Subject: [PATCH] refs #14068

* restyles hierarchy tree
---
 local/languages/de.ini                        |   3 +
 local/languages/en.ini                        |   3 +
 themes/finc/js/hierarchyTree.js               | 257 ++++++++++++++++++
 themes/finc/scss/_customVariables.scss        |   3 +
 themes/finc/scss/compiled.scss                | 113 +++++---
 .../templates/RecordTab/hierarchytree.phtml   |  11 +-
 6 files changed, 354 insertions(+), 36 deletions(-)
 create mode 100644 themes/finc/js/hierarchyTree.js

diff --git a/local/languages/de.ini b/local/languages/de.ini
index c4fcde43d73..26bdf479aff 100644
--- a/local/languages/de.ini
+++ b/local/languages/de.ini
@@ -1957,3 +1957,6 @@ support_by_dfg = "Die Nationallizenzen wurden gefördert durch die"
 (fa-home object) = "Online-Ressource"
 (fa-globe) = "Online-Ressource"
 (fa-home passive) = "beschaffbar"
+
+; HierarchyTree select
+hierarchyTreeSelect = "Ãœbergeordnete Werke:"
\ No newline at end of file
diff --git a/local/languages/en.ini b/local/languages/en.ini
index 6db676eafe6..e6563aebb07 100644
--- a/local/languages/en.ini
+++ b/local/languages/en.ini
@@ -1893,3 +1893,6 @@ support_by_dfg = "National licenses were sponsored by "
 (fa-home object) = "online resource"
 (fa-globe) = "online resource"
 (fa-home passive) = "available on request"
+
+; HierarchyTree select
+hierarchyTreeSelect = "Parent items:"
\ No newline at end of file
diff --git a/themes/finc/js/hierarchyTree.js b/themes/finc/js/hierarchyTree.js
new file mode 100644
index 00000000000..1d25c1a9958
--- /dev/null
+++ b/themes/finc/js/hierarchyTree.js
@@ -0,0 +1,257 @@
+/*global VuFind */
+
+var hierarchyID, recordID, htmlID, hierarchyContext, hierarchySettings;
+
+/* Utility functions */
+function htmlEncodeId(id) {
+  return id.replace(/\W/g, "-"); // Also change Hierarchy/TreeRenderer/JSTree.php
+}
+function html_entity_decode(string) {
+  var hash_map = {
+    '&': '&amp;',
+    '>': '&gt;',
+    '<': '&lt;'
+  };
+  var tmp_str = string.toString();
+
+  for (var symbol in hash_map) {
+    if (hash_map.hasOwnProperty(symbol)) {
+      var entity = hash_map[symbol];
+      tmp_str = tmp_str.split(entity).join(symbol);
+    }
+  }
+  tmp_str = tmp_str.split('&#039;').join("'");
+
+  return tmp_str;
+}
+
+function getRecord(id) {
+  $.ajax({
+    url: VuFind.path + '/Hierarchy/GetRecord?' + $.param({id: id}),
+    dataType: 'html'
+  })
+  .done(function getRecordDone(response) {
+    $('#tree-preview').html(html_entity_decode(response));
+    // Remove the old path highlighting
+    $('#hierarchyTree a').removeClass("jstree-highlight");
+    // Add Current path highlighting
+    var jsTreeNode = $(":input[value='" + id + "']").parent();
+    jsTreeNode.children("a").addClass("jstree-highlight");
+    jsTreeNode.parents("li").children("a").addClass("jstree-highlight");
+  });
+}
+
+function changeNoResultLabel(display) {
+  if (display) {
+    $("#treeSearchNoResults").removeClass('hidden');
+  } else {
+    $("#treeSearchNoResults").addClass('hidden');
+  }
+}
+
+function changeLimitReachedLabel(display) {
+  if (display) {
+    $("#treeSearchLimitReached").removeClass('hidden');
+  } else {
+    $("#treeSearchLimitReached").addClass('hidden');
+  }
+}
+
+var searchAjax = false;
+function doTreeSearch() {
+  $('#treeSearchLoadingImg').removeClass('hidden');
+  var keyword = $("#treeSearchText").val();
+  if (keyword.length === 0) {
+    $('#hierarchyTree').find('.jstree-search').removeClass('jstree-search');
+    var tree = $('#hierarchyTree').jstree(true);
+    tree.close_all();
+    tree._open_to(htmlID);
+    $('#treeSearchLoadingImg').addClass('hidden');
+  } else {
+    if (searchAjax) {
+      searchAjax.abort();
+    }
+    searchAjax = $.ajax({
+      url: VuFind.path + '/Hierarchy/SearchTree?' + $.param({
+        lookfor: keyword,
+        hierarchyID: hierarchyID,
+        type: $("#treeSearchType").val()
+      }) + "&format=true"
+    })
+    .done(function searchTreeAjaxDone(data) {
+      if (data.results.length > 0) {
+        $('#hierarchyTree').find('.jstree-search').removeClass('jstree-search');
+        var jstree = $('#hierarchyTree').jstree(true);
+        jstree.close_all();
+        for (var i = data.results.length; i--;) {
+          var id = htmlEncodeId(data.results[i]);
+          jstree._open_to(id);
+        }
+        for (var j = data.results.length; j--;) {
+          var tid = htmlEncodeId(data.results[j]);
+          $('#hierarchyTree').find('#' + tid).addClass('jstree-search');
+        }
+        changeNoResultLabel(false);
+        changeLimitReachedLabel(data.limitReached);
+      } else {
+        changeNoResultLabel(true);
+      }
+      $('#treeSearchLoadingImg').addClass('hidden');
+    });
+  }
+}
+
+function scrollToClicked() {
+  // Scroll to the current record
+  var hTree = $('#hierarchyTree');
+  hTree.animate({
+    scrollTop: $('.jstree-clicked').offset().top - hTree.offset().top + hTree.scrollTop() - 50
+  }, 1000);
+}
+
+function hideFullHierarchy() {
+  var $selected = $('.jstree-clicked');
+  // Hide all nodes
+  $('#hierarchyTree li').hide();
+  // Show the nodes on the current path
+  $selected.show().parents().show();
+  // Show the nodes below the current path
+  $selected.find("li").show();
+}
+
+function buildJSONNodes(xml) {
+  var jsonNode = [];
+  $(xml).children('item').each(function xmlTreeChildren() {
+    var content = $(this).children('content');
+    var id = content.children("name[class='JSTreeID']");
+    var name = content.children('name[href]');
+    jsonNode.push({
+      id: htmlEncodeId(id.text()),
+      text: name.text(),
+      li_attr: { recordid: id.text() },
+      a_attr: {
+        href: name.attr('href'),
+        title: name.text()
+      },
+      type: name.attr('href').match(/\/Collection\//) ? 'collection' : 'record',
+      children: buildJSONNodes(this)
+    });
+  });
+  return jsonNode;
+}
+
+function buildTreeWithXml(cb) {
+  $.ajax({
+    url: VuFind.path + '/Hierarchy/GetTree',
+    data: {
+      hierarchyID: hierarchyID,
+      id: recordID,
+      context: hierarchyContext,
+      mode: 'Tree'
+    }
+  })
+  .done(function getTreeDone(xml) {
+    var nodes = buildJSONNodes($(xml).find('root'));
+    cb.call(this, nodes);
+  });
+}
+
+$(document).ready(function hierarchyTreeReady() {
+  // Code for the search button
+  hierarchyID = $("#hierarchyTree").find(".hiddenHierarchyId")[0].value;
+  recordID = $("#hierarchyTree").find(".hiddenRecordId")[0].value;
+  htmlID = htmlEncodeId(recordID);
+  hierarchyContext = $("#hierarchyTree").find(".hiddenContext")[0].value;
+  var inLightbox = $("#hierarchyTree").parents("#modal").length > 0;
+
+  if (!hierarchySettings.fullHierarchy) {
+    // Set Up Partial Hierarchy View Toggle
+    $('#hierarchyTree').parent().prepend('<a href="#" id="toggleTree" class="closed">' + VuFind.translate("showTree") + '</a>');
+    $('#toggleTree').click(function toggleFullTree(e) {
+      e.preventDefault();
+      $(this).toggleClass("open");
+      if ($(this).hasClass("open")) {
+        $(this).html(VuFind.translate("hideTree"));
+        $('#hierarchyTree li').show();
+      } else {
+        $(this).html(VuFind.translate("showTree"));
+        hideFullHierarchy();
+      }
+      scrollToClicked();
+      $("#hierarchyTree").jstree("toggle_dots");
+    });
+  }
+
+  $("#hierarchyLoading").removeClass('hide');
+
+  $("#hierarchyTree")
+    .bind("ready.jstree", function jsTreeReady(/*event, data*/) {
+      $("#hierarchyLoading").addClass('hide');
+      var tree = $("#hierarchyTree").jstree(true);
+      tree.select_node(htmlID);
+      tree._open_to(htmlID);
+
+      if (!inLightbox && hierarchyContext === "Collection") {
+        getRecord(recordID);
+      }
+
+      $("#hierarchyTree").bind('select_node.jstree', function jsTreeSelect(e, resp) {
+        if (inLightbox || hierarchyContext === "Record") {
+          window.location.href = resp.node.a_attr.href;
+        } else {
+          getRecord(resp.node.li_attr.recordid);
+        }
+      });
+
+      if (!hierarchySettings.fullHierarchy) {
+        // Initial hide of nodes outside current path
+        hideFullHierarchy();
+        $("#hierarchyTree").jstree("toggle_dots");
+      }
+
+      scrollToClicked();
+    })
+    .jstree({
+      plugins: ['search', 'types'],
+      core: {
+        data: function jsTreeCoreData(obj, cb) {
+          $.ajax({
+            url: VuFind.path + '/Hierarchy/GetTreeJSON',
+            data: {
+              hierarchyID: hierarchyID,
+              id: recordID
+            },
+            statusCode: {
+              200: function jsTree200Status(json /*, status, request*/) {
+                cb.call(this, json);
+              },
+              204: function jsTree204Status(/*json, status, request*/) { // No Content
+                buildTreeWithXml(cb);
+              },
+              503: function jsTree503Status(/*json, status, request*/) { // Service Unavailable
+                buildTreeWithXml(cb);
+              }
+            }
+          });
+        }
+      },
+      types: {
+        record: {
+          // remove file icon, insert via SCSS, CK
+//          icon: 'fa fa-file-o'
+        },
+        collection: {
+          icon: 'fa fa-folder'
+        }
+      }
+    });
+
+  $('#treeSearch').removeClass('hidden');
+  $('#treeSearch [type=submit]').click(doTreeSearch);
+  $('#treeSearchText').keyup(function treeSearchKeyup(e) {
+    var code = (e.keyCode ? e.keyCode : e.which);
+    if (code === 13 || $(this).val().length === 0) {
+      doTreeSearch();
+    }
+  });
+});
diff --git a/themes/finc/scss/_customVariables.scss b/themes/finc/scss/_customVariables.scss
index f9c5dbe6415..c3c5503d225 100644
--- a/themes/finc/scss/_customVariables.scss
+++ b/themes/finc/scss/_customVariables.scss
@@ -185,6 +185,9 @@ $sidebar-item-padding: .75em 1em !default;
 //// Table cell padding - adjust in themes to avoid content jumps when switching tabs
 // $table-cell-padding: 5px !default;
 
+// JS Tree
+// left padding on JSTree child icon elements
+$jstree-li-left-padding: 1.5em !default;
 
 //// MODAL dimensions
 $modal-lg: 900px !default;
diff --git a/themes/finc/scss/compiled.scss b/themes/finc/scss/compiled.scss
index 7eb2069bdf7..87454c4e71d 100644
--- a/themes/finc/scss/compiled.scss
+++ b/themes/finc/scss/compiled.scss
@@ -1512,47 +1512,93 @@ footer {
 }
 
 ////// Hierarchy tree
-#hierarchyTree {
-  margin-top: 1em;
+#treeSelector {
+  border-bottom: $border-default-styles;
+  margin-bottom: 1em;
+  padding-bottom: 1em;
+
+  .item {
+    margin-right: 2em;
+
+    &:last-of-type {
+      margin-right: 0;
+    }
+  }
 }
 
-.jstree-anchor + .jstree-children {
-  margin-left: 18px;
-  margin-top: 7.5px;
-  padding-left: 0;
-  // 2nd level link, align in one vertical line
-  .jstree-anchor {
+
+#hierarchyTreeHolder {
+  border-right: 0;
+}
+
+#hierarchyTree {
+  ul.jstree-container-ul {
+    padding-left: 0;
+  }
+
+  /////// Make dropdown-icon wider
+  .jstree-closed > .jstree-ocl::before,
+  .jstree-open > .jstree-ocl::before {
+    font-size: 150%;
     margin-left: 0;
+    margin-top: -5px;
+    min-width: 18px;// do not translate into em
+  }
+
+  li.jstree-facet,
+  li.jstree-node {
+    line-height: 1.75;
+    list-style: none;
+    padding-left: $jstree-li-left-padding;
+  }
+
+  li li:before {
+    // FontAwesome Unicode fa-file-o
+    content: "\f016";
+    font-family: FontAwesome;
+    display: inline-block;
+    // same as padding-left set on li, above
+    margin-left: -$jstree-li-left-padding;
+    //same as padding-left set on li, above
+    width: $jstree-li-left-padding;
+  }
+
+  // Align child elements under open/close toggle
+  .jstree-children {
+    margin-top: .75em;
     padding-left: 0;
   }
-}
 
-/////// prevent tree LIs from overspilling on small!
-.hierarchy-tree .jstree-anchor {
-  hyphens: auto;
-  white-space: normal;
-}
+  // prevent tree LIs from overspilling on small!
+  .jstree-anchor {
+    hyphens: auto;
+    padding-left: 0;
+    white-space: normal;
+  }
 
-// Current item
-.hierarchy-tree .jstree-clicked {
-  //background-color: $color-action;
-  padding-bottom: .25em;
-  padding-right: .5em;
-  padding-top: .5em;
-  &:hover,
-  &:focus {
-    color: $brand-secondary;
+  // Current item
+  .jstree-clicked {
+    padding-bottom: .25em;
+    padding-right: .5em;
+    padding-top: .3em;
+
+    &:hover,
+    &:focus {
+      color: $brand-secondary;
+    }
   }
 }
 
-/////// Make dropdown-icon wider
-.hierarchy-tree .jstree-closed > .jstree-ocl::before,
-.hierarchy-tree .jstree-open > .jstree-ocl::before {
-  font-size: 150%;
-  margin-top: -5px;
-  min-width: 30px; // do not translate into em
+.hierarchy-tree .jstree-ocl::before,
+.jstree-facet .jstree-ocl::before {
+  float: left;
+  margin-left: 0;
+  padding: 0;
+  width: 0;
 }
 
+
+
 ////// Hierarchy tree - END
 
 ////// Tabs - END
@@ -1654,8 +1700,8 @@ footer {
     }
   }
 }
-//// Sources List - END
 
+//// Sources List - END
 
 // MAIN CONTENT - END
 
@@ -1686,7 +1732,6 @@ footer {
   padding: $sidebar-item-padding;
 }
 
-
 .facet-group .facetOR.active {
   // Style active facets to get proper left padding
   .text {
@@ -1699,9 +1744,9 @@ footer {
 .sidebar.right {
   .facet-group .facet,
   .facet-group .title,
-.facet-group .collapse,
-.facet-group .collapsing,
-.facet-group > .facet {
+  .facet-group .collapse,
+  .facet-group .collapsing,
+  .facet-group > .facet {
     @include right-border-on-sidebar($border-right-width);
   }
 }
diff --git a/themes/finc/templates/RecordTab/hierarchytree.phtml b/themes/finc/templates/RecordTab/hierarchytree.phtml
index 29e71d067ec..5550db12e45 100644
--- a/themes/finc/templates/RecordTab/hierarchytree.phtml
+++ b/themes/finc/templates/RecordTab/hierarchytree.phtml
@@ -22,11 +22,18 @@
 ?>
 <? if (count($hierarchyTreeList) > 1): ?>
   <div id="treeSelector">
+    <strong><?=$this->transEsc('hierarchyTreeSelect')?></strong><br>
+
     <? foreach ($hierarchyTreeList as $hierarchy => $hierarchyTitle): ?>
       <? if($activeTree == $hierarchy): ?>
-        <i class="fa fa-sitemap" aria-hidden="true"></i> <?=$this->escapeHtml($hierarchyTitle)?>
+      <span class="item">
+        <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <?=$this->escapeHtml($hierarchyTitle)?>
+      </span>
       <? else: ?>
-        <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchy)?>"><?=$this->escapeHtml($hierarchyTitle)?></a>
+      <span class="item">
+        <i class="fa fa-sitemap" aria-hidden="true"></i>
+        <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchy)?>"><?=$this->escapeHtml($hierarchyTitle)?></a>
+      </span>
       <? endif; ?>
     <? endforeach; ?>
   </div>
-- 
GitLab