diff --git a/README.md b/README.md index 824225a..ed74275 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,12 @@ As said, Disqus is supportted, but besides setting the username in `config.toml` ### Animations All JavaScript animations can be set at `static/js/zola386.js`. Basically you can disable all animations, use one or two scans, and change the scan speed. Personally, I prefer only one scan with a speed factor of 5. - ### Language - Under the `label_` variables, you can set names to better localize your site. Note that you can change the language of a single page, by using `page.extra.lang`, which causes `` to change only on that page. A theme to provide information for its owner and SEO-friendly. +### Search +Search was implemented according to the [official documentation](https://www.getzola.org/documentation/content/search/). It uses JavaScript to search on an indexed version of the site based on `search_index.LANG.js`, `elasticlunr.min.js`, and `search.js` --the first two are generated after each build. If you're running your site in other default language other than English, you **must** change the `search_index.LANG.js` line in `index.html`, setting up `LANG` accordingly. + ### Other files The `content\_index.md` file must be properly configured to provide better experience. Check out this file for more information. diff --git a/config.toml b/config.toml index b28fb14..67ededa 100644 --- a/config.toml +++ b/config.toml @@ -1,4 +1,5 @@ base_url = "https://zola386.netlify.app" +# theme = "zola.386" default_language = "en" title = "ZOLA.386" @@ -33,7 +34,7 @@ zola386_menu = [ # disqus = "disqus-user" # social media -image = "" +# image = "" twitter_user = "lopesoj" linkedin_user = "jlopesjr" github_user = "lopes" @@ -49,4 +50,3 @@ label_date = "Date" label_taxonomy = "Taxonomy" label_reading = "Reading time" label_read_more = "Read more" -label_welcome = "Welcome to " diff --git a/content/unlisted.md b/content/unlisted.md new file mode 100644 index 0000000..1aad31d --- /dev/null +++ b/content/unlisted.md @@ -0,0 +1,5 @@ ++++ +title = "Unlisted page" ++++ + +Empty. diff --git a/screenshot.png b/screenshot.png index ca4f714..ba6ba69 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/static/js/search.js b/static/js/search.js new file mode 100644 index 0000000..936b14a --- /dev/null +++ b/static/js/search.js @@ -0,0 +1,182 @@ +// https://raw.githubusercontent.com/getzola/zola/master/docs/static/search.js + +function debounce(func, wait) { + var timeout; + + return function () { + var context = this; + var args = arguments; + clearTimeout(timeout); + + timeout = setTimeout(function () { + timeout = null; + func.apply(context, args); + }, wait); + }; + } + + // Taken from mdbook + // The strategy is as follows: + // First, assign a value to each word in the document: + // Words that correspond to search terms (stemmer aware): 40 + // Normal words: 2 + // First word in a sentence: 8 + // Then use a sliding window with a constant number of words and count the + // sum of the values of the words within the window. Then use the window that got the + // maximum sum. If there are multiple maximas, then get the last one. + // Enclose the terms in . + function makeTeaser(body, terms) { + var TERM_WEIGHT = 40; + var NORMAL_WORD_WEIGHT = 2; + var FIRST_WORD_WEIGHT = 8; + var TEASER_MAX_WORDS = 30; + + var stemmedTerms = terms.map(function (w) { + return elasticlunr.stemmer(w.toLowerCase()); + }); + var termFound = false; + var index = 0; + var weighted = []; // contains elements of ["word", weight, index_in_document] + + // split in sentences, then words + var sentences = body.toLowerCase().split(". "); + + for (var i in sentences) { + var words = sentences[i].split(" "); + var value = FIRST_WORD_WEIGHT; + + for (var j in words) { + var word = words[j]; + + if (word.length > 0) { + for (var k in stemmedTerms) { + if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) { + value = TERM_WEIGHT; + termFound = true; + } + } + weighted.push([word, value, index]); + value = NORMAL_WORD_WEIGHT; + } + + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + } + + index += 1; // because we split at a two-char boundary '. ' + } + + if (weighted.length === 0) { + return body; + } + + var windowWeights = []; + var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS); + // We add a window with all the weights first + var curSum = 0; + for (var i = 0; i < windowSize; i++) { + curSum += weighted[i][1]; + } + windowWeights.push(curSum); + + for (var i = 0; i < weighted.length - windowSize; i++) { + curSum -= weighted[i][1]; + curSum += weighted[i + windowSize][1]; + windowWeights.push(curSum); + } + + // If we didn't find the term, just pick the first window + var maxSumIndex = 0; + if (termFound) { + var maxFound = 0; + // backwards + for (var i = windowWeights.length - 1; i >= 0; i--) { + if (windowWeights[i] > maxFound) { + maxFound = windowWeights[i]; + maxSumIndex = i; + } + } + } + + var teaser = []; + var startIndex = weighted[maxSumIndex][2]; + for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) { + var word = weighted[i]; + if (startIndex < word[2]) { + // missing text from index to start of `word` + teaser.push(body.substring(startIndex, word[2])); + startIndex = word[2]; + } + + // add around search terms + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + startIndex = word[2] + word[0].length; + teaser.push(body.substring(word[2], startIndex)); + + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + } + teaser.push("…"); + return teaser.join(""); + } + + function formatSearchResultItem(item, terms) { + return '
' + + `${item.doc.title}` + + `
${makeTeaser(item.doc.body, terms)}
` + + '
'; + } + + function initSearch() { + var $searchInput = document.getElementById("search"); + var $searchResults = document.querySelector(".search-results"); + var $searchResultsItems = document.querySelector(".search-results__items"); + var MAX_ITEMS = 10; + + var options = { + bool: "AND", + fields: { + title: {boost: 2}, + body: {boost: 1}, + } + }; + var currentTerm = ""; + var index = elasticlunr.Index.load(window.searchIndex); + + $searchInput.addEventListener("keyup", debounce(function() { + var term = $searchInput.value.trim(); + if (term === currentTerm || !index) { + return; + } + $searchResults.style.display = term === "" ? "none" : "block"; + $searchResultsItems.innerHTML = ""; + if (term === "") { + return; + } + + var results = index.search(term, options); + if (results.length === 0) { + $searchResults.style.display = "none"; + return; + } + + currentTerm = term; + for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) { + var item = document.createElement("li"); + item.innerHTML = formatSearchResultItem(results[i], term.split(" ")); + $searchResultsItems.appendChild(item); + } + }, 150)); + } + + + if (document.readyState === "complete" || + (document.readyState !== "loading" && !document.documentElement.doScroll) + ) { + initSearch(); + } else { + document.addEventListener("DOMContentLoaded", initSearch); + } diff --git a/templates/index.html b/templates/index.html index 72b4b88..d686827 100644 --- a/templates/index.html +++ b/templates/index.html @@ -109,6 +109,14 @@
{% block sidebar %} +

Search

+ +
+
+
+

Categories