From 5e2efafc17543f1b0cf2478d7050b511e9f2ebf4 Mon Sep 17 00:00:00 2001 From: Steve Krenek Date: Tue, 5 Aug 2014 12:16:52 -0500 Subject: [PATCH 1/4] Added an error state. If rendering errors out, we don't go to the ready state, but to the error state instead. --- lib/widget.js | 152 +++++++++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 69 deletions(-) diff --git a/lib/widget.js b/lib/widget.js index ea8bd87..7f6b9c8 100755 --- a/lib/widget.js +++ b/lib/widget.js @@ -23,7 +23,7 @@ var localId = simpleId(); */ var Widget = module.exports = function(options) { Widget.super.apply(this, arguments); - + this.dom = options.dom; this.container = options.container; this.containerId = options.containerId; @@ -31,7 +31,7 @@ var Widget = module.exports = function(options) { this.request = options.request; this.client = options.client; this.scripts = []; - + //children/parent relationships if (options.parent) { options.parent.children = options.parent.children || new Registry(); @@ -59,7 +59,7 @@ Widget.template = [ ].join(""); /** - * + * */ Widget.prototype.states = { initial: { @@ -73,7 +73,7 @@ Widget.prototype.states = { cache.getItemsWait([ "feather-logger", "feather-options" - ], + ], function(err, cacheItems) { var logger = cacheItems["feather-logger"]; var appOptions = cacheItems["feather-options"]; @@ -83,16 +83,21 @@ Widget.prototype.states = { // TODO: Support loading templates from alternate location? If we get in here, there is no template file. } var $j = args.dom.$j; - + //if we're still in this state, finish rendering if (me.preserveTemplateNewlines) { me.template = me.template.replace(/\n/g, '{{html newline}}'); me.newline = '\\n'; } - var _t = $j.template(null, me.template); - var _html = _t($j, {data: me}).join(""); - me.container.html(_html); - + + try { + var _t = $j.template(null, me.template); + var _html = _t($j, {data: me}).join(""); + me.container.html(_html); + } catch(templateException) { + me.fire('error', {err: "Error rendering template: " + JSON.stringify(templateException)}, cb) + } + //loop the custom tags and execute their handlers tagHandlers.each(function(tagHandler) { $j(tagHandler.id, me.container).each(function(index, tag) { @@ -110,7 +115,7 @@ Widget.prototype.states = { }); }); }); - + if (me.contentTemplate) { var tmpl = $j.tmpl(me.contentTemplate.html(), me); if (tmpl && tmpl.appendTo) { @@ -126,7 +131,7 @@ Widget.prototype.states = { }); } } - + //now render any children widgets that just got embedded Widget.render(_.extend(_.clone(args), { node: me.container, @@ -135,7 +140,7 @@ Widget.prototype.states = { scripts: me.scripts }), function(err, _result) { if (!me.serverOnly) { - //output the collected scripts into the container that came 1 level up from this render + //output the collected scripts into the container that came 1 level up from this render args.scripts.push([ '(function() {\\n', me.scripts.join("\n") + "\\n", @@ -145,13 +150,13 @@ Widget.prototype.states = { } me.fire("ready", args, cb); - }); + }); } } //invoke onRender function if implemented if (typeof me.onRender === "function") { - var _render = render; + var _render = render; var renderCalled = false; var timer = setTimeout(function(){ if(!renderCalled) { @@ -184,12 +189,21 @@ Widget.prototype.states = { } else { render(); } - + }); - + }, ready: function(args, cb) { return this.states.ready; + }, + error: function(args, cb) { + return this.states.error; + } + }, + error: { + stateStartup: function(args, cb) { + if (typeof this.onError === 'function') this.onError(args); + cb(args.err); } }, ready: { @@ -218,7 +232,7 @@ Widget.prototype.get = function(selector) { /** * method that can be used within templates to inject a jQuery tmpl expression into another nested widget's template, * to be evaluated in the context of that widget. - * + * * @param {String} tmplString */ Widget.prototype.$$ = function(tmplString) { @@ -229,8 +243,8 @@ Widget.prototype.$$ = function(tmplString) { * Initiates rendering of the widget * @param {Object} options */ -Widget.prototype.render = function(options, cb) { - this.fire("render", options, cb); // behavior implemented via FSM controller +Widget.prototype.render = function(options, cb) { + this.fire("render", options, cb); // behavior implemented via FSM controller }; /** @@ -281,7 +295,7 @@ var tagHandlers = new Registry(); * Registers a new tag handler. * @param {Object} tagName The name of the custom tag being handled * @param {Object} tagRenderer The function that will handling rendering the custom tag into the document - * @param {Object} disallowedTags An optional list of tags that cannot be embedded within this tag. + * @param {Object} disallowedTags An optional list of tags that cannot be embedded within this tag. */ Widget.registerTagHandler = function(tagName, tagRenderer, disallowedTags) { try { @@ -307,7 +321,7 @@ var tmplExpressions = [ replace: "{{$1}}" }, { - expression: /\$\[([^\]]*)\]/g, + expression: /\$\[([^\]]*)\]/g, replace: "${$1}" } ]; @@ -317,12 +331,12 @@ var tmplExpressions = [ */ Widget.registerTagHandler("template", function(options) { var $j = options.dom.$j; - + //first, throw if the template is not at the top level if ($j(options.tag).parent()[0] !== options.widget.container[0]) { throw new Error("