diff --git a/plugins/line-highlight/index.html b/plugins/line-highlight/index.html
index 5c37fb7eb4..2a974ea40b 100644
--- a/plugins/line-highlight/index.html
+++ b/plugins/line-highlight/index.html
@@ -9,6 +9,7 @@
+
@@ -51,6 +52,8 @@
How to use
You can also link to specific lines on any code snippet, by using the following as a url hash: #{element-id}.{lines}
where
{element-id}
is the id of the <pre>
element and {lines}
is one or more lines or line ranges that follow the format
outlined above. For example, if there is an element with id="play"
on the page, you can link to lines 5-6 by linking to #play.5-6
+
+ If line numbers are also enabled for a code block and the <pre>
element has an id, you can add the linkable-line-numbers
class to the <pre>
element. This will make all line numbers clickable and when clicking any line number, it will change the hash of the current page to link to that specific line.
@@ -69,12 +72,20 @@ Line 43, starting from line 41
Linking example
+
+ With line numbers
+
+
+ With linkable line numbers
+
+
+
diff --git a/plugins/line-highlight/prism-line-highlight.css b/plugins/line-highlight/prism-line-highlight.css
index 6058db44ac..1262b9a342 100644
--- a/plugins/line-highlight/prism-line-highlight.css
+++ b/plugins/line-highlight/prism-line-highlight.css
@@ -47,3 +47,13 @@ pre[data-line] {
.line-numbers .line-highlight:after {
content: none;
}
+
+pre[id].linkable-line-numbers span.line-numbers-rows {
+ pointer-events: all;
+}
+pre[id].linkable-line-numbers span.line-numbers-rows > span:before {
+ cursor: pointer;
+}
+pre[id].linkable-line-numbers span.line-numbers-rows > span:hover:before {
+ background-color: rgba(128, 128, 128, .2);
+}
diff --git a/plugins/line-highlight/prism-line-highlight.js b/plugins/line-highlight/prism-line-highlight.js
index bebbcbf1cf..5f0a5e0ef1 100644
--- a/plugins/line-highlight/prism-line-highlight.js
+++ b/plugins/line-highlight/prism-line-highlight.js
@@ -4,15 +4,33 @@
return;
}
- function $$(expr, con) {
- return Array.prototype.slice.call((con || document).querySelectorAll(expr));
+ /**
+ * @param {string} selector
+ * @param {ParentNode} [container]
+ * @returns {HTMLElement[]}
+ */
+ function $$(selector, container) {
+ return Array.prototype.slice.call((container || document).querySelectorAll(selector));
}
+ /**
+ * Returns whether the given element has the given class.
+ *
+ * @param {Element} element
+ * @param {string} className
+ * @returns {boolean}
+ */
function hasClass(element, className) {
className = " " + className + " ";
return (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(className) > -1
}
+ /**
+ * Calls the given function.
+ *
+ * @param {() => any} func
+ * @returns {void}
+ */
function callFunction(func) {
func();
}
@@ -26,8 +44,8 @@
var d = document.createElement('div');
d.style.fontSize = '13px';
d.style.lineHeight = '1.5';
- d.style.padding = 0;
- d.style.border = 0;
+ d.style.padding = '0';
+ d.style.border = '0';
d.innerHTML = ' ';
document.body.appendChild(d);
// Browsers that round the line-height should have offsetHeight === 38
@@ -53,7 +71,7 @@
function highlightLines(pre, lines, classes) {
lines = typeof lines === 'string' ? lines : pre.getAttribute('data-line');
- var ranges = lines.replace(/\s+/g, '').split(',');
+ var ranges = lines.replace(/\s+/g, '').split(',').filter(Boolean);
var offset = +pre.getAttribute('data-line-offset') || 0;
var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
@@ -68,6 +86,7 @@
var start = +range[0];
var end = +range[1] || start;
+ /** @type {HTMLElement} */
var line = pre.querySelector('.line-highlight[data-range="' + currentRange + '"]') || document.createElement('div');
mutateActions.push(function () {
@@ -115,11 +134,58 @@
});
});
+ var id = pre.id;
+ if (hasLineNumbers && id) {
+ // This implements linkable line numbers. Linkable line numbers use Line Highlight to create a link to a
+ // specific line. For this to work, the pre element has to:
+ // 1) have line numbers,
+ // 2) have the `linkable-line-numbers` class or an ascendant that has that class, and
+ // 3) have an id.
+
+ var linkableLineNumbersClass = 'linkable-line-numbers';
+ var linkableLineNumbers = false;
+ var node = pre;
+ while (node) {
+ if (hasClass(node, linkableLineNumbersClass)) {
+ linkableLineNumbers = true;
+ break;
+ }
+ node = node.parentElement;
+ }
+
+ if (linkableLineNumbers) {
+ if (!hasClass(pre, linkableLineNumbersClass)) {
+ // add class to pre
+ mutateActions.push(function () {
+ pre.className = (pre.className + ' ' + linkableLineNumbersClass).trim();
+ });
+ }
+
+ var start = parseInt(pre.getAttribute('data-start') || '1');
+
+ // iterate all line number spans
+ $$('.line-numbers-rows > span', pre).forEach(function (lineSpan, i) {
+ var lineNumber = i + start;
+ lineSpan.onclick = function () {
+ var hash = id + '.' + lineNumber;
+
+ // this will prevent scrolling since the span is obviously in view
+ scrollIntoView = false;
+ location.hash = hash;
+ setTimeout(function () {
+ scrollIntoView = true;
+ }, 1);
+ };
+ });
+ }
+ }
+
return function () {
mutateActions.forEach(callFunction);
};
}
+ var scrollIntoView = true;
function applyHash() {
var hash = location.hash.slice(1);
@@ -148,7 +214,9 @@
var mutateDom = highlightLines(pre, range, 'temporary ');
mutateDom();
- document.querySelector('.temporary.line-highlight').scrollIntoView();
+ if (scrollIntoView) {
+ document.querySelector('.temporary.line-highlight').scrollIntoView();
+ }
}
var fakeTimer = 0; // Hack to limit the number of times applyHash() runs
@@ -203,9 +271,8 @@
window.addEventListener('hashchange', applyHash);
window.addEventListener('resize', function () {
- var actions = [];
- $$('pre[data-line]').forEach(function (pre) {
- actions.push(highlightLines(pre));
+ var actions = $$('pre[data-line]').map(function (pre) {
+ return highlightLines(pre);
});
actions.forEach(callFunction);
});
diff --git a/plugins/line-highlight/prism-line-highlight.min.js b/plugins/line-highlight/prism-line-highlight.min.js
index adfe7f3738..adf8d37755 100644
--- a/plugins/line-highlight/prism-line-highlight.min.js
+++ b/plugins/line-highlight/prism-line-highlight.min.js
@@ -1 +1 @@
-!function(){if("undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector){var t,n=function(){if(void 0===t){var e=document.createElement("div");e.style.fontSize="13px",e.style.lineHeight="1.5",e.style.padding=0,e.style.border=0,e.innerHTML=" ",document.body.appendChild(e),t=38===e.offsetHeight,document.body.removeChild(e)}return t},a=0;Prism.hooks.add("before-sanity-check",function(e){var t=e.element.parentNode,n=t&&t.getAttribute("data-line");if(t&&n&&/pre/i.test(t.nodeName)){var i=0;r(".line-highlight",t).forEach(function(e){i+=e.textContent.length,e.parentNode.removeChild(e)}),i&&/^( \n)+$/.test(e.code.slice(-i))&&(e.code=e.code.slice(0,-i))}}),Prism.hooks.add("complete",function e(t){var n=t.element.parentNode,i=n&&n.getAttribute("data-line");if(n&&i&&/pre/i.test(n.nodeName)){clearTimeout(a);var r=Prism.plugins.lineNumbers,o=t.plugins&&t.plugins.lineNumbers;if(l(n,"line-numbers")&&r&&!o)Prism.hooks.add("line-numbers",e);else s(n,i)(),a=setTimeout(u,1)}}),window.addEventListener("hashchange",u),window.addEventListener("resize",function(){var t=[];r("pre[data-line]").forEach(function(e){t.push(s(e))}),t.forEach(i)})}function r(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function l(e,t){return t=" "+t+" ",-1<(" "+e.className+" ").replace(/[\n\t]/g," ").indexOf(t)}function i(e){e()}function s(u,e,d){var t=(e="string"==typeof e?e:u.getAttribute("data-line")).replace(/\s+/g,"").split(","),c=+u.getAttribute("data-line-offset")||0,f=(n()?parseInt:parseFloat)(getComputedStyle(u).lineHeight),h=l(u,"line-numbers"),p=h?u:u.querySelector("code")||u,m=[];return t.forEach(function(e){var t=e.split("-"),n=+t[0],i=+t[1]||n,r=u.querySelector('.line-highlight[data-range="'+e+'"]')||document.createElement("div");if(m.push(function(){r.setAttribute("aria-hidden","true"),r.setAttribute("data-range",e),r.className=(d||"")+" line-highlight"}),h&&Prism.plugins.lineNumbers){var o=Prism.plugins.lineNumbers.getLine(u,n),a=Prism.plugins.lineNumbers.getLine(u,i);if(o){var l=o.offsetTop+"px";m.push(function(){r.style.top=l})}if(a){var s=a.offsetTop-o.offsetTop+a.offsetHeight+"px";m.push(function(){r.style.height=s})}}else m.push(function(){r.setAttribute("data-start",n),n ",document.body.appendChild(e),t=38===e.offsetHeight,document.body.removeChild(e)}return t},l=!0,a=0;Prism.hooks.add("before-sanity-check",function(e){var t=e.element.parentNode,n=t&&t.getAttribute("data-line");if(t&&n&&/pre/i.test(t.nodeName)){var i=0;g(".line-highlight",t).forEach(function(e){i+=e.textContent.length,e.parentNode.removeChild(e)}),i&&/^( \n)+$/.test(e.code.slice(-i))&&(e.code=e.code.slice(0,-i))}}),Prism.hooks.add("complete",function e(t){var n=t.element.parentNode,i=n&&n.getAttribute("data-line");if(n&&i&&/pre/i.test(n.nodeName)){clearTimeout(a);var r=Prism.plugins.lineNumbers,o=t.plugins&&t.plugins.lineNumbers;if(b(n,"line-numbers")&&r&&!o)Prism.hooks.add("line-numbers",e);else u(n,i)(),a=setTimeout(c,1)}}),window.addEventListener("hashchange",c),window.addEventListener("resize",function(){g("pre[data-line]").map(function(e){return u(e)}).forEach(v)})}function g(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function b(e,t){return t=" "+t+" ",-1<(" "+e.className+" ").replace(/[\n\t]/g," ").indexOf(t)}function v(e){e()}function u(u,e,c){var t=(e="string"==typeof e?e:u.getAttribute("data-line")).replace(/\s+/g,"").split(",").filter(Boolean),d=+u.getAttribute("data-line-offset")||0,f=(s()?parseInt:parseFloat)(getComputedStyle(u).lineHeight),m=b(u,"line-numbers"),p=m?u:u.querySelector("code")||u,h=[];t.forEach(function(e){var t=e.split("-"),n=+t[0],i=+t[1]||n,r=u.querySelector('.line-highlight[data-range="'+e+'"]')||document.createElement("div");if(h.push(function(){r.setAttribute("aria-hidden","true"),r.setAttribute("data-range",e),r.className=(c||"")+" line-highlight"}),m&&Prism.plugins.lineNumbers){var o=Prism.plugins.lineNumbers.getLine(u,n),a=Prism.plugins.lineNumbers.getLine(u,i);if(o){var s=o.offsetTop+"px";h.push(function(){r.style.top=s})}if(a){var l=a.offsetTop-o.offsetTop+a.offsetHeight+"px";h.push(function(){r.style.height=l})}}else h.push(function(){r.setAttribute("data-start",n),n span",u).forEach(function(e,t){var n=t+a;e.onclick=function(){var e=i+"."+n;l=!1,location.hash=e,setTimeout(function(){l=!0},1)}})}}return function(){h.forEach(v)}}function c(){var e=location.hash.slice(1);g(".temporary.line-highlight").forEach(function(e){e.parentNode.removeChild(e)});var t=(e.match(/\.([\d,-]+)$/)||[,""])[1];if(t&&!document.getElementById(e)){var n=e.slice(0,e.lastIndexOf(".")),i=document.getElementById(n);if(i)i.hasAttribute("data-line")||i.setAttribute("data-line",""),u(i,t,"temporary ")(),l&&document.querySelector(".temporary.line-highlight").scrollIntoView()}}}();
\ No newline at end of file
diff --git a/plugins/line-numbers/prism-line-numbers.css b/plugins/line-numbers/prism-line-numbers.css
index 08b29ed615..57705307cb 100644
--- a/plugins/line-numbers/prism-line-numbers.css
+++ b/plugins/line-numbers/prism-line-numbers.css
@@ -27,7 +27,6 @@ pre[class*="language-"].line-numbers > code {
}
.line-numbers-rows > span {
- pointer-events: none;
display: block;
counter-increment: linenumber;
}