UNPKG

3.31 kBJavaScriptView Raw
1(function () {
2
3 if (typeof Prism === 'undefined' || typeof document === 'undefined' || !document.createRange) {
4 return;
5 }
6
7 Prism.plugins.KeepMarkup = true;
8
9 Prism.hooks.add('before-highlight', function (env) {
10 if (!env.element.children.length) {
11 return;
12 }
13
14 if (!Prism.util.isActive(env.element, 'keep-markup', true)) {
15 return;
16 }
17
18 var dropTokens = Prism.util.isActive(env.element, 'drop-tokens', false);
19 /**
20 * Returns whether the given element should be kept.
21 *
22 * @param {HTMLElement} element
23 * @returns {boolean}
24 */
25 function shouldKeep(element) {
26 if (dropTokens && element.nodeName.toLowerCase() === 'span' && element.classList.contains('token')) {
27 return false;
28 }
29 return true;
30 }
31
32 var pos = 0;
33 var data = [];
34 function processElement(element) {
35 if (!shouldKeep(element)) {
36 // don't keep this element and just process its children
37 processChildren(element);
38 return;
39 }
40
41 var o = {
42 // Clone the original tag to keep all attributes
43 clone: element.cloneNode(false),
44 posOpen: pos
45 };
46 data.push(o);
47
48 processChildren(element);
49
50 o.posClose = pos;
51 }
52 function processChildren(element) {
53 for (var i = 0, l = element.childNodes.length; i < l; i++) {
54 var child = element.childNodes[i];
55 if (child.nodeType === 1) { // element
56 processElement(child);
57 } else if (child.nodeType === 3) { // text
58 pos += child.data.length;
59 }
60 }
61 }
62 processChildren(env.element);
63
64 if (data.length) {
65 // data is an array of all existing tags
66 env.keepMarkup = data;
67 }
68 });
69
70 Prism.hooks.add('after-highlight', function (env) {
71 if (env.keepMarkup && env.keepMarkup.length) {
72
73 var walk = function (elt, nodeState) {
74 for (var i = 0, l = elt.childNodes.length; i < l; i++) {
75
76 var child = elt.childNodes[i];
77
78 if (child.nodeType === 1) { // element
79 if (!walk(child, nodeState)) {
80 return false;
81 }
82
83 } else if (child.nodeType === 3) { // text
84 if (!nodeState.nodeStart && nodeState.pos + child.data.length > nodeState.node.posOpen) {
85 // We found the start position
86 nodeState.nodeStart = child;
87 nodeState.nodeStartPos = nodeState.node.posOpen - nodeState.pos;
88 }
89 if (nodeState.nodeStart && nodeState.pos + child.data.length >= nodeState.node.posClose) {
90 // We found the end position
91 nodeState.nodeEnd = child;
92 nodeState.nodeEndPos = nodeState.node.posClose - nodeState.pos;
93 }
94
95 nodeState.pos += child.data.length;
96 }
97
98 if (nodeState.nodeStart && nodeState.nodeEnd) {
99 // Select the range and wrap it with the clone
100 var range = document.createRange();
101 range.setStart(nodeState.nodeStart, nodeState.nodeStartPos);
102 range.setEnd(nodeState.nodeEnd, nodeState.nodeEndPos);
103 nodeState.node.clone.appendChild(range.extractContents());
104 range.insertNode(nodeState.node.clone);
105 range.detach();
106
107 // Process is over
108 return false;
109 }
110 }
111 return true;
112 };
113
114 // For each tag, we walk the DOM to reinsert it
115 env.keepMarkup.forEach(function (node) {
116 walk(env.element, {
117 node: node,
118 pos: 0
119 });
120 });
121 // Store new highlightedCode for later hooks calls
122 env.highlightedCode = env.element.innerHTML;
123 }
124 });
125}());