From da57e0f89e1dad89e2221f2f232528ef5cbd7ba2 Mon Sep 17 00:00:00 2001
From: Julian Viereck <julian.viereck@gmail.com>
Date: Tue, 11 Sep 2012 09:30:44 -0700
Subject: [PATCH 01/17] Julian's html-search-bar work

---
 web/viewer.css  |  52 ++++-
 web/viewer.html |  13 +-
 web/viewer.js   | 563 ++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 611 insertions(+), 17 deletions(-)

diff --git a/web/viewer.css b/web/viewer.css
index 0f615094a..caf3a09f1 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -245,7 +245,7 @@ html[dir='rtl'] #sidebarContent {
               0 1px 1px hsla(0,0%,0%,.1);
 }
 
-#toolbarViewer {
+#toolbarViewer, .findbar {
   position: relative;
   height: 32px;
   background-image: url(images/texture.png),
@@ -265,6 +265,32 @@ html[dir='rtl'] #sidebarContent {
               0 1px 0 hsla(0,0%,0%,.15),
               0 1px 1px hsla(0,0%,0%,.1);
 }
+
+.findbar {
+  top: 40px;
+  left: 40px;
+  position: absolute;
+  z-index: 100;
+  height: 20px;
+
+  min-width: 16px;
+  padding: 3px 6px 3px 6px;
+  margin: 4px 2px 4px 0;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  color: hsl(0,0%,85%);
+  font-size: 12px;
+  line-height: 14px;
+  text-align: left;
+  -webkit-user-select:none;
+  -moz-user-select:none;
+  cursor: default;
+}
+
+.notFound {
+  background-color: rgb(255, 137, 153);
+}
+
 html[dir='ltr'] #toolbarViewerLeft {
   margin-left: -1px;
 }
@@ -1069,6 +1095,30 @@ canvas {
   white-space:pre;
 }
 
+.textLayer .highlight {
+  margin: -1px;
+  padding: 1px;
+
+  background-color: rgba(0, 137, 26, 0.2);
+  border-radius: 4px;
+}
+
+.textLayer .highlight.begin {
+  border-radius: 4px 0px 0px 4px;
+}
+
+.textLayer .highlight.end {
+  border-radius: 0px 4px 4px 0px;
+}
+
+.textLayer .highlight.middle {
+  border-radius: 0px;
+}
+
+.textLayer .highlight.selected {
+  background-color: rgba(255, 0, 0, 0.2);
+}
+
 /* TODO: file FF bug to support ::-moz-selection:window-inactive
    so we can override the opaque grey background when the window is inactive;
    see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
diff --git a/web/viewer.html b/web/viewer.html
index 70cd8cdb0..0ee6313a6 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -108,9 +108,20 @@ limitations under the License.
       </div>  <!-- sidebarContainer -->
 
       <div id="mainContainer">
+        <div class="findbar hidden" id="findbar">
+          <label for="findInput">Find: </label>
+          <input id="findInput" type="search">
+          <button id="findPrevious">&lt;</button>
+          <button id="findNext">&gt;</button>
+          <input type="checkbox" id="findHighlightAll">
+          <label for="findHighlightAll">Highlight all</label>
+          <input type="checkbox" id="findMatchCase">
+          <label for="findMatchCase">Match case</label>
+          <span id="findMsgWrap" class="hidden">Reached end of page, continued from top</span>
+          <span id="findMsgNotFound" class="hidden">Phrase not found</span>
+        </div>
         <div class="toolbar">
           <div id="toolbarContainer">
-
             <div id="toolbarViewer">
               <div id="toolbarViewerLeft">
                 <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="4" data-l10n-id="toggle_slider">
diff --git a/web/viewer.js b/web/viewer.js
index 2662adff7..65c87740f 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -208,6 +208,345 @@ var Settings = (function SettingsClosure() {
 var cache = new Cache(kCacheSize);
 var currentPageNumber = 1;
 
+var PDFFindController = {
+  startedTextExtraction: false,
+
+  // If active, search resulsts will be highlighted.
+  active: false,
+
+  // Stores the text for each page.
+  pageContents: [],
+
+  pageMatches: [],
+
+  selected: {
+    pageIdx: 0,
+    matchIdx: 0
+  },
+
+  dirtyMatch: false,
+
+  findTimeout: null,
+
+  initialize: function() {
+    var events = [
+      'find',
+      'findagain',
+      'findhighlightallchange',
+      'findcasesensitivitychange'
+    ];
+
+    this.handelEvent = this.handelEvent.bind(this);
+
+    for (var i = 0; i < events.length; i++) {
+      window.addEventListener(events[i], this.handelEvent);
+    }
+  },
+
+  calcFindMatch: function(pageContent) {
+    // TODO: Handle the other search options here as well.
+
+    var query = this.state.query;
+    var queryLen = query.length;
+
+    if (queryLen === 0)
+      return [];
+
+    var matches = [];
+
+    var matchIdx = -queryLen;
+    while (true) {
+      matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
+      if (matchIdx === -1) {
+        break;
+      }
+
+      matches.push(matchIdx);
+    }
+    return matches;
+  },
+
+  extractText: function() {
+    if (this.startedTextExtraction)
+      return;
+    this.startedTextExtraction = true;
+    var self = this;
+    function extractPageText(pageIndex) {
+      PDFView.pages[pageIndex].getTextContent().then(
+        function textContentResolved(data) {
+          // Bulid the search string.
+          var bidiTexts = data.bidiTexts;
+          var str = '';
+
+          for (var i = 0; i < bidiTexts.length; i++) {
+            str += bidiTexts[i].str;
+          }
+
+          // Store the pageContent as a string.
+          self.pageContents.push(str);
+          // Ensure there is a empty array of matches.
+          self.pageMatches.push([]);
+
+          if ((pageIndex + 1) < PDFView.pages.length)
+            extractPageText(pageIndex + 1);
+        }
+      );
+    }
+    extractPageText(0);
+  },
+
+  handelEvent: function(e) {
+    this.state = e.detail;
+    if (e.detail.findPrevious === undefined) {
+      this.dirtyMatch = true;
+    }
+
+    clearTimeout(this.findTimeout);
+    if (e.type === 'find') {
+      // Only trigger the find action after 250ms of silence.
+      this.findTimeout = setTimeout(this.performFind.bind(this), 250);
+    } else {
+      this.performFind();
+    }
+  },
+
+  updatePage: function(idx) {
+    var page = PDFView.pages[idx];
+
+    if (page.textLayer) {
+      page.textLayer.updateMatches();
+    } else if (this.selected.pageIdx === idx) {
+      // If the page is selected, scroll the page into view, which triggers
+      // rendering the page, which adds the textLayer. Once the textLayer is
+      // build, it will scroll onto the selected match.
+      page.scrollIntoView();
+    }
+  },
+
+  performFind: function() {
+    // Recalculate all the matches.
+    // TODO: Make one match show up as the current match
+
+    var pages = PDFView.pages;
+    var pageContents = this.pageContents;
+    var pageMatches = this.pageMatches;
+
+    this.active = true;
+
+    if (this.dirtyMatch) {
+      // Need to recalculate the matches.
+      this.dirtyMatch = false;
+
+      this.selected = {
+        pageIdx: -1,
+        matchIdx: -1
+      };
+
+      // TODO: Make this way more lasily (aka. efficient) - e.g. calculate only
+      // the matches for the current visible pages.
+      var firstMatch = true;
+      for (var i = 0; i < pageContents.length; i++) {
+        var matches = pageMatches[i] = this.calcFindMatch(pageContents[i]);
+        if (firstMatch && matches.length !== 0) {
+          firstMatch = false;
+          this.selected = {
+            pageIdx: i,
+            matchIdx: 0
+          };
+        }
+        this.updatePage(i, true);
+      }
+    } else {
+      // If there is NO selection, then there is no match at all -> no sense to
+      // handel previous/next action.
+      if (this.selected.pageIdx === -1)
+        return;
+
+      // Handle findAgain case.
+      var previous = this.state.findPrevious;
+      var sPageIdx = this.selected.pageIdx;
+      var sMatchIdx = this.selected.matchIdx;
+
+      if (previous) {
+        // Select previous match.
+
+        if (sMatchIdx !== 0) {
+          this.selected.matchIdx -= 1;
+        } else {
+          var len = pageMatches.length;
+          for (var i = sPageIdx - 1; i != sPageIdx; i--) {
+            if (i < 0)
+              i += len;
+
+            if (pageMatches[i].length !== 0) {
+              this.selected = {
+                pageIdx: i,
+                matchIdx: pageMatches[i].length - 1
+              };
+              break;
+            }
+          }
+          // If pageIdx stayed the same, select last match on the page.
+          if (this.selected.pageIdx === sPageIdx) {
+            this.selected.matchIdx = pageMatches[sPageIdx].length - 1;
+          }
+        }
+      } else {
+        // Select next match.
+
+        if (pageMatches[sPageIdx].length !== sMatchIdx + 1) {
+          this.selected.matchIdx += 1;
+        } else {
+          var len = pageMatches.length;
+          for (var i = sPageIdx + 1; i < len + sPageIdx; i++) {
+            if (pageMatches[i % len].length !== 0) {
+              this.selected = {
+                pageIdx: i % len,
+                matchIdx: 0
+              };
+              break;
+            }
+          }
+          // If pageIdx stayed the same, select last match on the page.
+          if (this.selected.pageIdx === sPageIdx) {
+            this.selected.matchIdx = 0;
+          }
+        }
+      }
+
+      this.updatePage(sPageIdx, sPageIdx === this.selected.pageIdx);
+      if (sPageIdx !== this.selected.pageIdx) {
+        this.updatePage(this.selected.pageIdx, true);
+      }
+    }
+  }
+};
+
+var PDFFindBar = {
+  // TODO: Enable the FindBar *AFTER* the pagesPromise in the load function
+  // got resolved
+
+  opened: false,
+
+  FIND_FOUND: 0,    // Successful find
+  FIND_NOTFOUND: 1, // Unsuccessful find
+  FIND_WRAPPED: 2,  // Successful find, but wrapped around
+
+  initialize: function() {
+    this.bar = document.getElementById('findbar');
+    this.toggleButton = document.getElementById('viewSearch');
+    this.findField = document.getElementById('findInput');
+    this.highlightAll = document.getElementById('findHighlightAll');
+    this.caseSensitive = document.getElementById('findMatchCase');
+    this.findMsgWrap = document.getElementById('findMsgWrap');
+    this.findMsgNotFound = document.getElementById('findMsgNotFound');
+
+    var self = this;
+    this.toggleButton.addEventListener('click',
+    function() {
+      self.toggle();
+    });
+
+    this.findField.addEventListener('input', function() {
+      self.dispatchEvent('');
+    });
+
+    // TODO: Add keybindings like enter, shift-enter, CMD-G etc. to go to prev/
+    // next match when the findField is selected.
+
+    document.getElementById('findPrevious').addEventListener('click',
+    function() {
+      self.dispatchEvent('again', true);
+    });
+
+    document.getElementById('findNext').addEventListener('click',
+    function() {
+      self.dispatchEvent('again', false);
+    });
+
+    this.highlightAll.addEventListener('click',
+    function() {
+      self.dispatchEvent('highlightallchange');
+    });
+
+    this.caseSensitive.addEventListener('click',
+    function() {
+      self.dispatchEvent('casesensitivitychange');
+    });
+  },
+
+  dispatchEvent: function(aType, aFindPrevious) {
+    var event = document.createEvent('CustomEvent');
+    event.initCustomEvent('find' + aType, true, true, {
+      query: this.findField.value,
+      caseSensitive: this.caseSensitive.checked,
+      highlightAll: this.highlightAll.checked,
+      findPrevious: aFindPrevious
+    });
+    return window.dispatchEvent(event);
+  },
+
+  updateUIState: function(aState) {
+    var notFound = false;
+    var wrapped = false;
+
+    switch (aState) {
+      case this.FIND_FOUND:
+        break;
+
+      case this.FIND_NOTFOUND:
+        notFound = true;
+        break;
+
+      case this.FIND_WRAPPED:
+        wrapped = true;
+        break;
+    }
+
+    if (notFound) {
+      this.findField.classList.add('notFound');
+      this.findMsgNotFound.classList.remove('hidden');
+    } else {
+      this.findField.classList.remove('notFound');
+      this.findMsgNotFound.classList.add('hidden');
+    }
+
+    if (wrapped) {
+      this.findMsgWrap.classList.remove('hidden');
+    } else {
+      this.findMsgWrap.classList.add('hidden');
+    }
+  },
+
+  open: function() {
+    if (this.opened) return;
+
+    this.opened = true;
+    this.toggleButton.classList.add('toggled');
+    this.bar.classList.remove('hidden');
+    this.findField.select();
+    this.findField.focus();
+  },
+
+  close: function() {
+    if (!this.opened) return;
+
+    this.opened = false;
+    this.toggleButton.classList.remove('toggled');
+    this.bar.classList.add('hidden');
+
+    PDFFindController.active = false;
+  },
+
+  toggle: function() {
+    if (this.opened) {
+      this.close();
+    } else {
+      this.open();
+    }
+  }
+};
+
 var PDFView = {
   pages: [],
   thumbnails: [],
@@ -242,6 +581,9 @@ var PDFView = {
     this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
                      this.renderHighestPriority.bind(this));
 
+    PDFFindBar.initialize();
+    PDFFindController.initialize();
+
     this.initialized = true;
     container.addEventListener('scroll', function() {
       self.lastScroll = Date.now();
@@ -736,6 +1078,9 @@ var PDFView = {
         thumbnails.push(thumbnailView);
         var pageRef = page.ref;
         pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
+
+        // Trigger text extraction. TODO: Make this happen lasyliy if needed.
+        PDFFindController.extractText();
       }
 
       self.pagesRefMap = pagesRefMap;
@@ -1229,6 +1574,7 @@ var PageView = function pageView(container, pdfPage, id, scale,
   this.resume = null;
 
   this.textContent = null;
+  this.textLayer = null;
 
   var anchor = document.createElement('a');
   anchor.name = '' + this.id;
@@ -1475,7 +1821,8 @@ var PageView = function pageView(container, pdfPage, id, scale,
       textLayerDiv.className = 'textLayer';
       div.appendChild(textLayerDiv);
     }
-    var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null;
+    var textLayer = this.textLayer =
+          textLayerDiv ? new TextLayerBuilder(textLayerDiv, this.id - 1) : null;
 
     var scale = this.scale, viewport = this.viewport;
     canvas.width = viewport.width;
@@ -1834,21 +2181,25 @@ var CustomStyle = (function CustomStyleClosure() {
   return CustomStyle;
 })();
 
-var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
+var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
   var textLayerFrag = document.createDocumentFragment();
+
   this.textLayerDiv = textLayerDiv;
   this.layoutDone = false;
   this.divContentDone = false;
+  this.pageIdx = pageIdx;
+  this.matches = [];
 
   this.beginLayout = function textLayerBuilderBeginLayout() {
     this.textDivs = [];
     this.textLayerQueue = [];
+    this.renderingDone = false;
   };
 
   this.endLayout = function textLayerBuilderEndLayout() {
     this.layoutDone = true;
     this.insertDivContent();
-  },
+  };
 
   this.renderLayer = function textLayerBuilderRenderLayer() {
     var self = this;
@@ -1862,8 +2213,11 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
     if (textDivs.length > 100000)
       return;
 
-    while (textDivs.length > 0) {
-      var textDiv = textDivs.shift();
+    var i = textDivs.length;
+
+    while (i !== 0) {
+      i--;
+      var textDiv = textDivs[i];
       textLayerFrag.appendChild(textDiv);
 
       ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
@@ -1875,9 +2229,14 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
         CustomStyle.setProp('transform' , textDiv,
           'scale(' + textScale + ', 1)');
         CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
+
+        textLayerDiv.appendChild(textDiv);
       }
     }
 
+    this.renderingDone = true;
+    this.updateMatches();
+
     textLayerDiv.appendChild(textLayerFrag);
   };
 
@@ -1944,6 +2303,184 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
     this.textContent = textContent;
     this.insertDivContent();
   };
+
+  this.convertMatches = function textLayerBuilderConvertMatches(matches) {
+    var i = 0;
+    var iIndex = 0;
+    var bidiTexts = this.textContent.bidiTexts;
+    var end = bidiTexts.length - 1;
+    var queryLen = PDFFindController.state.query.length;
+
+    var lastDivIdx = -1;
+    var pos;
+
+    var ret = [];
+
+    // Loop over all the matches.
+    for (var m = 0; m < matches.length; m++) {
+      var matchIdx = matches[m];
+      // # Calculate the begin position.
+
+      // Loop over the divIdxs.
+      while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
+        iIndex += bidiTexts[i].str.length;
+        i++;
+      }
+
+      // TODO: Do proper handling here if something goes wrong.
+      if (i == bidiTexts.length) {
+        console.error('Could not find matching mapping');
+      }
+
+      var match = {
+        begin: {
+          divIdx: i,
+          offset: matchIdx - iIndex
+        }
+      };
+
+      // # Calculate the end position.
+      matchIdx += queryLen;
+
+      // Somewhat same array as above, but use a > instead of >= to get the end
+      // position right.
+      while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
+        iIndex += bidiTexts[i].str.length;
+        i++;
+      }
+
+      match.end = {
+        divIdx: i,
+        offset: matchIdx - iIndex
+      };
+      ret.push(match);
+    }
+
+    return ret;
+  };
+
+  this.renderMatches = function textLayerBuilder_renderMatches(matches) {
+    var bidiTexts = this.textContent.bidiTexts;
+    var textDivs = this.textDivs;
+    var prevEnd = null;
+    var isSelectedPage = this.pageIdx === PDFFindController.selected.pageIdx;
+    var selectedMatchIdx = PDFFindController.selected.matchIdx;
+
+    var infty = {
+      divIdx: -1,
+      offset: undefined
+    };
+
+    function beginText(begin, className) {
+      var divIdx = begin.divIdx;
+      var div = textDivs[divIdx];
+      div.innerHTML = '';
+
+      var content = bidiTexts[divIdx].str.substring(0, begin.offset);
+      var node = document.createTextNode(content);
+      if (className) {
+        var isSelected = isSelectedPage &&
+                          divIdx === selectedMatchIdx;
+        var span = document.createElement('span');
+        span.className = className + (isSelected ? ' selected' : '');
+        span.appendChild(node);
+        div.appendChild(span);
+        return;
+      }
+      div.appendChild(node);
+    }
+
+    function appendText(from, to, className) {
+      var divIdx = from.divIdx;
+      var div = textDivs[divIdx];
+
+      var content = bidiTexts[divIdx].str.substring(from.offset, to.offset);
+      var node = document.createTextNode(content);
+      if (className) {
+        var span = document.createElement('span');
+        span.className = className;
+        span.appendChild(node);
+        div.appendChild(span);
+        return;
+      }
+      div.appendChild(node);
+    }
+
+    function highlightDiv(divIdx, className) {
+      textDivs[divIdx].className = className;
+    }
+
+    for (var i = 0; i < matches.length; i++) {
+      var match = matches[i];
+      var begin = match.begin;
+      var end = match.end;
+
+      var isSelected = isSelectedPage && i === selectedMatchIdx;
+      var highlightSuffix = (isSelected ? ' selected' : '');
+      if (isSelected)
+        scrollIntoView(textDivs[begin.divIdx], {top: -50});
+
+      // Match inside new div.
+      if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
+        // If there was a previous div, then add the text at the end
+        if (prevEnd !== null) {
+          appendText(prevEnd, infty);
+        }
+        // clears the divs and set the content until the begin point.
+        beginText(begin);
+      } else {
+        appendText(prevEnd, begin);
+      }
+
+      if (begin.divIdx === end.divIdx) {
+        appendText(begin, end, 'highlight' + highlightSuffix);
+      } else {
+        appendText(begin, infty, 'highlight begin' + highlightSuffix);
+        for (var n = begin.divIdx + 1; n < end.divIdx; n++) {
+          highlightDiv(n, 'highlight middle' + highlightSuffix);
+        }
+        beginText(end, 'highlight end' + highlightSuffix);
+      }
+      prevEnd = end;
+    }
+
+    if (prevEnd) {
+      appendText(prevEnd, infty);
+    }
+  };
+
+  this.updateMatches = function textLayerUpdateMatches() {
+    // Only show matches, once all rendering is done.
+    if (!this.renderingDone)
+      return;
+
+    // Clear out all matches.
+    var matches = this.matches;
+    var textDivs = this.textDivs;
+    var bidiTexts = this.textContent.bidiTexts;
+    var clearedUntilDivIdx = -1;
+
+    for (var i = 0; i < matches.length; i++) {
+      var match = matches[i];
+      var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
+      for (var n = begin; n <= match.end.divIdx; n++) {
+        var div = bidiTexts[n].str;
+        div.textContent = div.textContent;
+        div.className = '';
+      }
+      clearedUntilDivIdx = match.end.divIdx + 1;
+    }
+
+    if (!PDFFindController.active)
+      return;
+
+    // Convert the matches on the page controller into the match format used
+    // for the textLayer.
+    this.matches = matches =
+      this.convertMatches(PDFFindController.pageMatches[this.pageIdx] || []);
+
+    this.renderMatches(this.matches);
+  };
 };
 
 document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
@@ -2059,16 +2596,6 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
       PDFView.switchSidebarView('outline');
     });
 
-  document.getElementById('viewSearch').addEventListener('click',
-    function() {
-      PDFView.switchSidebarView('search');
-    });
-
-  document.getElementById('searchButton').addEventListener('click',
-    function() {
-      PDFView.search();
-    });
-
   document.getElementById('previous').addEventListener('click',
     function() {
       PDFView.page--;
@@ -2331,6 +2858,12 @@ window.addEventListener('keydown', function keydown(evt) {
   // control is selected or not.
   if (cmd == 1 || cmd == 8) { // either CTRL or META key.
     switch (evt.keyCode) {
+//#if !(FIREFOX || MOZCENTRAL)
+      case 70:
+        PDFFindBar.toggle();
+        handled = true;
+        break;
+//#endif
       case 61: // FF/Mac '='
       case 107: // FF '+' and '='
       case 187: // Chrome '+'

From d94bbad575bbeea6d5e875d5031784e02bf26a9f Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Tue, 25 Sep 2012 14:54:29 -0400
Subject: [PATCH 02/17] typo fix

---
 web/viewer.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web/viewer.js b/web/viewer.js
index 65c87740f..aa91f5da0 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -236,10 +236,10 @@ var PDFFindController = {
       'findcasesensitivitychange'
     ];
 
-    this.handelEvent = this.handelEvent.bind(this);
+    this.handleEvent = this.handleEvent.bind(this);
 
     for (var i = 0; i < events.length; i++) {
-      window.addEventListener(events[i], this.handelEvent);
+      window.addEventListener(events[i], this.handleEvent);
     }
   },
 
@@ -295,7 +295,7 @@ var PDFFindController = {
     extractPageText(0);
   },
 
-  handelEvent: function(e) {
+  handleEvent: function(e) {
     this.state = e.detail;
     if (e.detail.findPrevious === undefined) {
       this.dirtyMatch = true;
@@ -358,7 +358,7 @@ var PDFFindController = {
       }
     } else {
       // If there is NO selection, then there is no match at all -> no sense to
-      // handel previous/next action.
+      // handle previous/next action.
       if (this.selected.pageIdx === -1)
         return;
 

From 6864a072215619c6c650a4600ea21a98c1288903 Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Tue, 25 Sep 2012 16:17:27 -0400
Subject: [PATCH 03/17] Use "find" instead of both find & search

---
 .../firefox/components/PdfStreamConverter.js  |  4 +-
 l10n/ar/viewer.properties                     | 10 +--
 l10n/da/viewer.properties                     |  8 +-
 l10n/en-US/viewer.properties                  | 10 +--
 l10n/es/viewer.properties                     | 10 +--
 l10n/fi/viewer.properties                     | 10 +--
 l10n/ja/viewer.properties                     | 10 +--
 l10n/nl/viewer.properties                     | 10 +--
 l10n/sv/viewer.properties                     | 10 +--
 l10n/zh-TW/viewer.properties                  |  8 +-
 web/viewer.css                                | 16 ++--
 web/viewer.html                               | 14 ++--
 web/viewer.js                                 | 82 +++++++++----------
 13 files changed, 101 insertions(+), 101 deletions(-)

diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
index 76b7788a8..58d142204 100644
--- a/extensions/firefox/components/PdfStreamConverter.js
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -317,8 +317,8 @@ ChromeActions.prototype = {
   pdfBugEnabled: function() {
     return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false);
   },
-  searchEnabled: function() {
-    return getBoolPref(PREF_PREFIX + '.searchEnabled', false);
+  findEnabled: function() {
+    return getBoolPref(PREF_PREFIX + '.findEnabled', false);
   },
   fallback: function(url, sendResponse) {
     var self = this;
diff --git a/l10n/ar/viewer.properties b/l10n/ar/viewer.properties
index 3ef9c1cd0..d1ceae44e 100644
--- a/l10n/ar/viewer.properties
+++ b/l10n/ar/viewer.properties
@@ -36,8 +36,8 @@ outline.title=إظهار ملخص المستند
 outline_label=ملخص المستند
 thumbs.title=إظهار الصور المصغرة
 thumbs_label=الصور المصغرة
-search_panel.title=البحث في المستند
-search_panel_label=بحث
+find_panel.title=البحث في المستند
+find_panel_label=بحث
 
 # Document outline messages
 no_outline=لا يوجد ملخص
@@ -54,9 +54,9 @@ thumb_page_canvas=صورة مصغرة من الصفحة {{page}}
 page_rotate_cw.label=تدوير مع عقارب الساعة
 page_rotate_ccw.label=تدوير عكس عقارب الساعة
 
-# Search panel button title and messages
-search=بحث
-search_terms_not_found=(لا يوجد)
+# Find panel button title and messages
+find=بحث
+find_terms_not_found=(لا يوجد)
 
 # Error panel labels
 error_more_info=مزيد من المعلومات
diff --git a/l10n/da/viewer.properties b/l10n/da/viewer.properties
index 72e04bb62..90231c7ca 100644
--- a/l10n/da/viewer.properties
+++ b/l10n/da/viewer.properties
@@ -36,8 +36,8 @@ outline.title=Vis dokumentoversigt
 outline_label=Dokumentoversigt
 thumbs.title=Vis thumbnails
 thumbs_label=Thumbnails
-search_panel.title=Søg i dokumentet
-search_panel_label=Søg
+find_panel.title=Søg i dokumentet
+find_panel_label=Søg
 
 # Dokumentoversigtsbeskeder
 no_outline=Ingen dokumentoversigt tilgængelig
@@ -51,8 +51,8 @@ thumb_page_title=Side {{page}}
 thumb_page_canvas=Thumbnail af side {{page}}
 
 # Søgepanelet
-search=Søg
-search_terms_not_found=(Ikke fundet)
+find=Søg
+find_terms_not_found=(Ikke fundet)
 
 # Fejlpanel
 error_more_info=Mere information
diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties
index 6afb232e7..678e23c8e 100644
--- a/l10n/en-US/viewer.properties
+++ b/l10n/en-US/viewer.properties
@@ -36,8 +36,8 @@ outline.title=Show Document Outline
 outline_label=Document Outline
 thumbs.title=Show Thumbnails
 thumbs_label=Thumbnails
-search_panel.title=Search Document
-search_panel_label=Search
+find_panel.title=Find in Document
+find_panel_label=Find
 
 # Document outline messages
 no_outline=No Outline Available
@@ -54,9 +54,9 @@ thumb_page_canvas=Thumbnail of Page {{page}}
 page_rotate_cw.label=Rotate Clockwise
 page_rotate_ccw.label=Rotate Counter-Clockwise
 
-# Search panel button title and messages
-search=Find
-search_terms_not_found=(Not found)
+# Find panel button title and messages
+find=Find
+find_terms_not_found=(Not found)
 
 # Error panel labels
 error_more_info=More Information
diff --git a/l10n/es/viewer.properties b/l10n/es/viewer.properties
index 8485cbf17..bac4cee67 100644
--- a/l10n/es/viewer.properties
+++ b/l10n/es/viewer.properties
@@ -36,8 +36,8 @@ outline.title=Mostrar esquema del documento
 outline_label=Esquema del documento
 thumbs.title=Mostrar miniaturas
 thumbs_label=Miniaturas
-search_panel.title=Buscar en el documento
-search_panel_label=Buscar
+find_panel.title=Buscar en el documento
+find_panel_label=Buscar
 
 # Document outline messages
 no_outline=No hay un esquema disponible
@@ -50,9 +50,9 @@ thumb_page_title=Página {{page}}
 # number.
 thumb_page_canvas=Miniatura de la página {{page}}
 
-# Search panel button title and messages
-search=Buscar
-search_terms_not_found=(No encontrado)
+# Find panel button title and messages
+find=Buscar
+find_terms_not_found=(No encontrado)
 
 # Error panel labels
 error_more_info=Más información
diff --git a/l10n/fi/viewer.properties b/l10n/fi/viewer.properties
index 4cc0ba70e..5fcde5eaf 100644
--- a/l10n/fi/viewer.properties
+++ b/l10n/fi/viewer.properties
@@ -36,8 +36,8 @@ outline.title=Näytä asiakirjan jäsennys
 outline_label=Asiakirjan jäsennys
 thumbs.title=Näytä esikatselukuvat
 thumbs_label=Esikatselukuvat
-search_panel.title=Etsi asiakirjasta
-search_panel_label=Etsi
+find_panel.title=Etsi asiakirjasta
+find_panel_label=Etsi
 
 # Document outline messages
 no_outline=Jäsennystä ei ole tarjolla
@@ -50,9 +50,9 @@ thumb_page_title=Sivu {{page}}
 # number.
 thumb_page_canvas=Sivun {{page}} esikatselukuva
 
-# Search panel button title and messages
-search=Etsi
-search_terms_not_found=(Ei löytynyt)
+# Find panel button title and messages
+find=Etsi
+find_terms_not_found=(Ei löytynyt)
 
 # Error panel labels
 error_more_info=Enemmän tietoa
diff --git a/l10n/ja/viewer.properties b/l10n/ja/viewer.properties
index 33f00ac4c..a79d65940 100644
--- a/l10n/ja/viewer.properties
+++ b/l10n/ja/viewer.properties
@@ -36,8 +36,8 @@ outline.title=文書の目次
 outline_label=文書の目次
 thumbs.title=縮小版
 thumbs_label=縮小版
-search_panel.title=検索
-search_panel_label=検索
+find_panel.title=検索
+find_panel_label=検索
 
 # Document outline messages
 no_outline=利用可能な目次はありません
@@ -54,9 +54,9 @@ thumb_page_canvas=ページの縮小版 {{page}}
 page_rotate_cw.label=右回転
 page_rotate_ccw.label=左回転
 
-# Search panel button title and messages
-search=検索
-search_terms_not_found=(見つかりませんでした)
+# Find panel button title and messages
+find=検索
+find_terms_not_found=(見つかりませんでした)
 
 # Error panel labels
 error_more_info=詳細情報
diff --git a/l10n/nl/viewer.properties b/l10n/nl/viewer.properties
index 5164862ea..45bbec253 100644
--- a/l10n/nl/viewer.properties
+++ b/l10n/nl/viewer.properties
@@ -34,8 +34,8 @@ outline.title=Toon document structuur
 outline_label=Document structuur
 thumbs.title=Toon miniaturen
 thumbs_label=Miniaturen
-search_panel.title=Doorzoek document
-search_panel_label=Zoek
+find_panel.title=Doorzoek document
+find_panel_label=Zoek
 
 # Document outline messages
 no_outline=Geen structuur beschikbaar
@@ -48,9 +48,9 @@ thumb_page_title=Pagina {{page}}
 # number.
 thumb_page_canvas=Miniatuur van pagina {{page}}
 
-# Search panel button title and messages
-search=Zoek
-search_terms_not_found=(Niet gevonden)
+# Find panel button title and messages
+find=Zoek
+find_terms_not_found=(Niet gevonden)
 
 # Error panel labels
 error_more_info=Meer informatie
diff --git a/l10n/sv/viewer.properties b/l10n/sv/viewer.properties
index 81d4a3958..e09418442 100644
--- a/l10n/sv/viewer.properties
+++ b/l10n/sv/viewer.properties
@@ -34,8 +34,8 @@ outline.title=Visa dokumentdisposition
 outline_label=Dokumentdisposition
 thumbs.title=Visa miniatyrer
 thumbs_label=Miniatyrer
-search_panel.title=Sök i dokumentet
-search_panel_label=Sök
+find_panel.title=Sök i dokumentet
+find_panel_label=Sök
 
 # Document outline messages
 no_outline=Ingen dokumentdisposition tillgänglig
@@ -48,9 +48,9 @@ thumb_page_title=Sida {{page}}
 # number.
 thumb_page_canvas=Miniatyr av sida {{page}}
 
-# Search panel button title and messages
-search=Hitta
-search_terms_not_found=(hittades inte)
+# Find panel button title and messages
+find=Hitta
+find_terms_not_found=(hittades inte)
 
 # Error panel labels
 error_more_info=Mer information
diff --git a/l10n/zh-TW/viewer.properties b/l10n/zh-TW/viewer.properties
index b7ff63235..99d85811b 100644
--- a/l10n/zh-TW/viewer.properties
+++ b/l10n/zh-TW/viewer.properties
@@ -34,8 +34,8 @@ outline.title=顯示文件綱要
 outline_label=文件綱要
 thumbs.title=顯示縮圖
 thumbs_label=縮圖
-search_panel.title=搜索文件
-search_panel_label=搜索
+find_panel.title=搜索文件
+find_panel_label=搜索
 
 # 文件綱要相關訊息
 no_outline=無可用的綱要
@@ -51,8 +51,8 @@ page_rotate_cw.label=順時針旋轉
 page_rotate_ccw.label=逆時針旋轉
 
 # 搜尋面板按鍵文字及訊息
-search=搜索
-search_terms_not_found=(沒有找到)
+find=搜索
+find_terms_not_found=(沒有找到)
 
 # 錯誤面板標籤
 error_more_info=更多資訊
diff --git a/web/viewer.css b/web/viewer.css
index caf3a09f1..bf172fe5b 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -728,7 +728,7 @@ html[dir='rtl'] .toolbarButton.pageDown::before {
   content: url(images/toolbarButton-viewOutline.png);
 }
 
-#viewSearch.toolbarButton::before {
+#viewFind.toolbarButton::before {
   display: inline-block;
   content: url(images/toolbarButton-search.png);
 }
@@ -871,7 +871,7 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
 }
 
 .outlineItem > a,
-#searchResults > a {
+#findResults > a {
   text-decoration: none;
   display: inline-block;
   min-width: 95%;
@@ -888,7 +888,7 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
 }
 
 .outlineItem > a:hover,
-#searchResults > a:hover {
+#findResults > a:hover {
   background-color: hsla(0,0%,100%,.02);
   background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   background-clip: padding-box;
@@ -915,7 +915,7 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
   font-style: italic;
 }
 
-#searchScrollView {
+#findScrollView {
   position: absolute;
   top: 10px;
   bottom: 10px;
@@ -923,26 +923,26 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
   width: 280px;
 }
 
-#searchToolbar {
+#findToolbar {
   padding-left: 0px;
   right: 0px;
   padding-top: 0px;
   padding-bottom: 5px;
 }
 
-#searchToolbar > input {
+#findToolbar > input {
   margin-left: 4px;
   width: 124px;
 }
 
-#searchToolbar button {
+#findToolbar button {
   width: auto;
   margin: 0;
   padding: 0 6px;
   height: 22px;
 }
 
-#searchResults {
+#findResults {
   overflow: auto;
   position: absolute;
   top: 30px;
diff --git a/web/viewer.html b/web/viewer.html
index 0ee6313a6..836cc554a 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -88,8 +88,8 @@ limitations under the License.
           <button id="viewOutline" class="toolbarButton group" title="Show Document Outline" tabindex="2" data-l10n-id="outline">
              <span data-l10n-id="outline_label">Document Outline</span>
           </button>
-          <button id="viewSearch" class="toolbarButton group hidden" title="Search Document" tabindex="3" data-l10n-id="search_panel">
-             <span data-l10n-id="search_panel_label">Search Document</span>
+          <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="3" data-l10n-id="find_panel">
+             <span data-l10n-id="find_panel_label">Find in Document</span>
           </button>
         </div>
         <div id="sidebarContent">
@@ -97,12 +97,12 @@ limitations under the License.
           </div>
           <div id="outlineView" class="hidden">
           </div>
-          <div id="searchView" class="hidden">
-            <div id="searchToolbar">
-              <input id="searchTermsInput" class="toolbarField">
-              <button id="searchButton" class="textButton toolbarButton" data-l10n-id="search">Find</button>
+          <div id="findView" class="hidden">
+            <div id="findToolbar">
+              <input id="findTermsInput" class="toolbarField">
+              <button id="findButton" class="textButton toolbarButton" data-l10n-id="find">Find</button>
             </div>
-            <div id="searchResults"></div>
+            <div id="findResults"></div>
           </div>
         </div>
       </div>  <!-- sidebarContainer -->
diff --git a/web/viewer.js b/web/viewer.js
index aa91f5da0..f7ed8a7aa 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -211,7 +211,7 @@ var currentPageNumber = 1;
 var PDFFindController = {
   startedTextExtraction: false,
 
-  // If active, search resulsts will be highlighted.
+  // If active, find results will be highlighted.
   active: false,
 
   // Stores the text for each page.
@@ -244,7 +244,7 @@ var PDFFindController = {
   },
 
   calcFindMatch: function(pageContent) {
-    // TODO: Handle the other search options here as well.
+    // TODO: Handle the other find options here as well.
 
     var query = this.state.query;
     var queryLen = query.length;
@@ -274,7 +274,7 @@ var PDFFindController = {
     function extractPageText(pageIndex) {
       PDFView.pages[pageIndex].getTextContent().then(
         function textContentResolved(data) {
-          // Bulid the search string.
+          // Build the find string.
           var bidiTexts = data.bidiTexts;
           var str = '';
 
@@ -434,7 +434,7 @@ var PDFFindBar = {
 
   initialize: function() {
     this.bar = document.getElementById('findbar');
-    this.toggleButton = document.getElementById('viewSearch');
+    this.toggleButton = document.getElementById('viewFind');
     this.findField = document.getElementById('findInput');
     this.highlightAll = document.getElementById('findHighlightAll');
     this.caseSensitive = document.getElementById('findMatchCase');
@@ -1224,39 +1224,39 @@ var PDFView = {
     return true;
   },
 
-  search: function pdfViewStartSearch() {
-    // Limit this function to run every <SEARCH_TIMEOUT>ms.
-    var SEARCH_TIMEOUT = 250;
-    var lastSearch = this.lastSearch;
+  find: function pdfViewStartFind() {
+    // Limit this function to run every <FIND_TIMEOUT>ms.
+    var FIND_TIMEOUT = 250;
+    var lastFind = this.lastFind;
     var now = Date.now();
-    if (lastSearch && (now - lastSearch) < SEARCH_TIMEOUT) {
-      if (!this.searchTimer) {
-        this.searchTimer = setTimeout(function resumeSearch() {
-            PDFView.search();
+    if (lastFind && (now - lastFind) < FIND_TIMEOUT) {
+      if (!this.findTimer) {
+        this.findTimer = setTimeout(function resumeFind() {
+            PDFView.find();
           },
-          SEARCH_TIMEOUT - (now - lastSearch)
+          FIND_TIMEOUT - (now - lastFind)
         );
       }
       return;
     }
-    this.searchTimer = null;
-    this.lastSearch = now;
+    this.FindTimer = null;
+    this.lastFind = now;
 
     function bindLink(link, pageNumber) {
       link.href = '#' + pageNumber;
-      link.onclick = function searchBindLink() {
+      link.onclick = function findBindLink() {
         PDFView.page = pageNumber;
         return false;
       };
     }
 
-    var searchResults = document.getElementById('searchResults');
+    var findResults = document.getElementById('findResults');
 
-    var searchTermsInput = document.getElementById('searchTermsInput');
-    searchResults.removeAttribute('hidden');
-    searchResults.textContent = '';
+    var findTermsInput = document.getElementById('findTermsInput');
+    findResults.removeAttribute('hidden');
+    findResults.textContent = '';
 
-    var terms = searchTermsInput.value;
+    var terms = findTermsInput.value;
 
     if (!terms)
       return;
@@ -1276,17 +1276,17 @@ var PDFView = {
       var link = document.createElement('a');
       bindLink(link, pageNumber);
       link.textContent = 'Page ' + pageNumber + ': ' + textSample;
-      searchResults.appendChild(link);
+      findResults.appendChild(link);
 
       pageFound = true;
     }
     if (!pageFound) {
-      searchResults.textContent = '';
+      findResults.textContent = '';
       var noResults = document.createElement('div');
       noResults.classList.add('noResults');
-      noResults.textContent = mozL10n.get('search_terms_not_found', null,
+      noResults.textContent = mozL10n.get('find_terms_not_found', null,
                                               '(Not found)');
-      searchResults.appendChild(noResults);
+      findResults.appendChild(noResults);
     }
   },
 
@@ -1331,20 +1331,20 @@ var PDFView = {
   switchSidebarView: function pdfViewSwitchSidebarView(view) {
     var thumbsView = document.getElementById('thumbnailView');
     var outlineView = document.getElementById('outlineView');
-    var searchView = document.getElementById('searchView');
+    var findView = document.getElementById('findView');
 
     var thumbsButton = document.getElementById('viewThumbnail');
     var outlineButton = document.getElementById('viewOutline');
-    var searchButton = document.getElementById('viewSearch');
+    var findButton = document.getElementById('viewFind');
 
     switch (view) {
       case 'thumbs':
         thumbsButton.classList.add('toggled');
         outlineButton.classList.remove('toggled');
-        searchButton.classList.remove('toggled');
+        findButton.classList.remove('toggled');
         thumbsView.classList.remove('hidden');
         outlineView.classList.add('hidden');
-        searchView.classList.add('hidden');
+        findView.classList.add('hidden');
 
         PDFView.renderHighestPriority();
         break;
@@ -1352,25 +1352,25 @@ var PDFView = {
       case 'outline':
         thumbsButton.classList.remove('toggled');
         outlineButton.classList.add('toggled');
-        searchButton.classList.remove('toggled');
+        findButton.classList.remove('toggled');
         thumbsView.classList.add('hidden');
         outlineView.classList.remove('hidden');
-        searchView.classList.add('hidden');
+        findView.classList.add('hidden');
 
         if (outlineButton.getAttribute('disabled'))
           return;
         break;
 
-      case 'search':
+      case 'find':
         thumbsButton.classList.remove('toggled');
         outlineButton.classList.remove('toggled');
-        searchButton.classList.add('toggled');
+        findButton.classList.add('toggled');
         thumbsView.classList.add('hidden');
         outlineView.classList.add('hidden');
-        searchView.classList.remove('hidden');
+        findView.classList.remove('hidden');
 
-        var searchTermsInput = document.getElementById('searchTermsInput');
-        searchTermsInput.focus();
+        var findTermsInput = document.getElementById('findTermsInput');
+        findTermsInput.focus();
         // Start text extraction as soon as the search gets displayed.
         this.extractText();
         break;
@@ -1386,7 +1386,7 @@ var PDFView = {
       self.pages[pageIndex].pdfPage.getTextContent().then(
         function textContentResolved(textContent) {
           self.pageText[pageIndex] = textContent.join('');
-          self.search();
+          self.find();
           if ((pageIndex + 1) < self.pages.length)
             extractPageText(pageIndex + 1);
         }
@@ -2545,8 +2545,8 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
 
 //#if !(FIREFOX || MOZCENTRAL)
 //#else
-//if (FirefoxCom.requestSync('searchEnabled')) {
-//  document.querySelector('#viewSearch').classList.remove('hidden');
+//if (FirefoxCom.requestSync('findEnabled')) {
+//  document.querySelector('#viewFind').classList.remove('hidden');
 //}
 //#endif
 
@@ -2636,10 +2636,10 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
       PDFView.download();
     });
 
-  document.getElementById('searchTermsInput').addEventListener('keydown',
+  document.getElementById('findTermsInput').addEventListener('keydown',
     function(event) {
       if (event.keyCode == 13) {
-        PDFView.search();
+        PDFView.find();
       }
     });
 

From 6022476f036b4829a9b91c02ee988f2bf2f94bd2 Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Wed, 26 Sep 2012 12:14:57 -0400
Subject: [PATCH 04/17] add support for case sensitive

---
 web/viewer.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/web/viewer.js b/web/viewer.js
index f7ed8a7aa..80c25aafb 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -247,11 +247,17 @@ var PDFFindController = {
     // TODO: Handle the other find options here as well.
 
     var query = this.state.query;
+    var caseSensitive = this.state.caseSensitive;
     var queryLen = query.length;
 
     if (queryLen === 0)
       return [];
 
+    if (!caseSensitive) {
+      pageContent = pageContent.toLowerCase();
+      query = query.toLowerCase();
+    }
+
     var matches = [];
 
     var matchIdx = -queryLen;

From 73b96e0e580a6921868b07abeaed476c0d94a392 Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Wed, 26 Sep 2012 14:09:55 -0400
Subject: [PATCH 05/17] bug fix: clear previous highlights

---
 web/viewer.js | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/web/viewer.js b/web/viewer.js
index 80c25aafb..214b272fd 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -2365,6 +2365,18 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
     return ret;
   };
 
+  this.clearMatches = function textLayerBuilder_clearMatches() {
+    var textDivs = this.textDivs;
+
+    // Clear all previous highlights
+    textDivs.forEach(function(div) {
+      var spans = div.querySelectorAll('span');
+      for (var i = 0, ii = spans.length; i < ii; i++) {
+        spans[i].className = '';
+      }
+    });
+  }
+
   this.renderMatches = function textLayerBuilder_renderMatches(matches) {
     var bidiTexts = this.textContent.bidiTexts;
     var textDivs = this.textDivs;
@@ -2372,6 +2384,8 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
     var isSelectedPage = this.pageIdx === PDFFindController.selected.pageIdx;
     var selectedMatchIdx = PDFFindController.selected.matchIdx;
 
+    this.clearMatches();
+
     var infty = {
       divIdx: -1,
       offset: undefined

From 1ec2e2a1a2ac2260bbde0a5cee32209458dd2f9b Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Wed, 26 Sep 2012 16:38:02 -0400
Subject: [PATCH 06/17] remove old unused code

---
 web/viewer.css  |  36 +--------------
 web/viewer.html |   7 ---
 web/viewer.js   | 113 ------------------------------------------------
 3 files changed, 2 insertions(+), 154 deletions(-)

diff --git a/web/viewer.css b/web/viewer.css
index bf172fe5b..92e7d37f1 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -870,8 +870,7 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
   margin-left: 20px;
 }
 
-.outlineItem > a,
-#findResults > a {
+.outlineItem > a {
   text-decoration: none;
   display: inline-block;
   min-width: 95%;
@@ -887,8 +886,7 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
   white-space: nowrap;
 }
 
-.outlineItem > a:hover,
-#findResults > a:hover {
+.outlineItem > a:hover {
   background-color: hsla(0,0%,100%,.02);
   background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   background-clip: padding-box;
@@ -923,36 +921,6 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
   width: 280px;
 }
 
-#findToolbar {
-  padding-left: 0px;
-  right: 0px;
-  padding-top: 0px;
-  padding-bottom: 5px;
-}
-
-#findToolbar > input {
-  margin-left: 4px;
-  width: 124px;
-}
-
-#findToolbar button {
-  width: auto;
-  margin: 0;
-  padding: 0 6px;
-  height: 22px;
-}
-
-#findResults {
-  overflow: auto;
-  position: absolute;
-  top: 30px;
-  bottom: 0px;
-  left: 0px;
-  right: 0;
-  padding: 4px 4px 0;
-  font-size: smaller;
-}
-
 #sidebarControls {
   position:absolute;
   width: 180px;
diff --git a/web/viewer.html b/web/viewer.html
index 836cc554a..a7e9091cc 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -97,13 +97,6 @@ limitations under the License.
           </div>
           <div id="outlineView" class="hidden">
           </div>
-          <div id="findView" class="hidden">
-            <div id="findToolbar">
-              <input id="findTermsInput" class="toolbarField">
-              <button id="findButton" class="textButton toolbarButton" data-l10n-id="find">Find</button>
-            </div>
-            <div id="findResults"></div>
-          </div>
         </div>
       </div>  <!-- sidebarContainer -->
 
diff --git a/web/viewer.js b/web/viewer.js
index 214b272fd..bfd7cc972 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -244,8 +244,6 @@ var PDFFindController = {
   },
 
   calcFindMatch: function(pageContent) {
-    // TODO: Handle the other find options here as well.
-
     var query = this.state.query;
     var caseSensitive = this.state.caseSensitive;
     var queryLen = query.length;
@@ -1230,72 +1228,6 @@ var PDFView = {
     return true;
   },
 
-  find: function pdfViewStartFind() {
-    // Limit this function to run every <FIND_TIMEOUT>ms.
-    var FIND_TIMEOUT = 250;
-    var lastFind = this.lastFind;
-    var now = Date.now();
-    if (lastFind && (now - lastFind) < FIND_TIMEOUT) {
-      if (!this.findTimer) {
-        this.findTimer = setTimeout(function resumeFind() {
-            PDFView.find();
-          },
-          FIND_TIMEOUT - (now - lastFind)
-        );
-      }
-      return;
-    }
-    this.FindTimer = null;
-    this.lastFind = now;
-
-    function bindLink(link, pageNumber) {
-      link.href = '#' + pageNumber;
-      link.onclick = function findBindLink() {
-        PDFView.page = pageNumber;
-        return false;
-      };
-    }
-
-    var findResults = document.getElementById('findResults');
-
-    var findTermsInput = document.getElementById('findTermsInput');
-    findResults.removeAttribute('hidden');
-    findResults.textContent = '';
-
-    var terms = findTermsInput.value;
-
-    if (!terms)
-      return;
-
-    // simple search: removing spaces and hyphens, then scanning every
-    terms = terms.replace(/\s-/g, '').toLowerCase();
-    var index = PDFView.pageText;
-    var pageFound = false;
-    for (var i = 0, ii = index.length; i < ii; i++) {
-      var pageText = index[i].replace(/\s-/g, '').toLowerCase();
-      var j = pageText.indexOf(terms);
-      if (j < 0)
-        continue;
-
-      var pageNumber = i + 1;
-      var textSample = index[i].substr(j, 50);
-      var link = document.createElement('a');
-      bindLink(link, pageNumber);
-      link.textContent = 'Page ' + pageNumber + ': ' + textSample;
-      findResults.appendChild(link);
-
-      pageFound = true;
-    }
-    if (!pageFound) {
-      findResults.textContent = '';
-      var noResults = document.createElement('div');
-      noResults.classList.add('noResults');
-      noResults.textContent = mozL10n.get('find_terms_not_found', null,
-                                              '(Not found)');
-      findResults.appendChild(noResults);
-    }
-  },
-
   setHash: function pdfViewSetHash(hash) {
     if (!hash)
       return;
@@ -1337,20 +1269,16 @@ var PDFView = {
   switchSidebarView: function pdfViewSwitchSidebarView(view) {
     var thumbsView = document.getElementById('thumbnailView');
     var outlineView = document.getElementById('outlineView');
-    var findView = document.getElementById('findView');
 
     var thumbsButton = document.getElementById('viewThumbnail');
     var outlineButton = document.getElementById('viewOutline');
-    var findButton = document.getElementById('viewFind');
 
     switch (view) {
       case 'thumbs':
         thumbsButton.classList.add('toggled');
         outlineButton.classList.remove('toggled');
-        findButton.classList.remove('toggled');
         thumbsView.classList.remove('hidden');
         outlineView.classList.add('hidden');
-        findView.classList.add('hidden');
 
         PDFView.renderHighestPriority();
         break;
@@ -1358,49 +1286,15 @@ var PDFView = {
       case 'outline':
         thumbsButton.classList.remove('toggled');
         outlineButton.classList.add('toggled');
-        findButton.classList.remove('toggled');
         thumbsView.classList.add('hidden');
         outlineView.classList.remove('hidden');
-        findView.classList.add('hidden');
 
         if (outlineButton.getAttribute('disabled'))
           return;
         break;
-
-      case 'find':
-        thumbsButton.classList.remove('toggled');
-        outlineButton.classList.remove('toggled');
-        findButton.classList.add('toggled');
-        thumbsView.classList.add('hidden');
-        outlineView.classList.add('hidden');
-        findView.classList.remove('hidden');
-
-        var findTermsInput = document.getElementById('findTermsInput');
-        findTermsInput.focus();
-        // Start text extraction as soon as the search gets displayed.
-        this.extractText();
-        break;
     }
   },
 
-  extractText: function() {
-    if (this.startedTextExtraction)
-      return;
-    this.startedTextExtraction = true;
-    var self = this;
-    function extractPageText(pageIndex) {
-      self.pages[pageIndex].pdfPage.getTextContent().then(
-        function textContentResolved(textContent) {
-          self.pageText[pageIndex] = textContent.join('');
-          self.find();
-          if ((pageIndex + 1) < self.pages.length)
-            extractPageText(pageIndex + 1);
-        }
-      );
-    }
-    extractPageText(0);
-  },
-
   getVisiblePages: function pdfViewGetVisiblePages() {
     return this.getVisibleElements(this.container,
                                    this.pages, true);
@@ -2656,13 +2550,6 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
       PDFView.download();
     });
 
-  document.getElementById('findTermsInput').addEventListener('keydown',
-    function(event) {
-      if (event.keyCode == 13) {
-        PDFView.find();
-      }
-    });
-
   document.getElementById('pageNumber').addEventListener('change',
     function() {
       PDFView.page = this.value;

From e2a2085f6258e2e11b20b93b50ec131b91f4bb06 Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Wed, 26 Sep 2012 16:47:26 -0400
Subject: [PATCH 07/17] remove find button in MOZCENTRAL

---
 web/viewer.html | 2 ++
 web/viewer.js   | 3 +--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/web/viewer.html b/web/viewer.html
index a7e9091cc..90124508b 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -88,9 +88,11 @@ limitations under the License.
           <button id="viewOutline" class="toolbarButton group" title="Show Document Outline" tabindex="2" data-l10n-id="outline">
              <span data-l10n-id="outline_label">Document Outline</span>
           </button>
+<!--#if !MOZCENTRAL-->
           <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="3" data-l10n-id="find_panel">
              <span data-l10n-id="find_panel_label">Find in Document</span>
           </button>
+<!--#endif-->
         </div>
         <div id="sidebarContent">
           <div id="thumbnailView">
diff --git a/web/viewer.js b/web/viewer.js
index bfd7cc972..18954bca7 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -446,8 +446,7 @@ var PDFFindBar = {
     this.findMsgNotFound = document.getElementById('findMsgNotFound');
 
     var self = this;
-    this.toggleButton.addEventListener('click',
-    function() {
+    this.toggleButton.addEventListener('click', function() {
       self.toggle();
     });
 

From 6090b3edf9e3a3c66db09a9279290e7f4adaa72b Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Thu, 27 Sep 2012 12:21:04 -0400
Subject: [PATCH 08/17] colors closer to FF's, add highlightAll support

---
 web/viewer.css |  4 ++--
 web/viewer.js  | 24 ++++++++++++++----------
 2 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/web/viewer.css b/web/viewer.css
index 92e7d37f1..77d4807dc 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -1067,7 +1067,7 @@ canvas {
   margin: -1px;
   padding: 1px;
 
-  background-color: rgba(0, 137, 26, 0.2);
+  background-color: rgba(180, 0, 170, 0.2);
   border-radius: 4px;
 }
 
@@ -1084,7 +1084,7 @@ canvas {
 }
 
 .textLayer .highlight.selected {
-  background-color: rgba(255, 0, 0, 0.2);
+  background-color: rgba(0, 100, 0, 0.2);
 }
 
 /* TODO: file FF bug to support ::-moz-selection:window-inactive
diff --git a/web/viewer.js b/web/viewer.js
index 18954bca7..f55074263 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -458,22 +458,18 @@ var PDFFindBar = {
     // next match when the findField is selected.
 
     document.getElementById('findPrevious').addEventListener('click',
-    function() {
-      self.dispatchEvent('again', true);
-    });
+      function() { self.dispatchEvent('again', true); }
+    );
 
-    document.getElementById('findNext').addEventListener('click',
-    function() {
+    document.getElementById('findNext').addEventListener('click', function() {
       self.dispatchEvent('again', false);
     });
 
-    this.highlightAll.addEventListener('click',
-    function() {
+    this.highlightAll.addEventListener('click', function() {
       self.dispatchEvent('highlightallchange');
     });
 
-    this.caseSensitive.addEventListener('click',
-    function() {
+    this.caseSensitive.addEventListener('click', function() {
       self.dispatchEvent('casesensitivitychange');
     });
   },
@@ -2276,6 +2272,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
     var prevEnd = null;
     var isSelectedPage = this.pageIdx === PDFFindController.selected.pageIdx;
     var selectedMatchIdx = PDFFindController.selected.matchIdx;
+    var highlightAll = PDFFindController.state.highlightAll;
 
     this.clearMatches();
 
@@ -2323,7 +2320,14 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
       textDivs[divIdx].className = className;
     }
 
-    for (var i = 0; i < matches.length; i++) {
+    var i0 = selectedMatchIdx, i1 = i0 + 1, i;
+
+    if (highlightAll) {
+      i0 = 0;
+      i1 = matches.length;
+    }
+
+    for (i = i0; i < i1; i++) {
       var match = matches[i];
       var begin = match.begin;
       var end = match.end;

From ecf3dae7764990a0f0e7218541a7609fc61338bc Mon Sep 17 00:00:00 2001
From: Julian Viereck <julian.viereck@gmail.com>
Date: Fri, 28 Sep 2012 15:16:10 +0200
Subject: [PATCH 09/17] Fix clear matches functionality

---
 web/viewer.js | 24 ++++++++----------------
 1 file changed, 8 insertions(+), 16 deletions(-)

diff --git a/web/viewer.js b/web/viewer.js
index f55074263..1883333a5 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -2254,19 +2254,12 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
     return ret;
   };
 
-  this.clearMatches = function textLayerBuilder_clearMatches() {
-    var textDivs = this.textDivs;
-
-    // Clear all previous highlights
-    textDivs.forEach(function(div) {
-      var spans = div.querySelectorAll('span');
-      for (var i = 0, ii = spans.length; i < ii; i++) {
-        spans[i].className = '';
-      }
-    });
-  }
-
   this.renderMatches = function textLayerBuilder_renderMatches(matches) {
+    // Early exit if there is nothing to render.
+    if (matches.length === 0) {
+      return;
+    }
+
     var bidiTexts = this.textContent.bidiTexts;
     var textDivs = this.textDivs;
     var prevEnd = null;
@@ -2274,8 +2267,6 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
     var selectedMatchIdx = PDFFindController.selected.matchIdx;
     var highlightAll = PDFFindController.state.highlightAll;
 
-    this.clearMatches();
-
     var infty = {
       divIdx: -1,
       offset: undefined
@@ -2377,12 +2368,13 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
     var bidiTexts = this.textContent.bidiTexts;
     var clearedUntilDivIdx = -1;
 
+    // Clear out all current matches.
     for (var i = 0; i < matches.length; i++) {
       var match = matches[i];
       var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
       for (var n = begin; n <= match.end.divIdx; n++) {
-        var div = bidiTexts[n].str;
-        div.textContent = div.textContent;
+        var div = textDivs[n];
+        div.textContent = bidiTexts[n].str;
         div.className = '';
       }
       clearedUntilDivIdx = match.end.divIdx + 1;

From 2a1264a74646eecb1ce5d4b59c5dbaa169a32ede Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Fri, 28 Sep 2012 09:03:35 -0700
Subject: [PATCH 10/17] Don't try to highlight anything if the page isn't
 selected and we aren't highlighting all.

---
 web/viewer.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/web/viewer.js b/web/viewer.js
index 1883333a5..1d743d668 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -2316,6 +2316,9 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
     if (highlightAll) {
       i0 = 0;
       i1 = matches.length;
+    } else if(!isSelectedPage) {
+      // Not highlighting all and this isn't the selected page, so do nothing.
+      return;
     }
 
     for (i = i0; i < i1; i++) {

From 38193b1887cbe7f920cc99e728e5281cd76f262c Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Fri, 28 Sep 2012 11:18:45 -0700
Subject: [PATCH 11/17] Update the find bar ui status.

---
 web/viewer.css  | 10 ++++++-
 web/viewer.html |  3 +-
 web/viewer.js   | 76 ++++++++++++++++++++++++++++++++++---------------
 3 files changed, 63 insertions(+), 26 deletions(-)

diff --git a/web/viewer.css b/web/viewer.css
index 77d4807dc..95b4dc5ab 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -282,9 +282,17 @@ html[dir='rtl'] #sidebarContent {
   font-size: 12px;
   line-height: 14px;
   text-align: left;
+  cursor: default;
+}
+
+.findbar label {
   -webkit-user-select:none;
   -moz-user-select:none;
-  cursor: default;
+}
+
+#findMsg {
+  font-style: italic;
+  color: #A6B7D0;
 }
 
 .notFound {
diff --git a/web/viewer.html b/web/viewer.html
index 90124508b..73c4cb87a 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -112,8 +112,7 @@ limitations under the License.
           <label for="findHighlightAll">Highlight all</label>
           <input type="checkbox" id="findMatchCase">
           <label for="findMatchCase">Match case</label>
-          <span id="findMsgWrap" class="hidden">Reached end of page, continued from top</span>
-          <span id="findMsgNotFound" class="hidden">Phrase not found</span>
+          <span id="findMsg"></span>
         </div>
         <div class="toolbar">
           <div id="toolbarContainer">
diff --git a/web/viewer.js b/web/viewer.js
index 1d743d668..759133127 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -34,6 +34,12 @@ var RenderingStates = {
   PAUSED: 2,
   FINISHED: 3
 };
+var FindStates = {
+  FIND_FOUND: 0,
+  FIND_NOTFOUND: 1,
+  FIND_WRAPPED: 2,
+  FIND_PENDING: 3
+};
 
 //#if (GENERIC || CHROME)
 //PDFJS.workerSrc = '../build/pdf.js';
@@ -337,6 +343,12 @@ var PDFFindController = {
 
     this.active = true;
 
+    if (!this.state.query) {
+      this.updateUIState(FindStates.FIND_FOUND);
+      return;
+    }
+    this.updateUIState(FindStates.FIND_PENDING);
+
     if (this.dirtyMatch) {
       // Need to recalculate the matches.
       this.dirtyMatch = false;
@@ -360,16 +372,24 @@ var PDFFindController = {
         }
         this.updatePage(i, true);
       }
+      if (!firstMatch) {
+        this.updateUIState(FindStates.FIND_FOUND);
+      } else {
+        this.updateUIState(FindStates.FIND_NOTFOUND);
+      }
     } else {
       // If there is NO selection, then there is no match at all -> no sense to
       // handle previous/next action.
-      if (this.selected.pageIdx === -1)
+      if (this.selected.pageIdx === -1) {
+        this.updateUIState(FindStates.FIND_NOTFOUND);
         return;
+      }
 
       // Handle findAgain case.
       var previous = this.state.findPrevious;
       var sPageIdx = this.selected.pageIdx;
       var sMatchIdx = this.selected.matchIdx;
+      var findState = FindStates.FIND_FOUND;
 
       if (previous) {
         // Select previous match.
@@ -393,6 +413,9 @@ var PDFFindController = {
           // If pageIdx stayed the same, select last match on the page.
           if (this.selected.pageIdx === sPageIdx) {
             this.selected.matchIdx = pageMatches[sPageIdx].length - 1;
+            findState = FindStates.FIND_WRAPPED;
+          } else if (this.selected.pageIdx > sPageIdx) {
+            findState = FindStates.FIND_WRAPPED;
           }
         }
       } else {
@@ -411,18 +434,28 @@ var PDFFindController = {
               break;
             }
           }
-          // If pageIdx stayed the same, select last match on the page.
+
+          // If pageIdx stayed the same, select first match on the page.
           if (this.selected.pageIdx === sPageIdx) {
             this.selected.matchIdx = 0;
+            findState = FindStates.FIND_WRAPPED;
+          } else if (this.selected.pageIdx < sPageIdx) {
+            findState = FindStates.FIND_WRAPPED;
           }
         }
       }
 
+      this.updateUIState(findState, previous);
       this.updatePage(sPageIdx, sPageIdx === this.selected.pageIdx);
       if (sPageIdx !== this.selected.pageIdx) {
         this.updatePage(this.selected.pageIdx, true);
       }
     }
+  },
+
+  updateUIState: function(state, previous) {
+    // TODO: Update the firefox find bar or update the html findbar.
+    PDFFindBar.updateUIState(state, previous);
   }
 };
 
@@ -432,18 +465,13 @@ var PDFFindBar = {
 
   opened: false,
 
-  FIND_FOUND: 0,    // Successful find
-  FIND_NOTFOUND: 1, // Unsuccessful find
-  FIND_WRAPPED: 2,  // Successful find, but wrapped around
-
   initialize: function() {
     this.bar = document.getElementById('findbar');
     this.toggleButton = document.getElementById('viewFind');
     this.findField = document.getElementById('findInput');
     this.highlightAll = document.getElementById('findHighlightAll');
     this.caseSensitive = document.getElementById('findMatchCase');
-    this.findMsgWrap = document.getElementById('findMsgWrap');
-    this.findMsgNotFound = document.getElementById('findMsgNotFound');
+    this.findMsg = document.getElementById('findMsg');
 
     var self = this;
     this.toggleButton.addEventListener('click', function() {
@@ -485,36 +513,38 @@ var PDFFindBar = {
     return window.dispatchEvent(event);
   },
 
-  updateUIState: function(aState) {
+  updateUIState: function(state, previous) {
     var notFound = false;
-    var wrapped = false;
+    var findMsg = '';
+    var status = 'pending';
 
-    switch (aState) {
-      case this.FIND_FOUND:
+    switch (state) {
+      case FindStates.FIND_FOUND:
         break;
 
-      case this.FIND_NOTFOUND:
+      case FindStates.FIND_NOTFOUND:
+        findMsg = mozL10n.get('find_not_found', null, 'Phrase not found');
         notFound = true;
         break;
 
-      case this.FIND_WRAPPED:
-        wrapped = true;
+      case FindStates.FIND_WRAPPED:
+        if (previous) {
+          findMsg = mozL10n.get('find_wrapped_to_bottom', null,
+                                'Reached end of page, continued from bottom');
+        } else {
+          findMsg = mozL10n.get('find_wrapped_to_top', null,
+                                'Reached end of page, continued from top');
+        }
         break;
     }
 
     if (notFound) {
       this.findField.classList.add('notFound');
-      this.findMsgNotFound.classList.remove('hidden');
     } else {
       this.findField.classList.remove('notFound');
-      this.findMsgNotFound.classList.add('hidden');
     }
 
-    if (wrapped) {
-      this.findMsgWrap.classList.remove('hidden');
-    } else {
-      this.findMsgWrap.classList.add('hidden');
-    }
+    this.findMsg.textContent = findMsg;
   },
 
   open: function() {
@@ -2316,7 +2346,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
     if (highlightAll) {
       i0 = 0;
       i1 = matches.length;
-    } else if(!isSelectedPage) {
+    } else if (!isSelectedPage) {
       // Not highlighting all and this isn't the selected page, so do nothing.
       return;
     }

From 53672af0f79c64a88eb4280063e9df7e55f7144a Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Fri, 28 Sep 2012 11:43:22 -0700
Subject: [PATCH 12/17] Fix un-highlight when there is no query.

---
 web/viewer.js | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/web/viewer.js b/web/viewer.js
index 759133127..4381e6c2f 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -343,10 +343,6 @@ var PDFFindController = {
 
     this.active = true;
 
-    if (!this.state.query) {
-      this.updateUIState(FindStates.FIND_FOUND);
-      return;
-    }
     this.updateUIState(FindStates.FIND_PENDING);
 
     if (this.dirtyMatch) {
@@ -372,7 +368,7 @@ var PDFFindController = {
         }
         this.updatePage(i, true);
       }
-      if (!firstMatch) {
+      if (!firstMatch || !this.state.query) {
         this.updateUIState(FindStates.FIND_FOUND);
       } else {
         this.updateUIState(FindStates.FIND_NOTFOUND);

From dd0cb37f38d4ebb22f16198f4051fc29cea33452 Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Fri, 28 Sep 2012 12:30:07 -0700
Subject: [PATCH 13/17] Add some door hanger styling for the find bar

---
 web/viewer.css  | 37 +++++++++++++++++++++++++++++++------
 web/viewer.html | 14 +++++++-------
 2 files changed, 38 insertions(+), 13 deletions(-)

diff --git a/web/viewer.css b/web/viewer.css
index 95b4dc5ab..30877a6d6 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -267,17 +267,15 @@ html[dir='rtl'] #sidebarContent {
 }
 
 .findbar {
-  top: 40px;
-  left: 40px;
+  top: 32px;
+  left: 68px;
   position: absolute;
-  z-index: 100;
+  z-index: 10000;
   height: 20px;
 
   min-width: 16px;
   padding: 3px 6px 3px 6px;
   margin: 4px 2px 4px 0;
-  border: 1px solid transparent;
-  border-radius: 2px;
   color: hsl(0,0%,85%);
   font-size: 12px;
   line-height: 14px;
@@ -290,6 +288,33 @@ html[dir='rtl'] #sidebarContent {
   -moz-user-select:none;
 }
 
+.doorHanger {
+  border: 1px solid hsla(0,0%,0%,.5);
+  border-radius: 2px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+}
+.doorHanger:after, .doorHanger:before {
+  bottom: 100%;
+  border: solid transparent;
+  content: " ";
+  height: 0;
+  width: 0;
+  position: absolute;
+  pointer-events: none;
+}
+.doorHanger:after {
+  border-bottom-color: hsla(0,0%,32%,.99);
+  border-width: 8px;
+  left: 16px;
+  margin-left: -8px;
+}
+.doorHanger:before {
+  border-bottom-color: hsla(0,0%,0%,.5);
+  border-width: 9px;
+  left: 16px;
+  margin-left: -9px;
+}
+
 #findMsg {
   font-style: italic;
   color: #A6B7D0;
@@ -1317,7 +1342,7 @@ canvas {
 }
 
 @media all and (max-width: 600px) {
-  #toolbarViewerRight {
+  #toolbarViewerRight, #findbar, #viewFind {
     display: none;
   }
 }
diff --git a/web/viewer.html b/web/viewer.html
index 73c4cb87a..e59120471 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -88,11 +88,6 @@ limitations under the License.
           <button id="viewOutline" class="toolbarButton group" title="Show Document Outline" tabindex="2" data-l10n-id="outline">
              <span data-l10n-id="outline_label">Document Outline</span>
           </button>
-<!--#if !MOZCENTRAL-->
-          <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="3" data-l10n-id="find_panel">
-             <span data-l10n-id="find_panel_label">Find in Document</span>
-          </button>
-<!--#endif-->
         </div>
         <div id="sidebarContent">
           <div id="thumbnailView">
@@ -103,7 +98,7 @@ limitations under the License.
       </div>  <!-- sidebarContainer -->
 
       <div id="mainContainer">
-        <div class="findbar hidden" id="findbar">
+        <div class="findbar hidden doorHanger" id="findbar">
           <label for="findInput">Find: </label>
           <input id="findInput" type="search">
           <button id="findPrevious">&lt;</button>
@@ -118,10 +113,15 @@ limitations under the License.
           <div id="toolbarContainer">
             <div id="toolbarViewer">
               <div id="toolbarViewerLeft">
-                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="4" data-l10n-id="toggle_slider">
+                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="3" data-l10n-id="toggle_slider">
                   <span data-l10n-id="toggle_slider_label">Toggle Sidebar</span>
                 </button>
                 <div class="toolbarButtonSpacer"></div>
+<!--#if !MOZCENTRAL-->
+                <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="4" data-l10n-id="find_panel">
+                   <span data-l10n-id="find_panel_label">Find in Document</span>
+                </button>
+<!--#endif-->
                 <div class="splitToolbarButton">
                   <button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="5" data-l10n-id="previous">
                     <span data-l10n-id="previous_label">Previous</span>

From 3bd20a7415d7ae99c969bb66eae0a2090ead6ee1 Mon Sep 17 00:00:00 2001
From: Artur Adib <arturadib@gmail.com>
Date: Tue, 2 Oct 2012 16:19:51 -0400
Subject: [PATCH 14/17] bug fix: textLayer ready still needs scroll

---
 web/viewer.js | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/web/viewer.js b/web/viewer.js
index 4381e6c2f..467f9515d 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -323,14 +323,16 @@ var PDFFindController = {
   updatePage: function(idx) {
     var page = PDFView.pages[idx];
 
-    if (page.textLayer) {
-      page.textLayer.updateMatches();
-    } else if (this.selected.pageIdx === idx) {
+    if (this.selected.pageIdx === idx) {
       // If the page is selected, scroll the page into view, which triggers
       // rendering the page, which adds the textLayer. Once the textLayer is
       // build, it will scroll onto the selected match.
       page.scrollIntoView();
     }
+
+    if (page.textLayer) {
+      page.textLayer.updateMatches();
+    }
   },
 
   performFind: function() {

From 45fe76e752dc6042f0f6e3df346ccb8deaf3c36b Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Tue, 2 Oct 2012 15:59:57 -0700
Subject: [PATCH 15/17] Add support for enter/shift  for find.

---
 web/viewer.js | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/web/viewer.js b/web/viewer.js
index 467f9515d..7933e5393 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -480,9 +480,17 @@ var PDFFindBar = {
       self.dispatchEvent('');
     });
 
-    // TODO: Add keybindings like enter, shift-enter, CMD-G etc. to go to prev/
+    // TODO: Add keybindings CMD-G etc. to go to prev/
     // next match when the findField is selected.
 
+    this.findField.addEventListener('keydown', function(evt) {
+      switch (evt.keyCode) {
+        case 13: // Enter
+          self.dispatchEvent('again', evt.shiftKey);
+          break;
+      }
+    });
+
     document.getElementById('findPrevious').addEventListener('click',
       function() { self.dispatchEvent('again', true); }
     );

From 1f85b8b4ed79bedd6d5aeb641889772107878e89 Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Tue, 2 Oct 2012 17:08:47 -0700
Subject: [PATCH 16/17] Update the l10n for the html find bar.

---
 l10n/en-US/viewer.properties | 12 ++++++++----
 web/viewer.html              | 14 +++++++-------
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties
index 678e23c8e..8e0ca9879 100644
--- a/l10n/en-US/viewer.properties
+++ b/l10n/en-US/viewer.properties
@@ -36,8 +36,6 @@ outline.title=Show Document Outline
 outline_label=Document Outline
 thumbs.title=Show Thumbnails
 thumbs_label=Thumbnails
-find_panel.title=Find in Document
-find_panel_label=Find
 
 # Document outline messages
 no_outline=No Outline Available
@@ -55,8 +53,14 @@ page_rotate_cw.label=Rotate Clockwise
 page_rotate_ccw.label=Rotate Counter-Clockwise
 
 # Find panel button title and messages
-find=Find
-find_terms_not_found=(Not found)
+find.title=Find in Document
+find_label=Find
+find_previous.title=Find the previous occurrence of the phrase
+find_next.title=Find the next occurrence of the phrase
+find_highlight=Highlight all
+find_match_case_label=Match case
+find_wrapped_to_bottom=Reached end of page, continued from bottom
+find_wrapped_to_top=Reached end of page, continued from top
 
 # Error panel labels
 error_more_info=More Information
diff --git a/web/viewer.html b/web/viewer.html
index e59120471..7af1d919b 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -99,14 +99,14 @@ limitations under the License.
 
       <div id="mainContainer">
         <div class="findbar hidden doorHanger" id="findbar">
-          <label for="findInput">Find: </label>
+          <label for="findInput" data-l10n-id="find_label">Find</label>:
           <input id="findInput" type="search">
-          <button id="findPrevious">&lt;</button>
-          <button id="findNext">&gt;</button>
+          <button id="findPrevious" data-l10n-id="find_previous" title="">&lt;</button>
+          <button id="findNext" data-l10n-id="find_next">&gt;</button>
           <input type="checkbox" id="findHighlightAll">
-          <label for="findHighlightAll">Highlight all</label>
+          <label for="findHighlightAll" data-l10n-id="find_highlight">Highlight all</label>
           <input type="checkbox" id="findMatchCase">
-          <label for="findMatchCase">Match case</label>
+          <label for="findMatchCase" data-l10n-id="find_match_case_label">Match case</label>
           <span id="findMsg"></span>
         </div>
         <div class="toolbar">
@@ -118,8 +118,8 @@ limitations under the License.
                 </button>
                 <div class="toolbarButtonSpacer"></div>
 <!--#if !MOZCENTRAL-->
-                <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="4" data-l10n-id="find_panel">
-                   <span data-l10n-id="find_panel_label">Find in Document</span>
+                <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="4" data-l10n-id="find">
+                   <span data-l10n-id="find_label">Find</span>
                 </button>
 <!--#endif-->
                 <div class="splitToolbarButton">

From 5d848ca31a726bc5cf5fd6a09039001ab070f660 Mon Sep 17 00:00:00 2001
From: Brendan Dahl <brendan.dahl@gmail.com>
Date: Thu, 4 Oct 2012 13:44:17 -0700
Subject: [PATCH 17/17] Ignore find key shortcut only for mozcentral for now.

---
 web/viewer.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/viewer.js b/web/viewer.js
index 7933e5393..2fb9c90a6 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -2799,7 +2799,7 @@ window.addEventListener('keydown', function keydown(evt) {
   // control is selected or not.
   if (cmd == 1 || cmd == 8) { // either CTRL or META key.
     switch (evt.keyCode) {
-//#if !(FIREFOX || MOZCENTRAL)
+//#if !MOZCENTRAL
       case 70:
         PDFFindBar.toggle();
         handled = true;