|
5 | 5 |
|
6 | 6 | var TABLE_NAME = 'hljs-ln',
|
7 | 7 | LINE_NAME = 'hljs-ln-line',
|
| 8 | + NUMBERS_CONTAINER_NAME = 'hljs-ln-numbers-container', |
| 9 | + CODE_CONTAINER_NAME = 'hljs-ln-code-container', |
8 | 10 | CODE_BLOCK_NAME = 'hljs-ln-code',
|
9 | 11 | NUMBERS_BLOCK_NAME = 'hljs-ln-numbers',
|
10 | 12 | NUMBER_LINE_NAME = 'hljs-ln-n',
|
11 | 13 | DATA_ATTR_NAME = 'data-line-number',
|
12 | 14 | BREAK_LINE_REGEXP = /\r\n|\r|\n/g;
|
13 | 15 |
|
| 16 | + var resizeHandlerSet = false; |
| 17 | + |
14 | 18 | if (w.hljs) {
|
15 | 19 | w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad;
|
16 | 20 | w.hljs.lineNumbersBlock = lineNumbersBlock;
|
|
25 | 29 | var css = d.createElement('style');
|
26 | 30 | css.type = 'text/css';
|
27 | 31 | css.innerHTML = format(
|
28 |
| - '.{0}{border-collapse:collapse}' + |
29 |
| - '.{0} td{padding:0}' + |
30 |
| - '.{1}:before{content:attr({2})}', |
| 32 | + '.{0} table {float: left; border-collapse: collapse}' + |
| 33 | + '.{0} table td {padding: 0}' + |
| 34 | + '.{1}::before {content: attr({2})}' + |
| 35 | + // force display of empty lines of code |
| 36 | + '.{3}::before {content: "\\200B"}' + |
| 37 | + // ensure consistent line heights in dom elements for numbers and code |
| 38 | + '.{3},.{4} {line-height: 16px; line-height: 1rem}', |
31 | 39 | [
|
32 | 40 | TABLE_NAME,
|
33 | 41 | NUMBER_LINE_NAME,
|
34 |
| - DATA_ATTR_NAME |
| 42 | + DATA_ATTR_NAME, |
| 43 | + CODE_BLOCK_NAME, |
| 44 | + NUMBERS_BLOCK_NAME |
35 | 45 | ]);
|
36 | 46 | d.getElementsByTagName('head')[0].appendChild(css);
|
37 | 47 | }
|
|
60 | 70 | }
|
61 | 71 | }
|
62 | 72 |
|
| 73 | + function adjustLineNumbersHeights(element) { |
| 74 | + var lnNumbers = element.querySelectorAll('.' + NUMBERS_BLOCK_NAME); |
| 75 | + var lnCode = element.querySelectorAll('.' + CODE_BLOCK_NAME); |
| 76 | + |
| 77 | + for (var i = 0 ; i < lnNumbers.length; ++i) { |
| 78 | + lnNumbers[i].style.height = lnCode[i].offsetHeight + 'px'; |
| 79 | + } |
| 80 | + } |
| 81 | + |
63 | 82 | function lineNumbersBlock (element, options) {
|
64 | 83 | if (typeof element !== 'object') return;
|
65 |
| - |
66 | 84 | async(function () {
|
67 | 85 | element.innerHTML = lineNumbersInternal(element, options);
|
| 86 | + // adjust left margin of code div as line numbers is a float left dom element |
| 87 | + var lineNumbersContainer = element.querySelector('.' + NUMBERS_CONTAINER_NAME); |
| 88 | + if (lineNumbersContainer) { |
| 89 | + var codeMargin = lineNumbersContainer.offsetWidth; |
| 90 | + var codeContainerStyle = 'margin-left:' + codeMargin + 'px'; |
| 91 | + var codeContainer = element.querySelector('.' + CODE_CONTAINER_NAME); |
| 92 | + codeContainer.style.cssText = codeContainerStyle; |
| 93 | + |
| 94 | + // adjust each line number cell height to the one of the div containing |
| 95 | + // the wrapped line of code and set a handler to execute this |
| 96 | + // operation when the browser window gets resized. |
| 97 | + // execute this operation asynchronously once the code has beed rendered |
| 98 | + async(adjustLineNumbersHeights(element)); |
| 99 | + if (!resizeHandlerSet) { |
| 100 | + window.addEventListener('resize', function() { |
| 101 | + async(function() { |
| 102 | + var hljsLnElts = document.querySelectorAll('.' + TABLE_NAME); |
| 103 | + for (var i = 0 ; i < hljsLnElts.length; ++i) { |
| 104 | + adjustLineNumbersHeights(hljsLnElts[i]); |
| 105 | + } |
| 106 | + }); |
| 107 | + }); |
| 108 | + resizeHandlerSet = true; |
| 109 | + } |
| 110 | + } |
68 | 111 | });
|
69 | 112 | }
|
70 | 113 |
|
|
101 | 144 | }
|
102 | 145 |
|
103 | 146 | if (lines.length > firstLineIndex) {
|
104 |
| - var html = ''; |
| 147 | + // Previous implementation was using a single table element |
| 148 | + // to render the line numbers and the lines of code. |
| 149 | + // But to overcome an annoying copy/paste behavior when using Firefox or Edge |
| 150 | + // (see https://github.com/wcoder/highlightjs-line-numbers.js/issues/51) |
| 151 | + // the following workaround is used while obtaining the exact same rendering |
| 152 | + // as before: |
| 153 | + // 1. render the lines number in a table with single column |
| 154 | + // 2. render the lines of code in a div |
| 155 | + // 3. wrap these in a div and make the table float left |
| 156 | + // 4. adjust the left margin of the code div once inserted in the dom |
| 157 | + var htmlLinesNumber = ''; |
| 158 | + var htmlCode = ''; |
105 | 159 |
|
106 | 160 | for (var i = 0, l = lines.length; i < l; i++) {
|
107 |
| - html += format( |
108 |
| - '<tr>' + |
109 |
| - '<td class="{0}">' + |
110 |
| - '<div class="{1} {2}" {3}="{5}"></div>' + |
111 |
| - '</td>' + |
112 |
| - '<td class="{4}">' + |
113 |
| - '<div class="{1}">{6}</div>' + |
| 161 | + htmlLinesNumber += format( |
| 162 | + '<tr class="{0} {1}" {3}="{4}">' + |
| 163 | + '<td>' + |
| 164 | + '<div class="{2}" {3}="{4}"></div>' + |
114 | 165 | '</td>' +
|
115 | 166 | '</tr>',
|
116 | 167 | [
|
117 |
| - NUMBERS_BLOCK_NAME, |
118 | 168 | LINE_NAME,
|
| 169 | + NUMBERS_BLOCK_NAME, |
119 | 170 | NUMBER_LINE_NAME,
|
120 | 171 | DATA_ATTR_NAME,
|
| 172 | + i + 1 |
| 173 | + ]); |
| 174 | + |
| 175 | + htmlCode += format( |
| 176 | + '<div class="{0} {1}" {2}="{3}">{4}</div>', |
| 177 | + [ |
| 178 | + LINE_NAME, |
121 | 179 | CODE_BLOCK_NAME,
|
| 180 | + DATA_ATTR_NAME, |
122 | 181 | i + 1,
|
123 | 182 | lines[i].length > 0 ? lines[i] : ' '
|
124 | 183 | ]);
|
125 | 184 | }
|
126 | 185 |
|
127 |
| - return format('<table class="{0}">{1}</table>', [ TABLE_NAME, html ]); |
| 186 | + return format( |
| 187 | + '<div class="{0}">' + |
| 188 | + '<table class="{1}">{2}</table>' + |
| 189 | + '<div class="{3}">{4}</div>' + |
| 190 | + '</div>', |
| 191 | + [ |
| 192 | + TABLE_NAME, |
| 193 | + NUMBERS_CONTAINER_NAME, |
| 194 | + htmlLinesNumber, |
| 195 | + CODE_CONTAINER_NAME, |
| 196 | + htmlCode |
| 197 | + ]); |
128 | 198 | }
|
129 | 199 |
|
130 | 200 | return inputHtml;
|
|
0 commit comments