From 53d714591fed1f2f029376708ea3bc02d3a170cd Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 23 Oct 2019 16:19:48 +0200 Subject: [PATCH 1/8] Offline search --- config.toml | 2 +- layouts/index.json | 5 ++ layouts/partials/head-css.html | 3 + layouts/partials/head.html | 6 +- layouts/partials/navbar.html | 4 +- layouts/partials/search-input.html | 9 ++- static/css/offline-search.css | 39 +++++++++++ static/js/offline-search.js | 104 +++++++++++++++++++++++++++++ 8 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 layouts/index.json create mode 100644 static/css/offline-search.css create mode 100644 static/js/offline-search.js diff --git a/config.toml b/config.toml index 0b5b7535bc..9a65c6ac6a 100644 --- a/config.toml +++ b/config.toml @@ -10,7 +10,7 @@ time_format_default = "January 2, 2006" # Sections to publish in the main RSS feed. rss_sections = ["blog"] - +offlineSearch = true # For a full list of parameters used in Docsy sites, see: # https://github.com/google/docsy-example/blob/master/config.toml diff --git a/layouts/index.json b/layouts/index.json new file mode 100644 index 0000000000..687b2add38 --- /dev/null +++ b/layouts/index.json @@ -0,0 +1,5 @@ +{{- $.Scratch.Add "index" slice -}} +{{- range where .Site.Pages ".Params.exclude_search" "!=" true -}} +{{- $.Scratch.Add "index" (dict "title" .Title "ref" .Permalink "body" .Plain "excerpt" .Summary) -}} +{{- end -}} +{{- $.Scratch.Get "index" | jsonify -}} \ No newline at end of file diff --git a/layouts/partials/head-css.html b/layouts/partials/head-css.html index 603da38615..eb01fc4eed 100644 --- a/layouts/partials/head-css.html +++ b/layouts/partials/head-css.html @@ -8,4 +8,7 @@ {{ $css := resources.Get $scssMain | toCSS (dict "enableSourceMap" false) | postCSS | minify | fingerprint }} +{{ end }} +{{ if and (.Site.Params.offlineSearch) (not .Site.Params.gcs_engine_id) }} + {{ end }} \ No newline at end of file diff --git a/layouts/partials/head.html b/layouts/partials/head.html index 303eb8b8fa..193ee0d9b7 100644 --- a/layouts/partials/head.html +++ b/layouts/partials/head.html @@ -23,4 +23,8 @@ src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"> -{{ partial "hooks/head-end.html" . }} +{{ if and (.Site.Params.offlineSearch) (not .Site.Params.gcs_engine_id) }} + + +{{end}} +{{ partial "hooks/head-end.html" . }} \ No newline at end of file diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html index 284283b3c4..7343076733 100644 --- a/layouts/partials/navbar.html +++ b/layouts/partials/navbar.html @@ -29,5 +29,7 @@ {{ end }} + {{ if .Site.Params.ui.sidebar_search_disable }} - + {{ end }} + \ No newline at end of file diff --git a/layouts/partials/search-input.html b/layouts/partials/search-input.html index ee82a69884..29e4ae4791 100644 --- a/layouts/partials/search-input.html +++ b/layouts/partials/search-input.html @@ -1,3 +1,8 @@ -{{ with or .Site.Params.gcs_engine_id .Site.Params.algolia_docsearch }} - +{{ if .Site.Params.gcs_engine_id }} + +{{ else if .Site.Params.offlineSearch }} +
+ +
+
{{ end }} \ No newline at end of file diff --git a/static/css/offline-search.css b/static/css/offline-search.css new file mode 100644 index 0000000000..3973f65805 --- /dev/null +++ b/static/css/offline-search.css @@ -0,0 +1,39 @@ +#search-results{ + position: absolute; + width:90%; + font-size: 0.8rem; + top: 40px; + padding-left: 5px; + padding-right: 5px; + left:5%; + margin-top: -2px; + z-index: 1; +} + +#search-nav-container { + position: relative; + width: 100%; +} + +#search-results ul li{ + list-style-type: none; + list-style: none; + padding: 8px; +} + +.td-search-input.form-control:focus{ + box-shadow: 0 0 0 1px #ccc!important; +} + +#search-results ul{ + list-style-type: none; + list-style: none; + padding: 8px; + border-bottom:1px solid #ccc; + border-right:1px solid #ccc; + border-left:1px solid #ccc; + border-top:1px solid #ccc; + border-bottom-left-radius: 1rem; + border-bottom-right-radius: 1rem; + background: #fff; +} \ No newline at end of file diff --git a/static/js/offline-search.js b/static/js/offline-search.js new file mode 100644 index 0000000000..d0a2481313 --- /dev/null +++ b/static/js/offline-search.js @@ -0,0 +1,104 @@ +// Adapted from code by Matt Walters https://www.mattwalters.net/posts/hugo-and-lunr/ + +var idx = null; // Lunr index +var resultDetails = []; // Will hold the data for the search results (titles and summaries) +var $searchInput; // The search box element in the navbar +var $searchResults; // Results shown in the navbar + +$(window).on('load', function() { + // Set up for an Ajax call to request the JSON data file that is created by + // Hugo's build process, with the template we added above + var request = new XMLHttpRequest(); + var query = ''; + + // Get dom objects for the elements we'll be interacting with + $searchResults = document.getElementById('search-results'); + $searchInput = document.getElementById('search-input'); + + request.overrideMimeType("application/json"); + request.open("GET", "/index.json", true); // Request the JSON file created during build + request.onload = function() { + if (request.status >= 200 && request.status < 400) { + // Success response received in requesting the index.json file + var searchDocuments = JSON.parse(request.responseText); + + // Build the index so Lunr can search it. The `ref` field will hold the URL + // to the page/post. title, excerpt, and body will be fields searched. + idx = lunr(function lunrIndex() { + this.ref('ref'); + this.field('title'); + this.field('excerpt'); + this.field('body'); + + // Loop through all the items in the JSON file and add them to the index + // so they can be searched. + searchDocuments.forEach(function(doc) { + this.add(doc); + resultDetails[doc.ref] = { + 'title': doc.title, + 'excerpt': doc.excerpt, + }; + }, this); + }); + } else { + $searchResults.innerHTML = ''; + } + }; + + request.onerror = function() { + $searchResults.innerHTML = ''; + }; + + // Send the request to load the JSON + request.send(); + + // Register handler for the search input field + registerSearchHandler(); +}); + +function registerSearchHandler() { + $searchInput.oninput = function(event) { + var query = event.target.value; + var results = search(query); // Perform the search + + // Render search results + renderSearchResults(results); + + // Remove search results if the user empties the search phrase input field + if ($searchInput.value == '') { + $searchResults.innerHTML = ''; + } + } +} + +function renderSearchResults(results) { + // Create a list of results + if (results.length > 0) { + var ul = document.createElement('ul'); + results.forEach(function(result) { + // Create result item + var li = document.createElement('li'); + li.innerHTML = '' + resultDetails[result.ref].title + '
' + resultDetails[result.ref].excerpt.substring(0,100) + '...'; + ul.appendChild(li); + }); + + // Remove any existing content so results aren't continually added as the user types + while ($searchResults.hasChildNodes()) { + $searchResults.removeChild( + $searchResults.lastChild + ); + } + } else { + $searchResults.innerHTML = ''; + } + // Render the list + $searchResults.appendChild(ul); +} + +function search(query) { + return idx.search(query); +} +// Disables enter key on input fields except textarea +$(document).on("keydown", ":input:not(textarea)", function(event) { + return event.key != "Enter"; +}); \ No newline at end of file From e860e341c8cdef4ade4796fec6900fc7b7649758 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 23 Oct 2019 16:35:39 +0200 Subject: [PATCH 2/8] Algolia fix --- layouts/partials/search-input.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layouts/partials/search-input.html b/layouts/partials/search-input.html index 29e4ae4791..eb7ef5a8fc 100644 --- a/layouts/partials/search-input.html +++ b/layouts/partials/search-input.html @@ -1,4 +1,4 @@ -{{ if .Site.Params.gcs_engine_id }} +{{ if or .Site.Params.gcs_engine_id .Site.Params.algolia_docsearch }} {{ else if .Site.Params.offlineSearch }}
From a93c680b5b8b41b657ca1120960f8c5af04123d7 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 23 Oct 2019 16:37:10 +0200 Subject: [PATCH 3/8] Fix for Algolia --- layouts/partials/search-input.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layouts/partials/search-input.html b/layouts/partials/search-input.html index eb7ef5a8fc..41bcc82f49 100644 --- a/layouts/partials/search-input.html +++ b/layouts/partials/search-input.html @@ -1,4 +1,4 @@ -{{ if or .Site.Params.gcs_engine_id .Site.Params.algolia_docsearch }} +{{ if or ( .Site.Params.gcs_engine_id ) ( .Site.Params.algolia_docsearch ) }} {{ else if .Site.Params.offlineSearch }}
From b6cd4e87e43291f03e723ffff9566831aae5e964 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 23 Oct 2019 16:50:20 +0200 Subject: [PATCH 4/8] Revert fix --- layouts/partials/search-input.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layouts/partials/search-input.html b/layouts/partials/search-input.html index 41bcc82f49..eb7ef5a8fc 100644 --- a/layouts/partials/search-input.html +++ b/layouts/partials/search-input.html @@ -1,4 +1,4 @@ -{{ if or ( .Site.Params.gcs_engine_id ) ( .Site.Params.algolia_docsearch ) }} +{{ if or .Site.Params.gcs_engine_id .Site.Params.algolia_docsearch }} {{ else if .Site.Params.offlineSearch }}
From 473d41d58310a9300dd00b6d2e2f8e62c5e1c753 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Mon, 28 Oct 2019 10:59:02 +0100 Subject: [PATCH 5/8] Fixes after review - single.html --- layouts/partials/navbar.html | 2 -- layouts/{index.json => search-index/single.html} | 2 +- static/js/offline-search.js | 5 ++--- 3 files changed, 3 insertions(+), 6 deletions(-) rename layouts/{index.json => search-index/single.html} (79%) diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html index 7343076733..6954a5a645 100644 --- a/layouts/partials/navbar.html +++ b/layouts/partials/navbar.html @@ -29,7 +29,5 @@ {{ end }}
- {{ if .Site.Params.ui.sidebar_search_disable }} - {{ end }} \ No newline at end of file diff --git a/layouts/index.json b/layouts/search-index/single.html similarity index 79% rename from layouts/index.json rename to layouts/search-index/single.html index 687b2add38..571965cd3f 100644 --- a/layouts/index.json +++ b/layouts/search-index/single.html @@ -1,5 +1,5 @@ {{- $.Scratch.Add "index" slice -}} {{- range where .Site.Pages ".Params.exclude_search" "!=" true -}} -{{- $.Scratch.Add "index" (dict "title" .Title "ref" .Permalink "body" .Plain "excerpt" .Summary) -}} +{{- $.Scratch.Add "index" (dict "title" .Title "ref" .Permalink "body" .Plain "excerpt" (.Summary | truncate 100)) -}} {{- end -}} {{- $.Scratch.Get "index" | jsonify -}} \ No newline at end of file diff --git a/static/js/offline-search.js b/static/js/offline-search.js index d0a2481313..b660ac23aa 100644 --- a/static/js/offline-search.js +++ b/static/js/offline-search.js @@ -19,7 +19,7 @@ $(window).on('load', function() { request.open("GET", "/index.json", true); // Request the JSON file created during build request.onload = function() { if (request.status >= 200 && request.status < 400) { - // Success response received in requesting the index.json file + // Success response received in requesting the search-index file var searchDocuments = JSON.parse(request.responseText); // Build the index so Lunr can search it. The `ref` field will hold the URL @@ -27,7 +27,6 @@ $(window).on('load', function() { idx = lunr(function lunrIndex() { this.ref('ref'); this.field('title'); - this.field('excerpt'); this.field('body'); // Loop through all the items in the JSON file and add them to the index @@ -78,7 +77,7 @@ function renderSearchResults(results) { results.forEach(function(result) { // Create result item var li = document.createElement('li'); - li.innerHTML = '' + resultDetails[result.ref].title + '
' + resultDetails[result.ref].excerpt.substring(0,100) + '...'; + li.innerHTML = '' + resultDetails[result.ref].title + '
' + resultDetails[result.ref].excerpt + '...'; ul.appendChild(li); }); From 08ca6fe68cd29591a7a67f05728ddc8a92432f92 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 13 Nov 2019 12:11:01 +0100 Subject: [PATCH 6/8] Remove offlineSearch param from theme's config.toml --- config.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/config.toml b/config.toml index 9a65c6ac6a..84486c7141 100644 --- a/config.toml +++ b/config.toml @@ -10,7 +10,6 @@ time_format_default = "January 2, 2006" # Sections to publish in the main RSS feed. rss_sections = ["blog"] -offlineSearch = true # For a full list of parameters used in Docsy sites, see: # https://github.com/google/docsy-example/blob/master/config.toml From a8fc87d1f006da3d7e14bc830c108c04c9e2d416 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 13 Nov 2019 12:12:09 +0100 Subject: [PATCH 7/8] Line fix --- config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config.toml b/config.toml index 84486c7141..0b5b7535bc 100644 --- a/config.toml +++ b/config.toml @@ -10,6 +10,7 @@ time_format_default = "January 2, 2006" # Sections to publish in the main RSS feed. rss_sections = ["blog"] + # For a full list of parameters used in Docsy sites, see: # https://github.com/google/docsy-example/blob/master/config.toml From edeaf2884031984acaed69d54379457e97274cd0 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 13 Nov 2019 15:53:03 +0100 Subject: [PATCH 8/8] Add files and settings to userguide --- layouts/partials/navbar.html | 2 +- userguide/config.toml | 7 +++++++ userguide/content/en/search-index.md | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 userguide/content/en/search-index.md diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html index 6954a5a645..284283b3c4 100644 --- a/layouts/partials/navbar.html +++ b/layouts/partials/navbar.html @@ -30,4 +30,4 @@
- \ No newline at end of file + diff --git a/userguide/config.toml b/userguide/config.toml index bea899235c..4b78757446 100644 --- a/userguide/config.toml +++ b/userguide/config.toml @@ -34,6 +34,10 @@ pygmentsStyle = "tango" [permalinks] blog = "/:section/:year/:month/:day/:slug/" +[outputs] + home = [ "HTML", "JSON" ] + page = [ "HTML" ] + ## Configuration for BlackFriday markdown parser: https://github.com/russross/blackfriday [blackfriday] plainIDAnchors = true @@ -102,6 +106,9 @@ gcs_engine_id = "011217106833237091527:la2vtv2emlw" # Enable Algolia DocSearch algolia_docsearch = false +#Enable offline search with Lunr.js +offlineSearch = false + # User interface configuration [params.ui] # Enable to show the side bar menu in its compact state. diff --git a/userguide/content/en/search-index.md b/userguide/content/en/search-index.md new file mode 100644 index 0000000000..16c60f70d1 --- /dev/null +++ b/userguide/content/en/search-index.md @@ -0,0 +1,4 @@ +--- +type: "search-index" +url: "index.json" +--- \ No newline at end of file