diff --git a/plugins/line-numbers/prism-line-numbers.js b/plugins/line-numbers/prism-line-numbers.js index b627d2de94..16012f604d 100644 --- a/plugins/line-numbers/prism-line-numbers.js +++ b/plugins/line-numbers/prism-line-numbers.js @@ -16,20 +16,86 @@ */ var NEW_LINE_EXP = /\n(?!$)/g; + /** - * Resizes line numbers spans according to height of line of code - * @param {Element} element
element + * Global exports */ - var _resizeElement = function (element) { - var codeStyles = getStyles(element); - var whiteSpace = codeStyles['white-space']; + var config = Prism.plugins.lineNumbers = { + /** + * Get node for provided line number + * @param {Element} element pre element + * @param {Number} number line number + * @return {Element|undefined} + */ + getLine: function (element, number) { + if (element.tagName !== 'PRE' || !element.classList.contains(PLUGIN_NAME)) { + return; + } + + var lineNumberRows = element.querySelector('.line-numbers-rows'); + var lineNumberStart = parseInt(element.getAttribute('data-start'), 10) || 1; + var lineNumberEnd = lineNumberStart + (lineNumberRows.children.length - 1); + + if (number < lineNumberStart) { + number = lineNumberStart; + } + if (number > lineNumberEnd) { + number = lineNumberEnd; + } + + var lineIndex = number - lineNumberStart; + + return lineNumberRows.children[lineIndex]; + }, - if (whiteSpace === 'pre-wrap' || whiteSpace === 'pre-line') { + /** + * Resizes the line numbers of the given element. + * + * This function will not add line numbers. It will only resize existing ones. + * @param {HTMLElement} element A `` element with line numbers. + * @returns {void} + */ + resize: function (element) { + resizeElements([element]); + }, + + /** + * Whether the plugin can assume that the units font sizes and margins are not depended on the size of + * the current viewport. + * + * Setting this to `true` will allow the plugin to do certain optimizations for better performance. + * + * Set this to `false` if you use any of the following CSS units: `vh`, `vw`, `vmin`, `vmax`. + * + * @type {boolean} + */ + assumeViewportIndependence: true + }; + + /** + * Resizes the given elements. + * + * @param {HTMLElement[]} elements + */ + function resizeElements(elements) { + elements = elements.filter(function (e) { + var codeStyles = getStyles(e); + var whiteSpace = codeStyles['white-space']; + return whiteSpace === 'pre-wrap' || whiteSpace === 'pre-line'; + }); + + if (elements.length == 0) { + return; + } + + var infos = elements.map(function (element) { var codeElement = element.querySelector('code'); var lineNumbersWrapper = element.querySelector('.line-numbers-rows'); if (!codeElement || !lineNumbersWrapper) { - return; + return undefined; } + + /** @type {HTMLElement} */ var lineNumberSizer = element.querySelector('.line-numbers-sizer'); var codeLines = codeElement.textContent.split(NEW_LINE_EXP); @@ -40,18 +106,63 @@ codeElement.appendChild(lineNumberSizer); } + lineNumberSizer.innerHTML = '0'; lineNumberSizer.style.display = 'block'; - codeLines.forEach(function (line, lineNumber) { - lineNumberSizer.textContent = line || '\n'; - var lineSize = lineNumberSizer.getBoundingClientRect().height; - lineNumbersWrapper.children[lineNumber].style.height = lineSize + 'px'; + var oneLinerHeight = lineNumberSizer.getBoundingClientRect().height; + lineNumberSizer.innerHTML = ''; + + return { + element: element, + lines: codeLines, + lineHeights: [], + oneLinerHeight: oneLinerHeight, + sizer: lineNumberSizer, + }; + }).filter(Boolean); + + infos.forEach(function (info) { + var lineNumberSizer = info.sizer; + var lines = info.lines; + var lineHeights = info.lineHeights; + var oneLinerHeight = info.oneLinerHeight; + + lineHeights[lines.length - 1] = undefined; + lines.forEach(function (line, index) { + if (line && line.length > 1) { + var e = lineNumberSizer.appendChild(document.createElement('span')); + e.style.display = 'block'; + e.textContent = line; + } else { + lineHeights[index] = oneLinerHeight; + } }); + }); + + infos.forEach(function (info) { + var lineNumberSizer = info.sizer; + var lineHeights = info.lineHeights; + + var childIndex = 0; + for (var i = 0; i < lineHeights.length; i++) { + if (lineHeights[i] === undefined) { + lineHeights[i] = lineNumberSizer.children[childIndex++].getBoundingClientRect().height; + } + } + }); + + infos.forEach(function (info) { + var lineNumberSizer = info.sizer; + var wrapper = info.element.querySelector('.line-numbers-rows'); - lineNumberSizer.textContent = ''; lineNumberSizer.style.display = 'none'; - } - }; + lineNumberSizer.innerHTML = ''; + + info.lineHeights.forEach(function (height, lineNumber) { + wrapper.children[lineNumber].style.height = height + 'px'; + }); + }); + } /** * Returns style declarations for the element @@ -65,8 +176,14 @@ return window.getComputedStyle ? getComputedStyle(element) : (element.currentStyle || null); }; + var lastWidth = undefined; window.addEventListener('resize', function () { - Array.prototype.forEach.call(document.querySelectorAll('pre.' + PLUGIN_NAME), _resizeElement); + if (config.assumeViewportIndependence && lastWidth === window.innerWidth) { + return; + } + lastWidth = window.innerWidth; + + resizeElements(Array.prototype.slice.call(document.querySelectorAll('pre.' + PLUGIN_NAME))); }); Prism.hooks.add('complete', function (env) { @@ -75,7 +192,7 @@ } var code = /** @type {Element} */ (env.element); - var pre = /** @type {Element} */ (code.parentNode); + var pre = /** @type {HTMLElement} */ (code.parentNode); // works only forwrapped inside
(not inline) if (!pre || !/pre/i.test(pre.nodeName)) { @@ -114,7 +231,7 @@ env.element.appendChild(lineNumbersWrapper); - _resizeElement(pre); + resizeElements([pre]); Prism.hooks.run('line-numbers', env); }); @@ -124,46 +241,4 @@ env.plugins.lineNumbers = true; }); - /** - * Global exports - */ - Prism.plugins.lineNumbers = { - /** - * Returns the node of the given line number in the given element. - * @param {Element} element A `` element with line numbers. - * @param {Number} number - * @returns {Element | undefined} - */ - getLine: function (element, number) { - if (element.tagName !== 'PRE' || !element.classList.contains(PLUGIN_NAME)) { - return; - } - - var lineNumberRows = element.querySelector('.line-numbers-rows'); - var lineNumberStart = parseInt(element.getAttribute('data-start'), 10) || 1; - var lineNumberEnd = lineNumberStart + (lineNumberRows.children.length - 1); - - if (number < lineNumberStart) { - number = lineNumberStart; - } - if (number > lineNumberEnd) { - number = lineNumberEnd; - } - - var lineIndex = number - lineNumberStart; - - return lineNumberRows.children[lineIndex]; - }, - /** - * Resizes the line numbers of the given element. - * - * This function will not add line numbers. It will only resize existing ones. - * @param {Element} element A `` element with line numbers. - * @returns {void} - */ - resize: function (element) { - _resizeElement(element); - } - }; - }()); diff --git a/plugins/line-numbers/prism-line-numbers.min.js b/plugins/line-numbers/prism-line-numbers.min.js index f89b9b50e1..b0ee977c66 100644 --- a/plugins/line-numbers/prism-line-numbers.min.js +++ b/plugins/line-numbers/prism-line-numbers.min.js @@ -1 +1 @@ -!function(){if("undefined"!=typeof self&&self.Prism&&self.document){var a="line-numbers",o=/\n(?!$)/g,u=function(e){var t=l(e)["white-space"];if("pre-wrap"===t||"pre-line"===t){var n=e.querySelector("code"),r=e.querySelector(".line-numbers-rows");if(!n||!r)return;var i=e.querySelector(".line-numbers-sizer"),s=n.textContent.split(o);i||((i=document.createElement("span")).className="line-numbers-sizer",n.appendChild(i)),i.style.display="block",s.forEach(function(e,t){i.textContent=e||"\n";var n=i.getBoundingClientRect().height;r.children[t].style.height=n+"px"}),i.textContent="",i.style.display="none"}},l=function(e){return e?window.getComputedStyle?getComputedStyle(e):e.currentStyle||null:null};window.addEventListener("resize",function(){Array.prototype.forEach.call(document.querySelectorAll("pre."+a),u)}),Prism.hooks.add("complete",function(e){if(e.code){var t=e.element,n=t.parentNode;if(n&&/pre/i.test(n.nodeName)&&!t.querySelector(".line-numbers-rows")&&Prism.util.isActive(t,a)){t.classList.remove(a),n.classList.add(a);var r,i=e.code.match(o),s=i?i.length+1:1,l=new Array(s+1).join("");(r=document.createElement("span")).setAttribute("aria-hidden","true"),r.className="line-numbers-rows",r.innerHTML=l,n.hasAttribute("data-start")&&(n.style.counterReset="linenumber "+(parseInt(n.getAttribute("data-start"),10)-1)),e.element.appendChild(r),u(n),Prism.hooks.run("line-numbers",e)}}}),Prism.hooks.add("line-numbers",function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0}),Prism.plugins.lineNumbers={getLine:function(e,t){if("PRE"===e.tagName&&e.classList.contains(a)){var n=e.querySelector(".line-numbers-rows"),r=parseInt(e.getAttribute("data-start"),10)||1,i=r+(n.children.length-1);t");(i=document.createElement("span")).setAttribute("aria-hidden","true"),i.className="line-numbers-rows",i.innerHTML=l,t.hasAttribute("data-start")&&(t.style.counterReset="linenumber "+(parseInt(t.getAttribute("data-start"),10)-1)),e.element.appendChild(i),u([t]),Prism.hooks.run("line-numbers",e)}}}),Prism.hooks.add("line-numbers",function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0})}function u(e){if(0!=(e=e.filter(function(e){var n=t(e)["white-space"];return"pre-wrap"===n||"pre-line"===n})).length){var n=e.map(function(e){var n=e.querySelector("code"),t=e.querySelector(".line-numbers-rows");if(n&&t){var i=e.querySelector(".line-numbers-sizer"),r=n.textContent.split(a);i||((i=document.createElement("span")).className="line-numbers-sizer",n.appendChild(i)),i.innerHTML="0",i.style.display="block";var s=i.getBoundingClientRect().height;return i.innerHTML="",{element:e,lines:r,lineHeights:[],oneLinerHeight:s,sizer:i}}}).filter(Boolean);n.forEach(function(e){var i=e.sizer,n=e.lines,r=e.lineHeights,s=e.oneLinerHeight;r[n.length-1]=void 0,n.forEach(function(e,n){if(e&&1