hematite 0.1.11 → 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 450bc32b43ae3caf2e08adc9fda7154419b1fc617014d3e8cb75fe0708591664
4
- data.tar.gz: d388090f953e7e25f4a7232242d46bb6cb115abf9cb739fa8b45494b1f032fef
3
+ metadata.gz: e7d0e540229aa59560164e505b1f9c56bfc06410483f121f0f37de4b35f14648
4
+ data.tar.gz: e134ec448a27e0e1ab0b6c6fb058a71248d9a59c182956a6b873834c150eb120
5
5
  SHA512:
6
- metadata.gz: b3ed73686b9e75eeedd0c4033adaef7f2f7749d0554f08252803f8f908f6173efc869074944e042814760cf9d0150de0d39c92fc56d7bfdec87c250f9e6bb485
7
- data.tar.gz: b147929fea494e53217aa1dfb96957c0aa27d7b5aa4a812e0fe0d1997151d9a8eebfa6788d05871e640c2e6e5085494f221d3372d4304872d1736649dcf106c7
6
+ metadata.gz: f0629528aa7d95d26b68b488a2b5110625471856310273f74db557a8b543db35ccb072af00ffed6195d61e7eef1ed50ac755f55b034fe4c6fa41c22c7e0d35df
7
+ data.tar.gz: 31c8354bfa13a6ce9fb8531835c367214a6419d20ddb5246092271f2caf81fded7e0aba03db2e52e0166b40500d1bb2a4b0f4d31856feb94375c840f095b4f79
@@ -0,0 +1,70 @@
1
+ <svg
2
+ viewBox="0 0 64.0 64.0"
3
+ version="1.1"
4
+ class="loading-icon-root"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ xmlns:svg="http://www.w3.org/2000/svg">
7
+ <style>
8
+ .loading-icon-root {
9
+ cursor: pointer;
10
+ width: 64px;
11
+ height: 64px;
12
+ }
13
+
14
+ .loading-icon-root .rect {
15
+ fill: var(--primary-text-color);
16
+
17
+ transform: translate(0, 0);
18
+ transform-origin: 32px 32px;
19
+ animation: loading-icon-anim 1.5s ease infinite;
20
+ }
21
+
22
+ .loading-icon-root .rect2 {
23
+ animation-delay: 0.25s;
24
+ }
25
+
26
+ .loading-icon-root .rect3 {
27
+ animation-delay: 0.5s;
28
+ }
29
+
30
+ .loading-icon-root .rect4 {
31
+ animation-delay: 0.65s;
32
+ }
33
+
34
+ @keyframes loading-icon-anim {
35
+ 0% { transform: rotate(0); }
36
+ 100% { transform: rotate(-360deg) scale(1, 1); }
37
+ }
38
+ </style>
39
+ <g
40
+ id="layer1">
41
+ <rect
42
+ width="9"
43
+ height="9"
44
+ x="10"
45
+ y="10"
46
+ ry="4.5"
47
+ class="rect" />
48
+ <rect
49
+ width="9"
50
+ height="9"
51
+ x="10"
52
+ y="10"
53
+ ry="4.5"
54
+ class="rect rect2" />
55
+ <rect
56
+ width="9"
57
+ height="9"
58
+ x="10"
59
+ y="10"
60
+ ry="4.5"
61
+ class="rect rect3" />
62
+ <rect
63
+ width="9"
64
+ height="9"
65
+ x="10"
66
+ y="10"
67
+ ry="4.5"
68
+ class="rect rect4" />
69
+ </g>
70
+ </svg>
@@ -1,5 +1,5 @@
1
1
 
2
2
  @font-face {
3
3
  font-family: FoulisGreek;
4
- src: url({{ assets/fonts/FoulisGreek.ttf | absolute_path }});
4
+ src: url({{ "assets/fonts/FoulisGreek.ttf" | relative_url }});
5
5
  }
@@ -14,6 +14,12 @@ layout: default
14
14
 
15
15
  {% assign frame_resource_url = 'assets/html/remark_presentation_frame.html' | relative_url %}
16
16
 
17
+ <div class='loading-icon-container'>
18
+ <center>
19
+ {% include img/loading_icon.svg %}
20
+ </center>
21
+ </div>
22
+
17
23
  <main class="slideshow-mode">
18
24
  <iframe
19
25
  id="presentation_frame"
@@ -113,13 +119,18 @@ layout: default
113
119
  `);
114
120
 
115
121
  presentationWin.initPresentation = (async () => {
116
- presentationDoc.title = presentationFrame.title;
117
- await slideshow.start(presentationFrame.contentWindow, config);
118
-
119
- // Ensure that mermaid has already been run!
120
- if (presentationWin.mermaid) {
121
- presentationWin.mermaid.init();
122
- console.log("Ran mermaid!");
122
+ try {
123
+ presentationDoc.title = presentationFrame.title;
124
+ await slideshow.start(presentationFrame.contentWindow, config);
125
+
126
+ // Ensure that mermaid has already been run!
127
+ if (presentationWin.mermaid) {
128
+ presentationWin.mermaid.init();
129
+ }
130
+ }
131
+ finally {
132
+ // Hide the loading icon
133
+ document.querySelector(".loading-icon-container")?.remove();
123
134
  }
124
135
  });
125
136
 
data/_sass/_layout.scss CHANGED
@@ -178,6 +178,9 @@ main.slideshow-mode {
178
178
  // Transition immediately!
179
179
  transition: width 0s ease, max-width 0s ease;
180
180
  }
181
+
182
+ // Don't scroll, let the inner frame do that.
183
+ overflow: hidden;
181
184
  }
182
185
 
183
186
  // On mobile devices,
data/_sass/_nav.scss CHANGED
@@ -124,7 +124,6 @@ nav.sidebar {
124
124
  transform: scale(0, 1) translate(-30px, 0);
125
125
 
126
126
  color: var(--primary-text-color);
127
- background-color: var(--primary-background-color);
128
127
  box-shadow: 0 0 2px var(--shadow-color-light);
129
128
  padding: $navbar-padding;
130
129
 
@@ -138,6 +137,15 @@ nav.sidebar {
138
137
  transform-origin: left;
139
138
  transition: opacity 0.5s ease, transform 0.5s ease, width 0.5s ease;
140
139
 
140
+ // Work around a rendering bug in Safari — apply the background color to both
141
+ // the results pane and the container.
142
+ //
143
+ // Without this, Safari may display elements of pinned-pages in the same
144
+ // location as the search results.
145
+ &, .search-results {
146
+ background-color: var(--primary-background-color);
147
+ }
148
+
141
149
 
142
150
  // Container with search box and button
143
151
  > .search-container {
@@ -20,7 +20,15 @@ h1, h2, h3 {
20
20
  font-weight: normal;
21
21
  }
22
22
 
23
- .remark-code, .remark-inline-code { font-family: 'Ubuntu Mono', monospace; }
23
+ .remark-code, .remark-inline-code, body.mdSourceView {
24
+ font-family: 'Ubuntu Mono', monospace;
25
+ }
26
+
27
+ body.mdSourceView {
28
+ padding-top: 50px;
29
+ background: white;
30
+ color: black;
31
+ }
24
32
 
25
33
  /* Show hidden slides to allow preprocessors (e.g. mermaid) to
26
34
  properly account for container size.
@@ -0,0 +1,37 @@
1
+
2
+ import { assertEq } from "./assertions.mjs";
3
+
4
+ let htmlReplacements = [
5
+ [ /[&]/g, '&amp;' ],
6
+ [ /[<]/g, '&lt;' ],
7
+ [ /[>]/g, '&gt;' ],
8
+ ];
9
+
10
+ var EscapeHelper = {
11
+ escapeHTML(text) {
12
+ for (const item of htmlReplacements) {
13
+ text = text.replace(item[0], item[1]);
14
+ }
15
+
16
+ return text;
17
+ },
18
+ // Escape for usage within a regex expression
19
+ escapeRegex(text) {
20
+ return text.replace(/([\*\(\).\[\]\{\}\^\$\-\=\;\:\'\"\\])/g, '\\$1'); //'
21
+ },
22
+ // Escape text for usage within a replacement pattern
23
+ // E.g. "foobar".replaceAll("foo", text);
24
+ escapeReplacePattern(text) {
25
+ return text.replace(/\$/g, '$$$$');
26
+ }
27
+ };
28
+
29
+ assertEq("Test escape <", EscapeHelper.escapeHTML('<'), '&lt;');
30
+ assertEq("Test escape multiple <", EscapeHelper.escapeHTML('<a></a>'), '&lt;a&gt;&lt;/a&gt;');
31
+ assertEq("Test escape >", EscapeHelper.escapeHTML('>'), '&gt;');
32
+ assertEq("Test escape identity", EscapeHelper.escapeHTML('Hello, world.'), 'Hello, world.');
33
+ assertEq("Test regex escape simple", EscapeHelper.escapeRegex(".*"), "\\.\\*");
34
+ assertEq("Test regex more complicated escape", EscapeHelper.escapeRegex("This, is a test... :)"), "This, is a test\\.\\.\\. \\:\\)");
35
+ assertEq("Test replace pattern escape", EscapeHelper.escapeReplacePattern("0$ and 24 cents"), "0$$ and 24 cents");
36
+
37
+ export default EscapeHelper;
@@ -37,6 +37,12 @@ function createTagLinks(page) {
37
37
  let label = document.querySelector("#post_tags > #tag_header_lbl");
38
38
  label.innerText = stringLookup(`tags`);
39
39
 
40
+ // Don't show the tag container if there aren't any tags
41
+ if (!page.tags || page.tags.length == 0) {
42
+ container.style.display = "none";
43
+ return;
44
+ }
45
+
40
46
  for (const tag of page.tags) {
41
47
  let tagElem = document.createElement("a");
42
48
  tagElem.style.display = "inline-block";
@@ -47,11 +53,6 @@ function createTagLinks(page) {
47
53
 
48
54
  container.appendChild(tagElem);
49
55
  }
50
-
51
- // Don't show the tag container if there aren't any tags
52
- if (page.tags.length == 0) {
53
- container.style.display = "none";
54
- }
55
56
  }
56
57
 
57
58
  function fillDate(page) {
@@ -77,6 +77,20 @@ function focusSlideFromHash(slideshow) {
77
77
  }
78
78
  }
79
79
 
80
+ /// Returns true iff the slide viewer should only display markdown.
81
+ function shouldOnlyDisplayMd() {
82
+ let pageArgs = UrlHelper.getPageArgs();
83
+ if (!pageArgs) {
84
+ return false;
85
+ }
86
+
87
+ if (pageArgs.md_only) {
88
+ return true;
89
+ }
90
+
91
+ return false;
92
+ }
93
+
80
94
  async function main(targetWindow, config) {
81
95
  // True if touch navigation is enabled.
82
96
  let usingCustomTouchNav = false;
@@ -88,6 +102,13 @@ async function main(targetWindow, config) {
88
102
  }));
89
103
  }
90
104
 
105
+ // If the user has requested that only the page's markdown be shown,
106
+ if (shouldOnlyDisplayMd()) {
107
+ targetWindow.document.body.innerText = config?.source;
108
+ targetWindow.document.body.classList.add('mdSourceView');
109
+ return;
110
+ }
111
+
91
112
  // Customize touchscreen navigation — the default remark
92
113
  // navigation can break buttons, zooming.
93
114
  if (config?.navigation?.touch === true
@@ -150,6 +171,7 @@ async function main(targetWindow, config) {
150
171
 
151
172
  if (handlingGesture) {
152
173
  evt.preventDefault();
174
+ elemContainer.setPointerCapture();
153
175
  }
154
176
  }
155
177
  });
@@ -205,16 +227,20 @@ function addExtendedControls(targetWindow, slideshow) {
205
227
  targetWindow.print();
206
228
  };
207
229
 
230
+ let updateBtns = (slideIdx) => {
231
+ prevSlideBtn.disabled = (slideIdx == 0);
232
+ nextSlideBtn.disabled = (slideIdx + 1 >= slideshow.getSlideCount());
233
+ };
234
+
208
235
  slideshow.on('showSlide', function(newSlide) {
209
236
  if (!newSlide) {
210
237
  return;
211
238
  }
212
239
 
213
- prevSlideBtn.disabled = (newSlide.getSlideIndex() == 0);
214
- nextSlideBtn.disabled = (newSlide.getSlideIndex() + 1 >= slideshow.getSlideCount());
240
+ updateBtns(newSlide.getSlideIndex());
215
241
  });
216
242
 
217
-
243
+ updateBtns(0);
218
244
 
219
245
  nav.replaceChildren(prevSlideBtn, nextSlideBtn, spacer, printBtn);
220
246
  targetWindow.document.body.appendChild(nav);
data/assets/js/search.mjs CHANGED
@@ -7,6 +7,7 @@ import { expandContainingDropdowns } from "./dropdownExpander.mjs";
7
7
  import AnimationUtil from "./AnimationUtil.mjs";
8
8
  import AsyncUtil from "./AsyncUtil.mjs";
9
9
  import UrlHelper from "./UrlHelper.mjs";
10
+ import EscapeHelper from "./EscapeHelper.mjs";
10
11
 
11
12
  const PAGE_DATA_URL = `{{ "/assets/search_data.json" | relative_url }}`;
12
13
  const MATCHING_TITLE_PRIORITY_INCREMENT = 15;
@@ -50,15 +51,25 @@ class Searcher {
50
51
  .replaceAll(/[&]ldquo;/g, '"')
51
52
  .replaceAll(/[&]rdquo;/g, '"')
52
53
  .replaceAll(/[&]amp;/g, "&")
54
+ // Remove the astrisks around **bolded** text
55
+ .replaceAll(/(\s|^)[*]{2}[^*]+[*]{2}(\s|$)/g, "$1$2$3")
56
+ // Remove the backticks around `text` that is code-formatted
57
+ .replaceAll(/(\s|^)[`]([^`]+)[`](\s|$)/g, "$1$2$3") // `
53
58
  .replaceAll(/\s+/g, ' ');
54
59
 
55
60
  }
56
61
 
62
+ filterQuery_(query) {
63
+ return query
64
+ .toLowerCase()
65
+ .replaceAll(/\s+/g, ' ');
66
+ }
67
+
57
68
  /// Get number of full matches for [query] in [text].
58
69
  /// @precondition [text] is already in a searchable (i.e.
59
70
  /// filtered) form.
60
71
  getNumberOfMatches(query, text) {
61
- query = query.toLowerCase();
72
+ query = this.filterQuery_(query);
62
73
 
63
74
  return text.toLowerCase().split(query).length - 1;
64
75
  }
@@ -68,7 +79,7 @@ class Searcher {
68
79
  /// is found.
69
80
  /// @precondition [text] is already in a searchable form.
70
81
  getIdxOfFirstMatch(query, text, startPos) {
71
- query = query.toLowerCase();
82
+ query = this.filterQuery_(query);
72
83
 
73
84
  return text.toLowerCase().indexOf(query, startPos);
74
85
  }
@@ -167,6 +178,7 @@ class Searcher {
167
178
  results.push({
168
179
  index,
169
180
  context,
181
+ matchIndex: matchLoc,
170
182
  pageData,
171
183
  });
172
184
 
@@ -295,17 +307,35 @@ function handleSearch(searcher) {
295
307
 
296
308
  searchResults.replaceChildren(descriptionElem);
297
309
 
310
+ // Adds HTML to bold/italicize all results in [context].
311
+ // [context] is HTML-escaped before adding to the result.
312
+ const boldResults = (context) => {
313
+ let result = "";
314
+ let queryRegex = new RegExp(`(${EscapeHelper.escapeRegex(query)})`, `ig`);
315
+ let contextHTML = "";
316
+ let lastIdx = 0;
317
+ for (const match of context.matchAll(queryRegex)) {
318
+ result += EscapeHelper.escapeHTML(context.substring(lastIdx, match.index));
319
+ result += "<b><i>" + EscapeHelper.escapeHTML(match[0]) + "</i></b>";
320
+ lastIdx = match.index + match[0].length;
321
+ }
322
+ result += EscapeHelper.escapeHTML(context.substring(lastIdx));
323
+
324
+ return result;
325
+ };
326
+
298
327
  for (const result of results) {
299
328
  let link = document.createElement("a");
300
- let context = document.createElement("div");
301
- context.classList.add('context');
329
+ let contextElem = document.createElement("div");
330
+ contextElem.classList.add('context');
302
331
 
303
332
  link.innerText = result.pageData.title ?? stringLookup(`untitled`);
304
333
  link.href =
305
334
  result.pageData.url + `?query=${escape(query)},index=${result.index}`;
306
- context.innerText = result.context;
307
335
 
308
- link.appendChild(context);
336
+ contextElem.innerHTML = boldResults(result.context);
337
+
338
+ link.appendChild(contextElem);
309
339
  searchResults.appendChild(link);
310
340
  }
311
341
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hematite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henry Heino
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-28 00:00:00.000000000 Z
11
+ date: 2022-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -37,6 +37,7 @@ files:
37
37
  - _includes/extern_library_imports.html
38
38
  - _includes/footer.html
39
39
  - _includes/img/hamburger_menu.svg
40
+ - _includes/img/loading_icon.svg
40
41
  - _includes/img/search_icon.svg
41
42
  - _includes/katex_includes.html
42
43
  - _includes/mermaid_includes.html
@@ -74,6 +75,7 @@ files:
74
75
  - assets/js/AnimationUtil.mjs
75
76
  - assets/js/AsyncUtil.mjs
76
77
  - assets/js/DateUtil.mjs
78
+ - assets/js/EscapeHelper.mjs
77
79
  - assets/js/PageAlert.mjs
78
80
  - assets/js/Settings.mjs
79
81
  - assets/js/UrlHelper.mjs