Search: Move results to the right, fix mobile, history handling, highlight search terms.

This commit is contained in:
Phaiax 2017-10-03 02:59:30 +02:00
parent 18a1dc08c8
commit 379c6ff616
4 changed files with 287 additions and 117 deletions

View File

@ -141,7 +141,8 @@ table thead td {
max-width: 750px; max-width: 750px;
padding-bottom: 50px; padding-bottom: 50px;
} }
.content a { .content a,
#searchresults a {
text-decoration: none; text-decoration: none;
} }
.content a:hover { .content a:hover {
@ -153,6 +154,7 @@ table thead td {
.menu-bar { .menu-bar {
height: 50px; height: 50px;
display: flex; display: flex;
position: relative;
justify-content: space-between; justify-content: space-between;
align-items: baseline; align-items: baseline;
} }
@ -175,31 +177,52 @@ table thead td {
.menu-bar .right-buttons { .menu-bar .right-buttons {
float: right; float: right;
} }
#searchbar { .searchbar-outer {
border: 1px solid #BBB; display: none;
border-radius: 3px; margin-left: auto;
padding: 3px 5px; margin-right: auto;
width: 50px; max-width: 750px;
transition: width 0.5s ease-in-out;
} }
#searchbar:focus, #searchbar:hover, #searchbar.active { #searchbar {
width: 150px; display: block;
width: 98%;
border: 1px solid #CCC;
border-radius: 3px;
margin: 5px auto 0px auto;
padding: 10px 16px;
transition: box-shadow 300ms ease-in-out;
}
#searchbar:focus, #searchbar.active {
box-shadow: 0 0 3px #AAA;
} }
.searchresults-header { .searchresults-header {
color: #CCC;
font-weight: bold; font-weight: bold;
font-size: 1em; font-size: 1em;
padding: 18px 0 0 5px;
} }
.searchresults-outer { .searchresults-outer {
border-bottom: 1px dashed #CCC; border-bottom: 1px dashed #CCC;
display: none; display: none;
} }
ul#searchresults {
list-style: none;
padding-left: 20px;
}
ul#searchresults li {
margin: 10px 0px;
}
.menu-title { .menu-title {
display: inline; position: absolute;
display: block;
left: 0;
right: 0;
font-weight: 200; font-weight: 200;
font-size: 20px; font-size: 20px;
line-height: 50px; line-height: 50px;
text-align: center; text-align: center;
margin: 0; margin: 0;
z-index: -1;
opacity: 0; opacity: 0;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
filter: alpha(opacity=0); filter: alpha(opacity=0);
@ -381,11 +404,15 @@ table thead td {
.light .mobile-nav-chapters { .light .mobile-nav-chapters {
background-color: #fafafa; background-color: #fafafa;
} }
.light #searchresults a,
.light .content a:link, .light .content a:link,
.light a:visited, .light a:visited,
.light a > .hljs { .light a > .hljs {
color: #4183c4; color: #4183c4;
} }
.light mark {
background-color: #a2cff5;
}
.light .theme-popup { .light .theme-popup {
color: #333; color: #333;
background: #fafafa; background: #fafafa;
@ -502,11 +529,15 @@ table thead td {
.coal .mobile-nav-chapters { .coal .mobile-nav-chapters {
background-color: #292c2f; background-color: #292c2f;
} }
.coal #searchresults a,
.coal .content a:link, .coal .content a:link,
.coal a:visited, .coal a:visited,
.coal a > .hljs { .coal a > .hljs {
color: #2b79a2; color: #2b79a2;
} }
.coal mark {
background-color: #355c7d;
}
.coal .theme-popup { .coal .theme-popup {
color: #98a3ad; color: #98a3ad;
background: #141617; background: #141617;
@ -623,11 +654,15 @@ table thead td {
.navy .mobile-nav-chapters { .navy .mobile-nav-chapters {
background-color: #282d3f; background-color: #282d3f;
} }
.navy #searchresults a,
.navy .content a:link, .navy .content a:link,
.navy a:visited, .navy a:visited,
.navy a > .hljs { .navy a > .hljs {
color: #2b79a2; color: #2b79a2;
} }
.navy mark {
background-color: #a2cff5;
}
.navy .theme-popup { .navy .theme-popup {
color: #bcbdd0; color: #bcbdd0;
background: #161923; background: #161923;
@ -744,11 +779,15 @@ table thead td {
.rust .mobile-nav-chapters { .rust .mobile-nav-chapters {
background-color: #3b2e2a; background-color: #3b2e2a;
} }
.rust #searchresults a,
.rust .content a:link, .rust .content a:link,
.rust a:visited, .rust a:visited,
.rust a > .hljs { .rust a > .hljs {
color: #2b79a2; color: #2b79a2;
} }
.rust mark {
background-color: #e69f67;
}
.rust .theme-popup { .rust .theme-popup {
color: #262625; color: #262625;
background: #e1e1db; background: #e1e1db;
@ -865,11 +904,15 @@ table thead td {
.ayu .mobile-nav-chapters { .ayu .mobile-nav-chapters {
background-color: #14191f; background-color: #14191f;
} }
.ayu #searchresults a,
.ayu .content a:link, .ayu .content a:link,
.ayu a:visited, .ayu a:visited,
.ayu a > .hljs { .ayu a > .hljs {
color: #0096cf; color: #0096cf;
} }
.ayu mark {
background-color: #e3b171;
}
.ayu .theme-popup { .ayu .theme-popup {
color: #c5c5c5; color: #c5c5c5;
background: #14191f; background: #14191f;

View File

@ -1,5 +1,180 @@
$( document ).ready(function() { $( document ).ready(function() {
// Helpers for searching
function create_test_searchindex() {
var searchindex = elasticlunr(function () {
this.addField('body');
this.addField('title');
this.setRef('id');
});
var content = $("#content");
var paragraphs = content.children();
var curr_title = "";
var curr_body = "";
var curr_ref = "";
var push = function(ref) {
if ((curr_title.length > 0 || curr_body.length > 0) && curr_ref.length > 0) {
var doc = {
"id": curr_ref,
"body": curr_body,
"title": curr_title
}
searchindex.addDoc(doc);
}
curr_body = "";
curr_title = "";
curr_ref = "";
};
paragraphs.each(function(index, element) {
// todo uppercase
var el = $(element);
if (el.prop('nodeName').toUpperCase() == "A") {
// new header, push old paragraph to index
push(index);
curr_title = el.text();
curr_ref = el.attr('href');
} else {
curr_body += " \n " + el.text();
}
// last paragraph
if (index == paragraphs.length - 1) {
push(index);
}
});
return searchindex;
}
function parseURL(url) {
var a = document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':',''),
host: a.hostname,
port: a.port,
params: (function(){
var ret = {};
var seg = a.search.replace(/^\?/,'').split('&');
var len = seg.length, i = 0, s;
for (;i<len;i++) {
if (!seg[i]) { continue; }
s = seg[i].split('=');
ret[s[0]] = s[1];
}
return ret;
})(),
file: (a.pathname.match(/\/([^/?#]+)$/i) || [,''])[1],
hash: a.hash.replace('#',''),
path: a.pathname.replace(/^([^/])/,'/$1')
};
}
function renderURL(urlobject) {
var url = urlobject.protocol + "://" + urlobject.host;
if (urlobject.port != "") {
url += ":" + urlobject.port;
}
url += urlobject.path;
var joiner = "?";
for(var prop in urlobject.params) {
if(urlobject.params.hasOwnProperty(prop)) {
url += joiner + prop + "=" + urlobject.params[prop];
joiner = "&";
}
}
if (urlobject.hash != "") {
url += "#" + urlobject.hash;
}
return url;
}
var current_searchterm = "";
var teaser_size_half = 80;
function doSearch(searchindex, searchterm) {
var display = $('#searchresults');
// Don't search twice the same
if (current_searchterm == searchterm) { return; }
else { current_searchterm = searchterm; }
// Do the actual search
var results = searchindex.search(searchterm, {
bool: "AND",
expand: true
});
// Display search metrics
var searchheader = "";
if (results.length > 0) {
searchheader = results.length + " search results for '" + searchterm + "':";
} else if (results.length == 1) {
searchheader = results.length + " search result for '" + searchterm + "':";
} else {
searchheader = "No search results for '" + searchterm + "'.";
}
$('#searchresults-header').text(searchheader);
// Clear and insert results
var firstterm = searchterm.split(' ')[0];
display.empty();
for(var i = 0, size = results.length; i < size ; i++){
var result = results[i];
var firstoccurence = result.doc.body.search(firstterm);
var teaser = "";
if (firstoccurence != -1) {
var teaserstartindex = firstoccurence - teaser_size_half;
var nextwordindex = result.doc.body.indexOf(" ", teaserstartindex);
if (nextwordindex != -1) {
teaserstartindex = nextwordindex;
}
var teaserendindex = firstoccurence + teaser_size_half;
nextwordindex = result.doc.body.indexOf(" ", teaserendindex);
if (nextwordindex != -1) {
teaserendindex = nextwordindex;
}
teaser = (teaserstartindex > 0) ? "..." : "";
teaser += result.doc.body.substring(teaserstartindex, teaserendindex) + "...";
} else {
teaser = result.doc.body.substr(0, 80) + "...";
}
var url = result.ref.split("#");
if (url.length == 1) {
url.push("");
}
display.append('<li><a href="' + url[0] + '?highlight=' + searchterm + '#' + url[1] + '">'
+ result.doc.title + '</a>: ' + teaser + "</li>");
}
// Display and scroll to results
$("#menu-bar").scrollTop(0);
$("#searchresults-outer").slideDown();
}
function doSearchOrHighlightFromUrl() {
// Check current URL for search request
var url = parseURL(window.location.href);
if (url.params.hasOwnProperty('search')) {
$("#searchbar-outer").slideDown();
$("#searchbar")[0].value = url.params['search'];
$("#searchbar").trigger('keyup');
} else {
$("#searchbar-outer").slideUp();
}
if (url.params.hasOwnProperty('highlight')) {
var words = url.params['highlight'].split(' ');
var header = $('#' + url.hash);
$('.content').mark(words, {
// exclude : ['.hljs']
});
}
}
// url // url
var url = window.location.pathname; var url = window.location.pathname;
@ -60,6 +235,7 @@ $( document ).ready(function() {
break; break;
case KEY_CODES.SEARCH_KEY: case KEY_CODES.SEARCH_KEY:
e.preventDefault(); e.preventDefault();
$("#searchbar-outer").slideDown()
$('#searchbar').focus(); $('#searchbar').focus();
break; break;
} }
@ -89,9 +265,14 @@ $( document ).ready(function() {
} }
// For testing purposes: Index current page // For testing purposes: Index current page
var searchindex = create_text_searchindex(); var searchindex = create_test_searchindex();
var current_searchterm = "";
var teaser_size_half = 80; $("#search-icon").click(function(e) {
var outer = $("#searchbar-outer");
outer.slideToggle();
// TODO:
// If invisible, clear URL search parameter
});
// Searchbar // Searchbar
$("#searchbar").on('keyup', function (e) { $("#searchbar").on('keyup', function (e) {
@ -100,70 +281,36 @@ $( document ).ready(function() {
var searchterm = e.target.value.trim(); var searchterm = e.target.value.trim();
if (searchterm != "") { if (searchterm != "") {
// keep searchbar expanded
$(e.target).addClass("active"); $(e.target).addClass("active");
// Don't search twice the same doSearch(searchindex, searchterm);
if (current_searchterm == searchterm) { return; }
else { current_searchterm = searchterm; }
// Do the actual search
var results = searchindex.search(searchterm, {
bool: "AND",
expand: true
});
// Display search metrics
var searchheader = "";
if (results.length > 0) {
searchheader = results.length + " search results for '" + searchterm + "':";
} else if (results.length == 1) {
searchheader = results.length + " search result for '" + searchterm + "':";
} else { } else {
searchheader = "No search results for '" + searchterm + "'.";
}
$('#searchresults-header').text(searchheader);
// Clear and insert results
var firstterm = searchterm.split(' ')[0];
display.empty();
for(var i = 0, size = results.length; i < size ; i++){
var result = results[i];
document.lsd = result.doc;
var firstoccurence = result.doc.body.search(firstterm);
var teaser = "";
if (firstoccurence != -1) {
var teaserstartindex = firstoccurence - teaser_size_half;
var nextwordindex = result.doc.body.indexOf(" ", teaserstartindex);
if (nextwordindex != -1) {
teaserstartindex = nextwordindex;
}
var teaserendindex = firstoccurence + teaser_size_half;
nextwordindex = result.doc.body.indexOf(" ", teaserendindex);
if (nextwordindex != -1) {
teaserendindex = nextwordindex;
}
teaser = (teaserstartindex > 0) ? "..." : "";
teaser += result.doc.body.substring(teaserstartindex, teaserendindex) + "...";
} else {
teaser = result.doc.body.substr(0, 80) + "...";
}
display.append('<li><a href="' + result.ref + '">' + result.doc.title + '</a>: '
+ teaser + "</li>");
}
// Display and scroll to results
sidebar.scrollTop(0);
outer.slideDown();
} else {
// searchbar can shrink
$(e.target).removeClass("active"); $(e.target).removeClass("active");
outer.slideUp(); outer.slideUp();
display.empty(); display.empty();
} }
var url = parseURL(window.location.href);
var first_search = ! url.params.hasOwnProperty("search");
url.params["search"] = searchterm;
delete url.params["highlight"];
url.hash = "";
if (first_search) {
history.pushState({}, document.title, renderURL(url));
} else {
history.replaceState({}, document.title, renderURL(url));
}
$('.content').unmark();
}); });
window.onpopstate = function(e) {
doSearchOrHighlightFromUrl();
};
doSearchOrHighlightFromUrl();
// Theme button // Theme button
$("#theme-toggle").click(function(){ $("#theme-toggle").click(function(){
if($('.theme-popup').length) { if($('.theme-popup').length) {
@ -474,45 +621,3 @@ function run_rust_code(code_block) {
}); });
} }
function create_text_searchindex() {
var searchindex = elasticlunr(function () {
this.addField('body');
this.addField('title');
this.setRef('id');
});
var content = $("#content");
var paragraphs = content.children();
var curr_title = "";
var curr_body = "";
var curr_ref = "";
var push = function(ref) {
if ((curr_title.length > 0 || curr_body.length > 0) && curr_ref.length > 0) {
var doc = {
"id": curr_ref,
"body": curr_body,
"title": curr_title
}
searchindex.addDoc(doc);
}
curr_body = "";
curr_title = "";
curr_ref = "";
};
paragraphs.each(function(index, element) {
// todo uppercase
var el = $(element);
if (el.prop('nodeName').toUpperCase() == "A") {
// new header, push old paragraph to index
push(index);
curr_title = el.text();
curr_ref = el.attr('href');
} else {
curr_body += " \n " + el.text();
}
// last paragraph
if (index == paragraphs.length - 1) {
push(index);
}
});
return searchindex;
}

View File

@ -56,6 +56,16 @@
} }
</script> </script>
<!-- Fetch mark.js from CDN but have a local fallback -->
<script src="https://cdn.jsdelivr.net/npm/mark.js@8.11.0/dist/jquery.mark.min.js"></script>
<script>
if (typeof jQuery.fn.mark == 'undefined') {
document.write(unescape("%3Cscript src='jquery.mark.min.js'%3E%3C/script%3E"));
}
</script>
<!-- Fetch store.js from local - TODO add CDN when 2.x.x is available on cdnjs --> <!-- Fetch store.js from local - TODO add CDN when 2.x.x is available on cdnjs -->
<script src="store.js"></script> <script src="store.js"></script>
@ -76,11 +86,6 @@
</script> </script>
<div id="sidebar" class="sidebar"> <div id="sidebar" class="sidebar">
<div id="searchresults-outer" class="searchresults-outer">
<div class="searchresults-header" id="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
{{#toc}}{{/toc}} {{#toc}}{{/toc}}
</div> </div>
@ -91,8 +96,7 @@
<div class="left-buttons"> <div class="left-buttons">
<i id="sidebar-toggle" class="fa fa-bars" title="Toggle sidebar"></i> <i id="sidebar-toggle" class="fa fa-bars" title="Toggle sidebar"></i>
<i id="theme-toggle" class="fa fa-paint-brush" title="Change theme"></i> <i id="theme-toggle" class="fa fa-paint-brush" title="Change theme"></i>
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..."> <i id="search-icon" class="fa fa-search" title="Search!"></i>
<i id="search-go" class="fa fa-search" title="Search!"></i>
</div> </div>
<h1 class="menu-title">{{ book_title }}</h1> <h1 class="menu-title">{{ book_title }}</h1>
@ -104,6 +108,17 @@
</div> </div>
</div> </div>
<div id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ...">
<div id="searchresults-outer" class="searchresults-outer">
<div class="searchresults-header" id="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<div id="content" class="content"> <div id="content" class="content">
{{{ content }}} {{{ content }}}
</div> </div>

7
src/theme/jquery.mark.min.js vendored Normal file

File diff suppressed because one or more lines are too long