From 79aa92254f06c0cc2481030ec71ab4ecd8fdd4bd Mon Sep 17 00:00:00 2001 From: Patrick Steele-Idem Date: Mon, 23 Feb 2015 16:35:30 -0700 Subject: [PATCH] Fixes #36 Deprecated invokeBody in favor of renderBody --- README.md | 34 ++++++++------ compiler/TemplateBuilder.js | 6 +-- compiler/taglibs/Taglib.js | 7 +++ compiler/taglibs/taglib-loader.js | 27 ++++++++++- package.json | 4 +- runtime/helpers.js | 22 ++++++++- taglibs/core/TagHandlerNode.js | 45 ++++++++++++++++--- test/render-test.js | 2 +- .../html-templates/nested-handlers.marko | 20 +++++++-- .../nested-handlers.marko.expected.html | 2 +- test/test-project/marko-taglib.json | 13 ++++++ test/test-project/tab-new-tag.js | 4 ++ test/test-project/tabs-new-tag.js | 33 ++++++++++++++ 13 files changed, 186 insertions(+), 33 deletions(-) create mode 100644 test/test-project/tab-new-tag.js create mode 100644 test/test-project/tabs-new-tag.js diff --git a/README.md b/README.md index 5ef8f8b2e..816ad2486 100644 --- a/README.md +++ b/README.md @@ -1383,13 +1383,13 @@ exports.render = function(input, out) { } ``` -If, and only if, a tag has nested content, then a special `invokeBody` method will be added to the `input` object. If a renderer wants to render the nested body content then it must call the `invokeBody` method. For example: +If, and only if, a tag has nested content, then a special `renderBody` method will be added to the `input` object. If a renderer wants to render the nested body content then it must call the `renderBody` method. For example: ```javascript exports.render = function(input, out) { out.write('BEFORE BODY'); - if (input.invokeBody) { - input.invokeBody(); + if (input.renderBody) { + input.renderBody(out); } out.write('AFTER BODY'); } @@ -1531,12 +1531,12 @@ Marko supports this by leveraging JavaScript closures in the compiled output. A "tags": { "ui-tabs": { "renderer": "./tabs-tag", - "var": "tabs" + "body-function": "getTabs(__tabsHelper)" }, "ui-tab": { "renderer": "./tab-tag", "import-var": { - "tabs": "tabs" + "tabs": "__tabsHelper" }, "attributes": { "title": "string" @@ -1557,15 +1557,21 @@ var templatePath = require.resolve('./template.marko'); var template = require('marko').load(templatePath); exports.render = function(input, out) { - var nestedTabs = []; + var nestedTabs; + + if (input.getTabs) { + nestedTabs = []; + // Invoke the body function to discover nested tags + input.getTabs({ // Invoke the body with the scoped "tabs" variable + addTab: function(tab) { + tab.id = tab.id || ("tab" + tabs.length); + nestedTabs.push(tab); + } + }); + } else { + nestedTabs = input.tabs || []; + } - // Invoke the body function to discover nested tags - input.invokeBody({ // Invoke the body with the scoped "tabs" variable - addTab: function(tab) { - tab.id = tab.id || ("tab" + tabs.length); - nestedTabs.push(tab); - } - }); // Now render the markup for the tabs: template.render({ @@ -1596,7 +1602,7 @@ _components/tabs/template.marko:_
- +
diff --git a/compiler/TemplateBuilder.js b/compiler/TemplateBuilder.js index 017ee423b..cd7d7590d 100644 --- a/compiler/TemplateBuilder.js +++ b/compiler/TemplateBuilder.js @@ -134,7 +134,7 @@ CodeWriter.prototype = { thisObj = arguments[2]; } this.incIndent(delta); - func.call(thisObj); + func.call(thisObj, this); this.decIndent(delta); } else if (typeof arguments[0] === 'string') { this.code(this._indent + arguments[0]); @@ -264,8 +264,8 @@ TemplateBuilder.prototype = { var newWriter = new CodeWriter(this.concatWrites, oldWriter.indentStr()); try { this.writer = newWriter; - func.call(thisObj); - return newWriter.getOutput(); + var value = func.call(thisObj); + return value == null ? newWriter.getOutput() : value; } finally { this.writer = oldWriter; } diff --git a/compiler/taglibs/Taglib.js b/compiler/taglibs/Taglib.js index 912684e23..3b8d15259 100644 --- a/compiler/taglibs/Taglib.js +++ b/compiler/taglibs/Taglib.js @@ -88,6 +88,7 @@ Taglib.Tag = makeClass({ this.nestedVariables = {}; this.importedVariables = {}; this.patternAttributes = []; + this.bodyFunction = null; }, inheritFrom: function (superTag) { var subTag = this; @@ -182,6 +183,12 @@ Taglib.Tag = makeClass({ var key = transformer.path; transformer.taglibId = this.taglibId; this.transformers[key] = transformer; + }, + setBodyFunction: function(name, params) { + this.bodyFunction = { + name: name, + params: params + }; } }); diff --git a/compiler/taglibs/taglib-loader.js b/compiler/taglibs/taglib-loader.js index d39cd6849..090f2c67e 100644 --- a/compiler/taglibs/taglib-loader.js +++ b/compiler/taglibs/taglib-loader.js @@ -7,7 +7,6 @@ try { } - var ok = require('assert').ok; var nodePath = require('path'); var Taglib = require('./Taglib'); @@ -18,6 +17,9 @@ var tagDefFromCode = require('./tag-def-from-code'); var resolve = require('../util/resolve'); // NOTE: different implementation for browser var propertyHandlers = require('property-handlers'); +var safeVarName = /^[A-Za-z_$][A-Za-z0-9_]*$/; +var bodyFunctionRegExp = /^([A-Za-z_$][A-Za-z0-9_]*)(?:\(([^)]*)\))?$/; + function exists(path) { try { require.resolve(path); @@ -192,6 +194,29 @@ function buildTag(tagObject, path, taglib, dirname) { name: value }); }, + bodyFunction: function(value) { + var parts = bodyFunctionRegExp.exec(value); + if (!parts) { + throw new Error('Invalid value of "' + value + '" for "body-function". Expected value to be of the following form: ([param1, param2, ...])'); + } + + var functionName = parts[1]; + var params = parts[2]; + if (params) { + params = params.trim().split(/\s*,\s*/); + for (var i=0; i Tab 1 content - + Tab 2 content - + Tab 3 content - \ No newline at end of file + + + + + Tab 1 content + + + + Tab 2 content + + + + Tab 3 content + + \ No newline at end of file diff --git a/test/test-project/html-templates/nested-handlers.marko.expected.html b/test/test-project/html-templates/nested-handlers.marko.expected.html index 312dd3a68..1ac2c90a8 100644 --- a/test/test-project/html-templates/nested-handlers.marko.expected.html +++ b/test/test-project/html-templates/nested-handlers.marko.expected.html @@ -1 +1 @@ -
Tab 1 content
Tab 2 content
\ No newline at end of file +
Tab 1 content
Tab 2 content
Tab 1 content
Tab 2 content
\ No newline at end of file diff --git a/test/test-project/marko-taglib.json b/test/test-project/marko-taglib.json index ff54a6c13..7179fee97 100644 --- a/test/test-project/marko-taglib.json +++ b/test/test-project/marko-taglib.json @@ -26,6 +26,19 @@ "title": "string" } }, + "test-tabs-new": { + "renderer": "./tabs-new-tag.js", + "body-function": "buildTabs(__tabsHelper)" + }, + "test-tab-new": { + "renderer": "./tab-new-tag.js", + "import-var": { + "tabs": "__tabsHelper" + }, + "attributes": { + "title": "string" + } + }, "test-dynamic-attributes": { "renderer": "./dynamic-attributes-tag.js", "attributes": { diff --git a/test/test-project/tab-new-tag.js b/test/test-project/tab-new-tag.js new file mode 100644 index 000000000..a46c595e9 --- /dev/null +++ b/test/test-project/tab-new-tag.js @@ -0,0 +1,4 @@ +exports.render = function(input, out) { + var tabs = input.tabs; + tabs.addTab(input); +}; \ No newline at end of file diff --git a/test/test-project/tabs-new-tag.js b/test/test-project/tabs-new-tag.js new file mode 100644 index 000000000..b7d49defb --- /dev/null +++ b/test/test-project/tabs-new-tag.js @@ -0,0 +1,33 @@ +var marko = require('../../'); + +exports.render = function(input, out) { + var tabs = [], + activeFound = false; + + if (input.buildTabs) { + input.buildTabs({ + addTab: function(tab) { + if (tab.active) { + tab.activeFound = true; + } + + tab.id = "tab" + tabs.length; + tabs.push(tab); + } + }); + } + + if (!activeFound && tabs.length) { + tabs[0].active = true; + } + + tabs.forEach(function(tab) { + tab.liClass = tab.active ? "active" : ""; + tab.divClass = tab.active ? "tab-pane active" : "tab-pane"; + }); + + marko.render(require.resolve('./tabs.marko'), { + tabs: tabs + }, out); + +}; \ No newline at end of file