Fixes in JSON output module to escape newlines, added normal templates module, added start of haruki template.

This commit is contained in:
Michael Mathews 2010-08-05 19:18:39 +01:00
parent 1170f87b15
commit 532672c71d
5 changed files with 303 additions and 2 deletions

View File

@ -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 + '';

199
modules/normal/template.js Normal file
View File

@ -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, "&amp;").replace(/>/g, "&gt;").
replace(/</g, "&lt;");
},
attr: function (val) {
return val.toString().replace(/&/g, "&amp;").replace(/>/g, "&gt;").
replace(/</g, "&lt;").replace(/"/g, "&quot;");
},
uri: encodeURI
}
/**
* Compile the template source into the template function.
*/
exports.compile = function (src, options) {
// v = curent value, d = cursor, a = reduced array, df = default filter, res = result
var code = ['var v,a,d = data,res = [];'],
stack = ["data"],
nesting = [],
tokens = src.split(TOKEN_RE);
var filters, tag;
if (options && options.filters) {
filters = {};
for (var i in exports.filters) filters[i] = exports.filters[i];
for (var i in options.filters) filters[i] = options.filters[i];
} else {
filters = exports.filters;
}
if (filters.defaultfilter) {
code.push('var df = filters.defaultfilter;');
}
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (token == "") continue;
if (token.match(COMMAND_RE)) {
if (token[1] == ":") { // open tag
var parts = token.substring(2, token.length-1).split(" "),
cmd = parts[0],
arg = parts[1],
val;
switch (cmd) {
case "if": // checks for undefined and boolean.
nesting.push("if");
val = xpath(arg);
code.push('if (' + val + ') {');
continue;
case "select":
case "s":
nesting.push("select");
val = xpath(arg);
code.push('d = ' + val + ';if (d != undefined) {');
stack.unshift(val.replace(/^d\./, stack[0] + "."));
continue;
case "reduce":
case "r":
nesting.push("reduce");
val = xpath(arg);
var depth = stack.length;
code.push('var a' + depth + ' = ' + val + ';if ((a' + depth + ' != undefined) && (a' + depth + '.length > 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) };
}

View File

@ -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;
}
})();

View File

@ -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}
}
}

View File

@ -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)) :