Support variable lookup that spans the whole template
authorJames Burke <jrburke@mozillamessaging.com>
Wed, 23 Jun 2010 13:35:29 -0700
changeset 1785 74d179ddd5a33762d7ea66f203becc9d790eec59
parent 1784 395e1abc3bdfe60fc103791749fa97a8e4ae1389
child 1786 1543c4ae88308d6cc9da5abb98cd44285d181caf
push id988
push userjrburke@gmail.com
push dateWed, 23 Jun 2010 20:35:33 +0000
Support variable lookup that spans the whole template
client/api/scripts/blade/jig.js
--- a/client/api/scripts/blade/jig.js
+++ b/client/api/scripts/blade/jig.js
@@ -56,32 +56,43 @@ require.def("blade/jig", ["blade/object"
         attachData = true,
         dataIdCounter = 0,
         dataRegistry = {};
 
     function isArray(it) {
         return ostring.call(it) === "[object Array]";
     }
 
-    function getProp(parts, context) {
+    /**
+     * Gets a property from a context object. Allows for an alternative topContext
+     * object that can be used for the first part property lookup if it is not
+     * found in context first.
+     * @param {Array} parts the list of nested properties to look up on a context.
+     * @param {Object} context the context to start the property lookup
+     * @param {Object} [topContext] an object to use as an alternate context
+     * for the very first part property to look up if it is not found in context.
+     * @returns {Object}
+     */
+    function getProp(parts, context, topContext) {
         var obj = context, i, p;
         for (i = 0; obj && (p = parts[i]); i++) {
-            obj = (p in obj ? obj[p] : undefined);
+            obj = (p in obj ? obj[p] : (topContext && i === 0 && p in topContext ? topContext[p] : undefined));
         }
         return obj; // mixed
     }
 
     function strToInt(value) {
         return value ? parseInt(value, 10) : 0;
     }
 
     function getObject(name, data, options) {
         var brackRegExp = /\[([\w0-9\.'":]+)\]/,
             part = name,
             parent = data,
+            isTop = true,
             match, pre, prop, obj, startIndex, endIndex, indices, result,
             parenStart, parenEnd, func, funcName, arg, args, i;
 
         //If asking for the default arg it means giving back the current data.
         if (name === defaultArg) {
             return data;
         }
 
@@ -94,43 +105,42 @@ require.def("blade/jig", ["blade/object"
         if ((parenStart = name.indexOf('(')) !== -1) {
             parenEnd = name.lastIndexOf(')');
             funcName = name.substring(0, parenStart);
             func = options.funcs[funcName];
             if (!func) {
                 throw new Error('Cannot find function named: ' + funcName + ' for ' + name);
             }
             arg = name.substring(parenStart + 1, parenEnd);
-            if (arg.indexOf(',')) {
+            if (arg.indexOf(',') !== -1) {
                 args = arg.split(',');
                 for (i = args.length - 1; i >= 0; i--) {
                     args[i] = getObject(args[i], data, options);
                 }
                 result = func.apply(null, args);
             } else {
                 result = func(getObject(arg, data, options));
             }
             //If a function returns true, then use the current data as the
             //return object.
-            if (result === true) {
-                return data;
-            }
+            return result === true ? data : result;
         }
 
         //Now handle regular object references, which could have [] notation.
         while ((match = brackRegExp.exec(part))) {
             prop = match[1].replace(/['"]/g, "");
             pre = part.substring(0, match.index);
 
             part = part.substring(match.index + match[0].length, part.length);
             if (part.indexOf('.') === 0) {
                 part = part.substring(1, part.length);
             }
 
-            obj = getProp(pre.split('.'), parent);
+            obj = getProp(pre.split('.'), parent, isTop ? options.context : null);
+            isTop = false;
 
             if (!obj && prop) {
                 throw new Error('blade/jig: No property "' + prop + '" on ' + obj);
             }
 
             if (prop.indexOf(":") !== -1) {
                 //An array slice action
                 indices = prop.split(':');
@@ -146,17 +156,17 @@ require.def("blade/jig", ["blade/object"
                 obj = obj[prop];
             }
             parent = obj;
         }
 
         if (!part) {
             result = parent;
         } else {
-            result = getProp(part.split("."), parent);
+            result = getProp(part.split("."), parent, isTop ? options.context : null);
         }
 
         return result;
     }
 
     commands = {
         '_default_': {
             doc: 'Property reference',
@@ -221,17 +231,17 @@ require.def("blade/jig", ["blade/object"
                     throw new Error('blade/jig: no template with name: ' + args[0]);
                 }
                 return render(compiled, data, options);
             }
         },
         '.': {
             doc: 'Variable declaration',
             action: function (args, data, options, children, render) {
-                data[args[0]] = getObject(args[1], data, options);
+                options.context[args[0]] = getObject(args[1], data, options);
                 //TODO: allow definining a variable then doing a block with
                 //that variable.
                 return '';
             }
         }
     };
 
     jig = function (text, data, options) {
@@ -426,16 +436,19 @@ require.def("blade/jig", ["blade/object"
 
         //Mix in default funcs
         if (options.funcs) {
             object.mixin(options.funcs, defaultFuncs);
         } else {
             options.funcs = defaultFuncs;
         }
 
+        //Mix in top level context object
+        options.context = options.context || object.create(data);
+
         return render(compiled, data, options);
     };
 
     /**
      * Gets the data bound to a particular rendered template.
      * @param {String} dataId the data ID. It can be fetched from the
      * data-blade-jig attribute on a rendered template.
      * @returns {Object} the bound data. Can return undefined if there is