prism-line-highlight.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. (function(){
  2. if(!window.Prism) {
  3. return;
  4. }
  5. function $$(expr, con) {
  6. return Array.prototype.slice.call((con || document).querySelectorAll(expr));
  7. }
  8. function hasClass(element, className) {
  9. className = " " + className + " ";
  10. return (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(className) > -1
  11. }
  12. var CRLF = crlf = /\r?\n|\r/g;
  13. function highlightLines(pre, lines, classes) {
  14. var ranges = lines.replace(/\s+/g, '').split(','),
  15. offset = +pre.getAttribute('data-line-offset') || 0;
  16. var lineHeight = parseFloat(getComputedStyle(pre).lineHeight);
  17. for (var i=0, range; range = ranges[i++];) {
  18. range = range.split('-');
  19. var start = +range[0],
  20. end = +range[1] || start;
  21. var line = document.createElement('div');
  22. line.textContent = Array(end - start + 2).join(' \r\n');
  23. line.className = (classes || '') + ' line-highlight';
  24. //if the line-numbers plugin is enabled, then there is no reason for this plugin to display the line numbers
  25. if(!hasClass(pre, 'line-numbers')) {
  26. line.setAttribute('data-start', start);
  27. if(end > start) {
  28. line.setAttribute('data-end', end);
  29. }
  30. }
  31. line.style.top = (start - offset - 1) * lineHeight + 'px';
  32. //allow this to play nicely with the line-numbers plugin
  33. if(hasClass(pre, 'line-numbers')) {
  34. //need to attack to pre as when line-numbers is enabled, the code tag is relatively which screws up the positioning
  35. pre.appendChild(line);
  36. } else {
  37. (pre.querySelector('code') || pre).appendChild(line);
  38. }
  39. }
  40. }
  41. function applyHash() {
  42. var hash = location.hash.slice(1);
  43. // Remove pre-existing temporary lines
  44. $$('.temporary.line-highlight').forEach(function (line) {
  45. line.parentNode.removeChild(line);
  46. });
  47. var range = (hash.match(/\.([\d,-]+)$/) || [,''])[1];
  48. if (!range || document.getElementById(hash)) {
  49. return;
  50. }
  51. var id = hash.slice(0, hash.lastIndexOf('.')),
  52. pre = document.getElementById(id);
  53. if (!pre) {
  54. return;
  55. }
  56. if (!pre.hasAttribute('data-line')) {
  57. pre.setAttribute('data-line', '');
  58. }
  59. highlightLines(pre, range, 'temporary ');
  60. document.querySelector('.temporary.line-highlight').scrollIntoView();
  61. }
  62. var fakeTimer = 0; // Hack to limit the number of times applyHash() runs
  63. Prism.hooks.add('after-highlight', function(env) {
  64. var pre = env.element.parentNode;
  65. var lines = pre && pre.getAttribute('data-line');
  66. if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
  67. return;
  68. }
  69. clearTimeout(fakeTimer);
  70. $$('.line-highlight', pre).forEach(function (line) {
  71. line.parentNode.removeChild(line);
  72. });
  73. highlightLines(pre, lines);
  74. fakeTimer = setTimeout(applyHash, 1);
  75. });
  76. addEventListener('hashchange', applyHash);
  77. })();