|
57 | 57 | }
|
58 | 58 | }());
|
59 | 59 |
|
| 60 | + /** |
| 61 | + * Returns the top offset of the content box of the given parent and the content box of one of its children. |
| 62 | + * |
| 63 | + * @param {HTMLElement} parent |
| 64 | + * @param {HTMLElement} child |
| 65 | + */ |
| 66 | + function getContentBoxTopOffset(parent, child) { |
| 67 | + var parentStyle = getComputedStyle(parent); |
| 68 | + var childStyle = getComputedStyle(child); |
| 69 | + |
| 70 | + /** |
| 71 | + * Returns the numeric value of the given pixel value. |
| 72 | + * |
| 73 | + * @param {string} px |
| 74 | + */ |
| 75 | + function pxToNumber(px) { |
| 76 | + return +px.substr(0, px.length - 2); |
| 77 | + } |
| 78 | + |
| 79 | + return child.offsetTop |
| 80 | + + pxToNumber(childStyle.borderTopWidth) |
| 81 | + + pxToNumber(childStyle.paddingTop) |
| 82 | + - pxToNumber(parentStyle.paddingTop); |
| 83 | + } |
| 84 | + |
60 | 85 | /**
|
61 | 86 | * Highlights the lines of the given pre.
|
62 | 87 | *
|
63 | 88 | * This function is split into a DOM measuring and mutate phase to improve performance.
|
64 | 89 | * The returned function mutates the DOM when called.
|
65 | 90 | *
|
66 | 91 | * @param {HTMLElement} pre
|
67 |
| - * @param {string} [lines] |
| 92 | + * @param {string | null} [lines] |
68 | 93 | * @param {string} [classes='']
|
69 | 94 | * @returns {() => void}
|
70 | 95 | */
|
|
77 | 102 | var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
|
78 | 103 | var lineHeight = parseMethod(getComputedStyle(pre).lineHeight);
|
79 | 104 | var hasLineNumbers = hasClass(pre, 'line-numbers');
|
80 |
| - var parentElement = hasLineNumbers ? pre : pre.querySelector('code') || pre; |
| 105 | + var codeElement = pre.querySelector('code'); |
| 106 | + var parentElement = hasLineNumbers ? pre : codeElement || pre; |
81 | 107 | var mutateActions = /** @type {(() => void)[]} */ ([]);
|
82 | 108 |
|
| 109 | + /** |
| 110 | + * The top offset between the content box of the <code> element and the content box of the parent element of |
| 111 | + * the line highlight element (either `<pre>` or `<code>`). |
| 112 | + * |
| 113 | + * This offset might not be zero for some themes where the <code> element has a top margin. Some plugins |
| 114 | + * (or users) might also add element above the <code> element. Because the line highlight is aligned relative |
| 115 | + * to the <pre> element, we have to take this into account. |
| 116 | + * |
| 117 | + * This offset will be 0 if the parent element of the line highlight element is the `<code>` element. |
| 118 | + */ |
| 119 | + var codePreOffset = !codeElement || parentElement == codeElement ? 0 : getContentBoxTopOffset(pre, codeElement); |
| 120 | + |
83 | 121 | ranges.forEach(function (currentRange) {
|
84 | 122 | var range = currentRange.split('-');
|
85 | 123 |
|
|
101 | 139 | var endNode = Prism.plugins.lineNumbers.getLine(pre, end);
|
102 | 140 |
|
103 | 141 | if (startNode) {
|
104 |
| - var top = startNode.offsetTop + 'px'; |
| 142 | + var top = startNode.offsetTop + codePreOffset + 'px'; |
105 | 143 | mutateActions.push(function () {
|
106 | 144 | line.style.top = top;
|
107 | 145 | });
|
|
115 | 153 | }
|
116 | 154 | } else {
|
117 | 155 | mutateActions.push(function () {
|
118 |
| - line.setAttribute('data-start', start); |
| 156 | + line.setAttribute('data-start', String(start)); |
119 | 157 |
|
120 | 158 | if (end > start) {
|
121 |
| - line.setAttribute('data-end', end); |
| 159 | + line.setAttribute('data-end', String(end)); |
122 | 160 | }
|
123 | 161 |
|
124 |
| - line.style.top = (start - offset - 1) * lineHeight + 'px'; |
| 162 | + line.style.top = (start - offset - 1) * lineHeight + codePreOffset + 'px'; |
125 | 163 |
|
126 | 164 | line.textContent = new Array(end - start + 2).join(' \n');
|
127 | 165 | });
|
|
222 | 260 | var fakeTimer = 0; // Hack to limit the number of times applyHash() runs
|
223 | 261 |
|
224 | 262 | Prism.hooks.add('before-sanity-check', function (env) {
|
225 |
| - var pre = env.element.parentNode; |
| 263 | + var pre = env.element.parentElement; |
226 | 264 | var lines = pre && pre.getAttribute('data-line');
|
227 | 265 |
|
228 | 266 | if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
|
|
248 | 286 | });
|
249 | 287 |
|
250 | 288 | Prism.hooks.add('complete', function completeHook(env) {
|
251 |
| - var pre = env.element.parentNode; |
| 289 | + var pre = env.element.parentElement; |
252 | 290 | var lines = pre && pre.getAttribute('data-line');
|
253 | 291 |
|
254 | 292 | if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
|
|
0 commit comments