From d928cbe32e90a2f0e73c9a16ead2f7a5c621f8df Mon Sep 17 00:00:00 2001
From: Matthew Schranz <schranz.m@gmail.com>
Date: Wed, 27 May 2015 10:07:49 -0400
Subject: [PATCH] fix(search): Fix matched term highlighting

---
 .../quick-search/quick-search.controllers.ts  |  9 +--
 .../quick-search/quick-search.directives.ts   | 67 ++++++++++---------
 .../components/quick-search/styles.less       |  3 +-
 .../quick-search/templates/matched-term.html  |  1 -
 .../quick-search/templates/matched-terms.html |  3 +
 .../templates/quick-search-modal.html         |  4 +-
 6 files changed, 46 insertions(+), 41 deletions(-)
 delete mode 100644 app/scripts/components/quick-search/templates/matched-term.html
 create mode 100644 app/scripts/components/quick-search/templates/matched-terms.html

diff --git a/app/scripts/components/quick-search/quick-search.controllers.ts b/app/scripts/components/quick-search/quick-search.controllers.ts
index 646e71985..360db9ba8 100644
--- a/app/scripts/components/quick-search/quick-search.controllers.ts
+++ b/app/scripts/components/quick-search/quick-search.controllers.ts
@@ -49,14 +49,10 @@ module ngApp.components.quickSearch.controllers {
           if (typeof obj === "string") {
             if (obj.indexOf(this.searchQuery) > -1) {
               matchedTerms.push(obj);
-              return;
             }
-          }
-
-          if (typeof obj[key] === "string") {
+          } else if (typeof obj[key] === "string") {
             if (obj[key].indexOf(this.searchQuery) > -1) {
               matchedTerms.push(obj[key]);
-              return;
             }
           }
         }
@@ -229,6 +225,7 @@ module ngApp.components.quickSearch.controllers {
         if (!data.length) {
           this.selectedItem = null;
         }
+        this.results = null;
 
         for (var i = 0; i < data.hits.length; i++) {
           var matchedTerms = [];
@@ -257,7 +254,7 @@ module ngApp.components.quickSearch.controllers {
           data.hits[i].matchedTerms = matchedTerms;
         }
 
-        this.results = _.assign([], data);
+        this.results = _.assign({}, data);
         this.setupListeners();
       });
     }
diff --git a/app/scripts/components/quick-search/quick-search.directives.ts b/app/scripts/components/quick-search/quick-search.directives.ts
index 8f9b5271b..5706f3639 100644
--- a/app/scripts/components/quick-search/quick-search.directives.ts
+++ b/app/scripts/components/quick-search/quick-search.directives.ts
@@ -15,27 +15,30 @@ module ngApp.components.quickSearch.directives {
         });
 
         this.openModal = () => {
-          if (!(modalElem.data("bs.modal") || {}).isShown) {
+          // Modal stack is a helper service. Used to figure out if one is currently
+          // open already.
+          if ($modalStack.getTop()) {
+            return;
+          }
 
-            modalInstance = $modal.open({
-              templateUrl: "components/quick-search/templates/quick-search-modal.html",
-              controller: "QuickSearchModalController as qsmc",
-              backdrop: "static",
-              size: "lg",
-              keyboard: true
-            });
+          modalInstance = $modal.open({
+            templateUrl: "components/quick-search/templates/quick-search-modal.html",
+            controller: "QuickSearchModalController as qsmc",
+            backdrop: "static",
+            size: "lg",
+            keyboard: true
+          });
 
-            modalInstance.opened.then(() => {
-              // Apparently the Modal should support this by default but I'm running
-              // into cases where it is not.
-              docElem.on("click", (e) => {
-                if (angular.element(e.target).find("#quick-search-modal").length) {
-                  e.preventDefault();
-                  modalInstance.close();
-                }
-              });
+          modalInstance.opened.then(() => {
+            // Apparently the Modal should support this by default but I'm running
+            // into cases where it is not.
+            docElem.on("click", (e) => {
+              if (angular.element(e.target).find("#quick-search-modal").length) {
+                e.preventDefault();
+                modalInstance.close();
+              }
             });
-          }
+          });
         };
       },
       link: function($scope, $element, attrs, ctrl) {
@@ -44,12 +47,6 @@ module ngApp.components.quickSearch.directives {
         });
 
         angular.element($window.document).on("keypress", (e) => {
-          // Modal stack is a helper service. Used to figure out if one is currently
-          // open already.
-          if ($modalStack.getTop()) {
-            return;
-          }
-
           var modalElem = angular.element("#quick-search-modal");
           var validSpaceKeys = [
             0, // Webkit
@@ -65,17 +62,27 @@ module ngApp.components.quickSearch.directives {
     };
   }
 
-  function MatchedTerm($sce): ng.IDirective {
+  function MatchedTerms($sce): ng.IDirective {
     return {
       restrict: "E",
-      templateUrl: "components/quick-search/templates/matched-term.html",
+      templateUrl: "components/quick-search/templates/matched-terms.html",
       scope: {
-        term: "=",
+        terms: "=",
         query: "="
       },
       link: function($scope) {
-        var boldedQuery = "<span class='bolded'>" + $scope.query + "</span>";
-        $scope.term = $sce.trustAsHtml($scope.term.replace(new RegExp($scope.query, "g"), boldedQuery));
+        $scope.$watch("terms", (newVal) => {
+          if (newVal) {
+            var boldedQuery = "<span class='bolded'>" + $scope.query + "</span>";
+            var boldedTerms = [];
+            var regex = new RegExp("[" + $scope.query + "]{" + $scope.query.length + "}", "g");
+            _.forEach(newVal, (item) => {
+              boldedTerms.push(item.replace(regex, boldedQuery));
+            });
+
+            $scope.matchedTerms = _.assign([], boldedTerms);
+          }
+        });
       }
     };
   }
@@ -84,6 +91,6 @@ module ngApp.components.quickSearch.directives {
     .module("quickSearch.directives", [
       "ui.bootstrap.modal"
     ])
-    .directive("matchedTerm", MatchedTerm)
+    .directive("matchedTerms", MatchedTerms)
     .directive("quickSearch", QuickSearch);
 }
diff --git a/app/scripts/components/quick-search/styles.less b/app/scripts/components/quick-search/styles.less
index f0f7edc11..992215099 100644
--- a/app/scripts/components/quick-search/styles.less
+++ b/app/scripts/components/quick-search/styles.less
@@ -22,9 +22,10 @@
 
   .matched-term {
     margin-left: 20px;
-    padding: 5px 0;
+    padding-top: 5px;
     font-style: italic;
     font-size: 0.9em;
+    word-wrap: break-word;
 
     .bolded {
       font-weight: 700;
diff --git a/app/scripts/components/quick-search/templates/matched-term.html b/app/scripts/components/quick-search/templates/matched-term.html
deleted file mode 100644
index 69076c19a..000000000
--- a/app/scripts/components/quick-search/templates/matched-term.html
+++ /dev/null
@@ -1 +0,0 @@
-<div data-ng-bind-html="term" class="matched-term"></div>
\ No newline at end of file
diff --git a/app/scripts/components/quick-search/templates/matched-terms.html b/app/scripts/components/quick-search/templates/matched-terms.html
new file mode 100644
index 000000000..6a44e5e24
--- /dev/null
+++ b/app/scripts/components/quick-search/templates/matched-terms.html
@@ -0,0 +1,3 @@
+<div data-ng-repeat="term in matchedTerms">
+  <div data-ng-bind-html="term" class="matched-term"></div>
+</div>
\ No newline at end of file
diff --git a/app/scripts/components/quick-search/templates/quick-search-modal.html b/app/scripts/components/quick-search/templates/quick-search-modal.html
index da04c860b..33d21bd53 100644
--- a/app/scripts/components/quick-search/templates/quick-search-modal.html
+++ b/app/scripts/components/quick-search/templates/quick-search-modal.html
@@ -20,9 +20,7 @@
              data-ng-if="item._type === 'project'"/>
         {{ item._id }}
 
-        <div data-ng-repeat="term in item.matchedTerms track by $index">
-          <matched-term data-term="term" query="qsmc.searchQuery"></matched-term>
-        </div>
+        <matched-terms data-terms="item.matchedTerms" data-query="qsmc.searchQuery"></matched-terms>
       </div>
     </div>
     <div class="col-lg-7" data-ng-if="qsmc.selectedItem">