[gi-docgen: 2/10] feat: implement fzy searching




commit 6b86c71343278236fb8b2aa7e6ab9b2ad0388554
Author: Rom Grk <romgrk cc gmail com>
Date:   Thu Apr 1 01:45:34 2021 -0400

    feat: implement fzy searching

 gidocgen/templates/basic/base.html |   9 +-
 gidocgen/templates/basic/main.js   | 541 ++++++-------------------------------
 gidocgen/templates/basic/search.js | 281 ++++++++++++++++++-
 3 files changed, 359 insertions(+), 472 deletions(-)
---
diff --git a/gidocgen/templates/basic/base.html b/gidocgen/templates/basic/base.html
index ca3d3cb..d0bd361 100644
--- a/gidocgen/templates/basic/base.html
+++ b/gidocgen/templates/basic/base.html
@@ -42,6 +42,10 @@ SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
   {% if CONFIG.urlmap_file %}
   <script src="{{ CONFIG.urlmap_file }}"></script>
   {% endif %}
+  {% if CONFIG.search_index %}
+  <script src="fzy.js"></script>
+  <script src="search.js"></script>
+  {% endif %}
   <script src="stemmer.js"></script>
   <script src="main.js"></script>
   <!--[if IE]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js";></script><![endif]-->
@@ -82,11 +86,6 @@ SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
 
     <section id="search" class="content hidden"></section>
 
-    {% if CONFIG.search_index %}
-    <script src="fzy.js"></script>
-    <script src="search.js"></script>
-    {% endif %}
-
     <footer>
     {% block footer %}{% endblock %}
     </footer>
diff --git a/gidocgen/templates/basic/main.js b/gidocgen/templates/basic/main.js
index 46f03fb..990bb8c 100644
--- a/gidocgen/templates/basic/main.js
+++ b/gidocgen/templates/basic/main.js
@@ -1,450 +1,19 @@
 // SPDX-FileCopyrightText: 2021 GNOME Foundation
 //
 // SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
+"use strict;"
 
-// eslint-disable-next-line no-unused-vars
-function hasClass(elem, className) {
-    return elem && elem.classList && elem.classList.contains(className);
-}
-
-// eslint-disable-next-line no-unused-vars
-function addClass(elem, className) {
-    if (!elem || !elem.classList) {
-        return;
-    }
-    elem.classList.add(className);
-}
-
-// eslint-disable-next-line no-unused-vars
-function removeClass(elem, className) {
-    if (!elem || !elem.classList) {
-        return;
-    }
-    elem.classList.remove(className);
-}
-
-function insertAfter(newNode, referenceNode) {
-    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
-}
-
-function onEach(arr, func, reversed) {
-    if (arr && arr.length > 0 && func) {
-        var length = arr.length;
-        var i;
-        if (reversed !== true) {
-            for (i = 0; i < length; ++i) {
-                if (func(arr[i]) === true) {
-                    return true;
-                }
-            }
-        } else {
-            for (i = length - 1; i >= 0; --i) {
-                if (func(arr[i]) === true) {
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
-function onEachLazy(lazyArray, func, reversed) {
-    return onEach(
-        Array.prototype.slice.call(lazyArray),
-        func,
-        reversed);
-}
-
-// eslint-disable-next-line no-unused-vars
-function hasOwnProperty(obj, property) {
-    return Object.prototype.hasOwnProperty.call(obj, property);
-}
-
-function getSearchElement() {
-    return document.getElementById("search");
-}
-
-function getSearchInput() {
-    return document.getElementsByClassName("search-input")[0];
-}
-
-function getQueryStringParams() {
-    var params = {};
-    window.location.search.substring(1).split('&').
-        map(function(s) {
-            var pair = s.split('=');
-            params[decodeURIComponent(pair[0])] =
-                typeof pair[1] === 'undefined' ? null : decodeURIComponent(pair[1]);
-        });
-    return params;
-}
-
-function getQuery(query) {
-    var query_lower = query.toLowerCase(),
-        query_split = query_lower.split('+');
-
-    query_split = query_split.filter(function(chunk) { return chunk !== ""; });
-
-    return {
-        raw: query,
-        terms: query_split,
-        user: query_split.join(' '),
-    }
-}
-
-window.initSearch = function(searchIndex) {
-        var search_input = getSearchInput();
-        var params = getQueryStringParams();
-
-        var searchSymbols = searchIndex["symbols"];
-        var searchTerms = searchIndex["terms"];
-
-        if (search_input.value === "") {
-            search_input.value === params.q || "";
-        }
-
-        function runQuery(query) {
-            function getDocumentFromId(id) {
-                if (typeof id === "number") {
-                    return searchSymbols[id];
-                }
-                return null;
-            }
-
-            function getLinkForDocument(doc) {
-                switch (doc.type) {
-                    case "alias":
-                        return "alias." + doc.name + ".html";
-                    case "bitfield":
-                        return "flags." + doc.name + ".html";
-                    case "callback":
-                        return "callback." + doc.name + ".html";
-                    case "class":
-                        return "class." + doc.name + ".html";
-                    case "class_method":
-                        return "class_method." + doc.type_name + "." + doc.name + ".html";
-                    case "constant":
-                        return "const." + doc.name + ".html";
-                    case "ctor":
-                        return "ctor." + doc.type_name + "." + doc.name + ".html";
-                    case "domain":
-                        return "error." + doc.name + ".html";
-                    case "enum":
-                        return "enum." + doc.name + ".html";
-                    case "function":
-                        return "func." + doc.name + ".html";
-                    case "function_macro":
-                        return "func." + doc.name + ".html";
-                    case "interface":
-                        return "iface." + doc.name + ".html";
-                    case "method":
-                        return "method." + doc.type_name + "." + doc.name + ".html";
-                    case "property":
-                        return "property." + doc.type_name + "." + doc.name + ".html";
-                    case "record":
-                        return "struct." + doc.name + ".html";
-                    case "signal":
-                        return "signal." + doc.type_name + "." + doc.name + ".html";
-                    case "type_func":
-                        return "type_func." + doc.type_name + "." + doc.name + ".html";
-                    case "union":
-                        return "union." + doc.name + ".html";
-                    case "vfunc":
-                        return "vfunc." + doc.type_name + "." + doc.name + ".html";
-                }
-
-                return null;
-            }
-
-            function getTextForDocument(doc) {
-                switch (doc.type) {
-                    case "alias":
-                    case "bitfield":
-                    case "class":
-                    case "domain":
-                    case "enum":
-                    case "interface":
-                    case "record":
-                    case "union":
-                        return doc.ctype;
-                    case "class_method":
-                    case "constant":
-                    case "ctor":
-                    case "function":
-                    case "function_macro":
-                    case "method":
-                    case "type_func":
-                        return doc.ident;
-                    case "property":
-                        return doc.type_name + ":" + doc.name;
-                    case "signal":
-                        return doc.type_name + "::" + doc.name;
-                    case "vfunc":
-                        return doc.type_name + "." + doc.name;
-                    case "callback":
-                        return doc.name;
-                }
-
-                return null;
-            }
 
-            var queryTypes = [
-                "alias",
-                "bitfield",
-                "callback",
-                "class",
-                "constant",
-                "ctor",
-                "domain",
-                "enum",
-                "function_macro",
-                "function",
-                "interface",
-                "method",
-                "property",
-                "record",
-                "signal",
-                "type_func",
-                "union",
-                "vfunc",
-            ];
+const urlMap = new Map(typeof baseURLs !== 'undefined' ? baseURLs : []);
 
-            var query_re = new RegExp("^(" + queryTypes.join('|') + ")\\s*:\\s*", 'i');
+window.addEventListener("hashchange", onDidHashChange);
+window.addEventListener("load", onDidLoad, false);
 
-            function matchType(term) {
-                var matches = term.match(query_re);
-                if (matches) {
-                    var type = matches[1];
-                    var query = term.substring(matches[0].length);
-                    return {
-                        type: type,
-                        term: query,
-                    }
-                }
-
-                return {
-                    type: null,
-                    term: term,
-                }
-            }
-
-            const PREDICATE_ALL = 0;
-            const PREDICATE_ANY = 1;
-
-            function uniqueResults(arrays) {
-                var unique = [];
-
-                for (var it = 0; it < arrays.length; it++) {
-                    arrays[it].forEach(function(e) {
-                        if (unique.findIndex((doc) => doc.type === e.type && doc.name === e.name) == -1) {
-                            unique.push(e);
-                        }
-                    });
-                }
-
-                return unique;
-            }
-
-            function mergeArrays(arrays, predicate) {
-                if (predicate === PREDICATE_ANY) {
-                    return uniqueResults(arrays);
-                }
-
-                if (predicate === PREDICATE_ALL) {
-                    // find the smallest array
-                    var smallest = 0; 
-                    for (var it = 1; it < arrays.length; it++) {
-                        if (arrays[it].length < smallest) {
-                            smallest = it;
-                        }
-                    }
-
-                    if (smallest > arrays.length) {
-                        return [];
-                    }
-
-                    var results = [];
-
-                    // check for elements present in all arrays
-                    arrays[smallest].forEach(function(e) {
-                        var found = [];
-                        for (var it = 0; it < arrays.length; it++) {
-                            if (it == smallest) {
-                                found.push(smallest);
-                            } else {
-                                if (arrays[it].findIndex((doc) => doc.type === e.type && doc.name === 
e.name) != -1) {
-                                    found.push(it);
-                                }
-                            }
-                        }
-                        if (found.length === arrays.length) {
-                            results.push(e);
-                        }
-                    });
-
-                    return results;
-                }
-
-                return [];
-            }
-
-            var termIndex = 0;
-            var results = [];
-
-            query.terms.forEach(function(term) {
-                var q = matchType(term);
-                var stemmed_term = stemmer(q.term);
-                if (searchTerms.hasOwnProperty(stemmed_term)) {
-                    var docs = searchTerms[stemmed_term];
-
-                    results[termIndex] = [];
-
-                    docs.forEach(function(id) {
-                        var doc = getDocumentFromId(id);
-
-                        if (doc !== null) {
-                            var res = {
-                                name: doc.name,
-                                type: doc.type,
-                                text: getTextForDocument(doc),
-                                href: getLinkForDocument(doc),
-                                summary: doc.summary,
-                            };
-
-                            if (q.type === null || (q.type === doc.type)) {
-                                results[termIndex].push(res);
-                            }
-                        }
-                    });
-
-                    termIndex += 1;
-                }
-            });
-
-            if (termIndex == 0) {
-                return {
-                    all: [],
-                    any: [],
-                }
-            } else if (termIndex == 1) {
-                return {
-                    all: results[0],
-                    any: results[0],
-                }
-            } else {
-                return {
-                    all: mergeArrays(results, PREDICATE_ALL),
-                    any: mergeArrays(results, PREDICATE_ANY),
-                }
-            }
-        }
-
-        function showSearchResults(search) {
-            if (search === null || typeof search === 'undefined') {
-                search = getSearchElement();
-            }
-
-            addClass(main, "hidden");
-            removeClass(search, "hidden");
-        }
-
-        function hideSearchResults(search) {
-            if (search === null || typeof search === 'undefined') {
-                search = getSearchElement();
-            }
-
-            addClass(search, "hidden");
-            removeClass(search, "hidden");
-        }
-
-        function addResults(results) {
-            var output = "";
-
-            if (results.length > 0) {
-                output += "<table class=\"results\">" +
-                          "<tr><th>Name</th><th>Description</th></tr>";
-
-                results.forEach(function(item) {
-                    output += "<tr>" +
-                              "<td class=\"result " + item.type + "\">" +
-                              "<a href=\"" + item.href + "\"><code>" + item.text + "</code></a>" +
-                              "</td>" +
-                              "<td>" + item.summary + "</td>" +
-                              "</tr>";
-                });
-
-                output += "</table>";
-            } else {
-                output = "No results found.";
-            }
-
-            return output;
-        }
-
-        function showResults(query, results) {
-            var search = getSearchElement();
-
-            var res = [];
-            var len = 0;
-            if (results.all.length > 0) {
-                res = results.all;
-                len = results.all.length;
-            } else {
-                res = results.any;
-                len = results.any.length;
-            }
-
-            var output = "<h1>Results for &quot;" + query.user + "&quot; (" + len + ")</h1>" +
-                         "<div id=\"search-results\">" +
-                         addResults(res) +
-                         "</div>";
-
-            search.innerHTML = output;
-            showSearchResults(search);
-        }
-
-        function search() {
-            var query = getQuery(getQueryStringParams().q);
-
-            if (search_input.value === "" && query) {
-                if (query.terms.length === 0) {
-                    return;
-                }
-
-                search_input.value = query.user;
-            }
-
-            window.title = "Results for: " + query.user;
-
-            showResults(query, runQuery(query));
-        }
-
-        window.onpageshow = function() {
-            var query = getQuery(getQueryStringParams().q);
-            if (search_input.value === "" && query) {
-                search_input.value = query.user;
-            }
-            search();
-        };
-
-        if (getQueryStringParams().q) {
-            search();
-        }
-};
-
-window.addEventListener("load", function() {
-    "use strict;"
-
-    var main = document.getElementById("main");
-    var btnToTop = document.getElementById("btn-to-top");
-
-    var searchInput = getSearchInput();
+function onDidLoad() {
+    const btnToTop = document.getElementById("btn-to-top");
 
     function labelForToggleButton(isCollapsed) {
-        if (isCollapsed) {
-            return "+";
-        }
-        return "\u2212";
+        return isCollapsed ? "+" : "\u2212";
     }
 
     function createToggle(isCollapsed) {
@@ -479,10 +48,10 @@ window.addEventListener("load", function() {
     }
 
     onEachLazy(document.getElementsByClassName("toggle-wrapper"), function(e) {
-        let sectionHeader = e.querySelector(".section-header");
-        let fragmentMatches = sectionHeader !== null && location.hash === "#" + 
sectionHeader.getAttribute('id');
-        collapsedByDefault = hasClass(e, "default-hide") && !fragmentMatches;
-        var toggle = createToggle(collapsedByDefault);
+        const sectionHeader = e.querySelector(".section-header");
+        const fragmentMatches = sectionHeader !== null && location.hash === "#" + 
sectionHeader.getAttribute('id');
+        const collapsedByDefault = hasClass(e, "default-hide") && !fragmentMatches;
+        const toggle = createToggle(collapsedByDefault);
         toggle.onclick = toggleClicked;
         e.insertBefore(toggle, e.firstChild);
         if (collapsedByDefault) {
@@ -510,26 +79,18 @@ window.addEventListener("load", function() {
     }
 
     function resolveNamespaceLink(namespace) {
-        try {
-            let urlMap = new Map(baseURLs);
-            if (urlMap.has(namespace)) {
-                return urlMap.get(namespace);
-            }
-            return '';
-        } catch (e) {
-            return '';
-        }
+        return urlMap.get(namespace);
     }
 
-    window.onscroll = toggleScrollButton;
-    btnToTop.onclick = scrollBackTop;
+    window.addEventListener('scroll', toggleScrollButton);
+    btnToTop.addEventListener('click', scrollBackTop);
 
     onEachLazy(document.getElementsByClassName("external"), function(e) {
         if (e.tagName == "A" && e.dataset.hasOwnProperty('namespace')) {
             var data_namespace = e.dataset.namespace
             var data_link = e.dataset.link
             var base_url = resolveNamespaceLink(data_namespace)
-            if (base_url !== '') {
+            if (base_url !== undefined) {
                 e.href = base_url + data_link;
             } else {
                 e.title = "No reference to the " + data_namespace + " namespace";
@@ -553,14 +114,12 @@ window.addEventListener("load", function() {
         });
     }
 
-    if (window.buildIndex) {
-        window.buildIndex('index.json');
+    if (window.onInitSearch) {
+        window.onInitSearch()
     }
-}, false);
-
-window.addEventListener("hashchange", function() {
-    "use strict;"
+}
 
+function onDidHashChange() {
     // When URL fragment changes to ID of a collapsible section,
     // expand it when it is collapsed.
     // This is useful for clicking section links in the sidebar on the index page.
@@ -574,4 +133,64 @@ window.addEventListener("hashchange", function() {
             }
         }
     }
-});
+}
+
+// Helpers
+
+// eslint-disable-next-line no-unused-vars
+function hasClass(elem, className) {
+    return elem && elem.classList && elem.classList.contains(className);
+}
+
+// eslint-disable-next-line no-unused-vars
+function addClass(elem, className) {
+    if (!elem || !elem.classList) {
+        return;
+    }
+    elem.classList.add(className);
+}
+
+// eslint-disable-next-line no-unused-vars
+function removeClass(elem, className) {
+    if (!elem || !elem.classList) {
+        return;
+    }
+    elem.classList.remove(className);
+}
+
+function insertAfter(newNode, referenceNode) {
+    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
+}
+
+function onEach(arr, func, reversed) {
+    if (arr && arr.length > 0 && func) {
+        var length = arr.length;
+        var i;
+        if (reversed !== true) {
+            for (i = 0; i < length; ++i) {
+                if (func(arr[i]) === true) {
+                    return true;
+                }
+            }
+        } else {
+            for (i = length - 1; i >= 0; --i) {
+                if (func(arr[i]) === true) {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+function onEachLazy(lazyArray, func, reversed) {
+    return onEach(
+        Array.prototype.slice.call(lazyArray),
+        func,
+        reversed);
+}
+
+// eslint-disable-next-line no-unused-vars
+function hasOwnProperty(obj, property) {
+    return Object.prototype.hasOwnProperty.call(obj, property);
+}
diff --git a/gidocgen/templates/basic/search.js b/gidocgen/templates/basic/search.js
index 7ff137d..c26efa5 100644
--- a/gidocgen/templates/basic/search.js
+++ b/gidocgen/templates/basic/search.js
@@ -2,18 +2,287 @@
 //
 // SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
 
-window.buildIndex = function (rawSearchIndex) {
-    let request = new XMLHttpRequest();
-    request.open('GET', rawSearchIndex, true);
+(function() {
+
+const QUERY_TYPES = [
+    "alias",
+    "bitfield",
+    "callback",
+    "class",
+    "constant",
+    "ctor",
+    "domain",
+    "enum",
+    "function_macro",
+    "function",
+    "interface",
+    "method",
+    "property",
+    "record",
+    "signal",
+    "type_func",
+    "union",
+    "vfunc",
+];
+const QUERY_PATTERN = new RegExp("^(" + QUERY_TYPES.join('|') + ")\\s*:\\s*", 'i');
+
+
+const fzy = window.fzy;
+const searchParams = getSearchParams();
+
+let searchIndex = undefined;
+
+// Exports
+window.onInitSearch = onInitSearch;
+
+/* Event handlers */
+
+function onInitSearch() {
+    fetchJSON('index.json', onDidLoadSearchIndex);
+}
+
+function onDidLoadSearchIndex(data) {
+    const searchInput = getSearchInput();
+    const searchIndex = new SearchIndex(data)
+
+    if (searchInput.value === "") {
+        searchInput.value === searchParams.q || "";
+    }
+
+    function runQuery(query) {
+        const q = matchQuery(query);
+        const docs = searchIndex.searchDocs(q.term, q.type);
+
+        const results = docs.map(function(doc) {
+            return {
+                name: doc.name,
+                type: doc.type,
+                text: getTextForDocument(doc, searchIndex.meta),
+                href: getLinkForDocument(doc),
+                summary: doc.summary,
+            };
+        });
+
+        return results;
+    }
+
+    function search() {
+        const query = searchParams.q;
+        if (searchInput.value === "" && query) {
+            searchInput.value = query;
+        }
+        window.title = "Results for: " + query.user;
+        showResults(query, runQuery(query));
+    }
+
+    window.onpageshow = function() {
+        var query = getQuery(searchParams.q);
+        if (searchInput.value === "" && query) {
+            searchInput.value = query.user;
+        }
+        search();
+    };
+
+    if (searchParams.q) {
+        search();
+    }
+};
+
+
+/* Rendering */
+
+function showSearchResults(search) {
+    if (search === null || typeof search === 'undefined') {
+        search = getSearchElement();
+    }
+
+    addClass(main, "hidden");
+    removeClass(search, "hidden");
+
+}
+
+function hideSearchResults(search) {
+    if (search === null || typeof search === 'undefined') {
+        search = getSearchElement();
+    }
+
+    addClass(search, "hidden");
+    removeClass(search, "hidden");
+}
+
+function addResults(results) {
+    var output = "";
+
+    if (results.length > 0) {
+        output += "<table class=\"results\">" +
+                    "<tr><th>Name</th><th>Description</th></tr>";
+
+        results.forEach(function(item) {
+            output += "<tr>" +
+                        "<td class=\"result " + item.type + "\">" +
+                        "<a href=\"" + item.href + "\"><code>" + item.text + "</code></a>" +
+                        "</td>" +
+                        "<td>" + item.summary + "</td>" +
+                        "</tr>";
+        });
+
+        output += "</table>";
+    } else {
+        output = "No results found.";
+    }
+
+    return output;
+}
+
+function showResults(query, results) {
+    const search = getSearchElement();
+    const output =
+        "<h1>Results for &quot;" + query + "&quot; (" + results.length + ")</h1>" +
+        "<div id=\"search-results\">" +
+            addResults(results) +
+        "</div>";
+
+    search.innerHTML = output;
+    showSearchResults(search);
+}
+
+
+/* Search data instance */
+
+function SearchIndex(searchIndex) {
+    this.symbols = searchIndex.symbols;
+    this.meta = searchIndex.meta;
+}
+SearchIndex.prototype.searchDocs = function searchDocs(term, type) {
+    const filteredSymbols = type ? this.symbols.filter(s => s.type === type) : this.symbols;
+    const results = fzy.filter(term, filteredSymbols, doc => getTextForDocument(doc, this.meta))
+    return results.map(i => i.item)
+}
+SearchIndex.prototype.getDocumentFromId = function getDocumentFromId(id) {
+    if (typeof id === "number") {
+        return this.searchIndex.symbols[id];
+    }
+    return null;
+}
+
+
+/* Search metadata selectors */
+
+function getLinkForDocument(doc) {
+    switch (doc.type) {
+        case "alias": return "alias." + doc.name + ".html";
+        case "bitfield": return "flags." + doc.name + ".html";
+        case "callback": return "callback." + doc.name + ".html";
+        case "class": return "class." + doc.name + ".html";
+        case "class_method": return "class_method." + doc.type_name + "." + doc.name + ".html";
+        case "constant": return "const." + doc.name + ".html";
+        case "ctor": return "ctor." + doc.type_name + "." + doc.name + ".html";
+        case "domain": return "error." + doc.name + ".html";
+        case "enum": return "enum." + doc.name + ".html";
+        case "function": return "func." + doc.name + ".html";
+        case "function_macro": return "func." + doc.name + ".html";
+        case "interface": return "iface." + doc.name + ".html";
+        case "method": return "method." + doc.type_name + "." + doc.name + ".html";
+        case "property": return "property." + doc.type_name + "." + doc.name + ".html";
+        case "record": return "struct." + doc.name + ".html";
+        case "signal": return "signal." + doc.type_name + "." + doc.name + ".html";
+        case "type_func": return "type_func." + doc.type_name + "." + doc.name + ".html";
+        case "union": return "union." + doc.name + ".html";
+        case "vfunc": return "vfunc." + doc.type_name + "." + doc.name + ".html";
+    }
+    return null;
+}
+
+function getTextForDocument(doc, meta) {
+    switch (doc.type) {
+        case "alias":
+        case "bitfield":
+        case "class":
+        case "domain":
+        case "enum":
+        case "interface":
+        case "record":
+        case "union":
+            return doc.ctype;
+        case "class_method":
+        case "constant":
+        case "ctor":
+        case "function":
+        case "function_macro":
+        case "method":
+        case "type_func":
+            return doc.ident;
+
+        // NOTE: meta.ns added for more consistent results, otherwise
+        // searching for "Button" would return all signals, properties
+        // and vfuncs (eg "Button.clicked") before the actual object 
+        // (eg "GtkButton") because "Button" matches higher with starting
+        // sequences.
+        case "property":
+            return meta.ns + doc.type_name + ":" + doc.name;
+        case "signal":
+            return meta.ns + doc.type_name + "::" + doc.name;
+        case "vfunc":
+            return meta.ns + doc.type_name + "." + doc.name;
+
+        case "callback":
+            return doc.name;
+    }
+
+    return null;
+}
+
+
+// Helpers
+
+function fetchJSON(url, callback) {
+    const request = new XMLHttpRequest();
+    request.open('GET', url, true);
     request.onreadystatechange = function() {
         if (request.readyState === XMLHttpRequest.DONE) {
-            var status = request.status;
+            const status = request.status;
 
             if (status === 0 || (status >= 200 && status < 400)) {
-                var searchIndex = JSON.parse(request.responseText);
-                window.initSearch(searchIndex);
+                callback(JSON.parse(request.responseText));
             }
         }
     }
     request.send(null);
 }
+
+function getSearchElement() {
+    return document.getElementById("search");
+}
+
+function getSearchInput() {
+    return document.getElementsByClassName("search-input")[0];
+}
+
+function getSearchParams() {
+    const params = {};
+    window.location.search.substring(1).split('&')
+        .map(function(s) {
+            const pair = s.split('=');
+            params[decodeURIComponent(pair[0])] =
+                typeof pair[1] === 'undefined' ? null : decodeURIComponent(pair[1].replace(/\+/g, '%20'));
+        });
+    return params;
+}
+
+function matchQuery(input) {
+    let type = null
+    let term = input
+
+    const matches = term.match(QUERY_PATTERN);
+    if (matches) {
+        type = matches[1];
+        term = term.substring(matches[0].length);
+    }
+
+    // Remove all spaces, fzy will handle things gracefully.
+    term = term.replace(/\s+/g, '')
+
+    return { type: type, term: term }
+}
+
+})()


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]