diff --git a/404.html b/404.html index 56efce4..1e7a37e 100644 --- a/404.html +++ b/404.html @@ -8,4 +8,4 @@ search_exclude: true

Page not found

-

The page you requested could not be found. Try using the navigation {% if site.search_enabled %}or search {% endif %}to find what you're looking for or go to this site's home page.

+

The page you requested could not be found. Try using the navigation {% if site.search_enabled != false %}or search {% endif %}to find what you're looking for or go to this site's home page.

diff --git a/Rakefile b/Rakefile deleted file mode 100644 index b8b42c4..0000000 --- a/Rakefile +++ /dev/null @@ -1 +0,0 @@ -Dir.glob('lib/tasks/*.rake').each {|r| import r} diff --git a/_config.yml b/_config.yml index 42ccec2..054d926 100644 --- a/_config.yml +++ b/_config.yml @@ -19,16 +19,37 @@ baseurl: "/just-the-docs" # the subpath of your site, e.g. /blog url: "https://pmarsceill.github.io" # the base hostname & protocol for your site, e.g. http://example.com permalink: pretty -exclude: ["node_modules/", "*.gemspec", "*.gem", "Gemfile", "Gemfile.lock", "package.json", "package-lock.json", "script/", "LICENSE.txt", "lib/", "bin/", "README.md", "Rakefile"] +exclude: ["node_modules/", "*.gemspec", "*.gem", "Gemfile", "Gemfile.lock", "package.json", "package-lock.json", "script/", "LICENSE.txt", "README.md"] # Set a path/url to a logo that will be displayed instead of the title #logo: "/assets/images/just-the-docs.png" # Enable or disable the site search +# Supports true (default) or false search_enabled: true - -# Set the search token separator for hyphenated-word search: -search_tokenizer_separator: /[\s/]+/ +search: + # Split pages into sections that can be searched individually + # Supports 1 - 6, default: 2 + heading_level: 2 + # Maximum amount of previews per search result + # Default: 3 + previews: 3 + # Maximum amount of words to display before a matched word in the preview + # Default: 5 + preview_words_before: 5 + # Maximum amount of words to display after a matched word in the preview + # Default: 10 + preview_words_after: 10 + # Set the search token separator + # Default: /[\s\-/]+/ + # Example: enable support for hyphenated search words + tokenizer_separator: /[\s/]+/ + # Display the relative url in search results + # Supports true (default) or false + rel_url: false + # Enable or disable the search button + # Supports true or false (default) + button: true # Enable or disable heading anchors heading_anchors: true diff --git a/_includes/nav.html b/_includes/nav.html index 603d8d3..f2ec7e5 100644 --- a/_includes/nav.html +++ b/_includes/nav.html @@ -17,7 +17,7 @@ {%- assign first_level_url = node.url | absolute_url -%} {%- endif -%} {%- if node.has_children -%} - + {%- endif -%} {{ node.title }} {%- if node.has_children -%} @@ -30,7 +30,7 @@ {%- assign second_level_url = child.url | absolute_url -%} {%- endif -%} {%- if child.has_children -%} - + {%- endif -%} {{ child.title }} {%- if child.has_children -%} diff --git a/_layouts/default.html b/_layouts/default.html index 69e253c..860efe6 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -8,29 +8,33 @@ layout: table_wrappers {% include head.html %} - + Link - + Menu - + Search - + Expand + + Document + + + + {% if site.search_enabled != false %} + {% if site.search.button %} + + + + {% endif %} + +
+ {% endif %} - - {% if site.search_enabled != false %} - - - - {% endif %} - diff --git a/_sass/code.scss b/_sass/code.scss index 7c15577..affc8aa 100644 --- a/_sass/code.scss +++ b/_sass/code.scss @@ -16,8 +16,9 @@ figure.highlight { padding: $sp-3; margin-top: 0; margin-bottom: 0; - -webkit-overflow-scrolling: touch; background-color: $code-background-color; + border-radius: $border-radius; + -webkit-overflow-scrolling: touch; code { padding: 0; @@ -27,7 +28,6 @@ figure.highlight { .highlighter-rouge { margin-bottom: $sp-3; - border-radius: $border-radius; } .highlight .c { diff --git a/_sass/layout.scss b/_sass/layout.scss index 49be865..72e2dc3 100644 --- a/_sass/layout.scss +++ b/_sass/layout.scss @@ -3,7 +3,7 @@ // .side-bar { - z-index: 100; + z-index: 0; display: flex; flex-wrap: wrap; background-color: $sidebar-color; @@ -50,15 +50,13 @@ } .main-header { - @include container; + z-index: 0; display: none; - padding-top: $gutter-spacing-sm; - padding-bottom: $gutter-spacing-sm; background-color: $sidebar-color; @include mq(md) { display: flex; - justify-content: flex-end; + justify-content: space-between; height: $header-height; background-color: $body-background-color; border-bottom: $border $border-color; @@ -84,7 +82,14 @@ } .site-nav { + display: none; + + &.nav-open { + display: block; + } + @include mq(md) { + display: block; padding-top: $sp-8; padding-bottom: $gutter-spacing-sm; overflow-y: auto; @@ -98,7 +103,6 @@ align-items: center; @include mq(md) { - z-index: 101; height: $header-height; max-height: $header-height; border-bottom: $border $border-color; @@ -138,22 +142,28 @@ height: 100%; padding: $gutter-spacing-sm; align-items: center; +} - @include mq(md) { +@include mq(md) { + .site-header .site-button { display: none; } } -.site-title:hover, -.site-button:hover { +.site-title:hover { background-image: linear-gradient(-90deg, rgba($feedback-color, 1) 0%, rgba($feedback-color, 0.8) 80%, rgba($feedback-color, 0) 100%); } +.site-button:hover { + background-image: linear-gradient(-90deg, rgba($feedback-color, 1) 0%, rgba($feedback-color, 0.8) 100%); +} + // stylelint-disable selector-max-type body { position: relative; padding-bottom: $sp-10; + overflow-y: scroll; @include mq(md) { position: static; diff --git a/_sass/navigation.scss b/_sass/navigation.scss index 16ff5f0..32ac4b9 100644 --- a/_sass/navigation.scss +++ b/_sass/navigation.scss @@ -125,37 +125,30 @@ } } -// Small screen nav - -.site-nav { - display: none; - - &.nav-open { - display: block; - } - @include mq(md) { - display: block; - } -} +// Aux nav .aux-nav { - align-self: center; + height: 100%; + overflow-x: auto; + @include fs-2; .aux-nav-list { + display: flex; + height: 100%; padding: 0; margin: 0; list-style: none; - @include fs-2; } .aux-nav-list-item { display: inline-block; - margin-right: $sp-2; - @include fs-2; + height: 100%; + padding: 0; + margin: 0; + } - &:last-child { - margin-right: 0; - } + @include mq(md) { + padding-right: $gutter-spacing-sm; } } diff --git a/_sass/search.scss b/_sass/search.scss index e0d66fe..2a4d4e8 100644 --- a/_sass/search.scss +++ b/_sass/search.scss @@ -4,93 +4,115 @@ .search { position: relative; - z-index: 99; + z-index: 2; flex-grow: 1; - height: 100%; - margin-bottom: $sp-3; + height: $sp-10; + padding: $sp-2; + transition: padding linear #{$transition-duration / 2}; @include mq(md) { - margin-bottom: 0; + position: relative !important; + width: auto !important; + height: 100% !important; + padding: 0; + transition: none; } } .search-input-wrap { - display: flex; - height: 100%; - padding: $sp-2; - background-color: $search-background-color; + position: relative; + z-index: 1; + height: $sp-8; + overflow: hidden; border-radius: $border-radius; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + transition: height linear #{$transition-duration / 2}; @include mq(md) { - padding-top: 0; - padding-right: 0; - padding-bottom: 0; - padding-left: 0; - background-color: $body-background-color; + position: absolute; + width: 100%; + max-width: $search-results-width; + height: 100% !important; border-radius: 0; box-shadow: none; + transition: width ease $transition-duration; } } .search-input { - align-self: center; + position: absolute; width: 100%; - padding-top: $sp-1; - padding-bottom: $sp-1; + height: 100%; + padding-top: $sp-2; + padding-right: $gutter-spacing-sm; + padding-bottom: $sp-2; + padding-left: #{$gutter-spacing-sm + $sp-5}; + font-size: 16px; background-color: $search-background-color; border-top: 0; border-right: 0; border-bottom: 0; border-left: 0; - order: 2; - @include fs-4; + border-radius: 0; + + @include mq(md) { + padding-top: $gutter-spacing-sm; + padding-bottom: $gutter-spacing-sm; + padding-left: #{$gutter-spacing + $sp-5}; + font-size: 14px; + background-color: $body-background-color; + transition: padding-left linear #{$transition-duration / 2}; + } &:focus { outline: 0; - box-shadow: none; - + .search-icon { + + .search-label .search-icon { fill: $link-color; } } - - @include mq(md) { - background-color: $body-background-color; - @include fs-3; - } } -.search-icon { - width: 1.2rem; - height: 1.2rem; - align-self: center; - margin-right: $sp-2; - fill: $grey-dk-000; - order: 1; +.search-label { + position: absolute; + display: flex; + height: 100%; + padding-left: $gutter-spacing-sm; + + @include mq(md) { + padding-left: $gutter-spacing; + transition: padding-left linear #{$transition-duration / 2}; + } + + .search-icon { + width: #{$sp-4 * 1.2}; + height: #{$sp-4 * 1.2}; + align-self: center; + fill: $grey-dk-000; + } } .search-results { position: absolute; - z-index: 100; + left: 0; display: none; width: 100%; - background: $search-background-color; - border-radius: $border-radius; + max-height: calc(100% - #{$sp-10}); + overflow-y: auto; + background-color: $search-background-color; + border-bottom-right-radius: $border-radius; + border-bottom-left-radius: $border-radius; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); - &.active { - display: block; - } - @include mq(md) { + top: 100%; width: $search-results-width; + max-height: calc(100vh - 200%) !important; } } .search-results-list { padding-left: 0; - margin-top: $sp-1; margin-bottom: $sp-1; list-style: none; @include fs-4; @@ -116,29 +138,56 @@ &.active { background-color: $feedback-color; } - - @include mq(md) { - padding-right: $sp-4; - padding-left: $sp-4; - } } .search-result-title { display: block; padding-top: $sp-2; - padding-right: $sp-4; padding-bottom: $sp-2; @include mq(sm) { display: inline-block; width: 40%; - word-wrap: break-word; + padding-right: $sp-2; vertical-align: top; } } +.search-result-doc { + display: flex; + align-items: center; + word-wrap: break-word; + + &.search-result-doc-parent { + opacity: 0.5; + @include fs-3; + + @include mq(md) { + @include fs-2; + } + } + + .search-result-icon { + width: $sp-4; + height: $sp-4; + margin-right: $sp-2; + fill: $link-color; + flex-shrink: 0; + } + + .search-result-doc-title { + overflow: auto; + } +} + +.search-result-section { + margin-left: #{$sp-4 + $sp-2}; + word-wrap: break-word; +} + .search-result-rel-url { display: block; + margin-left: #{$sp-4 + $sp-2}; overflow: hidden; color: $search-result-preview-color; text-overflow: ellipsis; @@ -146,12 +195,14 @@ @include fs-1; } -.search-result-preview { +.search-result-previews { display: block; padding-top: $sp-2; padding-bottom: $sp-2; padding-left: $sp-4; + margin-left: $sp-2; color: $search-result-preview-color; + word-wrap: break-word; border-left: $border; border-left-color: $border-color; @include fs-2; @@ -159,13 +210,26 @@ @include mq(sm) { display: inline-block; width: 60%; + padding-left: $sp-2; + margin-left: 0; vertical-align: top; } } +.search-result-preview + .search-result-preview { + margin-top: $sp-1; +} + .search-result-highlight { font-weight: bold; - color: $link-color; +} + +.search-no-result { + padding-top: $sp-2; + padding-right: $sp-3; + padding-bottom: $sp-2; + padding-left: $sp-3; + @include fs-3; } .search-button { @@ -176,17 +240,81 @@ width: $sp-9; height: $sp-9; background-color: $search-background-color; + border: 1px solid rgba($link-color, 0.3); border-radius: #{$sp-9 / 2}; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); align-items: center; justify-content: center; } -.search-results, -.search-button { - border: 1px solid rgba($link-color, 0.3); +.search-overlay { + position: fixed; + top: 0; + left: 0; + z-index: 1; + width: 0; + height: 0; + background-color: rgba(0, 0, 0, 0.3); + opacity: 0; + transition: opacity ease $transition-duration, width 0s $transition-duration, height 0s $transition-duration; } -.blur { - filter: blur(5px); +.search-active { + .search { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 0; + } + + .search-input-wrap { + height: $sp-10; + border-radius: 0; + + @include mq(md) { + width: $search-results-width; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + } + } + + .search-input { + background-color: $search-background-color; + + @include mq(md) { + padding-left: #{$sp-4 * 1.25 + $sp-5}; + } + } + + .search-label { + @include mq(md) { + padding-left: #{$sp-4 * 1.25}; + } + } + + .search-results { + display: block; + } + + .search-overlay { + width: 100%; + height: 100%; + opacity: 1; + transition: opacity ease $transition-duration, width 0s, height 0s; + } + + @include mq(md) { + .main { + position: fixed; + } + } + + .main-header { + padding-top: $sp-10; + + @include mq(md) { + padding-top: 0; + } + } } diff --git a/_sass/support/_variables.scss b/_sass/support/_variables.scss index 2d155d3..b97b14e 100644 --- a/_sass/support/_variables.scss +++ b/_sass/support/_variables.scss @@ -95,8 +95,8 @@ $sp-5: map-get($spacers, sp-5) !default; // 1.5 rem == 24px $sp-6: map-get($spacers, sp-6) !default; // 2 rem == 32px $sp-7: map-get($spacers, sp-7) !default; // 2.5 rem == 40px $sp-8: map-get($spacers, sp-8) !default; // 3 rem == 48px -$sp-9: map-get($spacers, sp-9) !default; // 4 rem == 48px -$sp-10: map-get($spacers, sp-10) !default; // 4.5 rem == 48px +$sp-9: map-get($spacers, sp-9) !default; // 3.5 rem == 56px +$sp-10: map-get($spacers, sp-10) !default; // 4 rem == 64px // // Borders @@ -119,7 +119,8 @@ $nav-list-item-height-sm: $sp-8 !default; $nav-list-expander-right: true; $content-width: 800px !default; $header-height: 60px !default; -$search-results-width: 500px !default; +$search-results-width: $content-width - $nav-width !default; +$transition-duration: 400ms; // // Media queries in pixels diff --git a/assets/js/just-the-docs.js b/assets/js/just-the-docs.js index 6dee35b..c287ec0 100644 --- a/assets/js/just-the-docs.js +++ b/assets/js/just-the-docs.js @@ -51,7 +51,7 @@ function initNav() { } }); - {% if site.search_enabled != false -%} + {%- if site.search_enabled != false and site.search.button %} const searchInput = document.getElementById('search-input'); const searchButton = document.getElementById('search-button'); @@ -64,245 +64,384 @@ function initNav() { {%- endif %} } +{%- if site.search_enabled != false %} // Site search -{% if site.search_enabled != false -%} function initSearch() { var request = new XMLHttpRequest(); request.open('GET', '{{ "assets/js/search-data.json" | absolute_url }}', true); request.onload = function(){ if (request.status >= 200 && request.status < 400) { - // Success! - var data = JSON.parse(request.responseText); + var docs = JSON.parse(request.responseText); - {% if site.search_tokenizer_separator != nil %} - lunr.tokenizer.separator = {{ site.search_tokenizer_separator }} - {% else %} - lunr.tokenizer.separator = /[\s\-/]+/ - {% endif %} - - var index = lunr(function () { + lunr.tokenizer.separator = {{ site.search.tokenizer_separator | default: site.search_tokenizer_separator | default: "/[\s\-/]+/" }} + + var index = lunr(function(){ this.ref('id'); this.field('title', { boost: 200 }); this.field('content', { boost: 2 }); - this.field('url'); + {%- if site.search.rel_url != false %} + this.field('relUrl'); + {%- endif %} this.metadataWhitelist = ['position'] - for (var i in data) { + for (var i in docs) { this.add({ id: i, - title: data[i].title, - content: data[i].content, - url: data[i].url + title: docs[i].title, + content: docs[i].content, + {%- if site.search.rel_url != false %} + relUrl: docs[i].relUrl + {%- endif %} }); } }); - searchResults(index, data); + searchLoaded(index, docs); } else { - // We reached our target server, but it returned an error console.log('Error loading ajax request. Request status:' + request.status); } }; request.onerror = function(){ - // There was a connection error of some sort console.log('There was a connection error'); }; request.send(); +} - function searchResults(index, data) { - var index = index; - var docs = data; - var searchInput = document.getElementById('search-input'); - var searchResults = document.getElementById('search-results'); - var mainContentWrap = document.getElementById('main-content-wrap'); +function searchLoaded(index, docs) { + var index = index; + var docs = docs; + var searchInput = document.getElementById('search-input'); + var searchResults = document.getElementById('search-results'); + var mainHeader = document.getElementById('main-header'); + var currentInput; + var currentSearchIndex = 0; - function hideResults() { - searchResults.innerHTML = ''; - searchResults.classList.remove('active'); - mainContentWrap.classList.remove('blur'); + function showSearch() { + document.documentElement.classList.add('search-active'); + } + + function hideSearch() { + document.documentElement.classList.remove('search-active'); + } + + function update() { + currentSearchIndex++; + + var input = searchInput.value; + if (input === '') { + hideSearch(); + } else { + showSearch(); + // scroll search input into view, workaround for iOS Safari + window.scroll(0, -1); + setTimeout(function(){ window.scroll(0, 0); }, 0); + } + if (input === currentInput) { + return; + } + currentInput = input; + searchResults.innerHTML = ''; + if (input === '') { + return; } - jtd.addEvent(searchInput, 'keydown', function(e){ - switch (e.keyCode) { - case 38: // arrow up - e.preventDefault(); - var active = document.querySelector('.search-result.active'); - if (active) { - active.classList.remove('active'); - if (active.parentElement.previousSibling) { - var previous = active.parentElement.previousSibling.querySelector('.search-result'); - previous.classList.add('active'); - } - } - return; - case 40: // arrow down - e.preventDefault(); - var active = document.querySelector('.search-result.active'); - if (active) { - if (active.parentElement.nextSibling) { - var next = active.parentElement.nextSibling.querySelector('.search-result'); - active.classList.remove('active'); - next.classList.add('active'); - } - } else { - var next = document.querySelector('.search-result'); - if (next) { - next.classList.add('active'); - } - } - return; - case 13: // enter - e.preventDefault(); - var active = document.querySelector('.search-result.active'); - if (active) { - active.click(); - } else { - var first = document.querySelector('.search-result'); - if (first) { - first.click(); - } - } - return; - } + var results = index.query(function (query) { + var tokens = lunr.tokenizer(input) + query.term(tokens, { + boost: 10 + }); + query.term(tokens, { + wildcard: lunr.Query.wildcard.TRAILING + }); }); - jtd.addEvent(searchInput, 'keyup', function(e){ - switch (e.keyCode) { - case 27: // When esc key is pressed, hide the results and clear the field - hideResults(); - searchInput.value = ''; - return; - case 38: // arrow up - case 40: // arrow down - case 13: // enter - e.preventDefault(); - return; + if ((results.length == 0) && (input.length > 2)) { + var tokens = lunr.tokenizer(input).filter(function(token, i) { + return token.str.length < 20; + }) + if (tokens.length > 0) { + results = index.query(function (query) { + query.term(tokens, { + editDistance: Math.round(Math.sqrt(input.length / 2 - 1)) + }); + }); } + } - hideResults(); + if (results.length == 0) { + var noResultsDiv = document.createElement('div'); + noResultsDiv.classList.add('search-no-result'); + noResultsDiv.innerText = 'No results found'; + searchResults.appendChild(noResultsDiv); - var input = this.value; - if (input === '') { + } else { + var resultsList = document.createElement('ul'); + resultsList.classList.add('search-results-list'); + searchResults.appendChild(resultsList); + + addResults(resultsList, results, 0, 10, 100, currentSearchIndex); + } + + function addResults(resultsList, results, start, batchSize, batchMillis, searchIndex) { + if (searchIndex != currentSearchIndex) { return; } + for (var i = start; i < (start + batchSize); i++) { + if (i == results.length) { + return; + } + addResult(resultsList, results[i]); + } + setTimeout(function() { + addResults(resultsList, results, start + batchSize, batchSize, batchMillis, searchIndex); + }, batchMillis); + } - var results = index.query(function (query) { - var tokens = lunr.tokenizer(input) - query.term(tokens, { - boost: 10 - }); - query.term(tokens, { - wildcard: lunr.Query.wildcard.TRAILING - }); - }); + function addResult(resultsList, result) { + var doc = docs[result.ref]; - if (results.length > 0) { - searchResults.classList.add('active'); - mainContentWrap.classList.add('blur'); - var resultsList = document.createElement('ul'); - resultsList.classList.add('search-results-list'); - searchResults.appendChild(resultsList); + var resultsListItem = document.createElement('li'); + resultsListItem.classList.add('search-results-list-item'); + resultsList.appendChild(resultsListItem); - for (var i in results) { - var result = results[i]; - var doc = docs[result.ref]; + var resultLink = document.createElement('a'); + resultLink.classList.add('search-result'); + resultLink.setAttribute('href', doc.url); + resultsListItem.appendChild(resultLink); - var resultsListItem = document.createElement('li'); - resultsListItem.classList.add('search-results-list-item'); - resultsList.appendChild(resultsListItem); + var resultTitle = document.createElement('div'); + resultTitle.classList.add('search-result-title'); + resultLink.appendChild(resultTitle); - var resultLink = document.createElement('a'); - resultLink.classList.add('search-result'); - resultLink.setAttribute('href', doc.url); - resultsListItem.appendChild(resultLink); + var resultDoc = document.createElement('div'); + resultDoc.classList.add('search-result-doc'); + resultDoc.innerHTML = ''; + resultTitle.appendChild(resultDoc); - var resultTitle = document.createElement('div'); - resultTitle.classList.add('search-result-title'); - resultTitle.innerText = doc.title; - resultLink.appendChild(resultTitle); + var resultDocTitle = document.createElement('div'); + resultDocTitle.classList.add('search-result-doc-title'); + resultDocTitle.innerHTML = doc.doc; + resultDoc.appendChild(resultDocTitle); + var resultDocOrSection = resultDocTitle; - var resultRelUrl = document.createElement('span'); - resultRelUrl.classList.add('search-result-rel-url'); - resultRelUrl.innerText = doc.relUrl; - resultTitle.appendChild(resultRelUrl); + if (doc.doc != doc.title) { + resultDoc.classList.add('search-result-doc-parent'); + var resultSection = document.createElement('div'); + resultSection.classList.add('search-result-section'); + resultSection.innerHTML = doc.title; + resultTitle.appendChild(resultSection); + resultDocOrSection = resultSection; + } - var metadata = result.matchData.metadata; - var contentFound = false; - for (var j in metadata) { - if (metadata[j].title) { - var position = metadata[j].title.position[0]; - var start = position[0]; - var end = position[0] + position[1]; - resultTitle.innerHTML = doc.title.substring(0, start) + '' + doc.title.substring(start, end) + '' + doc.title.substring(end, doc.title.length)+''+doc.relUrl+''; - - } else if (metadata[j].content && !contentFound) { - contentFound = true; - - var position = metadata[j].content.position[0]; - var start = position[0]; - var end = position[0] + position[1]; - var previewStart = start; - var previewEnd = end; - var ellipsesBefore = true; - var ellipsesAfter = true; - for (var k = 0; k < 3; k++) { - var nextSpace = doc.content.lastIndexOf(' ', previewStart - 2); - var nextDot = doc.content.lastIndexOf('.', previewStart - 2); - if ((nextDot > 0) && (nextDot > nextSpace)) { - previewStart = nextDot + 1; - ellipsesBefore = false; - break; - } - if (nextSpace < 0) { - previewStart = 0; - ellipsesBefore = false; - break; - } - previewStart = nextSpace + 1; + var metadata = result.matchData.metadata; + var titlePositions = []; + var contentPositions = []; + for (var j in metadata) { + var meta = metadata[j]; + if (meta.title) { + var positions = meta.title.position; + for (var k in positions) { + titlePositions.push(positions[k]); + } + } + if (meta.content) { + var positions = meta.content.position; + for (var k in positions) { + var position = positions[k]; + var previewStart = position[0]; + var previewEnd = position[0] + position[1]; + var ellipsesBefore = true; + var ellipsesAfter = true; + for (var k = 0; k < {{ site.search.preview_words_before | default: 5 }}; k++) { + var nextSpace = doc.content.lastIndexOf(' ', previewStart - 2); + var nextDot = doc.content.lastIndexOf('. ', previewStart - 2); + if ((nextDot >= 0) && (nextDot > nextSpace)) { + previewStart = nextDot + 1; + ellipsesBefore = false; + break; } - for (var k = 0; k < 10; k++) { - var nextSpace = doc.content.indexOf(' ', previewEnd + 1); - var nextDot = doc.content.indexOf('.', previewEnd + 1); - if ((nextDot > 0) && (nextDot < nextSpace)) { - previewEnd = nextDot; - ellipsesAfter = false; - break; - } - if (nextSpace < 0) { - previewEnd = doc.content.length; - ellipsesAfter = false; - break; - } - previewEnd = nextSpace; + if (nextSpace < 0) { + previewStart = 0; + ellipsesBefore = false; + break; } - var preview = doc.content.substring(previewStart, start); - if (ellipsesBefore) { - preview = '... ' + preview; - } - preview += '' + doc.content.substring(start, end) + ''; - preview += doc.content.substring(end, previewEnd); - if (ellipsesAfter) { - preview += ' ...'; - } - - var resultPreview = document.createElement('div'); - resultPreview.classList.add('search-result-preview'); - resultPreview.innerHTML = preview; - resultLink.appendChild(resultPreview); + previewStart = nextSpace + 1; } + for (var k = 0; k < {{ site.search.preview_words_after | default: 10 }}; k++) { + var nextSpace = doc.content.indexOf(' ', previewEnd + 1); + var nextDot = doc.content.indexOf('. ', previewEnd + 1); + if ((nextDot >= 0) && (nextDot < nextSpace)) { + previewEnd = nextDot; + ellipsesAfter = false; + break; + } + if (nextSpace < 0) { + previewEnd = doc.content.length; + ellipsesAfter = false; + break; + } + previewEnd = nextSpace; + } + contentPositions.push({ + highlight: position, + previewStart: previewStart, previewEnd: previewEnd, + ellipsesBefore: ellipsesBefore, ellipsesAfter: ellipsesAfter + }); } } } - }); - jtd.addEvent(searchInput, 'blur', function(){ - setTimeout(function(){ hideResults() }, 300); - }); + if (titlePositions.length > 0) { + titlePositions.sort(function(p1, p2){ return p1[0] - p2[0] }); + resultDocOrSection.innerHTML = ''; + addHighlightedText(resultDocOrSection, doc.title, 0, doc.title.length, titlePositions); + } + + if (contentPositions.length > 0) { + contentPositions.sort(function(p1, p2){ return p1.highlight[0] - p2.highlight[0] }); + var contentPosition = contentPositions[0]; + var previewPosition = { + highlight: [contentPosition.highlight], + previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd, + ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter + }; + var previewPositions = [previewPosition]; + for (var j = 1; j < contentPositions.length; j++) { + contentPosition = contentPositions[j]; + if (previewPosition.previewEnd < contentPosition.previewStart) { + previewPosition = { + highlight: [contentPosition.highlight], + previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd, + ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter + } + previewPositions.push(previewPosition); + } else { + previewPosition.highlight.push(contentPosition.highlight); + previewPosition.previewEnd = contentPosition.previewEnd; + previewPosition.ellipsesAfter = contentPosition.ellipsesAfter; + } + } + + var resultPreviews = document.createElement('div'); + resultPreviews.classList.add('search-result-previews'); + resultLink.appendChild(resultPreviews); + + var content = doc.content; + for (var j = 0; j < Math.min(previewPositions.length, {{ site.search.previews | default: 3 }}); j++) { + var position = previewPositions[j]; + + var resultPreview = document.createElement('div'); + resultPreview.classList.add('search-result-preview'); + resultPreviews.appendChild(resultPreview); + + if (position.ellipsesBefore) { + resultPreview.appendChild(document.createTextNode('... ')); + } + addHighlightedText(resultPreview, content, position.previewStart, position.previewEnd, position.highlight); + if (position.ellipsesAfter) { + resultPreview.appendChild(document.createTextNode(' ...')); + } + } + } + + {%- if site.search.rel_url != false %} + var resultRelUrl = document.createElement('span'); + resultRelUrl.classList.add('search-result-rel-url'); + resultRelUrl.innerText = doc.relUrl; + resultTitle.appendChild(resultRelUrl); + {%- endif %} + } + + function addHighlightedText(parent, text, start, end, positions) { + var index = start; + for (var i in positions) { + var position = positions[i]; + var span = document.createElement('span'); + span.innerHTML = text.substring(index, position[0]); + parent.appendChild(span); + index = position[0] + position[1]; + var highlight = document.createElement('span'); + highlight.classList.add('search-result-highlight'); + highlight.innerHTML = text.substring(position[0], index); + parent.appendChild(highlight); + } + var span = document.createElement('span'); + span.innerHTML = text.substring(index, end); + parent.appendChild(span); + } } + + jtd.addEvent(searchInput, 'focus', function(){ + setTimeout(update, 0); + }); + + jtd.addEvent(searchInput, 'keyup', function(e){ + switch (e.keyCode) { + case 27: // When esc key is pressed, hide the results and clear the field + searchInput.value = ''; + break; + case 38: // arrow up + case 40: // arrow down + case 13: // enter + e.preventDefault(); + return; + } + update(); + }); + + jtd.addEvent(searchInput, 'keydown', function(e){ + switch (e.keyCode) { + case 38: // arrow up + e.preventDefault(); + var active = document.querySelector('.search-result.active'); + if (active) { + active.classList.remove('active'); + if (active.parentElement.previousSibling) { + var previous = active.parentElement.previousSibling.querySelector('.search-result'); + previous.classList.add('active'); + } + } + return; + case 40: // arrow down + e.preventDefault(); + var active = document.querySelector('.search-result.active'); + if (active) { + if (active.parentElement.nextSibling) { + var next = active.parentElement.nextSibling.querySelector('.search-result'); + active.classList.remove('active'); + next.classList.add('active'); + } + } else { + var next = document.querySelector('.search-result'); + if (next) { + next.classList.add('active'); + } + } + return; + case 13: // enter + e.preventDefault(); + var active = document.querySelector('.search-result.active'); + if (active) { + active.click(); + } else { + var first = document.querySelector('.search-result'); + if (first) { + first.click(); + } + } + return; + } + }); + + jtd.addEvent(document, 'click', function(e){ + if (e.target != searchInput) { + hideSearch(); + } + }); } {%- endif %} @@ -322,7 +461,7 @@ jtd.setTheme = function(theme) { jtd.onReady(function(){ initNav(); - {% if site.search_enabled != false -%} + {%- if site.search_enabled != false %} initSearch(); {%- endif %} }); diff --git a/assets/js/search-data.json b/assets/js/search-data.json deleted file mode 100644 index 8e1b986..0000000 --- a/assets/js/search-data.json +++ /dev/null @@ -1,12 +0,0 @@ ---- ---- -{ - {% assign comma = false %} - {% for page in site.html_pages %}{% if page.search_exclude != true %}{% if comma == true%},{% endif %}"{{ forloop.index0 }}": { - "title": "{{ page.title | replace: '&', '&' }}", - "content": "{{ page.content | markdownify | replace: '' -%} + {%- assign title = titleAndContent[0] | replace_first: '>', '

' | split: '

' -%} + {%- assign title = title[1] | strip_html -%} + {%- assign content = titleAndContent[1] -%} + {%- assign url = page.url -%} + {%- if title == page.title and parts[0] == '' -%} + {%- assign title_found = true -%} + {%- else -%} + {%- assign id = titleAndContent[0] -%} + {%- assign id = id | split: 'id="' -%} + {%- if id.size == 2 -%} + {%- assign id = id[1] -%} + {%- assign id = id | split: '"' -%} + {%- assign id = id[0] -%} + {%- capture url -%}{{ url | append: '#' | append: id }}{%- endcapture -%} + {%- endif -%} + {%- endif -%} + {%- unless i == 0 -%},{%- endunless -%} + "{{ i }}": { + "doc": {{ page.title | jsonify }}, + "title": {{ title | jsonify }}, + "content": {{ content | replace: '