diff --git a/specs/index.html b/specs/index.html index ecde449..f1ee616 100644 --- a/specs/index.html +++ b/specs/index.html @@ -2,16 +2,16 @@ "http://www.w3.org/TR/html4/loose.dtd"> - Jasmine Test Runner - - - + Jasmine Test Runner + + + - + diff --git a/specs/jaml_spec.js b/specs/jaml_spec.js index 448edc5..adfc142 100644 --- a/specs/jaml_spec.js +++ b/specs/jaml_spec.js @@ -5,7 +5,7 @@ describe("Jaml (top-level)", function() { beforeEach(function(){ Jaml.templates = {}; }) - + describe("you can register a template and then use it to render", function() { it("works with no data passed in (very simple)", function(){ Jaml.register("color", function(){ @@ -14,11 +14,11 @@ describe("Jaml (top-level)", function() { li("green") ) }); - + expect(Jaml.render("color")). toEqual("\n"); }); @@ -29,11 +29,11 @@ describe("Jaml (top-level)", function() { li(widget.secondaryColor) ) }); - + expect(Jaml.render("color", {primaryColor:"red", secondaryColor:"green"} )). toEqual("\n"); }); @@ -44,32 +44,32 @@ describe("Jaml (top-level)", function() { li(widget.secondaryColor) ) }); - + Jaml.register("shape", function(widget){ p(widget.shape) }); - + var christmasTree = {primaryColor:"red", secondaryColor:"green", shape:"round"}; - + expect(Jaml.render("color", christmasTree )). toEqual("\n"); - + expect(Jaml.render("shape", christmasTree )). - toEqual("

round

\n"); + toEqual("

round

\n"); }); - + it("can override a template by using the same name", function(){ var christmasTree = {shape:"round"}; - + Jaml.register("shape", function(widget){ p(widget.shape) }); expect(Jaml.render("shape", christmasTree )). toEqual("

round

\n"); - + Jaml.register("shape", function(widget){ div(widget.shape) }); @@ -77,7 +77,29 @@ describe("Jaml (top-level)", function() { toEqual("
round
\n"); }); - - }); + + + + it("can use a template without registering it", function() { + var christmasTree = {shape:"round"}; + expect(Jaml.render(function(widget){ + p(widget.shape) + }, christmasTree )). + toEqual("

round

\n"); + }); + + it("can override templates without registering them", function() { + var christmasTree = {shape:"round"}; + expect(Jaml.render({ + shape: function(widget){ + p(widget.shape) + }, + div: function(widget){ + div(render('shape', widget)) + } + }, "div", christmasTree )). + toEqual("

round

\n
\n"); + }); + }); }); diff --git a/specs/template_spec.js b/specs/template_spec.js index 43e3d52..4a8d216 100644 --- a/specs/template_spec.js +++ b/specs/template_spec.js @@ -1,10 +1,10 @@ require("./spec_helper.js"); describe("Jaml.Template", function() { - + describe("all html tags", function() { it("a giant integration test for all html tags, so we see what we're allowing." + - " intentionally locating this at the top of this spec file.", function(){ + " intentionally locating this at the top of this spec file.", function(){ expect(new Jaml.Template(function(){ html( head( @@ -17,7 +17,7 @@ describe("Jaml.Template", function() { thead(tr(th())), tfoot(tr(td())), tbody(tr(td())) - + ), ul(li(), ol()), dl(), dt(), dd(), @@ -32,7 +32,7 @@ describe("Jaml.Template", function() { ) ) ) - }).render()). + })._render()). toEqual("\n" + " \n" + " \n \n \n <link/>\n" + @@ -62,7 +62,7 @@ describe("Jaml.Template", function() { "</html>\n"); }); }); - + describe("basic", function() { it("renders", function(){ expect(new Jaml.Template(function(){ @@ -70,29 +70,29 @@ describe("Jaml.Template", function() { li("red"), li("green") ) - }).render()). + })._render()). toEqual("<ul>\n" + " <li>red</li>\n" + - " <li>green</li>\n" + + " <li>green</li>\n" + "</ul>\n"); }); - + it("renders with data", function(){ var theWidget = {primaryColor: "red", secondaryColor: "green"} - + expect(new Jaml.Template(function(widget){ ul( li(widget.primaryColor), li(widget.secondaryColor) ) - }).render(theWidget)). + })._render(theWidget)). toEqual("<ul>\n" + " <li>red</li>\n" + - " <li>green</li>\n" + + " <li>green</li>\n" + "</ul>\n"); - }); + }); }); - + describe("array data", function() { it("renders the template for each item in the array", function(){ expect(new Jaml.Template(function(widget, i){ @@ -100,22 +100,22 @@ describe("Jaml.Template", function() { li(widget.primaryColor), li(widget.secondaryColor) ) - }).render([ + })._render([ {primaryColor: "red", secondaryColor: "green"}, {primaryColor: "orange", secondaryColor: "blue"}, {primaryColor: "yellow", secondaryColor: "purple"} ])). toEqual("<ul id=\"0\">\n" + " <li>red</li>\n" + - " <li>green</li>\n" + + " <li>green</li>\n" + "</ul>\n" + "<ul id=\"1\">\n" + " <li>orange</li>\n" + - " <li>blue</li>\n" + + " <li>blue</li>\n" + "</ul>\n" + "<ul id=\"2\">\n" + " <li>yellow</li>\n" + - " <li>purple</li>\n" + + " <li>purple</li>\n" + "</ul>\n"); }); it("renders the template for each item in the array, using thisObj", function(){ @@ -124,26 +124,26 @@ describe("Jaml.Template", function() { li(widget.primaryColor), li(widget.secondaryColor) ) - }).render({data: "test"}, [ + })._render({data: "test"}, [ {primaryColor: "red", secondaryColor: "green"}, {primaryColor: "orange", secondaryColor: "blue"}, {primaryColor: "yellow", secondaryColor: "purple"} ])). toEqual("<ul data=\"test\" id=\"0\">\n" + " <li>red</li>\n" + - " <li>green</li>\n" + + " <li>green</li>\n" + "</ul>\n" + "<ul data=\"test\" id=\"1\">\n" + " <li>orange</li>\n" + - " <li>blue</li>\n" + + " <li>blue</li>\n" + "</ul>\n" + "<ul data=\"test\" id=\"2\">\n" + " <li>yellow</li>\n" + - " <li>purple</li>\n" + + " <li>purple</li>\n" + "</ul>\n"); }); }); - - + + }); diff --git a/src/Jaml.js b/src/Jaml.js index 72e4cb9..f2180ee 100644 --- a/src/Jaml.js +++ b/src/Jaml.js @@ -6,9 +6,18 @@ * Introduction: http://edspencer.net/2009/11/jaml-beautiful-html-generation-for-javascript.html */ Jaml = function() { + var merge = function() { + var obj = {}; + for(var x=0;x<arguments.length;x++) { + for(var name in arguments[x]) + obj[name] = arguments[x][name]; + } + return obj; + } + return { templates: {}, - + /** * Registers a template by name * @param {String} name The name of the template @@ -17,17 +26,27 @@ Jaml = function() { register: function(name, template) { this.templates[name] = template; }, - + /** * Renders the given template name with an optional data object * @param {String} name The name of the template to render * @param {Object} thisObj Optional data object * @param {Object} data Optional data object */ - render: function(name, thisObj, data) { - var template = this.templates[name], - renderer = new Jaml.Template(template); - return renderer.render.apply(renderer, Array.prototype.slice.call(arguments, 1)); + render: function(templates, name, thisObj, data) { + var tmpls, args; + if(typeof templates === 'object') { + tmpls = merge(this.templates, templates); + args = Array.prototype.slice.call(arguments, 1); + } + else { + args = arguments; + tmpls = this.templates; + } + + var template = typeof args[0] === 'function' ? args[0] : tmpls[args[0]], + renderer = new Jaml.Template(template, tmpls); + return renderer._render.apply(renderer, Array.prototype.slice.call(args, 1)); } }; }(); \ No newline at end of file diff --git a/src/Template.js b/src/Template.js index 25cfaa0..cffc86e 100644 --- a/src/Template.js +++ b/src/Template.js @@ -4,20 +4,21 @@ * When a template is rendered its node structure is computed with any provided template * data, culminating in one or more root nodes. The root node(s) are then joined together * and returned as a single output string. - * + * * The render process uses two dirty but necessary hacks. First, the template function is * decompiled into a string (but is not modified), so that it can be eval'ed within the scope * of Jaml.Template.prototype. This allows the second hack, which is the use of the 'with' keyword. * This allows us to keep the pretty DSL-like syntax, though is not as efficient as it could be. */ -Jaml.Template = function(tpl) { +Jaml.Template = function(tpl, tpls) { /** * @property tpl * @type Function * The function this template was created from */ this.tpl = tpl; - + this.tpls = tpls; + this.nodes = []; }; @@ -28,31 +29,37 @@ Jaml.Template.prototype = { * @param {Object} data Optional data object * @return {String} The rendered HTML string */ - render: function(thisObj, data) { + _render: function(thisObj, data) { data = data || (thisObj = thisObj || {}); - + //the 'data' argument can come in two flavours - array or non-array. Normalise it //here so that it always looks like an array. if (data.constructor.toString().indexOf("Array") == -1) { data = [data]; } - + with(this) { for (var i=0; i < data.length; i++) { eval("(" + this.tpl.toString() + ").call(thisObj, data[i], i)"); }; } - + var roots = this.getRoots(), output = ""; - + for (var i=0; i < roots.length; i++) { output += roots[i].render(); }; - + return output; }, - + + render: function(name, thisObj, data) { + var template = typeof name === 'function' ? name : this.tpls[name], + renderer = new Jaml.Template(template, this.tpls); + return renderer._render.apply(renderer, Array.prototype.slice.call(arguments, 1)); + }, + /** * Returns all top-level (root) nodes in this template tree. * Templates are tree structures, but there is no guarantee that there is a @@ -61,21 +68,21 @@ Jaml.Template.prototype = { */ getRoots: function() { var roots = []; - + for (var i=0; i < this.nodes.length; i++) { var node = this.nodes[i]; - + if (node.parent == undefined) roots.push(node); }; - + return roots; }, - + tags: [ "html", "head", "body", "script", "meta", "title", "link", "div", "p", "span", "a", "img", "br", "hr", "em", "strong", "table", "tr", "th", "td", "thead", "tbody", "tfoot", - "ul", "ol", "li", + "ul", "ol", "li", "dl", "dt", "dd", "h1", "h2", "h3", "h4", "h5", "h6", "h7", "form", "fieldset", "input", "textarea", "label", "select", "option"