|
4 | 4 | return;
|
5 | 5 | }
|
6 | 6 |
|
7 |
| - function $$(expr, con) { |
8 |
| - return Array.prototype.slice.call((con || document).querySelectorAll(expr)); |
| 7 | + /** |
| 8 | + * @param {string} selector |
| 9 | + * @param {ParentNode} [container] |
| 10 | + * @returns {HTMLElement[]} |
| 11 | + */ |
| 12 | + function $$(selector, container) { |
| 13 | + return Array.prototype.slice.call((container || document).querySelectorAll(selector)); |
9 | 14 | }
|
10 | 15 |
|
| 16 | + /** |
| 17 | + * Returns whether the given element has the given class. |
| 18 | + * |
| 19 | + * @param {Element} element |
| 20 | + * @param {string} className |
| 21 | + * @returns {boolean} |
| 22 | + */ |
11 | 23 | function hasClass(element, className) {
|
12 | 24 | className = " " + className + " ";
|
13 | 25 | return (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(className) > -1
|
14 | 26 | }
|
15 | 27 |
|
| 28 | + /** |
| 29 | + * Calls the given function. |
| 30 | + * |
| 31 | + * @param {() => any} func |
| 32 | + * @returns {void} |
| 33 | + */ |
16 | 34 | function callFunction(func) {
|
17 | 35 | func();
|
18 | 36 | }
|
|
26 | 44 | var d = document.createElement('div');
|
27 | 45 | d.style.fontSize = '13px';
|
28 | 46 | d.style.lineHeight = '1.5';
|
29 |
| - d.style.padding = 0; |
30 |
| - d.style.border = 0; |
| 47 | + d.style.padding = '0'; |
| 48 | + d.style.border = '0'; |
31 | 49 | d.innerHTML = ' <br /> ';
|
32 | 50 | document.body.appendChild(d);
|
33 | 51 | // Browsers that round the line-height should have offsetHeight === 38
|
|
53 | 71 | function highlightLines(pre, lines, classes) {
|
54 | 72 | lines = typeof lines === 'string' ? lines : pre.getAttribute('data-line');
|
55 | 73 |
|
56 |
| - var ranges = lines.replace(/\s+/g, '').split(','); |
| 74 | + var ranges = lines.replace(/\s+/g, '').split(',').filter(Boolean); |
57 | 75 | var offset = +pre.getAttribute('data-line-offset') || 0;
|
58 | 76 |
|
59 | 77 | var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
|
|
68 | 86 | var start = +range[0];
|
69 | 87 | var end = +range[1] || start;
|
70 | 88 |
|
| 89 | + /** @type {HTMLElement} */ |
71 | 90 | var line = pre.querySelector('.line-highlight[data-range="' + currentRange + '"]') || document.createElement('div');
|
72 | 91 |
|
73 | 92 | mutateActions.push(function () {
|
|
115 | 134 | });
|
116 | 135 | });
|
117 | 136 |
|
| 137 | + var id = pre.id; |
| 138 | + if (hasLineNumbers && id) { |
| 139 | + // This implements linkable line numbers. Linkable line numbers use Line Highlight to create a link to a |
| 140 | + // specific line. For this to work, the pre element has to: |
| 141 | + // 1) have line numbers, |
| 142 | + // 2) have the `linkable-line-numbers` class or an ascendant that has that class, and |
| 143 | + // 3) have an id. |
| 144 | + |
| 145 | + var linkableLineNumbersClass = 'linkable-line-numbers'; |
| 146 | + var linkableLineNumbers = false; |
| 147 | + var node = pre; |
| 148 | + while (node) { |
| 149 | + if (hasClass(node, linkableLineNumbersClass)) { |
| 150 | + linkableLineNumbers = true; |
| 151 | + break; |
| 152 | + } |
| 153 | + node = node.parentElement; |
| 154 | + } |
| 155 | + |
| 156 | + if (linkableLineNumbers) { |
| 157 | + if (!hasClass(pre, linkableLineNumbersClass)) { |
| 158 | + // add class to pre |
| 159 | + mutateActions.push(function () { |
| 160 | + pre.className = (pre.className + ' ' + linkableLineNumbersClass).trim(); |
| 161 | + }); |
| 162 | + } |
| 163 | + |
| 164 | + var start = parseInt(pre.getAttribute('data-start') || '1'); |
| 165 | + |
| 166 | + // iterate all line number spans |
| 167 | + $$('.line-numbers-rows > span', pre).forEach(function (lineSpan, i) { |
| 168 | + var lineNumber = i + start; |
| 169 | + lineSpan.onclick = function () { |
| 170 | + var hash = id + '.' + lineNumber; |
| 171 | + |
| 172 | + // this will prevent scrolling since the span is obviously in view |
| 173 | + scrollIntoView = false; |
| 174 | + location.hash = hash; |
| 175 | + setTimeout(function () { |
| 176 | + scrollIntoView = true; |
| 177 | + }, 1); |
| 178 | + }; |
| 179 | + }); |
| 180 | + } |
| 181 | + } |
| 182 | + |
118 | 183 | return function () {
|
119 | 184 | mutateActions.forEach(callFunction);
|
120 | 185 | };
|
121 | 186 | }
|
122 | 187 |
|
| 188 | + var scrollIntoView = true; |
123 | 189 | function applyHash() {
|
124 | 190 | var hash = location.hash.slice(1);
|
125 | 191 |
|
|
148 | 214 | var mutateDom = highlightLines(pre, range, 'temporary ');
|
149 | 215 | mutateDom();
|
150 | 216 |
|
151 |
| - document.querySelector('.temporary.line-highlight').scrollIntoView(); |
| 217 | + if (scrollIntoView) { |
| 218 | + document.querySelector('.temporary.line-highlight').scrollIntoView(); |
| 219 | + } |
152 | 220 | }
|
153 | 221 |
|
154 | 222 | var fakeTimer = 0; // Hack to limit the number of times applyHash() runs
|
|
203 | 271 |
|
204 | 272 | window.addEventListener('hashchange', applyHash);
|
205 | 273 | window.addEventListener('resize', function () {
|
206 |
| - var actions = []; |
207 |
| - $$('pre[data-line]').forEach(function (pre) { |
208 |
| - actions.push(highlightLines(pre)); |
| 274 | + var actions = $$('pre[data-line]').map(function (pre) { |
| 275 | + return highlightLines(pre); |
209 | 276 | });
|
210 | 277 | actions.forEach(callFunction);
|
211 | 278 | });
|
|
0 commit comments