Keep Markup: Added `drop-tokens` option class (#3166)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
diff --git a/plugins/keep-markup/index.html b/plugins/keep-markup/index.html
index 01d5269..542ef4e 100644
--- a/plugins/keep-markup/index.html
+++ b/plugins/keep-markup/index.html
@@ -27,7 +27,7 @@
<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
<script src="https://www.google-analytics.com/ga.js" async></script>
</head>
-<body>
+<body class="language-none">
<header data-plugin-header="keep-markup"></header>
@@ -39,6 +39,12 @@
<p>However, you can deactivate the plugin for certain code element by adding the <code>no-keep-markup</code> class to it. You can also deactivate the plugin for the whole page by adding the <code>no-keep-markup</code> class to the body of the page and then selectively activate it again by adding the <code>keep-markup</code> class to code elements.</p>
+ <h2>Double highlighting</h2>
+
+ <p>Some plugins (e.g. <a href="plugins/autoloader">Autoloader</a>) need to re-highlight code blocks. This is a problem for Keep Markup because it will keep the markup of the first highlighting pass resulting in a lot of unnecessary DOM nodes and causing problems for themes and other plugins.</p>
+
+ <p>This problem can be fixed by adding a <code>drop-tokens</code> class to a code block or any of its ancestors. If <code>drop-tokens</code> is present, Keep Markup will ignore all <code class="language-css">span.token</code> elements created by Prism.</p>
+
<h1>Examples</h1>
<p>The following source code</p>
diff --git a/plugins/keep-markup/prism-keep-markup.js b/plugins/keep-markup/prism-keep-markup.js
index 267d9d9..c160faa 100644
--- a/plugins/keep-markup/prism-keep-markup.js
+++ b/plugins/keep-markup/prism-keep-markup.js
@@ -15,31 +15,53 @@
return;
}
+ var dropTokens = Prism.util.isActive(env.element, 'drop-tokens', false);
+ /**
+ * Returns whether the given element should be kept.
+ *
+ * @param {HTMLElement} element
+ * @returns {boolean}
+ */
+ function shouldKeep(element) {
+ if (dropTokens && element.nodeName.toLowerCase() === 'span' && element.classList.contains('token')) {
+ return false;
+ }
+ return true;
+ }
+
var pos = 0;
var data = [];
- var f = function (elt, baseNode) {
- var o = {};
- if (!baseNode) {
- // Clone the original tag to keep all attributes
- o.clone = elt.cloneNode(false);
- o.posOpen = pos;
- data.push(o);
+ function processElement(element) {
+ if (!shouldKeep(element)) {
+ // don't keep this element and just process its children
+ processChildren(element);
+ return;
}
- for (var i = 0, l = elt.childNodes.length; i < l; i++) {
- var child = elt.childNodes[i];
+
+ var o = {
+ // Clone the original tag to keep all attributes
+ clone: element.cloneNode(false),
+ posOpen: pos
+ };
+ data.push(o);
+
+ processChildren(element);
+
+ o.posClose = pos;
+ }
+ function processChildren(element) {
+ for (var i = 0, l = element.childNodes.length; i < l; i++) {
+ var child = element.childNodes[i];
if (child.nodeType === 1) { // element
- f(child);
+ processElement(child);
} else if (child.nodeType === 3) { // text
pos += child.data.length;
}
}
- if (!baseNode) {
- o.posClose = pos;
- }
- };
- f(env.element, true);
+ }
+ processChildren(env.element);
- if (data && data.length) {
+ if (data.length) {
// data is an array of all existing tags
env.keepMarkup = data;
}
diff --git a/plugins/keep-markup/prism-keep-markup.min.js b/plugins/keep-markup/prism-keep-markup.min.js
index 229c510..6bea9d2 100644
--- a/plugins/keep-markup/prism-keep-markup.min.js
+++ b/plugins/keep-markup/prism-keep-markup.min.js
@@ -1 +1 @@
-"undefined"!=typeof Prism&&"undefined"!=typeof document&&document.createRange&&(Prism.plugins.KeepMarkup=!0,Prism.hooks.add("before-highlight",function(e){if(e.element.children.length&&Prism.util.isActive(e.element,"keep-markup",!0)){var a=0,s=[],p=function(e,n){var o={};n||(o.clone=e.cloneNode(!1),o.posOpen=a,s.push(o));for(var t=0,d=e.childNodes.length;t<d;t++){var r=e.childNodes[t];1===r.nodeType?p(r):3===r.nodeType&&(a+=r.data.length)}n||(o.posClose=a)};p(e.element,!0),s&&s.length&&(e.keepMarkup=s)}}),Prism.hooks.add("after-highlight",function(n){if(n.keepMarkup&&n.keepMarkup.length){var a=function(e,n){for(var o=0,t=e.childNodes.length;o<t;o++){var d=e.childNodes[o];if(1===d.nodeType){if(!a(d,n))return!1}else 3===d.nodeType&&(!n.nodeStart&&n.pos+d.data.length>n.node.posOpen&&(n.nodeStart=d,n.nodeStartPos=n.node.posOpen-n.pos),n.nodeStart&&n.pos+d.data.length>=n.node.posClose&&(n.nodeEnd=d,n.nodeEndPos=n.node.posClose-n.pos),n.pos+=d.data.length);if(n.nodeStart&&n.nodeEnd){var r=document.createRange();return r.setStart(n.nodeStart,n.nodeStartPos),r.setEnd(n.nodeEnd,n.nodeEndPos),n.node.clone.appendChild(r.extractContents()),r.insertNode(n.node.clone),r.detach(),!1}}return!0};n.keepMarkup.forEach(function(e){a(n.element,{node:e,pos:0})}),n.highlightedCode=n.element.innerHTML}}));
\ No newline at end of file
+"undefined"!=typeof Prism&&"undefined"!=typeof document&&document.createRange&&(Prism.plugins.KeepMarkup=!0,Prism.hooks.add("before-highlight",function(e){if(e.element.children.length&&Prism.util.isActive(e.element,"keep-markup",!0)){var o=Prism.util.isActive(e.element,"drop-tokens",!1),d=0,t=[];s(e.element),t.length&&(e.keepMarkup=t)}function r(e){if(function(e){return!o||"span"!==e.nodeName.toLowerCase()||!e.classList.contains("token")}(e)){var n={clone:e.cloneNode(!1),posOpen:d};t.push(n),s(e),n.posClose=d}else s(e)}function s(e){for(var n=0,o=e.childNodes.length;n<o;n++){var t=e.childNodes[n];1===t.nodeType?r(t):3===t.nodeType&&(d+=t.data.length)}}}),Prism.hooks.add("after-highlight",function(n){if(n.keepMarkup&&n.keepMarkup.length){var s=function(e,n){for(var o=0,t=e.childNodes.length;o<t;o++){var d=e.childNodes[o];if(1===d.nodeType){if(!s(d,n))return!1}else 3===d.nodeType&&(!n.nodeStart&&n.pos+d.data.length>n.node.posOpen&&(n.nodeStart=d,n.nodeStartPos=n.node.posOpen-n.pos),n.nodeStart&&n.pos+d.data.length>=n.node.posClose&&(n.nodeEnd=d,n.nodeEndPos=n.node.posClose-n.pos),n.pos+=d.data.length);if(n.nodeStart&&n.nodeEnd){var r=document.createRange();return r.setStart(n.nodeStart,n.nodeStartPos),r.setEnd(n.nodeEnd,n.nodeEndPos),n.node.clone.appendChild(r.extractContents()),r.insertNode(n.node.clone),r.detach(),!1}}return!0};n.keepMarkup.forEach(function(e){s(n.element,{node:e,pos:0})}),n.highlightedCode=n.element.innerHTML}}));
\ No newline at end of file
diff --git a/tests/plugins/keep-markup/test.js b/tests/plugins/keep-markup/test.js
index 006ce3c..9f57768 100644
--- a/tests/plugins/keep-markup/test.js
+++ b/tests/plugins/keep-markup/test.js
@@ -4,6 +4,7 @@ const { createScopedPrismDom } = require('../../helper/prism-dom-util');
describe('Keep Markup', function () {
const { Prism, document } = createScopedPrismDom(this, {
+ languages: 'javascript',
plugins: 'keep-markup'
});
@@ -42,6 +43,25 @@ describe('Keep Markup', function () {
keepMarkup(`xy<a>a</a>`);
});
+ it('should support double highlighting', function () {
+ const pre = document.createElement('pre');
+ pre.className = 'language-javascript drop-tokens';
+ pre.innerHTML = '<code>var <mark>a = 42</mark>;</code>';
+ const code = pre.childNodes[0];
+ const initial = code.innerHTML;
+
+ Prism.highlightElement(code);
+ const firstPass = code.innerHTML;
+
+ Prism.highlightElement(code);
+ const secondPass = code.innerHTML;
+
+ // check that we actually did some highlighting
+ assert.notStrictEqual(initial, firstPass);
+ // check that the highlighting persists
+ assert.strictEqual(firstPass, secondPass);
+ });
+
// The markup is removed if it's the last element and the element's name is a single letter: a(nchor), b(old), i(talic)...
// https://github.com/PrismJS/prism/issues/1618
/*