Commit ec8fb796bb8c81f8c70a8be790db78e3257c1f7c

Lea Verou 2012-07-31T17:47:56

Added helper method Prism.languages.DFS

diff --git a/components/prism-core.js b/components/prism-core.js
index a9d4874..d607919 100644
--- a/components/prism-core.js
+++ b/components/prism-core.js
@@ -35,6 +35,16 @@ var _ = self.Prism = {
 			}
 			
 			return root[inside] = ret;
+		},
+		
+		DFS: function(o, callback) {
+			for (var i in o) {
+				callback.call(o, i, o[i]);
+				
+				if (Object.prototype.toString.call(o) === '[object Object]') {
+					_.languages.DFS(o[i], callback);
+				}
+			}
 		}
 	},
 
@@ -201,7 +211,7 @@ var _ = self.Prism = {
 
 		return strarr;
 	},
-		
+	
 	hooks: {
 		all: {},
 		
diff --git a/components/prism-core.min.js b/components/prism-core.min.js
index 833c650..78d922e 100644
--- a/components/prism-core.min.js
+++ b/components/prism-core.min.js
@@ -2,4 +2,4 @@
  * Prism: Lightweight, robust, elegant syntax highlighting
  * MIT license http://www.opensource.org/licenses/mit-license.php/
  * @author Lea Verou http://lea.verou.me
- */(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={languages:{insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"")+" language-"+o;a=r.parentNode;if(/pre/i.test(a.nodeName)){var f=(a.className.match(e)||[,""])[1];a.className=a.className.replace(e,"")+" language-"+f}var l=r.textContent.trim();if(!l)return;l=l.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\u00a0/g," ");var c={element:r,language:o,grammar:u,code:l};t.hooks.run("before-highlight",c);if(i&&self.Worker){var h=new Worker(t.filename);h.onmessage=function(e){c.highlightedCode=n.stringify(JSON.parse(e.data));c.element.innerHTML=c.highlightedCode;s&&s.call(c.element);t.hooks.run("after-highlight",c)};h.postMessage(JSON.stringify({language:c.language,code:c.code}))}else{c.highlightedCode=t.highlight(c.code,c.grammar);c.element.innerHTML=c.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",c)}},highlight:function(e,r){return n.stringify(t.tokenize(e,r))},tokenize:function(e,n){var r=t.Token,i=[e],s=n.rest;if(s){for(var o in s)n[o]=s[o];delete n.rest}e:for(var o in n){if(!n.hasOwnProperty(o)||!n[o])continue;var u=n[o],a=u.inside,f=!!u.lookbehind||0;u=u.pattern||u;for(var l=0;l<i.length;l++){var c=i[l];if(i.length>e.length)break e;if(c instanceof r)continue;u.lastIndex=0;var h=u.exec(c);if(h){f&&(f=h[1].length);var p=h.index-1+f,h=h[0].slice(f),d=h.length,v=p+d,m=c.slice(0,p+1),g=c.slice(v+1),y=[l,1];m&&y.push(m);var b=new r(o,a?t.tokenize(h,a):h);y.push(b);g&&y.push(g);Array.prototype.splice.apply(i,y)}}}return i},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]"){for(var r=0;r<e.length;r++)e[r]=n.stringify(e[r]);return e.join("")}var i={type:e.type,content:n.stringify(e.content),tag:"span",classes:["token",e.type],attributes:{}};i.type=="comment"&&(i.attributes.spellcheck="true");t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+"</"+i.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language;code=n.code;self.postMessage(JSON.stringify(t.tokenize(code,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();
\ No newline at end of file
+ */(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={languages:{insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);Object.prototype.toString.call(e)==="[object Object]"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"")+" language-"+o;a=r.parentNode;if(/pre/i.test(a.nodeName)){var f=(a.className.match(e)||[,""])[1];a.className=a.className.replace(e,"")+" language-"+f}var l=r.textContent.trim();if(!l)return;l=l.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\u00a0/g," ");var c={element:r,language:o,grammar:u,code:l};t.hooks.run("before-highlight",c);if(i&&self.Worker){var h=new Worker(t.filename);h.onmessage=function(e){c.highlightedCode=n.stringify(JSON.parse(e.data));c.element.innerHTML=c.highlightedCode;s&&s.call(c.element);t.hooks.run("after-highlight",c)};h.postMessage(JSON.stringify({language:c.language,code:c.code}))}else{c.highlightedCode=t.highlight(c.code,c.grammar);c.element.innerHTML=c.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",c)}},highlight:function(e,r){return n.stringify(t.tokenize(e,r))},tokenize:function(e,n){var r=t.Token,i=[e],s=n.rest;if(s){for(var o in s)n[o]=s[o];delete n.rest}e:for(var o in n){if(!n.hasOwnProperty(o)||!n[o])continue;var u=n[o],a=u.inside,f=!!u.lookbehind||0;u=u.pattern||u;for(var l=0;l<i.length;l++){var c=i[l];if(i.length>e.length)break e;if(c instanceof r)continue;u.lastIndex=0;var h=u.exec(c);if(h){f&&(f=h[1].length);var p=h.index-1+f,h=h[0].slice(f),d=h.length,v=p+d,m=c.slice(0,p+1),g=c.slice(v+1),y=[l,1];m&&y.push(m);var b=new r(o,a?t.tokenize(h,a):h);y.push(b);g&&y.push(g);Array.prototype.splice.apply(i,y)}}}return i},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]"){for(var r=0;r<e.length;r++)e[r]=n.stringify(e[r]);return e.join("")}var i={type:e.type,content:n.stringify(e.content),tag:"span",classes:["token",e.type],attributes:{}};i.type=="comment"&&(i.attributes.spellcheck="true");t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+"</"+i.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language;code=n.code;self.postMessage(JSON.stringify(t.tokenize(code,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();
\ No newline at end of file
diff --git a/plugins/autolinker/index.html b/plugins/autolinker/index.html
index 12f6f87..bedbaa6 100644
--- a/plugins/autolinker/index.html
+++ b/plugins/autolinker/index.html
@@ -17,25 +17,41 @@
 	<div class="intro" data-src="templates/header-plugins.html" data-type="text/html"></div>
 
 	<h2>Autolinker</h2>
-	<p>Converts URLs and emails in code to clickable links</p>
+	<p>Converts URLs and emails in code to clickable links. Parses <a href="http://daringfireball.net/projects/markdown/">Markdown</a> links in comments.</p>
 </header>
 
 <section>
+	<h1>How to use</h1>
+	<p>URLs and emails will be linked automatically, you don’t need to do anything. To link some text inside a comment to a certain URL,
+	you may use the Markdown syntax: 
+	<pre><code class="language-markdown">[Text you want to see](http://url-goes-here.com)</code></pre>
+</section>
+
+<section>
 	<h1>Examples</h1>
 	
+	<h2>JavaScript</h2>
 	<pre><code class="language-javascript">/**
  * Prism: Lightweight, robust, elegant syntax highlighting
  * MIT license http://www.opensource.org/licenses/mit-license.php/
  * @author Lea Verou http://lea.verou.me 
  * Reach Lea at fake@email.com (no, not really)
+ * And this is [a Markdown link](http://prismjs.com). Sweet, huh?
  */
 var foo = 5;
 // And a single line comment http://google.com</code></pre>
  	
+ 	<h2>CSS</h2>
  	<pre><code class="language-css">@font-face {
 	src: url(http://lea.verou.me/logo.otf);
 	font-family: 'LeaVerou';
 }</code></pre>
+	
+	<h2>HTML</h2>
+	<pre><code class="language-markup">&lt;!-- Links in HTML, woo!
+Lea Verou http://lea.verou.me or, with Markdown, [Lea Verou](http://lea.verou.me) -->
+&lt;img src="http://prismjs.com/img/spectrum.png" alt="In attributes too!" />
+&lt;p>Autolinking in raw text: http://prismjs.com&lt;/p></code></pre>
 </section>
 
 <footer data-src="templates/footer.html" data-type="text/html"></footer>
diff --git a/plugins/autolinker/prism-autolinker.js b/plugins/autolinker/prism-autolinker.js
index 71f23eb..7cc53b6 100644
--- a/plugins/autolinker/prism-autolinker.js
+++ b/plugins/autolinker/prism-autolinker.js
@@ -4,15 +4,57 @@ if (!window.Prism) {
 	return;
 }
 
-var url = /\b([a-z]{3,7}:\/\/|tel:)\S+/g,
-    email = /\b\S+@[\w.]+[a-z]{2}/g,
-    candidates = ['comment', 'line-comment', 'url'];
+var url = /\b([a-z]{3,7}:\/\/|tel:)[\w-+%/.]+/,
+    email = /\b\S+@[\w.]+[a-z]{2}/,
+    linkMd = /\[([^\]]+)]\(([^)]+)\)/,
+    
+	// Tokens that may contain URLs and emails
+    candidates = ['comment', 'url', 'attr-value', 'string'];
+
+for (var language in Prism.languages) {
+	var tokens = Prism.languages[language];
+	
+	Prism.languages.DFS(tokens, function (type, def) {
+		if (candidates.indexOf(type) > -1) {
+			if (!def.pattern) {
+				def = this[type] = {
+					pattern: def
+				};
+			}
+			
+			def.inside = def.inside || {};
+			
+			if (type == 'comment') {
+				def.inside['md-link'] = linkMd;
+			}
+			
+			def.inside['url-link'] = url;
+			def.inside['email-link'] = email;
+		}
+	});
+	
+	tokens['url-link'] = url;
+	tokens['email-link'] = email;
+}
 
 Prism.hooks.add('wrap', function(env) {
-	if (candidates.indexOf(env.type) > -1) {
-		env.content = env.content
-			.replace(url, '<a href="$&">$&</a>')
-			.replace(email, '<a href="mailto:$&">$&</a>')
+	if (/-link$/.test(env.type)) {
+		env.tag = 'a';
+		
+		var href = env.content;
+		
+		if (env.type == 'email-link') {
+			href = 'mailto:' + href;
+		}
+		else if (env.type == 'md-link') {
+			// Markdown
+			var match = env.content.match(linkMd);
+			
+			href = match[2];
+			env.content = match[1];
+		}
+		
+		env.attributes.href = href;
 	}
 });
 
diff --git a/plugins/autolinker/prism-autolinker.min.js b/plugins/autolinker/prism-autolinker.min.js
index 5e43fd3..eb7678f 100644
--- a/plugins/autolinker/prism-autolinker.min.js
+++ b/plugins/autolinker/prism-autolinker.min.js
@@ -1 +1 @@
-(function(){if(!window.Prism)return;var e=/\b([a-z]{3,7}:\/\/|tel:)\S+/g,t=/\b\S+@[\w.]+[a-z]{2}/g,n=["comment","line-comment","url"];Prism.hooks.add("wrap",function(r){n.indexOf(r.type)>-1&&(r.content=r.content.replace(e,'<a href="$&">$&</a>').replace(t,'<a href="mailto:$&">$&</a>'))})})();
\ No newline at end of file
+(function(){if(!window.Prism)return;var e=/\b([a-z]{3,7}:\/\/|tel:)[\w-+%/.]+/,t=/\b\S+@[\w.]+[a-z]{2}/,n=/\[([^\]]+)]\(([^)]+)\)/,r=["comment","url","attr-value","string"];for(var i in Prism.languages){var s=Prism.languages[i];Prism.languages.DFS(s,function(i,s){if(r.indexOf(i)>-1){s.pattern||(s=this[i]={pattern:s});s.inside=s.inside||{};i=="comment"&&(s.inside["md-link"]=n);s.inside["url-link"]=e;s.inside["email-link"]=t}});s["url-link"]=e;s["email-link"]=t}Prism.hooks.add("wrap",function(e){if(/-link$/.test(e.type)){e.tag="a";var t=e.content;if(e.type=="email-link")t="mailto:"+t;else if(e.type=="md-link"){var r=e.content.match(n);t=r[2];e.content=r[1]}e.attributes.href=t}})})();
\ No newline at end of file
diff --git a/prism.js b/prism.js
index a2e62f7..75a2693 100644
--- a/prism.js
+++ b/prism.js
@@ -41,6 +41,16 @@ var _ = self.Prism = {
 			}
 			
 			return root[inside] = ret;
+		},
+		
+		DFS: function(o, callback) {
+			for (var i in o) {
+				callback.call(o, i, o[i]);
+				
+				if (Object.prototype.toString.call(o) === '[object Object]') {
+					_.languages.DFS(o[i], callback);
+				}
+			}
 		}
 	},
 
@@ -207,7 +217,7 @@ var _ = self.Prism = {
 
 		return strarr;
 	},
-		
+	
 	hooks: {
 		all: {},