diff --git a/assets/js/offline-search.js b/assets/js/offline-search.js index 80db769a..d6bec0bf 100644 --- a/assets/js/offline-search.js +++ b/assets/js/offline-search.js @@ -1,208 +1,212 @@ // Adapted from code by Matt Walters https://www.mattwalters.net/posts/2018-03-28-hugo-and-lunr/ (function ($) { - 'use strict'; - - $(document).ready(function () { - const $searchInput = $('.td-search input'); - const $searchResults = $('.td-offline-search-results'); + "use strict"; + + $(document).ready(function () { + const $searchInput = $(".td-search input"); + const $searchResults = $(".td-offline-search-results"); + // + // Register handler + // + + $searchInput.on("change", (event) => { + render($(event.target)); + + // Hide keyboard on mobile browser + $searchInput.blur(); + }); + + // Prevent reloading page by enter key on sidebar search. + $searchInput.closest("form").on("submit", () => { + return false; + }); + + // + // Lunr + // + + let idx = null; // Lunr index + const resultDetails = new Map(); // Will hold the data for the search results (titles and summaries) + + // Set up for an Ajax call to request the JSON data file that is created by Hugo's build process + $.ajax($searchInput.data("offline-search-index-json-src")).then((data) => { + idx = lunr(function () { + this.ref("ref"); + + // If you added more searchable fields to the search index, list them here. + // Here you can specify searchable fields to the search index - e.g. individual toxonomies for you project + // With "boost" you can add weighting for specific (default weighting without boost: 1) + this.field("title", { boost: 5 }); + this.field("categories", { boost: 3 }); + this.field("tags", { boost: 3 }); + // this.field('projects', { boost: 3 }); // example for an individual toxonomy called projects + this.field("description", { boost: 2 }); + this.field("body"); + + data.forEach((doc) => { + this.add(doc); + + resultDetails.set(doc.ref, { + title: doc.title, + excerpt: doc.excerpt, + }); + }); + }); + + $searchInput.trigger("change"); + }); + + const render = ($targetSearchInput) => { // - // Register handler + // Dispose existing popover // - - $searchInput.on('change', (event) => { - render($(event.target)); - - // Hide keyboard on mobile browser - $searchInput.blur(); - }); - - // Prevent reloading page by enter key on sidebar search. - $searchInput.closest('form').on('submit', () => { - return false; - }); - + + { + let popover = bootstrap.Popover.getInstance($targetSearchInput[0]); + if (popover !== null) { + popover.dispose(); + } + } + // - // Lunr + // Search // - - let idx = null; // Lunr index - const resultDetails = new Map(); // Will hold the data for the search results (titles and summaries) - - // Set up for an Ajax call to request the JSON data file that is created by Hugo's build process - $.ajax($searchInput.data('offline-search-index-json-src')).then((data) => { - idx = lunr(function () { - this.ref('ref'); - - // If you added more searchable fields to the search index, list them here. - // Here you can specify searchable fields to the search index - e.g. individual toxonomies for you project - // With "boost" you can add weighting for specific (default weighting without boost: 1) - this.field('title', { boost: 5 }); - this.field('categories', { boost: 3 }); - this.field('tags', { boost: 3 }); - // this.field('projects', { boost: 3 }); // example for an individual toxonomy called projects - this.field('description', { boost: 2 }); - this.field('body'); - - data.forEach((doc) => { - this.add(doc); - - resultDetails.set(doc.ref, { - title: doc.title, - excerpt: doc.excerpt, + + if (idx === null) { + return; + } + + const searchQuery = $targetSearchInput.val(); + if (searchQuery === "") { + return; + } + + const results = idx + .query((q) => { + const tokens = lunr.tokenizer(searchQuery.toLowerCase()); + tokens.forEach((token) => { + const queryString = token.toString(); + q.term(queryString, { + boost: 100, }); - }); - }); - - $searchInput.trigger('change'); - }); - - const render = ($targetSearchInput) => { - // - // Dispose existing popover - // - - { - let popover = bootstrap.Popover.getInstance($targetSearchInput[0]); - if (popover !== null) { - popover.dispose(); - } - } - - // - // Search - // - - if (idx === null) { - return; - } - - const searchQuery = $targetSearchInput.val(); - if (searchQuery === '') { - return; - } - - const results = idx - .query((q) => { - const tokens = lunr.tokenizer(searchQuery.toLowerCase()); - tokens.forEach((token) => { - const queryString = token.toString(); - q.term(queryString, { - boost: 100, - }); - q.term(queryString, { - wildcard: - lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING, - boost: 10, - }); - q.term(queryString, { - editDistance: 2, - }); + q.term(queryString, { + wildcard: + lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING, + boost: 10, + }); + q.term(queryString, { + editDistance: 2, }); + }); + }) + .slice(0, $targetSearchInput.data("offline-search-max-results")); + + // + // Make result html + // + + const $html = $("
"); + + $html.append( + $("
") + .css({ + display: "flex", + justifyContent: "space-between", + marginBottom: "1em", }) - .slice(0, $targetSearchInput.data('offline-search-max-results')); - - // - // Make result html - // - - const $html = $('
'); - - $html.append( - $('
') - .css({ - display: 'flex', - justifyContent: 'space-between', - marginBottom: '1em', - }) - .append( - $('').text('Search results').css({ fontWeight: 'bold' }) - ) - .append( - $('').addClass('td-offline-search-results__close-button') - ) + .append( + $("").text("Search results").css({ fontWeight: "bold" }) + ) + .append( + $("").addClass("td-offline-search-results__close-button") + ) + ); + + const $searchResultBody = $("
").css({ + maxHeight: `calc(100vh - ${ + $targetSearchInput.offset().top - $(window).scrollTop() + 180 + }px)`, + overflowY: "auto", + }); + $html.append($searchResultBody); + + if (results.length === 0) { + $searchResultBody.append( + $("

").text(`No results found for query "${searchQuery}"`) ); - - const $searchResultBody = $('

').css({ - maxHeight: `calc(100vh - ${ - $targetSearchInput.offset().top - $(window).scrollTop() + 180 - }px)`, - overflowY: 'auto', - }); - $html.append($searchResultBody); - - if (results.length === 0) { - $searchResultBody.append( - $('

').text(`No results found for query "${searchQuery}"`) + } else { + results.forEach((r) => { + const doc = resultDetails.get(r.ref); + const href = + $searchInput.data("offline-search-base-href") + + r.ref.replace(/^\//, ""); + + const $entry = $("

").addClass("mt-4"); + + $entry.append( + $("").addClass("d-block text-muted").text(r.ref) ); - } else { - results.forEach((r) => { - const doc = resultDetails.get(r.ref); - const href = - $searchInput.data('offline-search-base-href') + - r.ref.replace(/^\//, ''); - - const $entry = $('
').addClass('mt-4'); - - $entry.append( - $('').addClass('d-block text-muted').text(r.ref) - ); - - $entry.append( - $('') - .addClass('d-block') - .css({ - fontSize: '1.2rem', - }) - .attr('href', href) - .text(doc.title) - ); - - $entry.append($('

').text(doc.excerpt)); - - $searchResultBody.append($entry); - }); - } - - $targetSearchInput.one('shown.bs.popover', () => { - $('.td-offline-search-results__close-button').on('click', () => { - $targetSearchInput.val(''); - $targetSearchInput.trigger('change'); - }); - }); - - const popover = new bootstrap.Popover($targetSearchInput, { - content: $html[0], - html: true, - customClass: 'td-offline-search-results', - placement: 'bottom', + + $entry.append( + $("") + .addClass("d-block") + .css({ + fontSize: "1.2rem", + }) + .attr("href", href) + .text(doc.title) + ); + + $entry.append($("

").text(doc.excerpt)); + + $searchResultBody.append($entry); }); - popover.show(); - }; + } - //Bring focus to search bar - $(document).on('keydown', function (event) { - if (event.key === '/') { - $searchInput.focus(); - } + $targetSearchInput.one("shown.bs.popover", () => { + $(".td-offline-search-results__close-button").on("click", () => { + $targetSearchInput.val(""); + $targetSearchInput.trigger("change"); + }); }); - $(document).on('click', function (event) { - if (!$(event.target).closest('.td-search').length) { - // Clicked outside the search panel - $searchInput.val(''); - $searchInput.trigger('change'); - } + const popover = new bootstrap.Popover($targetSearchInput, { + content: $html[0], + html: true, + customClass: "td-offline-search-results", + placement: "bottom", }); + popover.show(); + }; - //close the search panel when the ESC key is pressed - $(document).on('keydown', function (event) { - if (event.key === 'Escape') { - $searchInput.val(''); - $searchInput.trigger('change'); - } - }); + //Bring focus to search bar + $(document).on("keydown", function (event) { + if (event.key === "/") { + $searchInput.focus(); + } + }); + $(document).on("click", function (event) { + if (!$(event.target).closest(".td-search").length) { + // Clicked outside the search panel + $searchInput.val(""); + $searchInput.trigger("change"); + } + }); + + //close the search panel when the ESC key is pressed + $(document).on("keydown", function (event) { + if (event.key === "Escape") { + $searchInput.val(""); + $searchInput.trigger("change"); + } + }); + $searchInput.on("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + render($searchInput); + } }); - })(jQuery); - \ No newline at end of file + }); +})(jQuery); diff --git a/layouts/partials/hotkey.html b/layouts/partials/hotkey.html index 6561782d..c1e1ca9d 100644 --- a/layouts/partials/hotkey.html +++ b/layouts/partials/hotkey.html @@ -13,11 +13,16 @@ href="https://unpkg.com/keyboard-css@1.2.4/dist/css/main.min.css" />