rdoc-babel 1.2.1 → 1.4.1

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.
@@ -1,5 +1,12 @@
1
+ /*
2
+ Script for the left iframe.
3
+ */
1
4
 
2
- document.addEventListener('DOMContentLoaded', function () {
5
+ // CSS must be loaded before finding the geography of indexes
6
+ // document.addEventListener('DOMContentLoaded', function () {
7
+ window.addEventListener('load', function () {
8
+
9
+ // --- class for index info
3
10
 
4
11
  // the info about an index block
5
12
  // index blocks are like this:
@@ -44,6 +51,7 @@ document.addEventListener('DOMContentLoaded', function () {
44
51
  const style = window.getComputedStyle(this.list);
45
52
  const listHeightPadding = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
46
53
  this.entries = this.list.getElementsByTagName('p');
54
+ this.links = this.list.getElementsByTagName('a');
47
55
  this.entryCount = this.entries.length;
48
56
  // height of one entry
49
57
  const listRect = this.list.getBoundingClientRect();
@@ -58,10 +66,12 @@ document.addEventListener('DOMContentLoaded', function () {
58
66
  this.searchBox = null;
59
67
  this.list = null;
60
68
  this.entries = [];
69
+ this.links = [];
61
70
  this.entryCount = 0;
62
71
  this.entryHeight = 0;
63
72
  this.fixedHeight = 0;
64
73
  }
74
+ // height of this index before the current resize
65
75
  this.prevHeight = null;
66
76
  }
67
77
 
@@ -72,14 +82,14 @@ document.addEventListener('DOMContentLoaded', function () {
72
82
  return 0;
73
83
  }
74
84
 
85
+ // enough room for 2 entries
75
86
  get minHeight() {
76
87
  return this.fixedHeight + 2 * this.entryHeight;
77
88
  }
78
89
 
79
90
  setHeight(height, { resetPrev = false } = {}) {
80
91
  if (!this.div) return;
81
- // floor multiple of 0.01
82
- const h = (~~((height - this.fixedHeight) * 100)) / 100;
92
+ const h = height - this.fixedHeight;
83
93
  if (this.prevHeight === null || resetPrev) {
84
94
  // no previous or reset asked: set prev = current
85
95
  this.list.style.height = `${h}px`;
@@ -92,58 +102,185 @@ document.addEventListener('DOMContentLoaded', function () {
92
102
  }
93
103
  }
94
104
 
105
+ // save the layout of this index, including the search text
106
+ saveInfo(storage) {
107
+ if (!this.div) return;
108
+ storage.setItem(`${this.name}Index.prevHeight`, this.prevHeight);
109
+ storage.setItem(`${this.name}Index.styleHeight`, this.list.style.height);
110
+ storage.setItem(`${this.name}Index.scrollTop`, this.list.scrollTop);
111
+ if (this.searchBox) {
112
+ storage.setItem(`${this.name}Index.searchWidth`, this.searchBox.style.width);
113
+ storage.setItem(`${this.name}Index.searchText`, this.searchBox.value || '');
114
+ }
115
+ }
116
+
117
+ // restore a previously saved layout & search text
118
+ restoreInfo(storage) {
119
+ if (!this.div) return;
120
+ this.prevHeight = storage.getItem(`${this.name}Index.prevHeight`);
121
+ this.list.style.height = storage.getItem(`${this.name}Index.styleHeight`);
122
+ this.list.scrollTop = storage.getItem(`${this.name}Index.scrollTop`);
123
+ if (this.searchBox) {
124
+ this.searchBox.style.width = storage.getItem(`${this.name}Index.searchWidth`);
125
+ this.searchBox.value = storage.getItem(`${this.name}Index.searchText`);
126
+ }
127
+ }
128
+
129
+ // highlight the entry for <a> aNode and make sure it is visible
130
+ setCurrent(aNode) {
131
+ for (const a of this.links)
132
+ if (a === aNode) {
133
+ a.classList.add('current-main');
134
+ this.ensureVisible(a);
135
+ }
136
+ else
137
+ a.classList.remove('current-main');
138
+ }
139
+
140
+ ensureVisible(a) {
141
+ const aTopOffset = a.offsetTop - this.links[0].offsetTop;
142
+ const aHeight = a.getBoundingClientRect().height;
143
+ const offScreen =
144
+ // not (completely) visible because above
145
+ aTopOffset < this.list.scrollTop ||
146
+ // not (completely) visible because below
147
+ aTopOffset + aHeight > this.list.scrollTop + this.list.clientHeight
148
+ ;
149
+ // position in the middle of the list
150
+ if (offScreen)
151
+ this.list.scrollTop = aTopOffset - this.list.clientHeight / 2;
152
+ }
153
+
95
154
  }
96
155
 
156
+ // --- global setup
157
+
97
158
  const fileIndex = new Index('file');
98
159
  const classIndex = new Index('class');
99
160
  const methodIndex = new Index('method');
100
161
 
101
162
  setupResizing();
102
- setupSearches();
103
- setupHighlighting();
163
+
164
+ // --- setup the vertical resizing of indexes by dragging
104
165
 
105
166
  const fileClassResizer = document.getElementById('file-class-resizer'); // may be null
106
167
  const classMethodResizer = document.getElementById('class-method-resizer');
107
168
 
108
- frameResized(true);
169
+ if (!restoreLayoutInfo())
170
+ frameResized(true);
171
+ setupSearches();
109
172
 
110
173
  if (fileClassResizer)
111
174
  fileClassResizer.addEventListener('mousedown', function(e) {startDrag(e, fileIndex, classIndex)});
112
175
  classMethodResizer.addEventListener('mousedown', function(e) {startDrag(e, classIndex, methodIndex)});
113
176
 
114
- // handle highlighting in main frame when the document
115
- // in the main frame does not change
116
- function highlightTarget(e) {
117
- // this is relative:
118
- var target_href = e.target.getAttribute('href');
119
- // this is absolute:
120
- var current_href = top.mainFrame.location.href;
121
- var parts = target_href.split('#');
122
- var target_path = parts[0];
123
- var target_id = parts[1];
124
- var current_path = top.mainFrame.location.pathname;
125
- var i = current_path.length - target_path.length;
126
- var x = current_path.substring(i);
127
- if (i > 0 && current_path.substring(i) == target_path)
128
- highlightElement(target_id);
177
+ // --- highlight of the current file/class/method
178
+
179
+ highlightCurrentIndexEntries();
180
+
181
+ function highlightCurrentIndexEntries() {
182
+
183
+ // /C:/docs/ruby/32/core/files/toc_core_md.html
184
+ // /C:/docs/ruby/32/core/classes/Process.html
185
+ const currentPath = window.parent.location.pathname;
186
+ const index = (currentPath.indexOf('/classes/') < 0) ? fileIndex : classIndex;
187
+
188
+ for (const a of index.links) {
189
+ const href = a.getAttribute('href');
190
+ if (currentPath.endsWith(href)) {
191
+ index.setCurrent(a);
192
+ break;
193
+ }
194
+ }
195
+
196
+ if (index === fileIndex)
197
+ return;
198
+
199
+ setCurrentMethod(currentPath);
200
+ }
201
+
202
+ function setCurrentMethod(currentPath) {
203
+ const hash = window.parent.location.hash;
204
+ if (!hash) return;
205
+
206
+ const currentMethod = `${currentPath}${hash}`;
207
+ for (const a of methodIndex.links) {
208
+ const href = a.getAttribute('href');
209
+ if (currentMethod.endsWith(href)) {
210
+ methodIndex.setCurrent(a);
211
+ break;
212
+ }
213
+ }
214
+ }
215
+
216
+ // --- when clicking on a link, remember the scroll positions & search state of each index
217
+
218
+ for (const a of fileIndex.links)
219
+ a.addEventListener('click', saveLayoutInfo);
220
+ for (const a of classIndex.links)
221
+ a.addEventListener('click', saveLayoutInfo);
222
+ for (const a of methodIndex.links)
223
+ a.addEventListener('click', saveLayoutInfo);
224
+
225
+ function saveLayoutInfo(e) {
226
+ const s = window.parent.sessionStorage;
227
+ s.setItem('infoSaved', 'true');
228
+
229
+ // the width of the left frame
230
+ const width = window.parent.document.getElementById('left-container').style.width;
231
+ s.setItem('left.frameWidth', width);
232
+
233
+ // position of the left frame resizer
234
+ const left = window.parent.document.getElementById('resizer').style.left;
235
+ s.setItem('left.resizerLeft', left);
236
+
237
+ // the height & position of each index
238
+ fileIndex.saveInfo(s);
239
+ classIndex.saveInfo(s);
240
+ methodIndex.saveInfo(s);
241
+ }
242
+
243
+ // restores the saved layout and returns true, or returns false if no saved layout
244
+ function restoreLayoutInfo() {
245
+ // restore previous scroll positions & search states if any
246
+ const s = window.parent.sessionStorage;
247
+ const saved = s.getItem(`infoSaved`);
248
+ if (saved) {
249
+ window.parent.document.getElementById('left-container').style.width = s.getItem('left.frameWidth');
250
+ window.parent.document.getElementById('resizer').style.left = s.getItem('left.resizerLeft');
251
+ fileIndex.restoreInfo(s);
252
+ classIndex.restoreInfo(s);
253
+ methodIndex.restoreInfo(s);
254
+ placeResizers();
255
+ }
256
+ return saved === 'true';
257
+ }
258
+
259
+ // --- when navigating inside the same main document, handle current method highlighting
260
+
261
+ window.parent.addEventListener('hashchange', mainHashChanged);
262
+
263
+ // highlight the new method in the main frame, and in the method index
264
+ function mainHashChanged(e) {
265
+ const id = e.newURL.split('#')[1];
266
+ highlightElement(id);
267
+ setCurrentMethod(window.parent.location.pathname);
129
268
  }
130
269
 
131
270
  // highlight the passed id in the main frame
132
271
  function highlightElement(id) {
133
- const doc = top.mainFrame.document;
272
+ const doc = window.parent.document;
134
273
  for (const h of doc.querySelectorAll('.highlighted'))
135
274
  h.classList.remove('highlighted');
275
+ if (id === 'header')
276
+ return;
136
277
  const e = doc.getElementById(id);
137
278
  if (e)
138
279
  e.classList.add('highlighted');
139
280
  }
140
281
 
141
- function setupHighlighting () {
142
- for (const a of methodIndex.list.querySelectorAll('a[href*="#method-"]'))
143
- a.addEventListener('click', highlightTarget);
144
- }
282
+ // --- search boxes
145
283
 
146
- // setup search boxes
147
284
  function setupSearches() {
148
285
  const helpText = 'filter...';
149
286
  setupSearch(classIndex.searchBox, classIndex.entries, helpText);
@@ -157,6 +294,13 @@ document.addEventListener('DOMContentLoaded', function () {
157
294
  searchBox.setAttribute('placeholder', helpText);
158
295
  }
159
296
 
297
+ // --- vertical manual resizing of the indexes
298
+ // div#file-index
299
+ // div#file-class-resizer (absent if the above div is not there)
300
+ // div#class-index
301
+ // div#class-method-resizer
302
+ // div#method-index
303
+
160
304
  let startY; // where drag begins
161
305
  let startTopIndex; // index object above when drag begins
162
306
  let startBottomIndex; // index object below when drag begins
@@ -164,7 +308,7 @@ document.addEventListener('DOMContentLoaded', function () {
164
308
  let startBottomHeight; // height of the index below when drag begins
165
309
 
166
310
  function startDrag(e, topIndex, bottomIndex) {
167
- startY = e.clientY; // FIXME: probably int
311
+ startY = e.clientY;
168
312
  startTopIndex = topIndex;
169
313
  startBottomIndex = bottomIndex;
170
314
  startTopHeight = topIndex.currentHeight;
@@ -189,6 +333,7 @@ document.addEventListener('DOMContentLoaded', function () {
189
333
  placeResizers();
190
334
  }
191
335
 
336
+ // place the vertical resizers between their indexes
192
337
  function placeResizers() {
193
338
  if (fileClassResizer)
194
339
  fileClassResizer.style.top = `${fileIndex.currentHeight}px`;
@@ -196,13 +341,14 @@ document.addEventListener('DOMContentLoaded', function () {
196
341
  classMethodResizer.style.top = `${fileIndex.currentHeight + classIndex.currentHeight}px`;
197
342
  }
198
343
 
199
- // setup callbacks resizing vertically & horizontally;
344
+ // --- resizing of the window by the user or by the left/main resizer in the main frame
345
+
200
346
  function setupResizing() {
201
- // callback on resize event
202
347
  window.addEventListener('resize', function(e) { frameResized(false) });
203
348
  }
204
349
 
205
350
  // resize the left index blocks
351
+ // if initial is true, this is the first resize, called above when loading
206
352
  function frameResized(initial) {
207
353
 
208
354
  resizeSearchField(classIndex);
@@ -222,7 +368,7 @@ document.addEventListener('DOMContentLoaded', function () {
222
368
  const box = indexBlock.searchBox;
223
369
  const text = indexBlock.text;
224
370
 
225
- const frameWidth = top.indexFrame.visualViewport.width;
371
+ const frameWidth = window.visualViewport.width;
226
372
 
227
373
  const textRect = text.getBoundingClientRect();
228
374
  const textWidth = textRect.width;
@@ -250,6 +396,11 @@ document.addEventListener('DOMContentLoaded', function () {
250
396
  }
251
397
 
252
398
  // returns the initial heights of index blocks for the current window size
399
+ // if the combined height of the 3 indexes is more than the window height:
400
+ // - shrinks the file index to 5 entries if more
401
+ // - shrinks the method index to 33%, but leaving at least 5 entries visible
402
+ // - shrinks the class index to the available space, but leaving at least 5 entries visible
403
+ // so if very little height, the result may not fit
253
404
  function initialHeights() {
254
405
 
255
406
  // returned information
@@ -316,6 +467,7 @@ document.addEventListener('DOMContentLoaded', function () {
316
467
  }
317
468
 
318
469
  // returns the updated heights of index blocks for the current window size
470
+ // resizes each index proportionally
319
471
  function updatedHeights() {
320
472
 
321
473
  let frameHeight = window.visualViewport.height;
@@ -354,7 +506,6 @@ document.addEventListener('DOMContentLoaded', function () {
354
506
  heights.methods = frameHeight - heights.files - heights.classes;
355
507
 
356
508
  return heights;
357
-
358
509
  }
359
510
 
360
511
  });
@@ -1,29 +1,188 @@
1
1
  /*
2
- * Script for the main frame.
3
- * An adaptation of (darkfish.js by Michael Granger)
4
- */
2
+ Script for the main frame.
3
+ Originally an adaptation of (darkfish.js by Michael Granger)
4
+ */
5
5
 
6
6
  document.addEventListener('DOMContentLoaded', function () {
7
7
 
8
+ const leftContainer = document.getElementById('left-container');
9
+ const leftFrame = document.getElementById('left-frame');
10
+ const resizer = document.getElementById('resizer');
11
+
12
+ setupTOC();
8
13
  setupShowSource();
9
14
  setupShowConstantValue();
10
15
  setupShowAllFiles();
11
16
  setupInnerLinksHighlight();
12
17
  highlightUrlHash();
13
18
 
14
- // <div id="method-c-_httpdate" class="method-detail">
15
- // <div class="method-heading">
16
- // ...
17
- // </div>
18
- // <div class="method-description">
19
- // ...
20
- // <pre class="method-source-code">
21
- // (code)
22
- // </pre>
19
+ // --- resizing of the left frame
20
+
21
+ resizer.addEventListener('mousedown', startWidthResize);
22
+
23
+ let startX; // where drag begins
24
+ let startWidth; // width of left frame when drag begins
25
+ function startWidthResize(e) {
26
+ startX = e.clientX;
27
+ startWidth = leftContainer.getBoundingClientRect().width;
28
+ document.documentElement.addEventListener('mousemove', doWidthResize);
29
+ document.documentElement.addEventListener('mouseup', stopWidthResize);
30
+ leftFrame.contentDocument.addEventListener('mousemove', doWidthResize);
31
+ leftFrame.contentDocument.addEventListener('mouseup', stopWidthResize);
32
+ }
33
+
34
+ function stopWidthResize(e) {
35
+ document.documentElement.removeEventListener('mousemove', doWidthResize);
36
+ document.documentElement.removeEventListener('mouseup', stopWidthResize);
37
+ leftFrame.contentDocument.removeEventListener('mousemove', doWidthResize);
38
+ leftFrame.contentDocument.removeEventListener('mouseup', stopWidthResize);
39
+ }
40
+
41
+ function doWidthResize(e) {
42
+ const dX = e.clientX - startX;
43
+ const newWidth = startWidth + dX;
44
+ if (newWidth > 100)
45
+ leftContainer.style.width = `${newWidth}px`;
46
+ }
47
+
48
+ // --- TOC button setup
49
+
50
+ function setupTOC() {
51
+
52
+ const nav = document.createElement('nav');
53
+ nav.innerHTML = `
54
+ <button id="menu-button">
55
+ <img class="icon-hamburger">
56
+ <img class="icon-cross">
57
+ </button>
58
+ <div id="menu-content" class="hidden">
59
+ <p id="menu-top">
60
+ <a href="#header">(top)</a>
61
+ </p>
62
+ <div id="toc-content">
63
+ </div>
64
+ </div>
65
+ `;
66
+ const main = document.getElementById('main-container');
67
+ const doc = document.getElementById('documentation');
68
+ main.insertBefore(nav, doc);
69
+
70
+ const menu_button = document.getElementById('menu-button');
71
+ const menu_content = document.getElementById('menu-content');
72
+ const toc_content = document.getElementById('toc-content');
73
+
74
+ function createTOC() {
75
+ const headings = document.body.querySelectorAll('h1, h2, h3, h4, h5, h6');
76
+ if (headings.length == 0)
77
+ return false;
78
+ let generated_id_index = 0;
79
+ const first_summary_heading = firstSummaryHeading();
80
+ // console.log(`${headings.length} headings`);
81
+ for (const h of headings) {
82
+ // console.log(`name = ${h.tagName} id=${h.id} text = ${h.innerText}`);
83
+ if (!h.id) {
84
+ generated_id_index++;
85
+ h.setAttribute('id', `toc-auto-id-${generated_id_index}`);
86
+ }
87
+ if (h === first_summary_heading) {
88
+ const sp = document.createElement('p');
89
+ toc_content.appendChild(sp);
90
+ sp.setAttribute('class', 'h2');
91
+ sp.textContent = 'Summary';
92
+ }
93
+ // <p class="toc2"><a href="#label-News">News</a></p>
94
+ const p = document.createElement('p');
95
+ toc_content.appendChild(p);
96
+ p.setAttribute('class', h.tagName.toLowerCase());
97
+ const a = document.createElement('a');
98
+ p.appendChild(a);
99
+ a.setAttribute('href', `#${h.id}`);
100
+ a.textContent = h.innerText; // h.textContent;
101
+ }
102
+
103
+ return true;
104
+ }
105
+
106
+ function firstSummaryHeading() {
107
+ const ids = [
108
+ 'class-aliases',
109
+ 'method-list',
110
+ 'namespace-list',
111
+ 'include-list',
112
+ 'constant-list',
113
+ 'external-aliases',
114
+ 'class-attributes',
115
+ 'instance-attributes',
116
+ ];
117
+ for (const id of ids) {
118
+ const h = document.querySelector(`#${id} h3`);
119
+ if (h) return h;
120
+ }
121
+ return null;
122
+ }
123
+
124
+ if (!createTOC())
125
+ nav.classList.add('hidden');
126
+
127
+ // show/hide the menu/toc
128
+ menu_button.addEventListener('click', function(e) {
129
+ e.preventDefault();
130
+ toggleMenu();
131
+ });
132
+
133
+ // hide the menu on Escape
134
+ document.addEventListener('keydown', function(e) {
135
+ if (!isMenuVisible()) return;
136
+ if (e.code == 'Escape' && !e.ctrlKey) {
137
+ e.preventDefault();
138
+ hideMenu();
139
+ }
140
+ });
141
+
142
+ // hide the menu when clicking outside of nav
143
+ document.addEventListener('click', function (event) {
144
+ if (!isMenuVisible()) return;
145
+ if (!nav.contains(event.target))
146
+ hideMenu();
147
+ });
148
+
149
+ function hideMenu() {
150
+ menu_content.classList.add('hidden');
151
+ menu_button.classList.remove('show-close');
152
+ }
153
+
154
+ function showMenu() {
155
+ menu_content.classList.remove('hidden');
156
+ menu_button.classList.add('show-close');
157
+ }
158
+
159
+ function isMenuVisible() {
160
+ return !menu_content.classList.contains('hidden');
161
+ }
162
+
163
+ function toggleMenu() {
164
+ if (isMenuVisible())
165
+ hideMenu();
166
+ else
167
+ showMenu();
168
+ }
169
+
170
+ }
171
+
172
+ // --- toggle method source display
23
173
 
24
174
  function setupShowSource() {
25
175
  for (const node of document.querySelectorAll('.method-heading'))
26
176
  node.addEventListener('click', toggleSource);
177
+ // <div id="method-c-_httpdate" class="method-detail">
178
+ // <div class="method-heading">
179
+ // ...
180
+ // </div>
181
+ // <div class="method-description">
182
+ // ...
183
+ // <pre class="method-source-code">
184
+ // (code)
185
+ // </pre>
27
186
  }
28
187
 
29
188
  function toggleSource(e) {
@@ -33,22 +192,23 @@ document.addEventListener('DOMContentLoaded', function () {
33
192
  slideToggle(source);
34
193
  }
35
194
 
36
- // <tr id="MONTHNAMES" class="const-display">
37
- // <td class="const-name"><p>MONTHNAMES</p></td>
38
- // <td class="const-desc">
39
- // <p>An array of strings of full month names in <a href="English.html"><code>English</code></a>. The first element is nil.</p>
40
- // </td>
41
- // <td><p class="click-advice">click to toggle value</p></td>
42
- // </tr>
43
- // <tr class="const-value">
44
- // <td></td>
45
- // <td><pre>mk_ary_of_str(13, monthnames)</pre></td>
46
- // <td></td>
47
- // </tr>
195
+ // --- toggle constant value display
48
196
 
49
197
  function setupShowConstantValue() {
50
198
  for (const node of document.querySelectorAll('#constant-list .const-display'))
51
199
  node.addEventListener('click', toggleValue);
200
+ // <tr id="MONTHNAMES" class="const-display">
201
+ // <td class="const-name"><p>MONTHNAMES</p></td>
202
+ // <td class="const-desc">
203
+ // <p>An array of strings of full month names in <a href="English.html"><code>English</code></a>. The first element is nil.</p>
204
+ // </td>
205
+ // <td><p class="click-advice">click to toggle value</p></td>
206
+ // </tr>
207
+ // <tr class="const-value">
208
+ // <td></td>
209
+ // <td><pre>mk_ary_of_str(13, monthnames)</pre></td>
210
+ // <td></td>
211
+ // </tr>
52
212
  }
53
213
 
54
214
  function toggleValue(e) {
@@ -56,13 +216,15 @@ document.addEventListener('DOMContentLoaded', function () {
56
216
  toggleDisplay(e.currentTarget.nextElementSibling, 'table-row');
57
217
  }
58
218
 
59
- function toggleDisplay(element, visible) {
60
- if (element.style.display !== visible)
61
- element.style.display = visible;
219
+ function toggleDisplay(element, visibleDisplayValue) {
220
+ if (element.style.display !== visibleDisplayValue)
221
+ element.style.display = visibleDisplayValue;
62
222
  else
63
223
  element.style.display = 'none';
64
224
  }
65
225
 
226
+ // --- toggle the display of all files where the class/module if defined
227
+
66
228
  function setupShowAllFiles() {
67
229
  const allFiles = document.getElementById('all-files');
68
230
  if (!allFiles) return;
@@ -80,11 +242,7 @@ document.addEventListener('DOMContentLoaded', function () {
80
242
  });
81
243
  }
82
244
 
83
- function highlightUrlHash() {
84
- const h = window.location.hash;
85
- if (h && h.length > 1)
86
- highlightElement(h.substring(1));
87
- }
245
+ // --- highlight the target method when clicked from an inner method link
88
246
 
89
247
  function setupInnerLinksHighlight() {
90
248
  for (const a of document.querySelectorAll('a[href*="#"]'))
@@ -93,7 +251,7 @@ document.addEventListener('DOMContentLoaded', function () {
93
251
 
94
252
  function highlightTarget(e) {
95
253
  const href = e.currentTarget.getAttribute('href');
96
- const match =/#(.*)/.exec(href);
254
+ const match = /#(.*)/.exec(href);
97
255
  if (match && match[1].length > 1)
98
256
  highlightElement(match[1]);
99
257
  }
@@ -101,11 +259,22 @@ document.addEventListener('DOMContentLoaded', function () {
101
259
  function highlightElement(id) {
102
260
  for (const h of document.querySelectorAll('.highlighted'))
103
261
  h.classList.remove('highlighted');
262
+ if (id === 'header')
263
+ return;
104
264
  const e = document.getElementById(id);
105
265
  if (e)
106
266
  e.classList.add('highlighted');
107
267
  }
108
268
 
269
+ // --- highlight the method if present in the location hash
270
+
271
+ function highlightUrlHash() {
272
+ const h = window.location.hash;
273
+ if (h && h.length > 1)
274
+ highlightElement(h.substring(1));
275
+ }
276
+
277
+ // --- smooth toggling (for source code)
109
278
  // from plain JS slideToggle https://github.com/ericbutler555/plain-js-slidetoggle
110
279
 
111
280
  function slideToggle(element, duration) {
@@ -7,26 +7,31 @@
7
7
  https://github.com/riklomas/quicksearch
8
8
  */
9
9
 
10
+ // setup quick search:
11
+ // - input: the text search box
12
+ // - paragraphs: the paragraphs to show/hide
10
13
  function setupQuickSearch(input, paragraphs) {
11
14
 
12
- let timeoutId;
13
- const textCache = [];
14
- const delay = 100;
15
-
16
- // console.log(`setupQuickSearch(${input}, ${paragraphs.length} paragraphs)`)
15
+ let timeoutId; // setTimeout id on key up
16
+ const delay = 100; // delay for search after key up
17
+ const textCache = []; // cache of search texts (lowercase)
17
18
 
19
+ // fill the cache
18
20
  for (const node of paragraphs)
19
21
  textCache.push(node.querySelector('a').textContent.toLowerCase().trim());
20
22
 
23
+ // filter with the current content of the input box
21
24
  filter();
22
25
 
23
26
  input.addEventListener('keyup', trigger);
24
27
 
28
+ // calls filter() after the delay
25
29
  function trigger() {
26
30
  window.clearTimeout(timeoutId);
27
31
  timeoutId = window.setTimeout(filter, delay);
28
32
  }
29
33
 
34
+ // perform filtering
30
35
  function filter() {
31
36
 
32
37
  if (!input.value) {
@@ -43,6 +48,7 @@ function setupQuickSearch(input, paragraphs) {
43
48
  paragraphs[i].style.display = 'none';
44
49
  }
45
50
 
51
+ // does text contain all words?
46
52
  function containsAll(text, words) {
47
53
  for (const word of words)
48
54
  if (text.indexOf(word) === -1)