hematite 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/_includes/extern_library_imports.html +7 -0
- data/_includes/katex_includes.html +1 -1
- data/_includes/mermaid_includes.html +24 -0
- data/_layouts/default.html +1 -1
- data/_layouts/remark_slideshow.html +77 -0
- data/_sass/_hljs.scss +36 -0
- data/_sass/_layout.scss +15 -0
- data/_sass/_nav.scss +8 -0
- data/_sass/hematite.scss +1 -0
- data/assets/html/remark_presentation_frame.html.resource +69 -0
- data/assets/js/UrlHelper.mjs +20 -0
- data/assets/js/dropdownExpander.mjs +2 -1
- data/assets/js/layout/remark_slideshow.mjs +164 -0
- data/assets/js/search.mjs +59 -14
- data/assets/plugin/mermaid/mermaid.core.js +31940 -0
- data/assets/plugin/mermaid/mermaid.core.js.map +1 -0
- data/assets/plugin/mermaid/mermaid.js +129094 -0
- data/assets/plugin/mermaid/mermaid.js.map +1 -0
- data/assets/plugin/remark_presenter/README.txt +2 -0
- data/assets/plugin/remark_presenter/remark.min.js +18 -0
- data/assets/string_data/en.mjs +5 -1
- data/assets/string_data/es.mjs +5 -1
- data/assets/style_only_syntax.scss +11 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c31b77be5a329f70985b04f78078a87cf74d79c1f462018d8eed576e18efadc
|
4
|
+
data.tar.gz: 643ca6f28aaf67740b9e71b9de4a0f21bbe08f0f139b95e7b67819b28f9b1648
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3200f91d6ddd479b360c08b74862ff2a1260d45a92d3e84aa5c9686556cea7303835129c10329bd1b84d0cf965225eeb0808771f4d8edb338870feeb2d9c52e2
|
7
|
+
data.tar.gz: 79a331beca39bdfb4c1f8fb9447a3f04f9f36cb7f17856103b24353c875bc2ca36a77f866613d03d11daa3fff95a45de727d4a7a977eb022f5f4df31f09590d7
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
<!--
|
3
|
+
Include mermaid-js scripts.
|
4
|
+
-->
|
5
|
+
{% if page.mermaid || site.mermaid %}
|
6
|
+
<script>
|
7
|
+
var MERMAID_CONFIG = window.MERMAID_CONFIG ?? {{ site.mermaid_config | jsonify }} ?? { };
|
8
|
+
|
9
|
+
// Load defaults
|
10
|
+
MERMAID_CONFIG.startOnLoad ??= true;
|
11
|
+
MERMAID_CONFIG.htmlLabels ??= true;
|
12
|
+
</script>
|
13
|
+
<script
|
14
|
+
defer
|
15
|
+
src="{{ "assets/plugin/mermaid/mermaid.js" | relative_url }}"
|
16
|
+
{% comment %}
|
17
|
+
Here, we're also calling "mermaid.init()" because of the defer attribute — it's
|
18
|
+
possible that the window has already loaded (and thus startOnLoad may have no effect).
|
19
|
+
{% endcomment %}
|
20
|
+
onload="mermaid.initialize(MERMAID_CONFIG); mermaid.init();" >
|
21
|
+
</script>
|
22
|
+
|
23
|
+
{% endif %}
|
24
|
+
|
data/_layouts/default.html
CHANGED
@@ -17,7 +17,7 @@ title: Untitled
|
|
17
17
|
{% endif %}
|
18
18
|
<script type="module" src="{{ "assets/js/main.mjs" | relative_url }}"></script>
|
19
19
|
|
20
|
-
{% include
|
20
|
+
{% include extern_library_imports.html %}
|
21
21
|
|
22
22
|
<title>{{ page.title }} — {{ site.title }}</title>
|
23
23
|
</head>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
---
|
2
|
+
layout: default
|
3
|
+
---
|
4
|
+
|
5
|
+
<!-- Defines a https://remarkjs.com/ slideshow -->
|
6
|
+
|
7
|
+
{% assign frame_resource_url = 'assets/html/remark_presentation_frame.html.resource' | relative_url %}
|
8
|
+
|
9
|
+
<main class="slideshow-mode">
|
10
|
+
<iframe
|
11
|
+
id="presentation_frame"
|
12
|
+
onload="window.presentationFrameLoaded = true;"></iframe>
|
13
|
+
</main>
|
14
|
+
|
15
|
+
<script defer type="module">
|
16
|
+
import slideshow from "{{ 'assets/js/layout/remark_slideshow.mjs' | relative_url }}";
|
17
|
+
|
18
|
+
let presentationFrame = document.querySelector("#presentation_frame");
|
19
|
+
presentationFrame.src = {{ frame_resource_url | jsonify }};
|
20
|
+
window.presentationFrameLoaded = false;
|
21
|
+
|
22
|
+
let config = {{ page.remark_presentation_config | default: site.remark_presentation_config | jsonify }};
|
23
|
+
config ??= {};
|
24
|
+
|
25
|
+
config.source = {{ page.content | jsonify }};
|
26
|
+
|
27
|
+
// Library imports intended for this page: Forward to the frame.
|
28
|
+
let libraryImports = unescape(`
|
29
|
+
{% capture library_imports %}
|
30
|
+
{% include extern_library_imports.html %}
|
31
|
+
{% endcapture %}
|
32
|
+
{{ library_imports | url_encode | replace: "+", " " }}
|
33
|
+
`);
|
34
|
+
|
35
|
+
// To be called after the presentation iframe loads.
|
36
|
+
window.initSlides = () => {
|
37
|
+
let presentationDoc = presentationFrame.contentDocument;
|
38
|
+
let presentationWin = presentationFrame.contentWindow;
|
39
|
+
|
40
|
+
// Ensure that onload doesn't get called again if the frame reloads.
|
41
|
+
presentationFrame.onload = null;
|
42
|
+
|
43
|
+
// Re-create the document, add the library imports to it.
|
44
|
+
let slideshowHtml = presentationDoc.documentElement.outerHTML;
|
45
|
+
|
46
|
+
presentationDoc.open();
|
47
|
+
presentationDoc.write("<!DOCTYPE html>");
|
48
|
+
presentationDoc.write(slideshowHtml);
|
49
|
+
presentationDoc.write(libraryImports);
|
50
|
+
presentationDoc.write(`
|
51
|
+
<${"script"}>
|
52
|
+
if (window.MERMAID_CONFIG) {
|
53
|
+
window.MERMAID_CONFIG.useMaxWidth = true;
|
54
|
+
window.MERMAID_CONFIG.cloneCssStyles = false;
|
55
|
+
window.MERMAID_CONFIG.flowchart = { useMaxWidth: true };
|
56
|
+
}
|
57
|
+
</${"script"}>
|
58
|
+
`);
|
59
|
+
presentationDoc.close();
|
60
|
+
(async () => {
|
61
|
+
await slideshow.start(presentationFrame.contentWindow, config);
|
62
|
+
|
63
|
+
// Ensure that mermaid has already been run!
|
64
|
+
if (presentationWin.mermaid) {
|
65
|
+
presentationWin.mermaid.init();
|
66
|
+
console.log("Ran mermaid!");
|
67
|
+
}
|
68
|
+
})();
|
69
|
+
};
|
70
|
+
|
71
|
+
if (window.presentationFrameLoaded) {
|
72
|
+
initSlides();
|
73
|
+
}
|
74
|
+
else {
|
75
|
+
presentationFrame.onload = initSlides;
|
76
|
+
}
|
77
|
+
</script>
|
data/_sass/_hljs.scss
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
// Created while referencing rouge-github.scss as published
|
3
|
+
// at https://github.com/pages-themes/cayman/blob/master/_sass/rouge-github.scss
|
4
|
+
|
5
|
+
.hljs {
|
6
|
+
color: var(--syntax-normal-fg);
|
7
|
+
display: block;
|
8
|
+
white-space: pre-wrap;
|
9
|
+
|
10
|
+
// Keywords and literals
|
11
|
+
.hljs-keyword, .hljs-literal {
|
12
|
+
color: var(--syntax-keyword-fg);
|
13
|
+
}
|
14
|
+
|
15
|
+
.hljs-literal {
|
16
|
+
font-weight: bold;
|
17
|
+
color: var(--syntax-name-fg);
|
18
|
+
}
|
19
|
+
|
20
|
+
// Strings
|
21
|
+
.hljs-string {
|
22
|
+
color: var(--syntax-string-fg);
|
23
|
+
}
|
24
|
+
|
25
|
+
// Numbers
|
26
|
+
.hljs-number {
|
27
|
+
color: var(--syntax-number-fg);
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
// Comments
|
32
|
+
.hljs-comment {
|
33
|
+
color: var(--syntax-comment-fg);
|
34
|
+
font-style: italic;
|
35
|
+
}
|
36
|
+
}
|
data/_sass/_layout.scss
CHANGED
@@ -154,6 +154,21 @@ nav#post_next_prev {
|
|
154
154
|
flex-grow: 1;
|
155
155
|
}
|
156
156
|
|
157
|
+
main.slideshow-mode {
|
158
|
+
position: absolute;
|
159
|
+
display: flex;
|
160
|
+
|
161
|
+
left: 0;
|
162
|
+
right: 0;
|
163
|
+
top: var(--header-effective-height);
|
164
|
+
bottom: 0;
|
165
|
+
|
166
|
+
iframe {
|
167
|
+
flex-grow: 1;
|
168
|
+
border: none;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
157
172
|
// On mobile devices,
|
158
173
|
@media screen and (max-width: $site-content-preferred-width) {
|
159
174
|
.main-container {
|
data/_sass/_nav.scss
CHANGED
@@ -41,6 +41,14 @@ body > header {
|
|
41
41
|
}
|
42
42
|
}
|
43
43
|
|
44
|
+
:root {
|
45
|
+
--header-effective-height: var(--header-height);
|
46
|
+
}
|
47
|
+
|
48
|
+
:root.minimizedNavHeader {
|
49
|
+
--header-effective-height: 0px;
|
50
|
+
}
|
51
|
+
|
44
52
|
:root.minimizedNavHeader body > header {
|
45
53
|
> *:not(#toggle_sidebar_btn) {
|
46
54
|
opacity: 0;
|
data/_sass/hematite.scss
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
---
|
2
|
+
---
|
3
|
+
<!-- Using a .html.resource file to prevent Jekyll from changing the extension -->
|
4
|
+
|
5
|
+
<!DOCTYPE html>
|
6
|
+
<html class="lightTheme" lang="{{ page.lang | default: site.lang | default: "en-US" }}">
|
7
|
+
<head>
|
8
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
9
|
+
<meta charset="utf-8"/>
|
10
|
+
<!-- Content of remark presentation iframe. Based on https://github.com/gnab/remark/wiki#getting-started -->
|
11
|
+
<style>
|
12
|
+
/* Stylesheet mostly taken from https://github.com/gnab/remark/wiki#getting-started */
|
13
|
+
@import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
|
14
|
+
@import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic);
|
15
|
+
@import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic);
|
16
|
+
|
17
|
+
body { font-family: 'Droid Serif'; }
|
18
|
+
h1, h2, h3 {
|
19
|
+
font-family: 'Yanone Kaffeesatz';
|
20
|
+
font-weight: normal;
|
21
|
+
}
|
22
|
+
|
23
|
+
.remark-code, .remark-inline-code { font-family: 'Ubuntu Mono'; }
|
24
|
+
|
25
|
+
/* Show hidden slides to allow preprocessors (e.g. mermaid) to
|
26
|
+
properly account for container size.
|
27
|
+
*/
|
28
|
+
@media screen {
|
29
|
+
.remark-slide-container:not(.remark-visible) {
|
30
|
+
display: block;
|
31
|
+
box-shadow: none;
|
32
|
+
z-index: -999;
|
33
|
+
visibility: hidden;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
nav {
|
38
|
+
display: flex;
|
39
|
+
|
40
|
+
position: absolute;
|
41
|
+
z-index: 10000;
|
42
|
+
bottom: 0;
|
43
|
+
left: 0;
|
44
|
+
right: 0;
|
45
|
+
opacity: 0.8;
|
46
|
+
}
|
47
|
+
|
48
|
+
nav:hover, nav:focus-within {
|
49
|
+
opacity: 1;
|
50
|
+
}
|
51
|
+
|
52
|
+
@media print {
|
53
|
+
nav {
|
54
|
+
display: none;
|
55
|
+
}
|
56
|
+
|
57
|
+
/* Prevent parts of slides from being cut off on Firefox */
|
58
|
+
.remark-slide-scaler {
|
59
|
+
transform: none !important;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
</style>
|
64
|
+
<link rel="stylesheet" href="{{ 'assets/style_only_syntax.css' | relative_url }}" />
|
65
|
+
</head>
|
66
|
+
<body>
|
67
|
+
</body>
|
68
|
+
<script src="{{ 'assets/plugin/remark_presenter/remark.min.js' | relative_url }}"></script>
|
69
|
+
</html>
|
data/assets/js/UrlHelper.mjs
CHANGED
@@ -62,6 +62,22 @@ var UrlHelper = {
|
|
62
62
|
return url.substring(hashLoc, argsStart);
|
63
63
|
},
|
64
64
|
|
65
|
+
// Replace an existing hash with a [newHash]. The new hash
|
66
|
+
//
|
67
|
+
withReplacedHash(url, newHash) {
|
68
|
+
let hashLoc = url.lastIndexOf('#');
|
69
|
+
|
70
|
+
if ((newHash + "").charAt(0) != '#') {
|
71
|
+
newHash = `#${newHash}`;
|
72
|
+
}
|
73
|
+
|
74
|
+
if (hashLoc == -1) {
|
75
|
+
return url + newHash;
|
76
|
+
}
|
77
|
+
|
78
|
+
return url.substring(0, hashLoc) + newHash;
|
79
|
+
},
|
80
|
+
|
65
81
|
/// Remove metadata encoded in the given URL and returns
|
66
82
|
/// it.
|
67
83
|
/// If [url] is undefined, uses the page's URL.
|
@@ -115,4 +131,8 @@ assertEq("Trimming metadata",
|
|
115
131
|
"https://example.com/"
|
116
132
|
);
|
117
133
|
|
134
|
+
assertEq("Replacing a hash",
|
135
|
+
UrlHelper.withReplacedHash("https://example.com/#foo", "bar"),
|
136
|
+
"https://example.com/#bar");
|
137
|
+
|
118
138
|
export default UrlHelper;
|
@@ -0,0 +1,164 @@
|
|
1
|
+
import { getUrlQuery, Searcher } from "../search.mjs";
|
2
|
+
import { stringLookup } from "../strings.mjs";
|
3
|
+
import UrlHelper from "../UrlHelper.mjs";
|
4
|
+
|
5
|
+
function isInPresenterMode(targetDoc) {
|
6
|
+
return targetDoc.body.classList.contains("remark-presenter-mode");
|
7
|
+
}
|
8
|
+
|
9
|
+
function focusSearchResult(targetWin, query, targetMatchNo, slideshow, searcher) {
|
10
|
+
let matchesFound = 0;
|
11
|
+
for (const slide of slideshow.getSlides()) {
|
12
|
+
let targetText = (slide.content ?? []).join("\n");
|
13
|
+
let currentSlideNo = slide.getSlideIndex() + 1;
|
14
|
+
|
15
|
+
matchesFound += searcher.getNumberOfMatches(query, targetText);
|
16
|
+
console.log("Considering ", targetText, " with ", matchesFound, "matches found so far");
|
17
|
+
|
18
|
+
if (matchesFound > targetMatchNo) {
|
19
|
+
slideshow.gotoSlide(currentSlideNo);
|
20
|
+
return;
|
21
|
+
}
|
22
|
+
|
23
|
+
// Now search the notes!
|
24
|
+
if (slide.notes) {
|
25
|
+
targetText = slide.notes.join("\n");
|
26
|
+
matchesFound += searcher.getNumberOfMatches(query, targetText);
|
27
|
+
if (matchesFound > targetMatchNo) {
|
28
|
+
slideshow.gotoSlide(currentSlideNo);
|
29
|
+
|
30
|
+
if (!isInPresenterMode(targetWin.document)) {
|
31
|
+
slideshow.togglePresenterMode();
|
32
|
+
}
|
33
|
+
return;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
console.log("Found ", matchesFound, " matches, which is less than the target of ", targetMatchNo);
|
39
|
+
slideshow.gotoLastSlide();
|
40
|
+
return -1;
|
41
|
+
}
|
42
|
+
|
43
|
+
/// Show a search result the user requested through the
|
44
|
+
/// page's URL.
|
45
|
+
function focusSearchResultFromUrl(targetWin, slideshow) {
|
46
|
+
let { query, resultIndex } = getUrlQuery() ?? {};
|
47
|
+
let index = resultIndex;
|
48
|
+
|
49
|
+
if (query === undefined || index === undefined) {
|
50
|
+
return;
|
51
|
+
}
|
52
|
+
|
53
|
+
console.log("Focusing a search result: ", query, index);
|
54
|
+
|
55
|
+
let searcher = new Searcher();
|
56
|
+
focusSearchResult(targetWin, query, index, slideshow, searcher);
|
57
|
+
}
|
58
|
+
|
59
|
+
function focusSlideFromHash(slideshow) {
|
60
|
+
let hash = UrlHelper.getPageHash();
|
61
|
+
if (!hash) {
|
62
|
+
return;
|
63
|
+
}
|
64
|
+
|
65
|
+
let targetSlide = parseInt(hash.substring(1));
|
66
|
+
if (targetSlide) {
|
67
|
+
console.log("Navigating to slide", targetSlide);
|
68
|
+
slideshow.gotoSlide(targetSlide);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
async function main(targetWindow, config) {
|
73
|
+
if (!targetWindow.remark) {
|
74
|
+
// Wait for page load if remark isn't available yet.
|
75
|
+
await (new Promise(resolve => {
|
76
|
+
targetWindow.addEventListener('load', resolve);
|
77
|
+
}));
|
78
|
+
}
|
79
|
+
|
80
|
+
// See https://remarkjs.com/#8
|
81
|
+
let slideshow = targetWindow.remark.create(config);
|
82
|
+
|
83
|
+
// For debugging
|
84
|
+
window.slideshow_debug = slideshow;
|
85
|
+
|
86
|
+
targetWindow.focus();
|
87
|
+
|
88
|
+
addExtendedControls(targetWindow, slideshow);
|
89
|
+
focusSearchResultFromUrl(targetWindow, slideshow);
|
90
|
+
|
91
|
+
targetWindow.history.replaceState(null, targetWindow.location.href);
|
92
|
+
let targetWinHistory = targetWindow.history.state;
|
93
|
+
|
94
|
+
focusSlideFromHash(slideshow);
|
95
|
+
|
96
|
+
window.addEventListener('hashchange', () => {
|
97
|
+
focusSlideFromHash(slideshow);
|
98
|
+
});
|
99
|
+
|
100
|
+
slideshow.on('showSlide', function(newSlide) {
|
101
|
+
if (!newSlide) {
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
let hashId = newSlide.getSlideIndex() + 1;
|
105
|
+
|
106
|
+
// Update the window's URL to match that of the interior
|
107
|
+
// (e.g. slide 3 = #3).
|
108
|
+
// Try not to have the forward/back arrows go forward/back in the iframe's history.
|
109
|
+
targetWindow.history.replaceState(
|
110
|
+
targetWinHistory,
|
111
|
+
UrlHelper.withReplacedHash(targetWindow.location.href, hashId));
|
112
|
+
window.location.hash = hashId;
|
113
|
+
});
|
114
|
+
}
|
115
|
+
|
116
|
+
/// Apply minor adjustments to the default remark layout
|
117
|
+
function addExtendedControls(targetWindow, slideshow) {
|
118
|
+
let slideContainer = targetWindow.document.querySelector(".remark-slides-area");
|
119
|
+
|
120
|
+
// Announce changes to the slide (e.g. going to the next slide).
|
121
|
+
slideContainer.setAttribute("aria-live", "polite");
|
122
|
+
|
123
|
+
// Add next/previous buttons
|
124
|
+
let nextSlideBtn = targetWindow.document.createElement("button");
|
125
|
+
let prevSlideBtn = targetWindow.document.createElement("button");
|
126
|
+
let printBtn = targetWindow.document.createElement("button");
|
127
|
+
let spacer = targetWindow.document.createElement("div");
|
128
|
+
|
129
|
+
let nav = targetWindow.document.createElement("nav");
|
130
|
+
|
131
|
+
nextSlideBtn.innerText = stringLookup('btn_next_slide');
|
132
|
+
prevSlideBtn.innerText = stringLookup('btn_prev_slide');
|
133
|
+
printBtn.innerText = stringLookup('btn_print');
|
134
|
+
|
135
|
+
spacer.style.flexGrow = 1;
|
136
|
+
|
137
|
+
nextSlideBtn.onclick = () => {
|
138
|
+
slideshow.gotoNextSlide();
|
139
|
+
};
|
140
|
+
|
141
|
+
prevSlideBtn.onclick = () => {
|
142
|
+
slideshow.gotoPreviousSlide();
|
143
|
+
};
|
144
|
+
|
145
|
+
printBtn.onclick = () => {
|
146
|
+
targetWindow.print();
|
147
|
+
};
|
148
|
+
|
149
|
+
slideshow.on('showSlide', function(newSlide) {
|
150
|
+
if (!newSlide) {
|
151
|
+
return;
|
152
|
+
}
|
153
|
+
|
154
|
+
prevSlideBtn.disabled = (newSlide.getSlideIndex() == 0);
|
155
|
+
nextSlideBtn.disabled = (newSlide.getSlideIndex() + 1 >= slideshow.getSlideCount());
|
156
|
+
});
|
157
|
+
|
158
|
+
|
159
|
+
|
160
|
+
nav.replaceChildren(prevSlideBtn, nextSlideBtn, spacer, printBtn);
|
161
|
+
targetWindow.document.body.appendChild(nav);
|
162
|
+
}
|
163
|
+
|
164
|
+
export default { start: main };
|
data/assets/js/search.mjs
CHANGED
@@ -54,6 +54,25 @@ class Searcher {
|
|
54
54
|
|
55
55
|
}
|
56
56
|
|
57
|
+
/// Get number of full matches for [query] in [text].
|
58
|
+
/// @precondition [text] is already in a searchable (i.e.
|
59
|
+
/// filtered) form.
|
60
|
+
getNumberOfMatches(query, text) {
|
61
|
+
query = query.toLowerCase();
|
62
|
+
|
63
|
+
return text.toLowerCase().split(query).length - 1;
|
64
|
+
}
|
65
|
+
|
66
|
+
/// Get the index of the first full match for [query] in
|
67
|
+
/// [text], starting at [startPos]. Returns -1 if no match
|
68
|
+
/// is found.
|
69
|
+
/// @precondition [text] is already in a searchable form.
|
70
|
+
getIdxOfFirstMatch(query, text, startPos) {
|
71
|
+
query = query.toLowerCase();
|
72
|
+
|
73
|
+
return text.toLowerCase().indexOf(query, startPos);
|
74
|
+
}
|
75
|
+
|
57
76
|
/// Get the container element for the [n]th search
|
58
77
|
/// result for [query] in the given [elem].
|
59
78
|
/// Returns an Element.
|
@@ -77,7 +96,7 @@ class Searcher {
|
|
77
96
|
|
78
97
|
// If the current node is a leaf,
|
79
98
|
if (elem.childNodes.length == 0) {
|
80
|
-
let numMatches =
|
99
|
+
let numMatches = this.getNumberOfMatches(query, searchText);
|
81
100
|
n -= numMatches;
|
82
101
|
|
83
102
|
// If we've considered enough matches,
|
@@ -154,7 +173,7 @@ class Searcher {
|
|
154
173
|
index ++;
|
155
174
|
pageData.numMatches ++;
|
156
175
|
startPos = matchLoc + query.length;
|
157
|
-
matchLoc =
|
176
|
+
matchLoc = this.getIdxOfFirstMatch(query, toSearch, startPos);
|
158
177
|
}
|
159
178
|
}
|
160
179
|
|
@@ -185,26 +204,52 @@ class Searcher {
|
|
185
204
|
}
|
186
205
|
}
|
187
206
|
|
207
|
+
/// Extract a search query and index from the current page's URL.
|
208
|
+
function getUrlQuery() {
|
209
|
+
let query, resultIndex;
|
210
|
+
|
211
|
+
let urlArgs = UrlHelper.getPageArgs();
|
212
|
+
let pageHash = UrlHelper.getPageHash();
|
213
|
+
|
214
|
+
if (urlArgs === null) {
|
215
|
+
return;
|
216
|
+
}
|
217
|
+
|
218
|
+
// The page's hash also causes scrolling. Don't focus
|
219
|
+
// if the page has a hash.
|
220
|
+
if (pageHash != null) {
|
221
|
+
return;
|
222
|
+
}
|
223
|
+
|
224
|
+
query = urlArgs.query;
|
225
|
+
resultIndex = parseInt(urlArgs.index);
|
226
|
+
|
227
|
+
if (urlArgs.index === undefined) {
|
228
|
+
resultIndex = 0;
|
229
|
+
}
|
230
|
+
|
231
|
+
return { query, resultIndex };
|
232
|
+
}
|
233
|
+
|
188
234
|
/// Scrolls to and shows the search result for the given [query]
|
189
235
|
/// If neither [query] nor [resultIndex] are given, attempt to get them from
|
190
236
|
/// the page's arguments (i.e. from https://example.com/...?search=...,n=...).
|
191
237
|
function focusSearchResult(searcher, elem, query, resultIndex) {
|
192
|
-
if (
|
193
|
-
|
194
|
-
|
238
|
+
if (elem === undefined) {
|
239
|
+
console.warn(`focusSearchResult requires [elem] to function. Not focusing a result.`);
|
240
|
+
return;
|
241
|
+
}
|
195
242
|
|
196
|
-
|
197
|
-
|
198
|
-
}
|
243
|
+
if (query === undefined && resultIndex === undefined) {
|
244
|
+
let pageArgs = getUrlQuery();
|
199
245
|
|
200
|
-
//
|
201
|
-
|
202
|
-
if (pageHash != null) {
|
246
|
+
// No args, nothing to focus.
|
247
|
+
if (!pageArgs) {
|
203
248
|
return;
|
204
249
|
}
|
205
250
|
|
206
|
-
|
207
|
-
|
251
|
+
resultIndex = pageArgs.resultIndex;
|
252
|
+
query = pageArgs.query;
|
208
253
|
|
209
254
|
if (isNaN(resultIndex)) {
|
210
255
|
console.warn("Unable to navigate to result. Given idx is NaN");
|
@@ -355,4 +400,4 @@ function handleSearch(searcher) {
|
|
355
400
|
}
|
356
401
|
|
357
402
|
export default handleSearch;
|
358
|
-
export { handleSearch, Searcher };
|
403
|
+
export { handleSearch, getUrlQuery, Searcher };
|