Search: Move results to the right, fix mobile, history handling, highlight search terms.
This commit is contained in:
parent
18a1dc08c8
commit
379c6ff616
|
@ -141,7 +141,8 @@ table thead td {
|
|||
max-width: 750px;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
.content a {
|
||||
.content a,
|
||||
#searchresults a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.content a:hover {
|
||||
|
@ -153,6 +154,7 @@ table thead td {
|
|||
.menu-bar {
|
||||
height: 50px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
@ -175,31 +177,52 @@ table thead td {
|
|||
.menu-bar .right-buttons {
|
||||
float: right;
|
||||
}
|
||||
#searchbar {
|
||||
border: 1px solid #BBB;
|
||||
border-radius: 3px;
|
||||
padding: 3px 5px;
|
||||
width: 50px;
|
||||
transition: width 0.5s ease-in-out;
|
||||
.searchbar-outer {
|
||||
display: none;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 750px;
|
||||
}
|
||||
#searchbar:focus, #searchbar:hover, #searchbar.active {
|
||||
width: 150px;
|
||||
#searchbar {
|
||||
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 {
|
||||
color: #CCC;
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
padding: 18px 0 0 5px;
|
||||
}
|
||||
.searchresults-outer {
|
||||
border-bottom: 1px dashed #CCC;
|
||||
display: none;
|
||||
}
|
||||
ul#searchresults {
|
||||
list-style: none;
|
||||
padding-left: 20px;
|
||||
}
|
||||
ul#searchresults li {
|
||||
margin: 10px 0px;
|
||||
}
|
||||
.menu-title {
|
||||
display: inline;
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: 0;
|
||||
right: 0;
|
||||
font-weight: 200;
|
||||
font-size: 20px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
filter: alpha(opacity=0);
|
||||
|
@ -381,11 +404,15 @@ table thead td {
|
|||
.light .mobile-nav-chapters {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.light #searchresults a,
|
||||
.light .content a:link,
|
||||
.light a:visited,
|
||||
.light a > .hljs {
|
||||
color: #4183c4;
|
||||
}
|
||||
.light mark {
|
||||
background-color: #a2cff5;
|
||||
}
|
||||
.light .theme-popup {
|
||||
color: #333;
|
||||
background: #fafafa;
|
||||
|
@ -502,11 +529,15 @@ table thead td {
|
|||
.coal .mobile-nav-chapters {
|
||||
background-color: #292c2f;
|
||||
}
|
||||
.coal #searchresults a,
|
||||
.coal .content a:link,
|
||||
.coal a:visited,
|
||||
.coal a > .hljs {
|
||||
color: #2b79a2;
|
||||
}
|
||||
.coal mark {
|
||||
background-color: #355c7d;
|
||||
}
|
||||
.coal .theme-popup {
|
||||
color: #98a3ad;
|
||||
background: #141617;
|
||||
|
@ -623,11 +654,15 @@ table thead td {
|
|||
.navy .mobile-nav-chapters {
|
||||
background-color: #282d3f;
|
||||
}
|
||||
.navy #searchresults a,
|
||||
.navy .content a:link,
|
||||
.navy a:visited,
|
||||
.navy a > .hljs {
|
||||
color: #2b79a2;
|
||||
}
|
||||
.navy mark {
|
||||
background-color: #a2cff5;
|
||||
}
|
||||
.navy .theme-popup {
|
||||
color: #bcbdd0;
|
||||
background: #161923;
|
||||
|
@ -744,11 +779,15 @@ table thead td {
|
|||
.rust .mobile-nav-chapters {
|
||||
background-color: #3b2e2a;
|
||||
}
|
||||
.rust #searchresults a,
|
||||
.rust .content a:link,
|
||||
.rust a:visited,
|
||||
.rust a > .hljs {
|
||||
color: #2b79a2;
|
||||
}
|
||||
.rust mark {
|
||||
background-color: #e69f67;
|
||||
}
|
||||
.rust .theme-popup {
|
||||
color: #262625;
|
||||
background: #e1e1db;
|
||||
|
@ -865,11 +904,15 @@ table thead td {
|
|||
.ayu .mobile-nav-chapters {
|
||||
background-color: #14191f;
|
||||
}
|
||||
.ayu #searchresults a,
|
||||
.ayu .content a:link,
|
||||
.ayu a:visited,
|
||||
.ayu a > .hljs {
|
||||
color: #0096cf;
|
||||
}
|
||||
.ayu mark {
|
||||
background-color: #e3b171;
|
||||
}
|
||||
.ayu .theme-popup {
|
||||
color: #c5c5c5;
|
||||
background: #14191f;
|
||||
|
|
|
@ -1,5 +1,180 @@
|
|||
$( 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
|
||||
var url = window.location.pathname;
|
||||
|
||||
|
@ -60,6 +235,7 @@ $( document ).ready(function() {
|
|||
break;
|
||||
case KEY_CODES.SEARCH_KEY:
|
||||
e.preventDefault();
|
||||
$("#searchbar-outer").slideDown()
|
||||
$('#searchbar').focus();
|
||||
break;
|
||||
}
|
||||
|
@ -89,9 +265,14 @@ $( document ).ready(function() {
|
|||
}
|
||||
|
||||
// For testing purposes: Index current page
|
||||
var searchindex = create_text_searchindex();
|
||||
var current_searchterm = "";
|
||||
var teaser_size_half = 80;
|
||||
var searchindex = create_test_searchindex();
|
||||
|
||||
$("#search-icon").click(function(e) {
|
||||
var outer = $("#searchbar-outer");
|
||||
outer.slideToggle();
|
||||
// TODO:
|
||||
// If invisible, clear URL search parameter
|
||||
});
|
||||
|
||||
// Searchbar
|
||||
$("#searchbar").on('keyup', function (e) {
|
||||
|
@ -100,70 +281,36 @@ $( document ).ready(function() {
|
|||
|
||||
var searchterm = e.target.value.trim();
|
||||
if (searchterm != "") {
|
||||
// keep searchbar expanded
|
||||
$(e.target).addClass("active");
|
||||
|
||||
// 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];
|
||||
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();
|
||||
doSearch(searchindex, searchterm);
|
||||
} else {
|
||||
// searchbar can shrink
|
||||
$(e.target).removeClass("active");
|
||||
outer.slideUp();
|
||||
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-toggle").click(function(){
|
||||
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;
|
||||
}
|
|
@ -56,6 +56,16 @@
|
|||
}
|
||||
</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 -->
|
||||
<script src="store.js"></script>
|
||||
|
||||
|
@ -76,11 +86,6 @@
|
|||
</script>
|
||||
|
||||
<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}}
|
||||
</div>
|
||||
|
||||
|
@ -91,8 +96,7 @@
|
|||
<div class="left-buttons">
|
||||
<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>
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ...">
|
||||
<i id="search-go" class="fa fa-search" title="Search!"></i>
|
||||
<i id="search-icon" class="fa fa-search" title="Search!"></i>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">{{ book_title }}</h1>
|
||||
|
@ -104,6 +108,17 @@
|
|||
</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">
|
||||
{{{ content }}}
|
||||
</div>
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue