rdoc-babel 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  });