ovto 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile.lock +16 -17
  4. data/Rakefile +2 -9
  5. data/book/SUMMARY.md +15 -11
  6. data/book/book.toml +10 -0
  7. data/docs/.nojekyll +1 -0
  8. data/docs/404.html +189 -0
  9. data/docs/FontAwesome/css/font-awesome.css +4 -0
  10. data/docs/FontAwesome/fonts/FontAwesome.ttf +0 -0
  11. data/docs/FontAwesome/fonts/fontawesome-webfont.eot +0 -0
  12. data/docs/FontAwesome/fonts/fontawesome-webfont.svg +2671 -0
  13. data/docs/{gitbook/fonts/fontawesome → FontAwesome/fonts}/fontawesome-webfont.ttf +0 -0
  14. data/docs/FontAwesome/fonts/fontawesome-webfont.woff +0 -0
  15. data/docs/FontAwesome/fonts/fontawesome-webfont.woff2 +0 -0
  16. data/docs/api/Array.html +4 -4
  17. data/docs/api/Hash.html +4 -4
  18. data/docs/api/MightyInspect.html +238 -0
  19. data/docs/api/Ovto/Actions.html +6 -10
  20. data/docs/api/Ovto/App.html +4 -4
  21. data/docs/api/Ovto/Component/MoreThanOneNode.html +5 -5
  22. data/docs/api/Ovto/Component.html +4 -4
  23. data/docs/api/Ovto/Middleware/Actions.html +4 -4
  24. data/docs/api/Ovto/Middleware/Base.html +5 -6
  25. data/docs/api/Ovto/Middleware/Component.html +5 -6
  26. data/docs/api/Ovto/Middleware.html +8 -10
  27. data/docs/api/Ovto/PureComponent/StateIsNotAvailable.html +4 -4
  28. data/docs/api/Ovto/PureComponent.html +4 -4
  29. data/docs/api/Ovto/Runtime.html +4 -4
  30. data/docs/api/Ovto/State/MissingValue.html +4 -4
  31. data/docs/api/Ovto/State/UnknownStateKey.html +4 -4
  32. data/docs/api/Ovto/State.html +12 -12
  33. data/docs/api/Ovto/WiredActionSet.html +4 -4
  34. data/docs/api/Ovto/WiredActions.html +4 -4
  35. data/docs/api/Ovto.html +26 -30
  36. data/docs/api/_index.html +5 -5
  37. data/docs/api/actions.html +215 -426
  38. data/docs/api/app.html +226 -432
  39. data/docs/api/component.html +268 -480
  40. data/docs/api/css/style.css +1 -0
  41. data/docs/api/fetch.html +188 -393
  42. data/docs/api/file.README.html +9 -15
  43. data/docs/api/frames.html +1 -1
  44. data/docs/api/index.html +9 -15
  45. data/docs/api/method_list.html +4 -4
  46. data/docs/api/middleware.html +249 -460
  47. data/docs/api/pure_component.html +186 -398
  48. data/docs/api/state.html +226 -438
  49. data/docs/api/top-level-namespace.html +6 -6
  50. data/docs/ayu-highlight.css +78 -0
  51. data/docs/book.js +688 -0
  52. data/docs/book.toml +10 -0
  53. data/docs/clipboard.min.js +7 -0
  54. data/docs/css/chrome.css +545 -0
  55. data/docs/css/general.css +203 -0
  56. data/docs/css/print.css +54 -0
  57. data/docs/css/variables.css +255 -0
  58. data/docs/elasticlunr.min.js +10 -0
  59. data/docs/favicon.png +0 -0
  60. data/docs/favicon.svg +22 -0
  61. data/docs/fonts/OPEN-SANS-LICENSE.txt +202 -0
  62. data/docs/fonts/SOURCE-CODE-PRO-LICENSE.txt +93 -0
  63. data/docs/fonts/fonts.css +100 -0
  64. data/docs/fonts/open-sans-v17-all-charsets-300.woff2 +0 -0
  65. data/docs/fonts/open-sans-v17-all-charsets-300italic.woff2 +0 -0
  66. data/docs/fonts/open-sans-v17-all-charsets-600.woff2 +0 -0
  67. data/docs/fonts/open-sans-v17-all-charsets-600italic.woff2 +0 -0
  68. data/docs/fonts/open-sans-v17-all-charsets-700.woff2 +0 -0
  69. data/docs/fonts/open-sans-v17-all-charsets-700italic.woff2 +0 -0
  70. data/docs/fonts/open-sans-v17-all-charsets-800.woff2 +0 -0
  71. data/docs/fonts/open-sans-v17-all-charsets-800italic.woff2 +0 -0
  72. data/docs/fonts/open-sans-v17-all-charsets-italic.woff2 +0 -0
  73. data/docs/fonts/open-sans-v17-all-charsets-regular.woff2 +0 -0
  74. data/docs/fonts/source-code-pro-v11-all-charsets-500.woff2 +0 -0
  75. data/docs/guides/debugging.html +184 -390
  76. data/docs/guides/development.html +171 -383
  77. data/docs/guides/install.html +206 -409
  78. data/docs/guides/tutorial.html +309 -525
  79. data/docs/highlight.css +82 -0
  80. data/docs/highlight.js +6 -0
  81. data/docs/index.html +390 -391
  82. data/docs/mark.min.js +7 -0
  83. data/docs/print.html +958 -0
  84. data/docs/searcher.js +483 -0
  85. data/docs/searchindex.js +1 -0
  86. data/docs/searchindex.json +1 -0
  87. data/docs/tomorrow-night.css +102 -0
  88. data/examples/sinatra/Gemfile.lock +25 -25
  89. data/examples/static/Gemfile.lock +8 -8
  90. data/lib/ovto/component.rb +2 -3
  91. data/lib/ovto/version.rb +1 -1
  92. data/ovto.gemspec +1 -1
  93. metadata +49 -28
  94. data/docs/gitbook/fonts/fontawesome/FontAwesome.otf +0 -0
  95. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot +0 -0
  96. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg +0 -685
  97. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff +0 -0
  98. data/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 +0 -0
  99. data/docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js +0 -240
  100. data/docs/gitbook/gitbook-plugin-fontsettings/website.css +0 -291
  101. data/docs/gitbook/gitbook-plugin-highlight/ebook.css +0 -135
  102. data/docs/gitbook/gitbook-plugin-highlight/website.css +0 -434
  103. data/docs/gitbook/gitbook-plugin-lunr/lunr.min.js +0 -7
  104. data/docs/gitbook/gitbook-plugin-lunr/search-lunr.js +0 -59
  105. data/docs/gitbook/gitbook-plugin-search/lunr.min.js +0 -7
  106. data/docs/gitbook/gitbook-plugin-search/search-engine.js +0 -50
  107. data/docs/gitbook/gitbook-plugin-search/search.css +0 -35
  108. data/docs/gitbook/gitbook-plugin-search/search.js +0 -213
  109. data/docs/gitbook/gitbook-plugin-sharing/buttons.js +0 -90
  110. data/docs/gitbook/gitbook.js +0 -4
  111. data/docs/gitbook/images/apple-touch-icon-precomposed-152.png +0 -0
  112. data/docs/gitbook/images/favicon.ico +0 -0
  113. data/docs/gitbook/style.css +0 -9
  114. data/docs/gitbook/theme.js +0 -4
  115. 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
+ '&': '&amp;',
117
+ '<': '&lt;',
118
+ '>': '&gt;',
119
+ '"': '&#34;',
120
+ "'": '&#39;'
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);