From d5a0d3a3d9468c20b32a1414ddb841fc1cc81016 Mon Sep 17 00:00:00 2001 From: Michael Mathews Date: Sat, 18 Jun 2011 12:52:24 +0100 Subject: [PATCH] Added Jake build script. --- Jake/lib/mustache.js | 335 +++++++++++++++++++++++++++++++ Jake/templates/package.json.tmpl | 32 +++ Jakefile.js | 32 +++ package.json | 14 +- plugins/README.md | 0 5 files changed, 404 insertions(+), 9 deletions(-) create mode 100644 Jake/lib/mustache.js create mode 100644 Jake/templates/package.json.tmpl create mode 100644 Jakefile.js create mode 100644 plugins/README.md diff --git a/Jake/lib/mustache.js b/Jake/lib/mustache.js new file mode 100644 index 00000000..60687a38 --- /dev/null +++ b/Jake/lib/mustache.js @@ -0,0 +1,335 @@ +/* + mustache.js — Logic-less templates in JavaScript + + See http://mustache.github.com/ for more info. +*/ + +var Mustache = function() { + var Renderer = function() {}; + + Renderer.prototype = { + otag: "{{", + ctag: "}}", + pragmas: {}, + buffer: [], + pragmas_implemented: { + "IMPLICIT-ITERATOR": true, + "ARRAY-ORDINALS": true // define #first? and #last? when looping arrays + }, + context: {}, + + render: function(template, context, partials, in_recursion) { + // reset buffer & set context + if(!in_recursion) { + this.context = context; + this.buffer = []; // TODO: make this non-lazy + } + + // fail fast + if(!this.includes("", template)) { + if(in_recursion) { + return template; + } else { + this.send(template); + return; + } + } + + template = this.render_pragmas(template); + var html = this.render_section(template, context, partials); + if(in_recursion) { + return this.render_tags(html, context, partials, in_recursion); + } + + this.render_tags(html, context, partials, in_recursion); + }, + + /* + Sends parsed lines + */ + send: function(line) { + if(line != "") { + this.buffer.push(line); + } + }, + + /* + Looks for %PRAGMAS + */ + render_pragmas: function(template) { + // no pragmas + if(!this.includes("%", template)) { + return template; + } + + var that = this; + var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + + this.ctag); + return template.replace(regex, function(match, pragma, options) { + if(!that.pragmas_implemented[pragma]) { + throw({message: + "This implementation of mustache doesn't understand the '" + + pragma + "' pragma"}); + } + that.pragmas[pragma] = {}; + if(options) { + var opts = options.split("="); + that.pragmas[pragma][opts[0]] = opts[1]; + } + return ""; + // ignore unknown pragmas silently + }); + }, + + /* + Tries to find a partial in the curent scope and render it + */ + render_partial: function(name, context, partials) { + name = this.trim(name); + if(!partials || partials[name] === undefined) { + throw({message: "unknown_partial '" + name + "'"}); + } + if(typeof(context[name]) != "object") { + return this.render(partials[name], context, partials, true); + } + return this.render(partials[name], context[name], partials, true); + }, + + /* + Renders inverted (^) and normal (#) sections + */ + render_section: function(template, context, partials) { + if(!this.includes("#", template) && !this.includes("^", template)) { + return template; + } + + var that = this; + // CSW - Added "+?" so it finds the tighest bound, not the widest + var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag + + "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag + + "\\s*", "mg"); + + // for each {{#foo}}{{/foo}} section do... + return template.replace(regex, function(match, type, name, content) { + var value = that.find(name, context); + if(type == "^") { // inverted section + if(!value || that.is_array(value) && value.length === 0) { + // false or empty list, render it + return that.render(content, context, partials, true); + } else { + return ""; + } + } else if(type == "#") { // normal section + if(that.is_array(value)) { // Enumerable, Let's loop! + var len = value.length; + return value.map(function(row, i) { + return that.render(content, that.create_context(row, {first: i === 0, last: i === len-1}), + partials, true); + }).join(""); + } else if(that.is_object(value)) { // Object, Use it as subcontext! + return that.render(content, that.create_context(value), + partials, true); + } else if(typeof value === "function") { + // higher order section + return value.call(context, content, function(text) { + return that.render(text, context, partials, true); + }); + } else if(value) { // boolean section + return that.render(content, context, partials, true); + } else { + return ""; + } + } + }); + }, + + /* + Replace {{foo}} and friends with values from our view + */ + render_tags: function(template, context, partials, in_recursion) { + // tit for tat + var that = this; + + var new_regex = function() { + return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + + that.ctag + "+", "g"); + }; + + var regex = new_regex(); + var tag_replace_callback = function(match, operator, name) { + switch(operator) { + case "!": // ignore comments + return ""; + case "=": // set new delimiters, rebuild the replace regexp + that.set_delimiters(name); + regex = new_regex(); + return ""; + case ">": // render partial + return that.render_partial(name, context, partials); + case "{": // the triple mustache is unescaped + return that.find(name, context); + default: // escape the value + return that.escape(that.find(name, context)); + } + }; + var lines = template.split("\n"); + for(var i = 0; i < lines.length; i++) { + lines[i] = lines[i].replace(regex, tag_replace_callback, this); + if(!in_recursion) { + this.send(lines[i]); + } + } + + if(in_recursion) { + return lines.join("\n"); + } + }, + + set_delimiters: function(delimiters) { + var dels = delimiters.split(" "); + this.otag = this.escape_regex(dels[0]); + this.ctag = this.escape_regex(dels[1]); + }, + + escape_regex: function(text) { + // thank you Simon Willison + if(!arguments.callee.sRE) { + var specials = [ + '/', '.', '*', '+', '?', '|', + '(', ')', '[', ']', '{', '}', '\\' + ]; + arguments.callee.sRE = new RegExp( + '(\\' + specials.join('|\\') + ')', 'g' + ); + } + return text.replace(arguments.callee.sRE, '\\$1'); + }, + + /* + find `name` in current `context`. That is find me a value + from the view object + */ + find: function(name, context) { + name = this.trim(name); + + // Checks whether a value is thruthy or false or 0 + function is_kinda_truthy(bool) { + return bool === false || bool === 0 || bool; + } + + var value; + if(is_kinda_truthy(context[name])) { + value = context[name]; + } else if(is_kinda_truthy(this.context[name])) { + value = this.context[name]; + } + + if(typeof value === "function") { + return value.apply(context); + } + if(value !== undefined) { + return value; + } + // silently ignore unkown variables + return ""; + }, + + // Utility methods + + /* includes tag */ + includes: function(needle, haystack) { + return haystack.indexOf(this.otag + needle) != -1; + }, + + /* + Does away with nasty characters + */ + escape: function(s) { + s = String(s === null ? "" : s); + return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) { + switch(s) { + case "&": return "&"; + case "\\": return "\\\\"; + case '"': return '"'; + case "'": return '''; + case "<": return "<"; + case ">": return ">"; + default: return s; + } + }); + }, + + // by @langalex, support for arrays of strings + create_context: function(_context, opts) { + if(this.is_object(_context)) { + if (this.pragmas["ARRAY-ORDINALS"] && opts) { + _context['first?'] = opts.first || false; + _context['last?'] = opts.last || false; + } + return _context; + } else { + var iterator = "."; + if(this.pragmas["IMPLICIT-ITERATOR"]) { + iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; + } + var ctx = {}; + ctx[iterator] = _context; + if (this.pragmas["ARRAY-ORDINALS"] && opts){ + ctx['first?'] = opts.first || false; + ctx['last?'] = opts.last || false; + } + return ctx; + } + }, + + is_object: function(a) { + return a && typeof a == "object"; + }, + + is_array: function(a) { + return Object.prototype.toString.call(a) === '[object Array]'; + }, + + /* + Gets rid of leading and trailing whitespace + */ + trim: function(s) { + return s.replace(/^\s*|\s*$/g, ""); + }, + + /* + Why, why, why? Because IE. Cry, cry cry. + */ + map: function(array, fn) { + if (typeof array.map == "function") { + return array.map(fn); + } else { + var r = []; + var l = array.length; + for(var i = 0; i < l; i++) { + r.push(fn(array[i])); + } + return r; + } + } + }; + + return({ + name: "mustache.js", + version: "0.3.1-dev", + + /* + Turns a template and view into HTML + */ + to_html: function(template, view, partials, send_fun) { + var renderer = new Renderer(); + if(send_fun) { + renderer.send = send_fun; + } + renderer.render(template, view, partials); + if(!send_fun) { + return renderer.buffer.join("\n"); + } + } + }); +}(); \ No newline at end of file diff --git a/Jake/templates/package.json.tmpl b/Jake/templates/package.json.tmpl new file mode 100644 index 00000000..2754bcf6 --- /dev/null +++ b/Jake/templates/package.json.tmpl @@ -0,0 +1,32 @@ +{ + "name": "{{appname}}", + "version": "{{appversion}}", + "revision": "{{timestamp}}", + "description": "An automatic documentation generator for javascript.", + "keywords": [ "documentation", "javascript" ], + "licenses": [ + { + "type": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + ], + "repositories": [ + { + "type": "git", + "url": "git://github.com/micmath/jsdoc.git" + } + ], + "bugs": "https://github.com/micmath/jsdoc/issues", + "contributors" : [ + { + "name": "Michael Mathews", + "email": "micmath@gmail.com" + } + ], + "maintainers": [ + { + "name": "Michael Mathews", + "email": "micmath@gmail.com" + } + ] +} diff --git a/Jakefile.js b/Jakefile.js new file mode 100644 index 00000000..3f11938b --- /dev/null +++ b/Jakefile.js @@ -0,0 +1,32 @@ +// see: http://howtonode.org/intro-to-jake + +desc('Updating package.json revision.'); +task('default', [], function (params) { + var fs = require('fs'), + sys = require('sys'); + + // import the Mustache template tool + eval(fs.readFileSync('Jake/lib/mustache.js', 'utf8')); + + var templates = { + packagejson: fs.readFileSync('Jake/templates/package.json.tmpl', 'utf8') + }; + + var metadata = { + appname: 'JSDoc', + appversion: '3.0.0alpha', + timestamp: ''+new Date().getTime() + }; + + var outdir = './'; + + var rendered = Mustache.to_html( + templates.packagejson, + metadata + ); + + fs.writeFileSync(outdir + 'package.json', rendered, 'utf8'); + + process.exit(0); + +}); \ No newline at end of file diff --git a/package.json b/package.json index 344152b7..617a3fd9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "jsdoc", - "version": "3.0.0beta1", - "revision": "2011-04-13-0000", + "name": "JSDoc", + "version": "3.0.0alpha", + "revision": "1308397909756", "description": "An automatic documentation generator for javascript.", "keywords": [ "documentation", "javascript" ], "licenses": [ @@ -14,13 +14,9 @@ { "type": "git", "url": "git://github.com/micmath/jsdoc.git" - }, - { - "type": "svn", - "url": "https://jsdoc.googlecode.com/svn/trunk" } ], - "bugs": "http://code.google.com/p/jsdoc/issues/list", + "bugs": "https://github.com/micmath/jsdoc/issues", "contributors" : [ { "name": "Michael Mathews", @@ -33,4 +29,4 @@ "email": "micmath@gmail.com" } ] -} +} \ No newline at end of file diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 00000000..e69de29b