ovto 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +16 -17
- data/Rakefile +2 -9
- data/book/SUMMARY.md +15 -11
- data/book/book.toml +10 -0
- data/docs/.nojekyll +1 -0
- data/docs/404.html +189 -0
- data/docs/FontAwesome/css/font-awesome.css +4 -0
- data/docs/FontAwesome/fonts/FontAwesome.ttf +0 -0
- data/docs/FontAwesome/fonts/fontawesome-webfont.eot +0 -0
- data/docs/FontAwesome/fonts/fontawesome-webfont.svg +2671 -0
- data/docs/{gitbook/fonts/fontawesome → FontAwesome/fonts}/fontawesome-webfont.ttf +0 -0
- data/docs/FontAwesome/fonts/fontawesome-webfont.woff +0 -0
- data/docs/FontAwesome/fonts/fontawesome-webfont.woff2 +0 -0
- data/docs/api/Array.html +4 -4
- data/docs/api/Hash.html +4 -4
- data/docs/api/MightyInspect.html +238 -0
- data/docs/api/Ovto/Actions.html +6 -10
- data/docs/api/Ovto/App.html +4 -4
- data/docs/api/Ovto/Component/MoreThanOneNode.html +5 -5
- data/docs/api/Ovto/Component.html +4 -4
- data/docs/api/Ovto/Middleware/Actions.html +4 -4
- data/docs/api/Ovto/Middleware/Base.html +5 -6
- data/docs/api/Ovto/Middleware/Component.html +5 -6
- data/docs/api/Ovto/Middleware.html +8 -10
- data/docs/api/Ovto/PureComponent/StateIsNotAvailable.html +4 -4
- data/docs/api/Ovto/PureComponent.html +4 -4
- data/docs/api/Ovto/Runtime.html +4 -4
- data/docs/api/Ovto/State/MissingValue.html +4 -4
- data/docs/api/Ovto/State/UnknownStateKey.html +4 -4
- data/docs/api/Ovto/State.html +12 -12
- data/docs/api/Ovto/WiredActionSet.html +4 -4
- data/docs/api/Ovto/WiredActions.html +4 -4
- data/docs/api/Ovto.html +26 -30
- data/docs/api/_index.html +5 -5
- data/docs/api/actions.html +215 -426
- data/docs/api/app.html +226 -432
- data/docs/api/component.html +268 -480
- data/docs/api/css/style.css +1 -0
- data/docs/api/fetch.html +188 -393
- data/docs/api/file.README.html +9 -15
- data/docs/api/frames.html +1 -1
- data/docs/api/index.html +9 -15
- data/docs/api/method_list.html +4 -4
- data/docs/api/middleware.html +249 -460
- data/docs/api/pure_component.html +186 -398
- data/docs/api/state.html +226 -438
- data/docs/api/top-level-namespace.html +6 -6
- data/docs/ayu-highlight.css +78 -0
- data/docs/book.js +688 -0
- data/docs/book.toml +10 -0
- data/docs/clipboard.min.js +7 -0
- data/docs/css/chrome.css +545 -0
- data/docs/css/general.css +203 -0
- data/docs/css/print.css +54 -0
- data/docs/css/variables.css +255 -0
- data/docs/elasticlunr.min.js +10 -0
- data/docs/favicon.png +0 -0
- data/docs/favicon.svg +22 -0
- data/docs/fonts/OPEN-SANS-LICENSE.txt +202 -0
- data/docs/fonts/SOURCE-CODE-PRO-LICENSE.txt +93 -0
- data/docs/fonts/fonts.css +100 -0
- data/docs/fonts/open-sans-v17-all-charsets-300.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-300italic.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-600.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-600italic.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-700.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-700italic.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-800.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-800italic.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-italic.woff2 +0 -0
- data/docs/fonts/open-sans-v17-all-charsets-regular.woff2 +0 -0
- data/docs/fonts/source-code-pro-v11-all-charsets-500.woff2 +0 -0
- data/docs/guides/debugging.html +184 -390
- data/docs/guides/development.html +171 -383
- data/docs/guides/install.html +206 -409
- data/docs/guides/tutorial.html +309 -525
- data/docs/highlight.css +82 -0
- data/docs/highlight.js +6 -0
- data/docs/index.html +390 -391
- data/docs/mark.min.js +7 -0
- data/docs/print.html +958 -0
- data/docs/searcher.js +483 -0
- data/docs/searchindex.js +1 -0
- data/docs/searchindex.json +1 -0
- data/docs/tomorrow-night.css +102 -0
- data/examples/sinatra/Gemfile.lock +25 -25
- data/examples/static/Gemfile.lock +8 -8
- data/lib/ovto/component.rb +2 -3
- data/lib/ovto/version.rb +1 -1
- data/ovto.gemspec +1 -1
- metadata +49 -28
- data/docs/gitbook/fonts/fontawesome/FontAwesome.otf +0 -0
- data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot +0 -0
- data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg +0 -685
- data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff +0 -0
- data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 +0 -0
- data/docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js +0 -240
- data/docs/gitbook/gitbook-plugin-fontsettings/website.css +0 -291
- data/docs/gitbook/gitbook-plugin-highlight/ebook.css +0 -135
- data/docs/gitbook/gitbook-plugin-highlight/website.css +0 -434
- data/docs/gitbook/gitbook-plugin-lunr/lunr.min.js +0 -7
- data/docs/gitbook/gitbook-plugin-lunr/search-lunr.js +0 -59
- data/docs/gitbook/gitbook-plugin-search/lunr.min.js +0 -7
- data/docs/gitbook/gitbook-plugin-search/search-engine.js +0 -50
- data/docs/gitbook/gitbook-plugin-search/search.css +0 -35
- data/docs/gitbook/gitbook-plugin-search/search.js +0 -213
- data/docs/gitbook/gitbook-plugin-sharing/buttons.js +0 -90
- data/docs/gitbook/gitbook.js +0 -4
- data/docs/gitbook/images/apple-touch-icon-precomposed-152.png +0 -0
- data/docs/gitbook/images/favicon.ico +0 -0
- data/docs/gitbook/style.css +0 -9
- data/docs/gitbook/theme.js +0 -4
- data/docs/search_index.json +0 -1
data/docs/searcher.js
ADDED
@@ -0,0 +1,483 @@
|
|
1
|
+
"use strict";
|
2
|
+
window.search = window.search || {};
|
3
|
+
(function search(search) {
|
4
|
+
// Search functionality
|
5
|
+
//
|
6
|
+
// You can use !hasFocus() to prevent keyhandling in your key
|
7
|
+
// event handlers while the user is typing their search.
|
8
|
+
|
9
|
+
if (!Mark || !elasticlunr) {
|
10
|
+
return;
|
11
|
+
}
|
12
|
+
|
13
|
+
//IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
|
14
|
+
if (!String.prototype.startsWith) {
|
15
|
+
String.prototype.startsWith = function(search, pos) {
|
16
|
+
return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
|
17
|
+
};
|
18
|
+
}
|
19
|
+
|
20
|
+
var search_wrap = document.getElementById('search-wrapper'),
|
21
|
+
searchbar = document.getElementById('searchbar'),
|
22
|
+
searchbar_outer = document.getElementById('searchbar-outer'),
|
23
|
+
searchresults = document.getElementById('searchresults'),
|
24
|
+
searchresults_outer = document.getElementById('searchresults-outer'),
|
25
|
+
searchresults_header = document.getElementById('searchresults-header'),
|
26
|
+
searchicon = document.getElementById('search-toggle'),
|
27
|
+
content = document.getElementById('content'),
|
28
|
+
|
29
|
+
searchindex = null,
|
30
|
+
doc_urls = [],
|
31
|
+
results_options = {
|
32
|
+
teaser_word_count: 30,
|
33
|
+
limit_results: 30,
|
34
|
+
},
|
35
|
+
search_options = {
|
36
|
+
bool: "AND",
|
37
|
+
expand: true,
|
38
|
+
fields: {
|
39
|
+
title: {boost: 1},
|
40
|
+
body: {boost: 1},
|
41
|
+
breadcrumbs: {boost: 0}
|
42
|
+
}
|
43
|
+
},
|
44
|
+
mark_exclude = [],
|
45
|
+
marker = new Mark(content),
|
46
|
+
current_searchterm = "",
|
47
|
+
URL_SEARCH_PARAM = 'search',
|
48
|
+
URL_MARK_PARAM = 'highlight',
|
49
|
+
teaser_count = 0,
|
50
|
+
|
51
|
+
SEARCH_HOTKEY_KEYCODE = 83,
|
52
|
+
ESCAPE_KEYCODE = 27,
|
53
|
+
DOWN_KEYCODE = 40,
|
54
|
+
UP_KEYCODE = 38,
|
55
|
+
SELECT_KEYCODE = 13;
|
56
|
+
|
57
|
+
function hasFocus() {
|
58
|
+
return searchbar === document.activeElement;
|
59
|
+
}
|
60
|
+
|
61
|
+
function removeChildren(elem) {
|
62
|
+
while (elem.firstChild) {
|
63
|
+
elem.removeChild(elem.firstChild);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
// Helper to parse a url into its building blocks.
|
68
|
+
function parseURL(url) {
|
69
|
+
var a = document.createElement('a');
|
70
|
+
a.href = url;
|
71
|
+
return {
|
72
|
+
source: url,
|
73
|
+
protocol: a.protocol.replace(':',''),
|
74
|
+
host: a.hostname,
|
75
|
+
port: a.port,
|
76
|
+
params: (function(){
|
77
|
+
var ret = {};
|
78
|
+
var seg = a.search.replace(/^\?/,'').split('&');
|
79
|
+
var len = seg.length, i = 0, s;
|
80
|
+
for (;i<len;i++) {
|
81
|
+
if (!seg[i]) { continue; }
|
82
|
+
s = seg[i].split('=');
|
83
|
+
ret[s[0]] = s[1];
|
84
|
+
}
|
85
|
+
return ret;
|
86
|
+
})(),
|
87
|
+
file: (a.pathname.match(/\/([^/?#]+)$/i) || [,''])[1],
|
88
|
+
hash: a.hash.replace('#',''),
|
89
|
+
path: a.pathname.replace(/^([^/])/,'/$1')
|
90
|
+
};
|
91
|
+
}
|
92
|
+
|
93
|
+
// Helper to recreate a url string from its building blocks.
|
94
|
+
function renderURL(urlobject) {
|
95
|
+
var url = urlobject.protocol + "://" + urlobject.host;
|
96
|
+
if (urlobject.port != "") {
|
97
|
+
url += ":" + urlobject.port;
|
98
|
+
}
|
99
|
+
url += urlobject.path;
|
100
|
+
var joiner = "?";
|
101
|
+
for(var prop in urlobject.params) {
|
102
|
+
if(urlobject.params.hasOwnProperty(prop)) {
|
103
|
+
url += joiner + prop + "=" + urlobject.params[prop];
|
104
|
+
joiner = "&";
|
105
|
+
}
|
106
|
+
}
|
107
|
+
if (urlobject.hash != "") {
|
108
|
+
url += "#" + urlobject.hash;
|
109
|
+
}
|
110
|
+
return url;
|
111
|
+
}
|
112
|
+
|
113
|
+
// Helper to escape html special chars for displaying the teasers
|
114
|
+
var escapeHTML = (function() {
|
115
|
+
var MAP = {
|
116
|
+
'&': '&',
|
117
|
+
'<': '<',
|
118
|
+
'>': '>',
|
119
|
+
'"': '"',
|
120
|
+
"'": '''
|
121
|
+
};
|
122
|
+
var repl = function(c) { return MAP[c]; };
|
123
|
+
return function(s) {
|
124
|
+
return s.replace(/[&<>'"]/g, repl);
|
125
|
+
};
|
126
|
+
})();
|
127
|
+
|
128
|
+
function formatSearchMetric(count, searchterm) {
|
129
|
+
if (count == 1) {
|
130
|
+
return count + " search result for '" + searchterm + "':";
|
131
|
+
} else if (count == 0) {
|
132
|
+
return "No search results for '" + searchterm + "'.";
|
133
|
+
} else {
|
134
|
+
return count + " search results for '" + searchterm + "':";
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
function formatSearchResult(result, searchterms) {
|
139
|
+
var teaser = makeTeaser(escapeHTML(result.doc.body), searchterms);
|
140
|
+
teaser_count++;
|
141
|
+
|
142
|
+
// The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor
|
143
|
+
var url = doc_urls[result.ref].split("#");
|
144
|
+
if (url.length == 1) { // no anchor found
|
145
|
+
url.push("");
|
146
|
+
}
|
147
|
+
|
148
|
+
// encodeURIComponent escapes all chars that could allow an XSS except
|
149
|
+
// for '. Due to that we also manually replace ' with its url-encoded
|
150
|
+
// representation (%27).
|
151
|
+
var searchterms = encodeURIComponent(searchterms.join(" ")).replace(/\'/g, "%27");
|
152
|
+
|
153
|
+
return '<a href="' + path_to_root + url[0] + '?' + URL_MARK_PARAM + '=' + searchterms + '#' + url[1]
|
154
|
+
+ '" aria-details="teaser_' + teaser_count + '">' + result.doc.breadcrumbs + '</a>'
|
155
|
+
+ '<span class="teaser" id="teaser_' + teaser_count + '" aria-label="Search Result Teaser">'
|
156
|
+
+ teaser + '</span>';
|
157
|
+
}
|
158
|
+
|
159
|
+
function makeTeaser(body, searchterms) {
|
160
|
+
// The strategy is as follows:
|
161
|
+
// First, assign a value to each word in the document:
|
162
|
+
// Words that correspond to search terms (stemmer aware): 40
|
163
|
+
// Normal words: 2
|
164
|
+
// First word in a sentence: 8
|
165
|
+
// Then use a sliding window with a constant number of words and count the
|
166
|
+
// sum of the values of the words within the window. Then use the window that got the
|
167
|
+
// maximum sum. If there are multiple maximas, then get the last one.
|
168
|
+
// Enclose the terms in <em>.
|
169
|
+
var stemmed_searchterms = searchterms.map(function(w) {
|
170
|
+
return elasticlunr.stemmer(w.toLowerCase());
|
171
|
+
});
|
172
|
+
var searchterm_weight = 40;
|
173
|
+
var weighted = []; // contains elements of ["word", weight, index_in_document]
|
174
|
+
// split in sentences, then words
|
175
|
+
var sentences = body.toLowerCase().split('. ');
|
176
|
+
var index = 0;
|
177
|
+
var value = 0;
|
178
|
+
var searchterm_found = false;
|
179
|
+
for (var sentenceindex in sentences) {
|
180
|
+
var words = sentences[sentenceindex].split(' ');
|
181
|
+
value = 8;
|
182
|
+
for (var wordindex in words) {
|
183
|
+
var word = words[wordindex];
|
184
|
+
if (word.length > 0) {
|
185
|
+
for (var searchtermindex in stemmed_searchterms) {
|
186
|
+
if (elasticlunr.stemmer(word).startsWith(stemmed_searchterms[searchtermindex])) {
|
187
|
+
value = searchterm_weight;
|
188
|
+
searchterm_found = true;
|
189
|
+
}
|
190
|
+
};
|
191
|
+
weighted.push([word, value, index]);
|
192
|
+
value = 2;
|
193
|
+
}
|
194
|
+
index += word.length;
|
195
|
+
index += 1; // ' ' or '.' if last word in sentence
|
196
|
+
};
|
197
|
+
index += 1; // because we split at a two-char boundary '. '
|
198
|
+
};
|
199
|
+
|
200
|
+
if (weighted.length == 0) {
|
201
|
+
return body;
|
202
|
+
}
|
203
|
+
|
204
|
+
var window_weight = [];
|
205
|
+
var window_size = Math.min(weighted.length, results_options.teaser_word_count);
|
206
|
+
|
207
|
+
var cur_sum = 0;
|
208
|
+
for (var wordindex = 0; wordindex < window_size; wordindex++) {
|
209
|
+
cur_sum += weighted[wordindex][1];
|
210
|
+
};
|
211
|
+
window_weight.push(cur_sum);
|
212
|
+
for (var wordindex = 0; wordindex < weighted.length - window_size; wordindex++) {
|
213
|
+
cur_sum -= weighted[wordindex][1];
|
214
|
+
cur_sum += weighted[wordindex + window_size][1];
|
215
|
+
window_weight.push(cur_sum);
|
216
|
+
};
|
217
|
+
|
218
|
+
if (searchterm_found) {
|
219
|
+
var max_sum = 0;
|
220
|
+
var max_sum_window_index = 0;
|
221
|
+
// backwards
|
222
|
+
for (var i = window_weight.length - 1; i >= 0; i--) {
|
223
|
+
if (window_weight[i] > max_sum) {
|
224
|
+
max_sum = window_weight[i];
|
225
|
+
max_sum_window_index = i;
|
226
|
+
}
|
227
|
+
};
|
228
|
+
} else {
|
229
|
+
max_sum_window_index = 0;
|
230
|
+
}
|
231
|
+
|
232
|
+
// add <em/> around searchterms
|
233
|
+
var teaser_split = [];
|
234
|
+
var index = weighted[max_sum_window_index][2];
|
235
|
+
for (var i = max_sum_window_index; i < max_sum_window_index+window_size; i++) {
|
236
|
+
var word = weighted[i];
|
237
|
+
if (index < word[2]) {
|
238
|
+
// missing text from index to start of `word`
|
239
|
+
teaser_split.push(body.substring(index, word[2]));
|
240
|
+
index = word[2];
|
241
|
+
}
|
242
|
+
if (word[1] == searchterm_weight) {
|
243
|
+
teaser_split.push("<em>")
|
244
|
+
}
|
245
|
+
index = word[2] + word[0].length;
|
246
|
+
teaser_split.push(body.substring(word[2], index));
|
247
|
+
if (word[1] == searchterm_weight) {
|
248
|
+
teaser_split.push("</em>")
|
249
|
+
}
|
250
|
+
};
|
251
|
+
|
252
|
+
return teaser_split.join('');
|
253
|
+
}
|
254
|
+
|
255
|
+
function init(config) {
|
256
|
+
results_options = config.results_options;
|
257
|
+
search_options = config.search_options;
|
258
|
+
searchbar_outer = config.searchbar_outer;
|
259
|
+
doc_urls = config.doc_urls;
|
260
|
+
searchindex = elasticlunr.Index.load(config.index);
|
261
|
+
|
262
|
+
// Set up events
|
263
|
+
searchicon.addEventListener('click', function(e) { searchIconClickHandler(); }, false);
|
264
|
+
searchbar.addEventListener('keyup', function(e) { searchbarKeyUpHandler(); }, false);
|
265
|
+
document.addEventListener('keydown', function(e) { globalKeyHandler(e); }, false);
|
266
|
+
// If the user uses the browser buttons, do the same as if a reload happened
|
267
|
+
window.onpopstate = function(e) { doSearchOrMarkFromUrl(); };
|
268
|
+
// Suppress "submit" events so the page doesn't reload when the user presses Enter
|
269
|
+
document.addEventListener('submit', function(e) { e.preventDefault(); }, false);
|
270
|
+
|
271
|
+
// If reloaded, do the search or mark again, depending on the current url parameters
|
272
|
+
doSearchOrMarkFromUrl();
|
273
|
+
}
|
274
|
+
|
275
|
+
function unfocusSearchbar() {
|
276
|
+
// hacky, but just focusing a div only works once
|
277
|
+
var tmp = document.createElement('input');
|
278
|
+
tmp.setAttribute('style', 'position: absolute; opacity: 0;');
|
279
|
+
searchicon.appendChild(tmp);
|
280
|
+
tmp.focus();
|
281
|
+
tmp.remove();
|
282
|
+
}
|
283
|
+
|
284
|
+
// On reload or browser history backwards/forwards events, parse the url and do search or mark
|
285
|
+
function doSearchOrMarkFromUrl() {
|
286
|
+
// Check current URL for search request
|
287
|
+
var url = parseURL(window.location.href);
|
288
|
+
if (url.params.hasOwnProperty(URL_SEARCH_PARAM)
|
289
|
+
&& url.params[URL_SEARCH_PARAM] != "") {
|
290
|
+
showSearch(true);
|
291
|
+
searchbar.value = decodeURIComponent(
|
292
|
+
(url.params[URL_SEARCH_PARAM]+'').replace(/\+/g, '%20'));
|
293
|
+
searchbarKeyUpHandler(); // -> doSearch()
|
294
|
+
} else {
|
295
|
+
showSearch(false);
|
296
|
+
}
|
297
|
+
|
298
|
+
if (url.params.hasOwnProperty(URL_MARK_PARAM)) {
|
299
|
+
var words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' ');
|
300
|
+
marker.mark(words, {
|
301
|
+
exclude: mark_exclude
|
302
|
+
});
|
303
|
+
|
304
|
+
var markers = document.querySelectorAll("mark");
|
305
|
+
function hide() {
|
306
|
+
for (var i = 0; i < markers.length; i++) {
|
307
|
+
markers[i].classList.add("fade-out");
|
308
|
+
window.setTimeout(function(e) { marker.unmark(); }, 300);
|
309
|
+
}
|
310
|
+
}
|
311
|
+
for (var i = 0; i < markers.length; i++) {
|
312
|
+
markers[i].addEventListener('click', hide);
|
313
|
+
}
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
// Eventhandler for keyevents on `document`
|
318
|
+
function globalKeyHandler(e) {
|
319
|
+
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; }
|
320
|
+
|
321
|
+
if (e.keyCode === ESCAPE_KEYCODE) {
|
322
|
+
e.preventDefault();
|
323
|
+
searchbar.classList.remove("active");
|
324
|
+
setSearchUrlParameters("",
|
325
|
+
(searchbar.value.trim() !== "") ? "push" : "replace");
|
326
|
+
if (hasFocus()) {
|
327
|
+
unfocusSearchbar();
|
328
|
+
}
|
329
|
+
showSearch(false);
|
330
|
+
marker.unmark();
|
331
|
+
} else if (!hasFocus() && e.keyCode === SEARCH_HOTKEY_KEYCODE) {
|
332
|
+
e.preventDefault();
|
333
|
+
showSearch(true);
|
334
|
+
window.scrollTo(0, 0);
|
335
|
+
searchbar.select();
|
336
|
+
} else if (hasFocus() && e.keyCode === DOWN_KEYCODE) {
|
337
|
+
e.preventDefault();
|
338
|
+
unfocusSearchbar();
|
339
|
+
searchresults.firstElementChild.classList.add("focus");
|
340
|
+
} else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE
|
341
|
+
|| e.keyCode === UP_KEYCODE
|
342
|
+
|| e.keyCode === SELECT_KEYCODE)) {
|
343
|
+
// not `:focus` because browser does annoying scrolling
|
344
|
+
var focused = searchresults.querySelector("li.focus");
|
345
|
+
if (!focused) return;
|
346
|
+
e.preventDefault();
|
347
|
+
if (e.keyCode === DOWN_KEYCODE) {
|
348
|
+
var next = focused.nextElementSibling;
|
349
|
+
if (next) {
|
350
|
+
focused.classList.remove("focus");
|
351
|
+
next.classList.add("focus");
|
352
|
+
}
|
353
|
+
} else if (e.keyCode === UP_KEYCODE) {
|
354
|
+
focused.classList.remove("focus");
|
355
|
+
var prev = focused.previousElementSibling;
|
356
|
+
if (prev) {
|
357
|
+
prev.classList.add("focus");
|
358
|
+
} else {
|
359
|
+
searchbar.select();
|
360
|
+
}
|
361
|
+
} else { // SELECT_KEYCODE
|
362
|
+
window.location.assign(focused.querySelector('a'));
|
363
|
+
}
|
364
|
+
}
|
365
|
+
}
|
366
|
+
|
367
|
+
function showSearch(yes) {
|
368
|
+
if (yes) {
|
369
|
+
search_wrap.classList.remove('hidden');
|
370
|
+
searchicon.setAttribute('aria-expanded', 'true');
|
371
|
+
} else {
|
372
|
+
search_wrap.classList.add('hidden');
|
373
|
+
searchicon.setAttribute('aria-expanded', 'false');
|
374
|
+
var results = searchresults.children;
|
375
|
+
for (var i = 0; i < results.length; i++) {
|
376
|
+
results[i].classList.remove("focus");
|
377
|
+
}
|
378
|
+
}
|
379
|
+
}
|
380
|
+
|
381
|
+
function showResults(yes) {
|
382
|
+
if (yes) {
|
383
|
+
searchresults_outer.classList.remove('hidden');
|
384
|
+
} else {
|
385
|
+
searchresults_outer.classList.add('hidden');
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
// Eventhandler for search icon
|
390
|
+
function searchIconClickHandler() {
|
391
|
+
if (search_wrap.classList.contains('hidden')) {
|
392
|
+
showSearch(true);
|
393
|
+
window.scrollTo(0, 0);
|
394
|
+
searchbar.select();
|
395
|
+
} else {
|
396
|
+
showSearch(false);
|
397
|
+
}
|
398
|
+
}
|
399
|
+
|
400
|
+
// Eventhandler for keyevents while the searchbar is focused
|
401
|
+
function searchbarKeyUpHandler() {
|
402
|
+
var searchterm = searchbar.value.trim();
|
403
|
+
if (searchterm != "") {
|
404
|
+
searchbar.classList.add("active");
|
405
|
+
doSearch(searchterm);
|
406
|
+
} else {
|
407
|
+
searchbar.classList.remove("active");
|
408
|
+
showResults(false);
|
409
|
+
removeChildren(searchresults);
|
410
|
+
}
|
411
|
+
|
412
|
+
setSearchUrlParameters(searchterm, "push_if_new_search_else_replace");
|
413
|
+
|
414
|
+
// Remove marks
|
415
|
+
marker.unmark();
|
416
|
+
}
|
417
|
+
|
418
|
+
// Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and #heading-anchor .
|
419
|
+
// `action` can be one of "push", "replace", "push_if_new_search_else_replace"
|
420
|
+
// and replaces or pushes a new browser history item.
|
421
|
+
// "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet.
|
422
|
+
function setSearchUrlParameters(searchterm, action) {
|
423
|
+
var url = parseURL(window.location.href);
|
424
|
+
var first_search = ! url.params.hasOwnProperty(URL_SEARCH_PARAM);
|
425
|
+
if (searchterm != "" || action == "push_if_new_search_else_replace") {
|
426
|
+
url.params[URL_SEARCH_PARAM] = searchterm;
|
427
|
+
delete url.params[URL_MARK_PARAM];
|
428
|
+
url.hash = "";
|
429
|
+
} else {
|
430
|
+
delete url.params[URL_MARK_PARAM];
|
431
|
+
delete url.params[URL_SEARCH_PARAM];
|
432
|
+
}
|
433
|
+
// A new search will also add a new history item, so the user can go back
|
434
|
+
// to the page prior to searching. A updated search term will only replace
|
435
|
+
// the url.
|
436
|
+
if (action == "push" || (action == "push_if_new_search_else_replace" && first_search) ) {
|
437
|
+
history.pushState({}, document.title, renderURL(url));
|
438
|
+
} else if (action == "replace" || (action == "push_if_new_search_else_replace" && !first_search) ) {
|
439
|
+
history.replaceState({}, document.title, renderURL(url));
|
440
|
+
}
|
441
|
+
}
|
442
|
+
|
443
|
+
function doSearch(searchterm) {
|
444
|
+
|
445
|
+
// Don't search the same twice
|
446
|
+
if (current_searchterm == searchterm) { return; }
|
447
|
+
else { current_searchterm = searchterm; }
|
448
|
+
|
449
|
+
if (searchindex == null) { return; }
|
450
|
+
|
451
|
+
// Do the actual search
|
452
|
+
var results = searchindex.search(searchterm, search_options);
|
453
|
+
var resultcount = Math.min(results.length, results_options.limit_results);
|
454
|
+
|
455
|
+
// Display search metrics
|
456
|
+
searchresults_header.innerText = formatSearchMetric(resultcount, searchterm);
|
457
|
+
|
458
|
+
// Clear and insert results
|
459
|
+
var searchterms = searchterm.split(' ');
|
460
|
+
removeChildren(searchresults);
|
461
|
+
for(var i = 0; i < resultcount ; i++){
|
462
|
+
var resultElem = document.createElement('li');
|
463
|
+
resultElem.innerHTML = formatSearchResult(results[i], searchterms);
|
464
|
+
searchresults.appendChild(resultElem);
|
465
|
+
}
|
466
|
+
|
467
|
+
// Display results
|
468
|
+
showResults(true);
|
469
|
+
}
|
470
|
+
|
471
|
+
fetch(path_to_root + 'searchindex.json')
|
472
|
+
.then(response => response.json())
|
473
|
+
.then(json => init(json))
|
474
|
+
.catch(error => { // Try to load searchindex.js if fetch failed
|
475
|
+
var script = document.createElement('script');
|
476
|
+
script.src = path_to_root + 'searchindex.js';
|
477
|
+
script.onload = () => init(window.search);
|
478
|
+
document.head.appendChild(script);
|
479
|
+
});
|
480
|
+
|
481
|
+
// Exported functions
|
482
|
+
search.hasFocus = hasFocus;
|
483
|
+
})(window.search);
|