rdoc-babel 1.1.1 → 1.2.0

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,311 +1,360 @@
1
1
 
2
- // returns the info about an index block
3
- // index blocks are like this:
4
- // div
5
- // .title
6
- // span
7
- // input
8
- // .entries
9
- // p ...
10
- function indexElements(type) {
11
- var id = '#' + type + '-index';
12
- var block = $(id);
13
- if (block.length > 0)
14
- return {
15
- div: block,
16
- fullHeight: block.height(),
17
- title: $(id + ' > .title'),
18
- searchBox: $(id + ' > .title input'),
19
- list: $(id + ' > .entries'),
20
- entries: $(id + ' > .entries > p')
21
- };
22
- else
23
- return {
24
- div: block,
25
- fullHeight: 0,
26
- title: block,
27
- searchBox: block,
28
- list: block,
29
- entries: block
30
- };
31
- }
32
-
33
- // handle highlighting in main frame when the document
34
- // in the main frame does not change
35
- function highlightTarget(e) {
36
- // this is relative:
37
- var target_href = $(e.target).attr('href');
38
- // this is absolute:
39
- var current_href = top.mainFrame.location.href;
40
- //console.debug('left: target href=%s', target_href);
41
- //console.debug('left: current href=%s', current_href);
42
- var parts = target_href.split('#');
43
- var target_path = parts[0], target_id = '#' + parts[1];
44
- var current_path = top.mainFrame.location.pathname;
45
- //console.debug('left: target path=%s', target_path);
46
- //console.debug('left: current path=%s', current_path);
47
- var i = current_path.length - target_path.length;
48
- var x = current_path.substring(i);
49
- if (i > 0 && current_path.substring(i) == target_path) {
50
- highlightElement(target_id);
51
- }
52
- }
53
-
54
- // highlight the passed id in the main frame
55
- function highlightElement(id) {
56
- //console.debug('Highlighting %s in main frame.', id);
57
- var context = top.mainFrame.document;
58
- $('.highlighted', context).removeClass('highlighted');
59
- var e = $(id, context);
60
- if (e.length > 0) {
61
- e.addClass('highlighted');
62
- //console.debug('added class "highlighted" to %s.', id);
63
- }
64
- else {
65
- //console.debug('not found: %s', id);
66
- }
67
- };
68
-
69
- function setupHighlighting (methodList) {
70
- methodList.find('a[href*="#method-"]').click(highlightTarget);
71
- }
72
-
73
- // setup search boxes
74
- function setupSearches(classIndex, methodIndex) {
75
- var helpText = 'filter...';
76
- setupSearch(classIndex.searchBox, classIndex.entries, helpText);
77
- setupSearch(methodIndex.searchBox, methodIndex.entries, helpText);
78
- }
79
-
80
- function setupSearch(searchBox, entries, helpText) {
81
- // hook quicksearch
82
- searchBox.quicksearch(entries);
83
- // set helper text
84
- searchBox[0].value = helpText;
85
- searchBox.focus(function() {
86
- if (this.value == helpText) {
87
- this.value = '';
88
- $(this).addClass('active');
2
+ document.addEventListener('DOMContentLoaded', function () {
3
+
4
+ // the info about an index block
5
+ // index blocks are like this:
6
+
7
+ // div#file-index
8
+ // div.title
9
+ // span.text
10
+ // Files
11
+ // div.entries
12
+ // <p><a href="files/standard_library_rdoc.html">doc/standard_library.rdoc</a></p>
13
+ // ...
14
+
15
+ // div#class-index
16
+ // div.title
17
+ // span.text
18
+ // Classes
19
+ // input.search-field
20
+ // div.entries
21
+ // p.class
22
+ // span.type
23
+ // C
24
+ // <a href="classes/ACL.html">ACL</a>
25
+ // ...
26
+
27
+ class Index {
28
+
29
+ constructor(name) {
30
+ this.name = name;
31
+ this.div = document.getElementById(`${name}-index`);
32
+ if (this.div) {
33
+ // the height fitting the whole content
34
+ this.fullHeight = this.div.getBoundingClientRect().height;
35
+ this.title = this.div.querySelector('div.title');
36
+ this.text = this.title.querySelector('span.text');
37
+ // null for files:
38
+ this.searchBox = this.title.querySelector('input');
39
+ if (this.searchBox)
40
+ this.fullBoxWidth = this.searchBox.getBoundingClientRect().width;
41
+ else
42
+ this.fullBoxWidth = 0;
43
+ this.list = this.div.querySelector('div.entries');
44
+ const style = window.getComputedStyle(this.list);
45
+ const listHeightPadding = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
46
+ this.entries = this.list.getElementsByTagName('p');
47
+ this.entryCount = this.entries.length;
48
+ // height of one entry
49
+ const listRect = this.list.getBoundingClientRect();
50
+ this.entryHeight = (listRect.height - listHeightPadding) / this.entries.length;
51
+ // amount of vertical space in an index other than the entries themselves
52
+ // (title, paddings, etc.)
53
+ this.fixedHeight = this.fullHeight - listRect.height + listHeightPadding;
54
+ }
55
+ else {
56
+ this.fullHeight = 0;
57
+ this.title = null;
58
+ this.searchBox = null;
59
+ this.list = null;
60
+ this.entries = [];
61
+ this.entryCount = 0;
62
+ this.entryHeight = 0;
63
+ this.fixedHeight = 0;
64
+ }
65
+ this.prevHeight = null;
66
+ }
67
+
68
+ get currentHeight() {
69
+ if (this.div)
70
+ return this.div.getBoundingClientRect().height;
71
+ else
72
+ return 0;
73
+ }
74
+
75
+ get minHeight() {
76
+ return this.fixedHeight + 2 * this.entryHeight;
89
77
  }
90
- });
91
- searchBox.blur(function() {
92
- if (this.value == '') {
93
- this.value = helpText;
94
- $(this).removeClass('active');
78
+
79
+ setHeight(height, { resetPrev = false } = {}) {
80
+ if (!this.div) return;
81
+ // floor multiple of 0.01
82
+ const h = (~~((height - this.fixedHeight) * 100)) / 100;
83
+ if (this.prevHeight === null || resetPrev) {
84
+ // no previous or reset asked: set prev = current
85
+ this.list.style.height = `${h}px`;
86
+ this.prevHeight = this.currentHeight;
87
+ }
88
+ else {
89
+ // save previous value before setting the current height
90
+ this.prevHeight = this.currentHeight;
91
+ this.list.style.height = `${h}px`;
92
+ }
95
93
  }
96
- });
97
- }
98
-
99
- // setup callbacks resizing vertically & horizontally;
100
- function setupResizing(classIndex, methodIndex) {
101
-
102
- // height of one entry
103
- var entryHeight = classIndex.list.height() / classIndex.entries.length;
104
-
105
- // amount of vertical space in an index other than the entries themselves
106
- // (title, paddings, etc.)
107
- var delta = classIndex.fullHeight - classIndex.list.height();
108
-
109
- //console.debug('entryHeight = %s', entryHeight);
110
- //console.debug('delta = %s', delta);
111
-
112
- // reference information for resize
113
- var resizeInfo = {
114
- fileIndex: indexElements('file'),
115
- classIndex: classIndex,
116
- methodIndex: methodIndex,
117
- entryHeight: entryHeight,
118
- fullBoxWidth: classIndex.searchBox.width(),
119
- delta: delta
120
- };
121
-
122
- //console.debug('fileIndex.fullHeight = %s', resizeInfo.fileIndex.fullHeight);
123
- //console.debug('classIndex.fullHeight = %s', resizeInfo.classIndex.fullHeight);
124
- //console.debug('methodIndex.fullHeight = %s', resizeInfo.methodIndex.fullHeight);
125
-
126
- // callback on resize event
127
- $(window).resize(function() {
128
- frameResized(resizeInfo);
129
- });
130
-
131
- // initial resize
132
- frameResized(resizeInfo);
133
- }
134
-
135
- // smart resize of left index blocks
136
- function frameResized(info) {
137
- //console.debug('resize fired');
138
- //console.debug('=== updating widths ===');
139
- resizeSearchField(info.classIndex, info.fullBoxWidth);
140
- resizeSearchField(info.methodIndex, info.fullBoxWidth);
141
- //console.debug('=== updating heights ===');
142
- var heights = updatedHeights(info);
143
- //console.debug('new file height = %s', heights.files);
144
- //console.debug('new class height = %s', heights.classes);
145
- //console.debug('new method height = %s', heights.methods);
146
- var delta = info.delta, speed = info.slideSpeed;
147
- info.fileIndex.list.height(heights.files - delta, speed);
148
- info.classIndex.list.height(heights.classes - delta, speed);
149
- info.methodIndex.list.height(heights.methods - delta, speed);
150
- //console.debug('=== resizing done ===');
151
- }
152
-
153
- // resize a search field to avoid overlapping text (if possible)
154
- function resizeSearchField(indexBlock, fullBoxWidth) {
155
- //console.debug('resizing search field for %s', indexBlock.title.text());
156
- var container = indexBlock.title,
157
- box = indexBlock.searchBox;
158
- var text = container.find('.text');
159
-
160
- var frameWidth = $(top.indexFrame).width();
161
- //console.debug('frameWidth = %s', frameWidth);
162
-
163
- var textWidth = text.width();
164
- var textRight = text.position().left + textWidth;
165
- //console.debug('text: width = %s, right = %s', textWidth, textRight);
166
-
167
- var boxLeft = box.position().left;
168
- var boxWidth = box.width();
169
- //console.debug('box: left = %s, width = %s', boxLeft, boxWidth);
170
-
171
- var boxRight = boxLeft + boxWidth;
172
- var offset = frameWidth - boxRight;
173
- //console.debug('box: right = %s, offset = %s', boxRight, offset);
174
-
175
- // try to ensure offset between the text & the box
176
- // if the box becomes too narrow, stop resizing it
177
-
178
- var boxSpace = frameWidth - 2 * offset - textRight;
179
- //console.debug('available space: %s', boxSpace);
180
-
181
- if (boxSpace >= fullBoxWidth) {
182
- //console.debug('full width: %s', fullBoxWidth);
183
- box.width(fullBoxWidth); // plenty of room
94
+
184
95
  }
185
- else if (boxSpace > textWidth) {
186
- //console.debug('shrink to: %s', boxSpace);
187
- box.width(boxSpace); // shrink it
96
+
97
+ const fileIndex = new Index('file');
98
+ const classIndex = new Index('class');
99
+ const methodIndex = new Index('method');
100
+
101
+ setupResizing();
102
+ setupSearches();
103
+ setupHighlighting();
104
+
105
+ const fileClassResizer = document.getElementById('file-class-resizer'); // may be null
106
+ const classMethodResizer = document.getElementById('class-method-resizer');
107
+
108
+ frameResized(true);
109
+
110
+ if (fileClassResizer)
111
+ fileClassResizer.addEventListener('mousedown', function(e) {startDrag(e, fileIndex, classIndex)});
112
+ classMethodResizer.addEventListener('mousedown', function(e) {startDrag(e, classIndex, methodIndex)});
113
+
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);
188
129
  }
189
- else {
190
- //console.debug('min width: %s', textWidth);
191
- box.width(textWidth); // min width
130
+
131
+ // highlight the passed id in the main frame
132
+ function highlightElement(id) {
133
+ const doc = top.mainFrame.document;
134
+ for (const h of doc.querySelectorAll('.highlighted'))
135
+ h.classList.remove('highlighted');
136
+ const e = doc.getElementById(id);
137
+ if (e)
138
+ e.classList.add('highlighted');
192
139
  }
193
140
 
194
- }
141
+ function setupHighlighting () {
142
+ for (const a of methodIndex.list.querySelectorAll('a[href*="#method-"]'))
143
+ a.addEventListener('click', highlightTarget);
144
+ }
195
145
 
196
- // returns the updated heights of index blocks for the current window size
197
- function updatedHeights(info) {
146
+ // setup search boxes
147
+ function setupSearches() {
148
+ const helpText = 'filter...';
149
+ setupSearch(classIndex.searchBox, classIndex.entries, helpText);
150
+ setupSearch(methodIndex.searchBox, methodIndex.entries, helpText);
151
+ }
198
152
 
199
- var fileIndex = info.fileIndex,
200
- classIndex = info.classIndex,
201
- methodIndex = info.methodIndex,
202
- entryHeight = info.entryHeight;
153
+ function setupSearch(searchBox, entries, helpText) {
154
+ // hook quicksearch
155
+ setupQuickSearch(searchBox, entries);
156
+ // set helper text
157
+ searchBox.setAttribute('placeholder', helpText);
158
+ }
203
159
 
204
- // returned information
205
- var heights = {
206
- files: fileIndex.fullHeight,
207
- classes: classIndex.fullHeight,
208
- methods: methodIndex.fullHeight
209
- };
160
+ let startY; // where drag begins
161
+ let startTopIndex; // index object above when drag begins
162
+ let startBottomIndex; // index object below when drag begins
163
+ let startTopHeight; // height of the index above when drag begins
164
+ let startBottomHeight; // height of the index below when drag begins
165
+
166
+ function startDrag(e, topIndex, bottomIndex) {
167
+ startY = e.clientY; // FIXME: probably int
168
+ startTopIndex = topIndex;
169
+ startBottomIndex = bottomIndex;
170
+ startTopHeight = topIndex.currentHeight;
171
+ startBottomHeight = bottomIndex.currentHeight;
172
+ document.documentElement.addEventListener('mousemove', doDrag);
173
+ document.documentElement.addEventListener('mouseup', stopDrag);
174
+ }
210
175
 
211
- var frameHeight = $(window).height();
212
- if ($.browser.mozilla) frameHeight--;
213
- var totalHeight = heights.files + heights.classes + heights.methods;
176
+ function stopDrag(e) {
177
+ document.documentElement.removeEventListener('mousemove', doDrag);
178
+ document.documentElement.removeEventListener('mouseup', stopDrag);
179
+ }
214
180
 
215
- //console.debug('frameHeight = %s', frameHeight);
216
- //console.debug('totalHeight = %s', totalHeight);
181
+ function doDrag(e) {
182
+ const dY = e.clientY - startY;
183
+ const newTopHeight = startTopHeight + dY;
184
+ const newBottomHeight = startBottomHeight - dY;
185
+ if (newTopHeight < startTopIndex.minHeight || newBottomHeight < startBottomIndex.minHeight)
186
+ return;
187
+ startTopIndex.setHeight(newTopHeight, { resetPrev: true });
188
+ startBottomIndex.setHeight(newBottomHeight, { resetPrev: true });
189
+ placeResizers();
190
+ }
217
191
 
218
- // if everything fits, we're done
219
- if (totalHeight <= frameHeight) {
220
- //console.debug('everything fits');
221
- return heights;
192
+ function placeResizers() {
193
+ if (fileClassResizer)
194
+ fileClassResizer.style.top = `${fileIndex.currentHeight}px`;
195
+ if (classMethodResizer)
196
+ classMethodResizer.style.top = `${fileIndex.currentHeight + classIndex.currentHeight}px`;
222
197
  }
223
198
 
224
- var excess = totalHeight - frameHeight;
225
- //console.debug('%s to gain', excess);
226
-
227
- // first try to reduce the file index
228
- if (fileIndex.entries.length > 5) {
229
- // the most we can gain:
230
- var gain = (fileIndex.entries.length - 5) * entryHeight;
231
- if (gain >= excess) {
232
- //console.debug('shrink files by %s, the rest fits', excess);
233
- // just shrinking the files will be fine
234
- heights.files -= excess;
235
- return heights;
236
- }
237
- // we will shrink something else: minimize the height for files
238
- heights.files -= gain;
239
- excess -= gain;
240
- //console.debug('shrink files to the max, still %s to gain', excess);
199
+ // setup callbacks resizing vertically & horizontally;
200
+ function setupResizing() {
201
+ // callback on resize event
202
+ window.addEventListener('resize', function(e) { frameResized(false) });
241
203
  }
242
- else {
243
- //console.debug('only %s files, cannot shrink them', fileIndex.entries.length);
204
+
205
+ // resize the left index blocks
206
+ function frameResized(initial) {
207
+
208
+ resizeSearchField(classIndex);
209
+ resizeSearchField(methodIndex);
210
+
211
+ const heights = initial ? initialHeights() : updatedHeights();
212
+ fileIndex.setHeight(heights.files);
213
+ classIndex.setHeight(heights.classes);
214
+ methodIndex.setHeight(heights.methods);
215
+
216
+ placeResizers();
244
217
  }
245
218
 
246
- // if the method list is more than 33% high,
247
- // try first to reduce it to 33%
219
+ // resize a search field to avoid overlapping text (if possible)
220
+ function resizeSearchField(indexBlock) {
221
+ const container = indexBlock.title;
222
+ const box = indexBlock.searchBox;
223
+ const text = indexBlock.text;
248
224
 
249
- var maxHeight = Math.floor(frameHeight / 3);
250
- if (methodIndex.entries.length > 5) {
251
- if (methodIndex.fullHeight > maxHeight) {
252
- var gain = methodIndex.fullHeight - maxHeight;
253
- var maxGain = (methodIndex.entries.length - 5) * entryHeight;
254
- if (gain > maxGain) {
255
- //console.debug('limit of 5 methods reached, so gain will be %s instead of %s', maxGain, gain);
256
- gain = maxGain;
257
- }
225
+ const frameWidth = top.indexFrame.visualViewport.width;
226
+
227
+ const textRect = text.getBoundingClientRect();
228
+ const textWidth = textRect.width;
229
+ const textRight = textRect.left + textWidth;
230
+
231
+ const boxRect = box.getBoundingClientRect();
232
+ const boxLeft = boxRect.left;
233
+ const boxWidth = boxRect.width;
234
+
235
+ const boxRight = boxLeft + boxWidth;
236
+ const offset = frameWidth - boxRight;
237
+
238
+ // try to ensure offset between the text & the box
239
+ // if the box becomes too narrow, stop resizing it
240
+
241
+ const boxSpace = frameWidth - 2 * offset - textRight;
242
+
243
+ if (boxSpace >= indexBlock.fullBoxWidth)
244
+ box.style.width = `${indexBlock.fullBoxWidth}px`; // plenty of room
245
+ else if (boxSpace > textWidth)
246
+ box.style.width = `${boxSpace}px`; // shrink it
247
+ else
248
+ box.style.width = `${textWidth}px`; // min width
249
+
250
+ }
251
+
252
+ // returns the initial heights of index blocks for the current window size
253
+ function initialHeights() {
254
+
255
+ // returned information
256
+ const heights = {
257
+ files: fileIndex.fullHeight,
258
+ classes: classIndex.fullHeight,
259
+ methods: methodIndex.fullHeight
260
+ };
261
+
262
+ let frameHeight = window.visualViewport.height;
263
+ const totalHeight = heights.files + heights.classes + heights.methods;
264
+
265
+ // if everything fits, we're done
266
+ if (totalHeight <= frameHeight)
267
+ return heights;
268
+
269
+ let excess = totalHeight - frameHeight;
270
+
271
+ // first try to reduce the file index to 5 files if more
272
+ if (fileIndex.entryCount > 5) {
273
+ // the most we can gain:
274
+ const gain = (fileIndex.entryCount - 5) * fileIndex.entryHeight;
258
275
  if (gain >= excess) {
259
- //console.debug('shrink methods by %s, classes fit', excess);
260
- // just shrinking the methods will be fine
261
- heights.methods -= excess;
276
+ heights.files -= excess;
262
277
  return heights;
263
278
  }
264
- // we will also shrink the classes: gain what we can
265
- heights.methods -= gain;
279
+ // we will shrink something else: minimize the height for files
280
+ heights.files -= gain;
266
281
  excess -= gain;
267
- //console.debug('shrink methods by %s, still %s to gain', gain, excess);
268
282
  }
269
- else {
270
- //console.debug('methods <= 50%, not shrinked');
283
+
284
+ // if the method list is more than 33% high,
285
+ // try first to reduce it to 33%
286
+ if (methodIndex.entryCount > 5) {
287
+ const maxHeight = frameHeight / 3;
288
+ if (methodIndex.fullHeight > maxHeight) {
289
+ let gain = methodIndex.fullHeight - maxHeight;
290
+ // leave at least 5 methods visible
291
+ const maxGain = (methodIndex.entryCount - 5) * methodIndex.entryHeight;
292
+ if (gain > maxGain)
293
+ gain = maxGain;
294
+ if (gain >= excess) {
295
+ // just shrinking the methods will be fine
296
+ heights.methods -= excess;
297
+ return heights;
298
+ }
299
+ // we will also shrink the classes: gain what we can
300
+ heights.methods -= gain;
301
+ excess -= gain;
302
+ }
271
303
  }
272
- }
273
- else {
274
- //console.debug('only %s methods, cannot shrink them', methodIndex.entries.length);
275
- }
276
304
 
277
- // shrink the classes if possible
278
- if (classIndex.entries.length > 5) {
279
- var gain = excess;
280
- var maxGain = (classIndex.entries.length - 5) * entryHeight;
281
- if (gain > maxGain) {
282
- //console.debug('limit of 5 classes reached, so gain will be %s instead of %s', maxGain, gain);
283
- gain = maxGain;
305
+ // shrink the classes if possible, leaving at least 5 classes visible
306
+ if (classIndex.entryCount > 5) {
307
+ let gain = excess;
308
+ const maxGain = (classIndex.entryCount - 5) * classIndex.entryHeight;
309
+ if (gain > maxGain)
310
+ gain = maxGain;
311
+ heights.classes -= gain;
312
+ excess -= gain;
284
313
  }
285
- heights.classes -= gain;
286
- excess -= gain;
287
- //console.debug('shrink classes by %s', gain);
288
- }
289
- else {
290
- //console.debug('only %s classes, cannot shrink them', classIndex.entries.length);
291
- }
292
314
 
293
- if (excess > 0) {
294
- //console.debug('minimal heights, excess = %', excess);
315
+ return heights;
295
316
  }
296
317
 
297
- return heights;
318
+ // returns the updated heights of index blocks for the current window size
319
+ function updatedHeights() {
320
+
321
+ let frameHeight = window.visualViewport.height;
322
+ const totalHeight = fileIndex.fullHeight + classIndex.fullHeight + methodIndex.fullHeight;
323
+
324
+ // if everything fits, we're done
325
+ if (totalHeight <= frameHeight)
326
+ return {
327
+ files: fileIndex.fullHeight,
328
+ classes: classIndex.fullHeight,
329
+ methods: methodIndex.fullHeight
330
+ };
331
+
332
+ // try to maintain proportionality
333
+ const heights = {
334
+ files: fileIndex.currentHeight,
335
+ classes: classIndex.currentHeight,
336
+ methods: methodIndex.currentHeight
337
+ };
338
+
339
+ // already sized as appropriate: nothing to do
340
+ if (frameHeight === fileIndex.currentHeight + classIndex.currentHeight + methodIndex.currentHeight)
341
+ return heights;
342
+
343
+ // proportional resize
344
+ const prevHeight = fileIndex.prevHeight + classIndex.prevHeight + methodIndex.prevHeight;
345
+ const factor = frameHeight / prevHeight;
346
+
347
+ if (fileIndex.prevHeight * factor >= fileIndex.minHeight)
348
+ heights.files = fileIndex.prevHeight * factor;
298
349
 
299
- }
350
+ if (classIndex.prevHeight * factor >= classIndex.minHeight)
351
+ heights.classes = classIndex.prevHeight * factor;
300
352
 
301
- $(document).ready(function() {
353
+ if (frameHeight - heights.files - heights.classes >= methodIndex.minHeight)
354
+ heights.methods = frameHeight - heights.files - heights.classes;
302
355
 
303
- // class index is used as reference, as there is always at least one class
304
- var classIndex = indexElements('class');
305
- var methodIndex = indexElements('method');
356
+ return heights;
306
357
 
307
- setupResizing(classIndex, methodIndex);
308
- setupSearches(classIndex, methodIndex);
309
- setupHighlighting(methodIndex.list);
358
+ }
310
359
 
311
360
  });