Commit bd3e9ea84039d0b784a0158220d7b093e9524f1d

Golmote 2015-09-22T20:03:26

Merge pull request #770 from Golmote/prism-keep-markup Keep Markup Plugin

diff --git a/components.js b/components.js
index e54ab68..44001f2 100644
--- a/components.js
+++ b/components.js
@@ -505,6 +505,11 @@ var components = {
 			"title": "Autoloader",
 			"owner": "Golmote",
 			"noCSS": true
+		},
+		"keep-markup": {
+			"title": "Keep Markup",
+			"owner": "Golmote",
+			"noCSS": true
 		}
 	}
 };
diff --git a/plugins/keep-markup/index.html b/plugins/keep-markup/index.html
new file mode 100644
index 0000000..1eba471
--- /dev/null
+++ b/plugins/keep-markup/index.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+
+	<meta charset="utf-8" />
+	<link rel="shortcut icon" href="favicon.png" />
+	<title>Keep markup ▲ Prism plugins</title>
+	<base href="../.." />
+	<link rel="stylesheet" href="style.css" />
+	<link rel="stylesheet" href="themes/prism.css" data-noprefix />
+
+	<style type="text/css">
+		code[class*="language-"] mark,
+		pre[class*="language-"] mark {
+			display: inline-block;
+			color: inherit;
+			background: none;
+			border: 1px solid #000;
+			box-shadow: 0 0 2px #fff;
+			padding: 1px;
+			background: rgba(0,0,0,0.2);
+		}
+	</style>
+
+	<script src="prefixfree.min.js"></script>
+
+	<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
+	<script src="http://www.google-analytics.com/ga.js" async></script>
+</head>
+<body>
+
+<header>
+	<div class="intro" data-src="templates/header-plugins.html" data-type="text/html"></div>
+
+	<h2>Keep markup</h2>
+	<p>Prevents custom markup from being dropped out during highlighting.</p>
+</header>
+
+<section>
+
+	<h1>How to use</h1>
+	<p>You have nothing to do. With this plugin loaded, all markup inside code will be kept.</p>
+
+	<h1>Examples</h1>
+
+	<p>The following source code</p>
+	<pre><code class="language-markup">&lt;pre>&lt;code class="language-css">
+@media &lt;mark>screen&lt;/mark> {
+	div {
+		&lt;mark>text&lt;/mark>-decoration: &lt;mark>&lt;mark>under&lt;/mark>line&lt;/mark>;
+		back&lt;mark>ground: url&lt;/mark>('foo.png');
+	}
+}&lt;/code>&lt;/pre></code></pre>
+
+	<p>would render like this:</p>
+	<pre><code class="language-css">
+@media <mark>screen</mark> {
+	div {
+		<mark>text</mark>-decoration: <mark><mark>under</mark>line</mark>;
+		back<mark>ground: url</mark>('foo.png');
+	}
+}</code></pre>
+
+	<p>
+		It also works for inline code:
+		<code class="language-javascript">v<mark>ar b</mark>ar = <mark>func</mark>tion () { <mark>/*</mark> foo <mark>*</mark>/ };</code>
+	</p>
+
+</section>
+
+<footer data-src="templates/footer.html" data-type="text/html"></footer>
+
+<script src="prism.js"></script>
+<script src="plugins/keep-markup/prism-keep-markup.js"></script>
+<script src="utopia.js"></script>
+<script src="components.js"></script>
+<script src="code.js"></script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/keep-markup/prism-keep-markup.js b/plugins/keep-markup/prism-keep-markup.js
new file mode 100644
index 0000000..9038ed0
--- /dev/null
+++ b/plugins/keep-markup/prism-keep-markup.js
@@ -0,0 +1,97 @@
+(function () {
+
+	if (typeof self === 'undefined' || !self.Prism || !self.document || !document.createRange) {
+		return;
+	}
+
+	Prism.hooks.add('before-highlight', function (env) {
+		var firstWhiteSpaces = false;
+		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);
+			}
+			for (var i = 0, l = elt.childNodes.length; i < l; i++) {
+				var child = elt.childNodes[i];
+				if (child.nodeType === 1) { // element
+					f(child);
+				} else if(child.nodeType === 3) { // text
+					if(!firstWhiteSpaces) {
+						// We need to ignore the first white spaces in the code block
+						child.data = child.data.replace(/^(?:\r?\n|\r)/, '');
+						firstWhiteSpaces = true;
+					}
+					pos += child.data.length;
+				}
+			}
+			if (!baseNode) {
+				o.posClose = pos;
+			}
+		};
+		f(env.element, true);
+
+		if (data && data.length) {
+			// data is an array of all existing tags
+			env.keepMarkup = data;
+		}
+	});
+
+	Prism.hooks.add('after-highlight', function (env) {
+		if(env.keepMarkup && env.keepMarkup.length) {
+
+			var walk = function (elt, nodeState) {
+				for (var i = 0, l = elt.childNodes.length; i < l; i++) {
+
+					var child = elt.childNodes[i];
+
+					if (child.nodeType === 1) { // element
+						if (!walk(child, nodeState)) {
+							return false;
+						}
+
+					} else if (child.nodeType === 3) { // text
+						if(!nodeState.nodeStart && nodeState.pos + child.data.length > nodeState.node.posOpen) {
+							// We found the start position
+							nodeState.nodeStart = child;
+							nodeState.nodeStartPos = nodeState.node.posOpen - nodeState.pos;
+						}
+						if(nodeState.nodeStart && nodeState.pos + child.data.length >= nodeState.node.posClose) {
+							// We found the end position
+							nodeState.nodeEnd = child;
+							nodeState.nodeEndPos = nodeState.node.posClose - nodeState.pos;
+						}
+
+						nodeState.pos += child.data.length;
+					}
+
+					if (nodeState.nodeStart && nodeState.nodeEnd) {
+						// Select the range and wrap it with the clone
+						var range = document.createRange();
+						range.setStart(nodeState.nodeStart, nodeState.nodeStartPos);
+						range.setEnd(nodeState.nodeEnd, nodeState.nodeEndPos);
+						nodeState.node.clone.appendChild(range.extractContents());
+						range.insertNode(nodeState.node.clone);
+						range.detach();
+
+						// Process is over
+						return false;
+					}
+				}
+				return true;
+			};
+
+			// For each tag, we walk the DOM to reinsert it
+			env.keepMarkup.forEach(function (node) {
+				walk(env.element, {
+					node: node,
+					pos: 0
+				});
+			});
+		}
+	});
+}());
\ No newline at end of file
diff --git a/plugins/keep-markup/prism-keep-markup.min.js b/plugins/keep-markup/prism-keep-markup.min.js
new file mode 100644
index 0000000..86a39c9
--- /dev/null
+++ b/plugins/keep-markup/prism-keep-markup.min.js
@@ -0,0 +1 @@
+!function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.createRange&&(Prism.hooks.add("before-highlight",function(e){var n=!1,o=0,t=[],d=function(e,a){var r={};a||(r.clone=e.cloneNode(!1),r.posOpen=o,t.push(r));for(var s=0,p=e.childNodes.length;p>s;s++){var l=e.childNodes[s];1===l.nodeType?d(l):3===l.nodeType&&(n||(l.data=l.data.replace(/^(?:\r?\n|\r)/,""),n=!0),o+=l.data.length)}a||(r.posClose=o)};d(e.element,!0),t&&t.length&&(e.keepMarkup=t)}),Prism.hooks.add("after-highlight",function(e){if(e.keepMarkup&&e.keepMarkup.length){var n=function(e,o){for(var t=0,d=e.childNodes.length;d>t;t++){var a=e.childNodes[t];if(1===a.nodeType){if(!n(a,o))return!1}else 3===a.nodeType&&(!o.nodeStart&&o.pos+a.data.length>o.node.posOpen&&(o.nodeStart=a,o.nodeStartPos=o.node.posOpen-o.pos),o.nodeStart&&o.pos+a.data.length>=o.node.posClose&&(o.nodeEnd=a,o.nodeEndPos=o.node.posClose-o.pos),o.pos+=a.data.length);if(o.nodeStart&&o.nodeEnd){var r=document.createRange();return r.setStart(o.nodeStart,o.nodeStartPos),r.setEnd(o.nodeEnd,o.nodeEndPos),o.node.clone.appendChild(r.extractContents()),r.insertNode(o.node.clone),r.detach(),!1}}return!0};e.keepMarkup.forEach(function(o){n(e.element,{node:o,pos:0})})}}))}();
\ No newline at end of file