From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001
From: Jari Vetoniemi <jari.vetoniemi@indooratlas.com>
Date: Mon, 16 Mar 2020 18:49:26 +0900
Subject: Fresh start

---
 .../generator/template/json_index/js/navigation.js | 142 +++++++++++++
 .../generator/template/json_index/js/searcher.js   | 228 +++++++++++++++++++++
 2 files changed, 370 insertions(+)
 create mode 100644 jni/ruby/lib/rdoc/generator/template/json_index/js/navigation.js
 create mode 100644 jni/ruby/lib/rdoc/generator/template/json_index/js/searcher.js

(limited to 'jni/ruby/lib/rdoc/generator/template/json_index/js')

diff --git a/jni/ruby/lib/rdoc/generator/template/json_index/js/navigation.js b/jni/ruby/lib/rdoc/generator/template/json_index/js/navigation.js
new file mode 100644
index 0000000..e412681
--- /dev/null
+++ b/jni/ruby/lib/rdoc/generator/template/json_index/js/navigation.js
@@ -0,0 +1,142 @@
+/*
+ * Navigation allows movement using the arrow keys through the search results.
+ *
+ * When using this library you will need to set scrollIntoView to the
+ * appropriate function for your layout.  Use scrollInWindow if the container
+ * is not scrollable and scrollInElement if the container is a separate
+ * scrolling region.
+ */
+Navigation = new function() {
+  this.initNavigation = function() {
+    var _this = this;
+
+    $(document).keydown(function(e) {
+      _this.onkeydown(e);
+    }).keyup(function(e) {
+      _this.onkeyup(e);
+    });
+
+    this.navigationActive = true;
+  }
+
+  this.setNavigationActive = function(state) {
+    this.navigationActive = state;
+    this.clearMoveTimeout();
+  }
+
+  this.onkeyup = function(e) {
+    if (!this.navigationActive) return;
+
+    switch(e.keyCode) {
+      case 37: //Event.KEY_LEFT:
+      case 38: //Event.KEY_UP:
+      case 39: //Event.KEY_RIGHT:
+      case 40: //Event.KEY_DOWN:
+        this.clearMoveTimeout();
+        break;
+    }
+  }
+
+  this.onkeydown = function(e) {
+    if (!this.navigationActive) return;
+    switch(e.keyCode) {
+      case 37: //Event.KEY_LEFT:
+        if (this.moveLeft()) e.preventDefault();
+        break;
+      case 38: //Event.KEY_UP:
+        if (e.keyCode == 38 || e.ctrlKey) {
+          if (this.moveUp()) e.preventDefault();
+          this.startMoveTimeout(false);
+        }
+        break;
+      case 39: //Event.KEY_RIGHT:
+        if (this.moveRight()) e.preventDefault();
+        break;
+      case 40: //Event.KEY_DOWN:
+        if (e.keyCode == 40 || e.ctrlKey) {
+          if (this.moveDown()) e.preventDefault();
+          this.startMoveTimeout(true);
+        }
+        break;
+      case 13: //Event.KEY_RETURN:
+        if (this.$current)
+          e.preventDefault();
+          this.select(this.$current);
+        break;
+    }
+    if (e.ctrlKey && e.shiftKey) this.select(this.$current);
+  }
+
+  this.clearMoveTimeout = function() {
+    clearTimeout(this.moveTimeout);
+    this.moveTimeout = null;
+  }
+
+  this.startMoveTimeout = function(isDown) {
+    if (!$.browser.mozilla && !$.browser.opera) return;
+    if (this.moveTimeout) this.clearMoveTimeout();
+    var _this = this;
+
+    var go = function() {
+      if (!_this.moveTimeout) return;
+      _this[isDown ? 'moveDown' : 'moveUp']();
+      _this.moveTimout = setTimeout(go, 100);
+    }
+    this.moveTimeout = setTimeout(go, 200);
+  }
+
+  this.moveRight = function() {
+  }
+
+  this.moveLeft = function() {
+  }
+
+  this.move = function(isDown) {
+  }
+
+  this.moveUp = function() {
+    return this.move(false);
+  }
+
+  this.moveDown = function() {
+    return this.move(true);
+  }
+
+  /*
+   * Scrolls to the given element in the scrollable element view.
+   */
+  this.scrollInElement = function(element, view) {
+    var offset, viewHeight, viewScroll, height;
+    offset = element.offsetTop;
+    height = element.offsetHeight;
+    viewHeight = view.offsetHeight;
+    viewScroll = view.scrollTop;
+
+    if (offset - viewScroll + height > viewHeight) {
+      view.scrollTop = offset - viewHeight + height;
+    }
+    if (offset < viewScroll) {
+      view.scrollTop = offset;
+    }
+  }
+
+  /*
+   * Scrolls to the given element in the window.  The second argument is
+   * ignored
+   */
+  this.scrollInWindow = function(element, ignored) {
+    var offset, viewHeight, viewScroll, height;
+    offset = element.offsetTop;
+    height = element.offsetHeight;
+    viewHeight = window.innerHeight;
+    viewScroll = window.scrollY;
+
+    if (offset - viewScroll + height > viewHeight) {
+      window.scrollTo(window.scrollX, offset - viewHeight + height);
+    }
+    if (offset < viewScroll) {
+      window.scrollTo(window.scrollX, offset);
+    }
+  }
+}
+
diff --git a/jni/ruby/lib/rdoc/generator/template/json_index/js/searcher.js b/jni/ruby/lib/rdoc/generator/template/json_index/js/searcher.js
new file mode 100644
index 0000000..f854b54
--- /dev/null
+++ b/jni/ruby/lib/rdoc/generator/template/json_index/js/searcher.js
@@ -0,0 +1,228 @@
+Searcher = function(data) {
+  this.data = data;
+  this.handlers = [];
+}
+
+Searcher.prototype = new function() {
+  // search is performed in chunks of 1000 for non-blocking user input
+  var CHUNK_SIZE = 1000;
+  // do not try to find more than 100 results
+  var MAX_RESULTS = 100;
+  var huid = 1;
+  var suid = 1;
+  var runs = 0;
+
+  this.find = function(query) {
+    var queries = splitQuery(query);
+    var regexps = buildRegexps(queries);
+    var highlighters = buildHilighters(queries);
+    var state = { from: 0, pass: 0, limit: MAX_RESULTS, n: suid++};
+    var _this = this;
+
+    this.currentSuid = state.n;
+
+    if (!query) return;
+
+    var run = function() {
+      // stop current search thread if new search started
+      if (state.n != _this.currentSuid) return;
+
+      var results =
+        performSearch(_this.data, regexps, queries, highlighters, state);
+      var hasMore = (state.limit > 0 && state.pass < 4);
+
+      triggerResults.call(_this, results, !hasMore);
+      if (hasMore) {
+        setTimeout(run, 2);
+      }
+      runs++;
+    };
+    runs = 0;
+
+    // start search thread
+    run();
+  }
+
+  /*  ----- Events ------  */
+  this.ready = function(fn) {
+    fn.huid = huid;
+    this.handlers.push(fn);
+  }
+
+  /*  ----- Utilities ------  */
+  function splitQuery(query) {
+    return jQuery.grep(query.split(/(\s+|::?|\(\)?)/), function(string) {
+      return string.match(/\S/)
+    });
+  }
+
+  function buildRegexps(queries) {
+    return jQuery.map(queries, function(query) {
+      return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i')
+    });
+  }
+
+  function buildHilighters(queries) {
+    return jQuery.map(queries, function(query) {
+      return jQuery.map(query.split(''), function(l, i) {
+        return '\u0001$' + (i*2+1) + '\u0002$' + (i*2+2);
+      }).join('');
+    });
+  }
+
+  // function longMatchRegexp(index, longIndex, regexps) {
+  //     for (var i = regexps.length - 1; i >= 0; i--){
+  //         if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
+  //     };
+  //     return true;
+  // }
+
+
+  /*  ----- Mathchers ------  */
+
+  /*
+   * This record matches if the index starts with queries[0] and the record
+   * matches all of the regexps
+   */
+  function matchPassBeginning(index, longIndex, queries, regexps) {
+    if (index.indexOf(queries[0]) != 0) return false;
+    for (var i=1, l = regexps.length; i < l; i++) {
+      if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
+        return false;
+    };
+    return true;
+  }
+
+  /*
+   * This record matches if the longIndex starts with queries[0] and the
+   * longIndex matches all of the regexps
+   */
+  function matchPassLongIndex(index, longIndex, queries, regexps) {
+    if (longIndex.indexOf(queries[0]) != 0) return false;
+    for (var i=1, l = regexps.length; i < l; i++) {
+      if (!longIndex.match(regexps[i]))
+        return false;
+    };
+    return true;
+  }
+
+  /*
+   * This record matches if the index contains queries[0] and the record
+   * matches all of the regexps
+   */
+  function matchPassContains(index, longIndex, queries, regexps) {
+    if (index.indexOf(queries[0]) == -1) return false;
+    for (var i=1, l = regexps.length; i < l; i++) {
+      if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
+        return false;
+    };
+    return true;
+  }
+
+  /*
+   * This record matches if regexps[0] matches the index and the record
+   * matches all of the regexps
+   */
+  function matchPassRegexp(index, longIndex, queries, regexps) {
+    if (!index.match(regexps[0])) return false;
+    for (var i=1, l = regexps.length; i < l; i++) {
+      if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
+        return false;
+    };
+    return true;
+  }
+
+
+  /*  ----- Highlighters ------  */
+  function highlightRegexp(info, queries, regexps, highlighters) {
+    var result = createResult(info);
+    for (var i=0, l = regexps.length; i < l; i++) {
+      result.title = result.title.replace(regexps[i], highlighters[i]);
+      result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
+    };
+    return result;
+  }
+
+  function hltSubstring(string, pos, length) {
+    return string.substring(0, pos) + '\u0001' + string.substring(pos, pos + length) + '\u0002' + string.substring(pos + length);
+  }
+
+  function highlightQuery(info, queries, regexps, highlighters) {
+    var result = createResult(info);
+    var pos = 0;
+    var lcTitle = result.title.toLowerCase();
+
+    pos = lcTitle.indexOf(queries[0]);
+    if (pos != -1) {
+      result.title = hltSubstring(result.title, pos, queries[0].length);
+    }
+
+    result.namespace = result.namespace.replace(regexps[0], highlighters[0]);
+    for (var i=1, l = regexps.length; i < l; i++) {
+      result.title = result.title.replace(regexps[i], highlighters[i]);
+      result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
+    };
+    return result;
+  }
+
+  function createResult(info) {
+    var result = {};
+    result.title = info[0];
+    result.namespace = info[1];
+    result.path = info[2];
+    result.params = info[3];
+    result.snippet = info[4];
+    return result;
+  }
+
+  /*  ----- Searching ------  */
+  function performSearch(data, regexps, queries, highlighters, state) {
+    var searchIndex = data.searchIndex;
+    var longSearchIndex = data.longSearchIndex;
+    var info = data.info;
+    var result = [];
+    var i = state.from;
+    var l = searchIndex.length;
+    var togo = CHUNK_SIZE;
+    var matchFunc, hltFunc;
+
+    while (state.pass < 4 && state.limit > 0 && togo > 0) {
+      if (state.pass == 0) {
+        matchFunc = matchPassBeginning;
+        hltFunc = highlightQuery;
+      } else if (state.pass == 1) {
+        matchFunc = matchPassLongIndex;
+        hltFunc = highlightQuery;
+      } else if (state.pass == 2) {
+        matchFunc = matchPassContains;
+        hltFunc = highlightQuery;
+      } else if (state.pass == 3) {
+        matchFunc = matchPassRegexp;
+        hltFunc = highlightRegexp;
+      }
+
+      for (; togo > 0 && i < l && state.limit > 0; i++, togo--) {
+        if (info[i].n == state.n) continue;
+        if (matchFunc(searchIndex[i], longSearchIndex[i], queries, regexps)) {
+          info[i].n = state.n;
+          result.push(hltFunc(info[i], queries, regexps, highlighters));
+          state.limit--;
+        }
+      };
+      if (searchIndex.length <= i) {
+        state.pass++;
+        i = state.from = 0;
+      } else {
+        state.from = i;
+      }
+    }
+    return result;
+  }
+
+  function triggerResults(results, isLast) {
+    jQuery.each(this.handlers, function(i, fn) {
+      fn.call(this, results, isLast)
+    })
+  }
+}
+
-- 
cgit v1.2.3-70-g09d2