rdoc-babel 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- textCache.push(node.textContent.toLowerCase().trim());
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)