diff --git a/core/modules/parsers/javascriptparser/javascriptparser.js b/core/modules/parsers/javascriptparser/javascriptparser.js new file mode 100644 index 000000000..1a50bc03d --- /dev/null +++ b/core/modules/parsers/javascriptparser/javascriptparser.js @@ -0,0 +1,107 @@ +/*\ +title: $:/core/modules/parsers/javascriptparser/javascriptparser.js +type: application/javascript +module-type: parser + +Parses a JSON object into a parse tree + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var esprima = require("./esprima.js"); + +// Initialise the parser +var JavaScriptParser = function(options) { + this.wiki = options.wiki; +}; + +// Parse a string of JavaScript code and return the parse tree as a wikitext parse tree +JavaScriptParser.prototype.parse = function(type,code) { + // Simplistically replace tabs with spaces. Browsers will happily render tabs but most default to 8 character tab stops + code = code.replace(/\t/mg," "); + // Try to parse the code + var parseTree; + try { + parseTree = esprima.parse(code,{comment: true,tokens: true,range: true}); + } catch(ex) { + // Return a helpful error if the parse failed + return new $tw.Renderer([ + $tw.Tree.Element("pre",{"class": "javascript-source"},[ + $tw.Tree.Text(code.substring(0,ex.index)), + $tw.Tree.errorNode(ex), + $tw.Tree.Text(code.substring(ex.index)) + ]) + ],new $tw.Dependencies(),this.wiki); + } + // Helpers to render the comments and tokens with the appropriate classes + var self = this, + result = [], + nextComment = 0, + nextToken = 0, + currPos = 0; + var renderWhitespace = function(nextPos) { + if(currPos < nextPos) { + result.push($tw.Tree.Text(code.substring(currPos,nextPos))); + } + }, + renderComment = function(comment) { + var text = comment.value, + element, + classes = ["javascript-comment"], + content = []; + renderWhitespace(comment.range[0]); + if(comment.type === "Block") { + element = "div"; + classes.push("javascript-block-comment"); + content.push($tw.Tree.Text("/*")); + } else { + element = "span"; + classes.push("javascript-line-comment"); + content.push($tw.Tree.Text("//")); + } + content.push.apply(content,self.wiki.parseText("text/x-tiddlywiki",text).tree); + if(comment.type === "Block") { + content.push($tw.Tree.Text("*/")); + } else { + content.push($tw.Tree.Text("\n")); + } + result.push($tw.Tree.Element(element,{"class": classes},content)); + currPos = comment.range[1] + 1; + }, + renderToken = function(token) { + renderWhitespace(token.range[0]); + result.push($tw.Tree.Element("span",{ + "class": "javascript-" + token.type.toLowerCase() + },[ + $tw.Tree.Text(token.value) + ])); + currPos = token.range[1] + 1; + }; + // Process the tokens interleaved with the comments + while(nextComment < parseTree.comments.length || nextToken < parseTree.tokens.length) { + if(nextComment < parseTree.comments.length && nextToken < parseTree.tokens.length) { + if(parseTree.comments[nextComment].range[0] < parseTree.tokens[nextToken].range[0]) { + renderComment(parseTree.comments[nextComment++]); + } else { + renderToken(parseTree.tokens[nextToken++]); + } + } else if(nextComment < parseTree.comments.length) { + renderComment(parseTree.comments[nextComment++]); + } else { + renderToken(parseTree.tokens[nextToken++]); + } + } + renderWhitespace(code.length); + // Wrap the whole lot in a `
`
+    return new $tw.Renderer([
+            $tw.Tree.Element("pre",{"class": "javascript-source"},result)
+        ],new $tw.Dependencies(),this.wiki);
+};
+
+exports["application/javascript"] = JavaScriptParser;
+
+})();
diff --git a/core/modules/parsers/javascriptparser/tiddlywiki.plugin b/core/modules/parsers/javascriptparser/tiddlywiki.plugin
index c988a77f8..b2b468745 100644
--- a/core/modules/parsers/javascriptparser/tiddlywiki.plugin
+++ b/core/modules/parsers/javascriptparser/tiddlywiki.plugin
@@ -4,8 +4,12 @@
 			"file": "../../../../node_modules/esprima/esprima.js", 
 			"fields": {
 				"title": "$:/core/modules/parsers/javascriptparser/esprima.js",
-				"type": "application/javascript"
+				"type": "application/javascript",
+				"module-type": "library"
 			}
+		},
+		{
+			"file": "javascriptparser.js"
 		}
 	]
 }
\ No newline at end of file
diff --git a/core/modules/treenodes/element.js b/core/modules/treenodes/element.js
index c6cede6fd..eb1d44aa2 100644
--- a/core/modules/treenodes/element.js
+++ b/core/modules/treenodes/element.js
@@ -52,7 +52,7 @@ Element.prototype.render = function(type) {
 	if(isHtml) {
 		output.push("<",this.type);
 		if(this.attributes) {
-			attr = []
+			attr = [];
 			for(a in this.attributes) {
 				attr.push(a);
 			}