Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Line highlight: Fixed top offset in combination with Line numbers #2237

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions plugins/line-highlight/index.html
Expand Up @@ -9,6 +9,7 @@
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="themes/prism.css" data-noprefix />
<link rel="stylesheet" href="plugins/line-highlight/prism-line-highlight.css" data-noprefix />
<link rel="stylesheet" href="plugins/line-numbers/prism-line-numbers.css" data-noprefix />
<script src="scripts/prefixfree.min.js"></script>

<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
Expand Down Expand Up @@ -69,12 +70,20 @@ <h2>Line 43, starting from line 41</h2>
<pre data-line="43" data-line-offset="40" data-src="plugins/line-highlight/prism-line-highlight.js"></pre>

<p><a href="plugins/line-highlight/#play.50-55,60">Linking example</a></p>

<h2>Compatible with <a href="plugins/line-numbers">Line numbers</a></h2>
<pre class="line-numbers" data-src="plugins/line-numbers/index.html" data-line="7" data-start="-5" style="white-space:pre-wrap;"></pre>

<p>Even with some extra content before the <code>code</code> element.</p>
<pre class="line-numbers" data-line="7" data-start="-5"><div style="padding: .25em">Some content</div><hr/><code class="language-markup" id="foo"></code></pre>
<script>document.querySelector('#foo').textContent = document.documentElement.innerHTML;</script>
</section>

<footer data-src="templates/footer.html" data-type="text/html"></footer>

<script src="prism.js"></script>
<script src="plugins/line-highlight/prism-line-highlight.js"></script>
<script src="plugins/line-numbers/prism-line-numbers.js"></script>
<script src="scripts/utopia.js"></script>
<script src="components.js"></script>
<script src="scripts/code.js"></script>
Expand Down
59 changes: 48 additions & 11 deletions plugins/line-highlight/prism-line-highlight.js
Expand Up @@ -26,8 +26,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 = '&nbsp;<br />&nbsp;';
document.body.appendChild(d);
// Browsers that round the line-height should have offsetHeight === 38
Expand All @@ -39,35 +39,72 @@
}
}());

/**
* Returns the top offset of the content box of the given parent and the content box of one of its children.
*
* @param {HTMLElement} parent
* @param {HTMLElement} child
*/
function getContentBoxTopOffset(parent, child) {
var parentStyle = getComputedStyle(parent);
var childStyle = getComputedStyle(child);

/**
* Returns the numeric value of the given pixel value.
*
* @param {string} px
*/
function pxToNumber(px) {
return +px.substr(0, px.length - 2);
}

return child.offsetTop + pxToNumber(childStyle.borderTopWidth) + pxToNumber(childStyle.paddingTop)
- pxToNumber(parentStyle.paddingTop);
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Highlights the lines of the given pre.
*
* This function is split into a DOM measuring and mutate phase to improve performance.
* The returned function mutates the DOM when called.
*
* @param {HTMLElement} pre
* @param {string} [lines]
* @param {string | null} [lines]
* @param {string} [classes='']
* @returns {() => void}
*/
function highlightLines(pre, lines, classes) {
lines = typeof lines === 'string' ? lines : pre.getAttribute('data-line');

var ranges = lines.replace(/\s+/g, '').split(',');
var offset = +pre.getAttribute('data-line-offset') || 0;
var offset = Number(pre.getAttribute('data-line-offset'));

var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
var lineHeight = parseMethod(getComputedStyle(pre).lineHeight);
var hasLineNumbers = hasClass(pre, 'line-numbers');
var parentElement = hasLineNumbers ? pre : pre.querySelector('code') || pre;
var codeElement = pre.querySelector('code');
var parentElement = hasLineNumbers ? pre : codeElement || pre;
var mutateActions = /** @type {(() => void)[]} */ ([]);

/**
* The top offset between the content box of the <code> element and the content box of the parent element of
* the line highlight element (either `<pre>` or `<code>`).
*
* This offset might not be zero for some themes where the <code> element has a top margin. Some plugins
* (or users) might also add element above the <code> element. Because the line highlight is aligned relative
* to the <pre> element, we have to take this into account.
*
* This offset will be 0 if the parent element of the line highlight element is the `<code>` element.
*/
var codePreOffset = !codeElement || parentElement == codeElement ? 0 : getContentBoxTopOffset(pre, codeElement);

ranges.forEach(function (currentRange) {
var range = currentRange.split('-');

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 () {
Expand All @@ -82,7 +119,7 @@
var endNode = Prism.plugins.lineNumbers.getLine(pre, end);

if (startNode) {
var top = startNode.offsetTop + 'px';
var top = startNode.offsetTop + codePreOffset + 'px';
mutateActions.push(function () {
line.style.top = top;
});
Expand All @@ -96,13 +133,13 @@
}
} else {
mutateActions.push(function () {
line.setAttribute('data-start', start);
line.setAttribute('data-start', String(start));

if (end > start) {
line.setAttribute('data-end', end);
line.setAttribute('data-end', String(end));
}

line.style.top = (start - offset - 1) * lineHeight + 'px';
line.style.top = (start - offset - 1) * lineHeight + codePreOffset + 'px';

line.textContent = new Array(end - start + 2).join(' \n');
});
Expand Down Expand Up @@ -154,7 +191,7 @@
var fakeTimer = 0; // Hack to limit the number of times applyHash() runs

Prism.hooks.add('before-sanity-check', function (env) {
var pre = env.element.parentNode;
var pre = env.element.parentElement;
var lines = pre && pre.getAttribute('data-line');

if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
Expand All @@ -180,7 +217,7 @@
});

Prism.hooks.add('complete', function completeHook(env) {
var pre = env.element.parentNode;
var pre = env.element.parentElement;
var lines = pre && pre.getAttribute('data-line');

if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
Expand Down
2 changes: 1 addition & 1 deletion plugins/line-highlight/prism-line-highlight.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.