diff --git a/LICENSE b/LICENSE index 4f023b7..c388371 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,23 @@ The MIT License (MIT) Copyright (c) 2017 Yauheni Pakala +Copyright (c) 2018 Lester L. Martin II -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/highlightjs-line-numbers.js b/src/highlightjs-line-numbers.js index a045a05..c0f9fde 100644 --- a/src/highlightjs-line-numbers.js +++ b/src/highlightjs-line-numbers.js @@ -14,6 +14,7 @@ if (w.hljs) { w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad; w.hljs.lineNumbersBlock = lineNumbersBlock; + w.hljs.initLineNumScroll = initLineNumScroll; addStyles(); } else { @@ -26,16 +27,37 @@ css.innerHTML = format( '.{0}{border-collapse:collapse}\ .{0} td{padding:0}\ - .{1}:before{content:attr({2})}', - [ - TABLE_NAME, - NUMBER_LINE_NAME, - DATA_ATTR_NAME - ]); + .{1}:before{content:attr({2})}{3}', + [ + TABLE_NAME, + NUMBER_LINE_NAME, + DATA_ATTR_NAME, + ]); d.getElementsByTagName('head')[0].appendChild(css); } function initLineNumbersOnLoad (options) { + var css = d.createElement('style'); + + css.innerHTML = '\ + pre>code{white-space:nowrap;}\ + .hljs-ln-line{white-space:pre;}\ + .hljs-ln-code .hljs-ln-line{padding-left:10px;}\ + .hljs-ln-numbers{width:1px;}\ + .hljs-ln-n{text-align:right;padding-right:10px;\ + border-right:1px solid;white-space:nowrap;\ + padding-left:5px;}\ + .hljs-ln-code:hover{background-color:rgba(0,0,0,.25)}\ + .hljs-ln-code *::selection{background-color:rgba(0,0,0,.45);}\ + .hljs-ln-code *::-moz-selection{background-color:rgba(0,0,0,.45);}\ + .selectedText{background-color:rgba(0,0,0,.25);\ + border-left:1px solid;border-right:1px solid;}\ + .selectedTextBoth{background-color:rgba(0,0,0,.25);border:1px solid;}\ + .selectedTextFirst{background-color:rgba(0,0,0,.25);\ + border-top:1px solid;border-left:1px solid;border-right:1px solid;}\ + .selectedTextLast{background-color:rgba(0,0,0,.25);\ + border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}' + d.getElementsByTagName('head')[0].appendChild(css); if (d.readyState === 'complete') { documentReady(options); } else { @@ -74,12 +96,12 @@ duplicateMultilineNodes(element); - element.innerHTML = addLineNumbersBlockFor(element.innerHTML, firstLineIndex); + element.innerHTML = + addLineNumbersBlockFor(element.innerHTML, firstLineIndex); }); } function addLineNumbersBlockFor (inputHtml, firstLineIndex) { - var lines = getLines(inputHtml); // if last line contains only carriage return remove it @@ -90,31 +112,101 @@ if (lines.length > firstLineIndex) { var html = ''; + // setup for linking lines + var currLoc = new URL(window.location.href); + currLoc.hash = ""; + var uri = currLoc.href.endsWith('#') ? currLoc.href : currLoc.href + '#'; + + // get our line ranges + var lnRanges = splitLineParam(getLineParam()); + + // our current lnRanges index, and something to tell us + // if we should increment it + var currIdx = 0; + + // do we highlight this line? + var hl = false; + var tmp; + for (var i = 0, l = lines.length; i < l; i++) { + tmp = doHighlight(i, lnRanges[currIdx]); + hl = tmp[0]; + currIdx += tmp[1] && currIdx + 1 <= lnRanges.length ? 1 : 0; html += format( - '\ - \ -
\ - \ - \ -
{6}
\ - \ + '\ + \ + \ +
\ +
\ +
\ + \ + \ +
{6}
\ + \ ', - [ - NUMBERS_BLOCK_NAME, - LINE_NAME, - NUMBER_LINE_NAME, - DATA_ATTR_NAME, - CODE_BLOCK_NAME, - i + 1, - lines[i].length > 0 ? lines[i] : ' ' - ]); + [ + NUMBERS_BLOCK_NAME, + LINE_NAME, + NUMBER_LINE_NAME, + DATA_ATTR_NAME, + CODE_BLOCK_NAME, + i + 1, + lines[i].length > 0 ? lines[i] : ' ', + uri, + hl ? whichSelectedText(tmp) : " " + ]); + hl = false; } return format('{1}
', [ TABLE_NAME, html ]); } + } - return inputHtml; + function whichSelectedText(check) { + if (check[2] && check[3]) { + return "selectedTextBoth"; + } else if (check[2]) { + return "selectedTextFirst"; + } else if (check[3]) { + return "selectedTextLast"; + } else { + return "selectedText"; + } + } + + function getLineParam() { + var idx1 = window.location.href.indexOf("&ln=") + 3; + var idx2 = window.location.href.indexOf("&", idx1); + idx2 = idx2 == -1 ? window.location.href.length : idx2 -1; + var len = idx2 - idx1; + return window.location.href.substr(idx1+1, len) + window.location.hash; + } + + function splitLineParam(param) { + // split into $NUM-$NUM ranges or $NUM singulars, then split + // each into arrays of numbers + var ps = param.match(/\d+(-\d+)?/g); + return ps !== null ? ps.map(x => (x.match(/\d+/g)).map (x => parseInt(x))) : + []; + } + + function doHighlight(idx, range) { + var nextRange = false; + var first = false; + var last = false; + try { + if (range.length == 1) { + nextRange = idx+1 == range[0]; + return [nextRange, nextRange, nextRange, nextRange]; + } else if (range.length == 2) { + nextRange = idx+1 == range[1]; + first = idx+1 == range[0]; + last = idx+1 == range[1]; + return [idx+1 >= range[0] && idx+1 <= range[1], nextRange, first, last]; + } + } catch (e) { + return [false, false, false, false]; + } } /** @@ -149,8 +241,10 @@ var lines = getLines(element.innerHTML); + var line_to_fmt = ''; for (var i = 0, result = ''; i < lines.length; i++) { - result += format('{1}\n', [ className, lines[i] ]); + line_to_fmt = '' + lines[i] +'\n'; + result += format(line_to_fmt, [ className ]); } element.innerHTML = result.trim(); @@ -180,4 +274,35 @@ }); } + function rafAsync() { + return new Promise(resolve => { + requestAnimationFrame(resolve); + }); + } + + function checkEl(id) { + if (document.getElementById(id) === null) { + return rafAsync().then(() => checkEl(id)); + } else { + return Promise.resolve(true); + } + } + + function initLineNumScroll (options) { + if (d.readyState === 'complete') { + documentReady(options); + } else { + var el = '#L' + splitLineParam(getLineParam())[0][0].toString(); + w.addEventListener('DOMContentLoaded', function () { + checkEl(el).then((element) => { + try { + document.getElementById(el).scrollIntoView(); + } catch (e) { + + } + }) + }); + } + } + }(window, document));