prism-keep-markup.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. (function () {
  2. if (typeof self === 'undefined' || !self.Prism || !self.document || !document.createRange) {
  3. return;
  4. }
  5. Prism.plugins.KeepMarkup = true;
  6. Prism.hooks.add('before-highlight', function (env) {
  7. if (!env.element.children.length) {
  8. return;
  9. }
  10. if (!Prism.util.isActive(env.element, 'keep-markup', true)) {
  11. return;
  12. }
  13. var pos = 0;
  14. var data = [];
  15. var f = function (elt, baseNode) {
  16. var o = {};
  17. if (!baseNode) {
  18. // Clone the original tag to keep all attributes
  19. o.clone = elt.cloneNode(false);
  20. o.posOpen = pos;
  21. data.push(o);
  22. }
  23. for (var i = 0, l = elt.childNodes.length; i < l; i++) {
  24. var child = elt.childNodes[i];
  25. if (child.nodeType === 1) { // element
  26. f(child);
  27. } else if(child.nodeType === 3) { // text
  28. pos += child.data.length;
  29. }
  30. }
  31. if (!baseNode) {
  32. o.posClose = pos;
  33. }
  34. };
  35. f(env.element, true);
  36. if (data && data.length) {
  37. // data is an array of all existing tags
  38. env.keepMarkup = data;
  39. }
  40. });
  41. Prism.hooks.add('after-highlight', function (env) {
  42. if(env.keepMarkup && env.keepMarkup.length) {
  43. var walk = function (elt, nodeState) {
  44. for (var i = 0, l = elt.childNodes.length; i < l; i++) {
  45. var child = elt.childNodes[i];
  46. if (child.nodeType === 1) { // element
  47. if (!walk(child, nodeState)) {
  48. return false;
  49. }
  50. } else if (child.nodeType === 3) { // text
  51. if(!nodeState.nodeStart && nodeState.pos + child.data.length > nodeState.node.posOpen) {
  52. // We found the start position
  53. nodeState.nodeStart = child;
  54. nodeState.nodeStartPos = nodeState.node.posOpen - nodeState.pos;
  55. }
  56. if(nodeState.nodeStart && nodeState.pos + child.data.length >= nodeState.node.posClose) {
  57. // We found the end position
  58. nodeState.nodeEnd = child;
  59. nodeState.nodeEndPos = nodeState.node.posClose - nodeState.pos;
  60. }
  61. nodeState.pos += child.data.length;
  62. }
  63. if (nodeState.nodeStart && nodeState.nodeEnd) {
  64. // Select the range and wrap it with the clone
  65. var range = document.createRange();
  66. range.setStart(nodeState.nodeStart, nodeState.nodeStartPos);
  67. range.setEnd(nodeState.nodeEnd, nodeState.nodeEndPos);
  68. nodeState.node.clone.appendChild(range.extractContents());
  69. range.insertNode(nodeState.node.clone);
  70. range.detach();
  71. // Process is over
  72. return false;
  73. }
  74. }
  75. return true;
  76. };
  77. // For each tag, we walk the DOM to reinsert it
  78. env.keepMarkup.forEach(function (node) {
  79. walk(env.element, {
  80. node: node,
  81. pos: 0
  82. });
  83. });
  84. // Store new highlightedCode for later hooks calls
  85. env.highlightedCode = env.element.innerHTML;
  86. }
  87. });
  88. }());