From 532672c71dd2ad38b7ed6dfe6e202ad52b8ebdc6 Mon Sep 17 00:00:00 2001 From: Michael Mathews Date: Thu, 5 Aug 2010 19:18:39 +0100 Subject: [PATCH] Fixes in JSON output module to escape newlines, added normal templates module, added start of haruki template. --- modules/flesler/jsdump.js | 9 +- modules/normal/template.js | 199 ++++++++++++++++++++++++++++++++ templates/haruki/publish.js | 73 ++++++++++++ templates/haruki/tmpl/docs.json | 22 ++++ templates/lib/resig/tmpl.js | 2 +- 5 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 modules/normal/template.js create mode 100644 templates/haruki/publish.js create mode 100644 templates/haruki/tmpl/docs.json diff --git a/modules/flesler/jsdump.js b/modules/flesler/jsdump.js index f0d00409..1cccc43d 100644 --- a/modules/flesler/jsdump.js +++ b/modules/flesler/jsdump.js @@ -14,7 +14,14 @@ var jsDump; (function(){ function quote( str ){ - return '"' + str.toString().replace(/"/g, '\\"') + '"'; + return '"' + str.toString() + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + .replace(/\f/g, "\\f") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/\t/g, "\\t") + + '"'; }; function literal( o ){ return o + ''; diff --git a/modules/normal/template.js b/modules/normal/template.js new file mode 100644 index 00000000..667e8762 --- /dev/null +++ b/modules/normal/template.js @@ -0,0 +1,199 @@ +/** + * Normal Template + */ + +var TOKEN_RE = new RegExp("(\{[\=\:\#\/].+?\})"), + COMMAND_RE = new RegExp("^\{[\:\/\=]"); + +var xpath = function (path) { + if (path === '$last') { + return "$last"; + } + + if (/\||;|\$|~/.test(path)) { + throw new Error("Invalid characters in path '" + path + "'"); + } + + path = path.replace(/\//g, ".").replace(/'|"/, ""); + + if (path == ".") { + return "d"; + } else if (/^\./.test(path)) { + return "data" + path; + } else { + return "d." + path; + } +} + +/** + * Template filters. Add your own to this dictionary. + */ +exports.filters = { + str: function (val) { // used to override default filtering. + return val.toString(); + }, + strip: function (val) { + return val.toString().replace(/<([^>]+)>/g, ""); + }, + html: function (val) { + return val.toString().replace(/&/g, "&").replace(/>/g, ">"). + replace(//g, ">"). + replace(/ 0)) '); + stack.unshift("a" + depth + "[i" + depth + "]"); + code.push('for (var i' + depth + ' = 0,l' + depth + ' = a' + depth + '.length; i' + depth + ' < l' + depth + '; i' + depth + '++) {$last = (i' + depth + ' == l' + depth + '-1); d = a' + depth + '[i' + depth + '];'); + continue; + + case "else": + case "e": + tag = nesting.pop(); + if (tag) { + code.push('} else {'); + nesting.push(tag); + } else { + throw new Error("Unbalanced 'else' tag"); + } + continue; + + case "lb": // output left curly bracket '{' + code.push('res.push("{");'); + continue; + + case "rb": // output right curly bracket '}' + code.push('res.push("}");'); + continue; + + case "!": // comment + continue; + } + } else if (token[1] == "/") { // close tag + if (token[2] == ":") { + var cmd = token.substring(3, token.length-1).split(" ")[0]; + + switch (cmd) { + case "if": + tag = nesting.pop(); + if (tag == "if") { + code.push('};'); + } else { + throw new Error("Unbalanced 'if' close tag" + (tag ? ", expecting '" + tag + "' close tag" : "")); + } + continue; + + case "select": + case "s": + tag = nesting.pop(); + if (tag == "select") { + stack.shift(); + code.push('};d = ' + stack[0] + ';'); + } else { + throw new Error("Unbalanced 'select' close tag" + (tag ? ", expecting '" + tag + "' close tag" : "")); + } + continue; + + case "reduce": + case "r": + tag = nesting.pop(); + if (tag == "reduce") { + stack.shift(); + code.push('}; $last = false; d = ' + stack[0] + ';'); + } else { + throw new Error("Unbalanced 'reduce' close tag" + (tag ? ", expecting '" + tag + "' close tag" : "")); + } + continue; + } + } + } else if (token[1] == "=") { // interpolation + var parts = token.substring(2, token.length-1).split(" "), + pre = "", post = ""; + for (var j = 0; j < parts.length-1; j++) { + pre += "filters." + parts[j] + "("; post += ")"; + } + if (pre == "") { + if (filters.defaultfilter) { + pre = "df("; post = ")"; + } + } + code.push('v = ' + xpath(parts[j]) + ';if (v != undefined) res.push(' + pre + 'v' + post +');'); + continue; + } + } + + // plain text + code.push('res.push("' + token.replace(/\\/g, "\\\\").replace(/\r/g, "").replace(/\n/g, "\\n").replace(/"/g, '\\"') + '");'); + } + + tag = nesting.pop(); + if (tag) { + throw new Error("Unbalanced '" + tag + "' tag, is not closed"); + } + + code.push('return res.join("");'); + + var func = new Function("data", "filters", code.join("")); + + return function (data) { return func(data, filters) }; +} \ No newline at end of file diff --git a/templates/haruki/publish.js b/templates/haruki/publish.js new file mode 100644 index 00000000..85550830 --- /dev/null +++ b/templates/haruki/publish.js @@ -0,0 +1,73 @@ +(function() { + var fs = require('common/fs'); + + publish = function(docs, opts) { // global + var classes; + + classes = docs.doc.filter(function(element, index, array) { + return (element.kind === 'constructor'); + }); + + // add properties and methods + classes.forEach(function(classElement) { + classElement.properties = docs.doc.filter(function(memberElement) { + return ( + memberElement.kind === 'property' + && memberElement.memberof === classElement.path + ); + }); + + classElement.methods = docs.doc.filter(function(memberElement) { + return ( + memberElement.kind === 'method' + && memberElement.memberof === classElement.path + ); + }); + + + }); + + + // templates! + var normal = require('normal/template'), + src = fs.read( BASEDIR + 'templates/haruki/tmpl/docs.json' ), + template = normal.compile(src, + { + 'filters': { + 'json': function(str) { + return str.replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + .replace(/\f/g, "\\f") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/\t/g, "\\t"); + } + } + } + ); + + print( template( {classes: classes} ) ); + } + + function getDoc(docpath) { + var i, doc; + + if ( doc = getDoc.cache[docpath] ) { + return doc; + } + + i = docs.doc.length; + while (i--) { + if (docs.doc[i].path === docpath) { + return docs.doc[i]; + } + } + } + getDoc.cache = {}; + + // helpers + publish.summarize = function(desc) { // just the first line + return /(.*)/.test(desc), RegExp.$1; + } + +})(); \ No newline at end of file diff --git a/templates/haruki/tmpl/docs.json b/templates/haruki/tmpl/docs.json new file mode 100644 index 00000000..a67dc781 --- /dev/null +++ b/templates/haruki/tmpl/docs.json @@ -0,0 +1,22 @@ +{ + "classes":{ + {:reduce classes} + "{=name}": { + "name": "{=name}", + "properties":{{:reduce properties} + "{=name}": { + "name": "{=name}", + "description": "{=json desc}" + }{:if $last}{:else},{/:if} + {/:reduce properties}}, + "methods":{{:reduce methods} + "{=name}": { + "name": "{=name}", + "description": "{=json desc}" + }{:if $last}{:else},{/:if} + {/:reduce methods}}, + "description": "{=json desc}" + } + {/:reduce classes} + } +} diff --git a/templates/lib/resig/tmpl.js b/templates/lib/resig/tmpl.js index 9eb47715..66c4fc03 100644 --- a/templates/lib/resig/tmpl.js +++ b/templates/lib/resig/tmpl.js @@ -5,7 +5,7 @@ (function(){ var cache = {}; - this.tmpl = function tmpl(/** templatefilename | templatesrc */str, data){ // templatefilename not contain `<` + this.tmpl = function tmpl(/** templatefilename | templatesrc */str, data){ // templatefilename must not contain `<`, templatesrc must contain < var fn = str.indexOf('<') === -1 ? cache[str] = cache[str] || tmpl(readFile(str)) :