Building out the templating, equal, less than, greater than are supported now, commenting out of template tags works.
authorJames Burke <jrburke@mozillamessaging.com>
Tue, 22 Jun 2010 17:15:31 -0700
changeset 1784 395e1abc3bdfe60fc103791749fa97a8e4ae1389
parent 1783 1f8176fb4587ac77243c6262b035dd5fc2bd160c
child 1785 74d179ddd5a33762d7ea66f203becc9d790eec59
push id987
push userjrburke@gmail.com
push dateWed, 23 Jun 2010 00:15:34 +0000
Building out the templating, equal, less than, greater than are supported now, commenting out of template tags works.
client/api/contacts.html
client/api/rdapi.html
client/api/scripts/blade/jig.js
client/api/scripts/blade/object.js
--- a/client/api/contacts.html
+++ b/client/api/contacts.html
@@ -13,17 +13,17 @@
         'use strict';
         /*global require: false, console: false */
 
         /*
          To detect orientation changes in Android:
          http://stackoverflow.com/questions/1649086/detect-rotation-of-android-phone-in-the-browser-with-javascript
         */
 
-        require(['jquery', 'rdapi', 'cards', 'blade/jig', 'friendly', 'json2', 'iscroll-min'],
+        require(['jquery', 'rdapi', 'cards', 'blade/jig', 'friendly', 'iscroll-min'],
         function ($,        rdapi,   cards,   jig,         friendly) {
 
             //Initialize the card setup
             cards('#display');
 
             //Set up extra template functions.
             rdapi.addJigFunctions({
                 getPhotoUrl: function (contact) {
@@ -212,17 +212,17 @@
                         <li class="mblListItem identity hbox"><span class="idType boxFlex">{type}</span><span class="idValue boxFlex">{value}</span></li>
                     </ul>
                 </li>
             {]}
         </ul>
         <ul class="mblRoundRectList">
             <!-- Interesting, for the delegate on this li to work in Mobile Safari,
                  need to put an onclick on here. -->
-            <li class="mblListItem conversationButton" onclick="return;">Show Recent Conversations<div class="mblArrow"></div></li>
+            <li class="mblListItem conversationButton" onclick="return;">Recent Conversations<div class="mblArrow"></div></li>
         </ul>
     </div>
 
     <!-- Template for showing conversation summaries -->
     <li class="template summary {id[0]}" data-id="conversations">
         <span class="subject">{subject}</span>
         <span class="bodyPreview">{messages[0].schemas['rd.msg.body'].body_preview}</span>
         <div class="mblArrow"></div>
--- a/client/api/rdapi.html
+++ b/client/api/rdapi.html
@@ -231,19 +231,19 @@
         </div>
       </div>
       <div class="c2">
         <div class="subject"><a class="list subject" href="#">{subject}</a></div>
         <div class="messageBody">{first.body_preview}</div>
         <div class="replies">{messages[1:] [}<div class="reply"><div class="arrow"></div>{. body schemas['rd.msg.body']}<div class="messageReply noArrow"><span class="name">{body.from_display}</span> {body.body_preview}</div></div>{]}</div>
       </div> 
       {. extraCount messages[1:].length}
-      {extraCount [}
+      {!extraCount 0 [}
       <div class="c3">
-        <div class="more"><a href="#">view all replies (<span class="totalReplies">{}</span>)</a></div>
+        <div class="more"><a href="#">view all replies (<span class="totalReplies">{extraCount}</span>)</a></div>
       </div>
       {]}
     </div>
   </div>
 
 <div class="widgets">
     <div class="row">
       <div class="c3 accounts">
--- a/client/api/scripts/blade/jig.js
+++ b/client/api/scripts/blade/jig.js
@@ -1,51 +1,63 @@
 /**
- * @license blade/jig Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
+ * @license blade/jig Copyright (c) 2010, The Dojo Foundation All Rights Reserved.
  * Available via the MIT, GPL or new BSD license.
  * see: http://github.com/jrburke/blade for details
  */
 /*jslint  nomen: false, plusplus: false */
 /*global require: false */
 
 "use strict";
 
-if (!String.prototype.trim) {
-    String.prototype.trim = function () {
-        return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
-    };
-}
-
 require.def("blade/jig", ["blade/object"], function (object) {
 
-    //Use _ to indicate current data value.
     //Need to allow scope lookup for vars
-    //Add comment command
     //Fix unit test: something is wrong with it, says it passes, but
     //with attachData change, the string is actually different now.
     //TODO: for attachData, only generate a new ID when the data value changes,
     //and similarly, only attach the data one time per data value.
-    //TODO: ALLOW && AND || in the getobject values?
 
     var jig, commands,
         ostring = Object.prototype.toString,
         decode = typeof decodeURIComponent === "undefined" ? function () {} : decodeURIComponent,
         startToken = '{',
         endToken = '}',
         rawHtmlToken = '^',
         argSeparator = ' ',
         //First character in an action cannot be something that
         //could be the start of a regular JS property name,
         //or an array indice indicator, [, or the HTML raw output
         //indicator, ^.
         propertyRegExp = /[_\[\^\w]/,
         defaultArg = '_',
         startTagRegExp = /<\s*\w+/,
+        wordRegExp = /^\d+$/,
+        badCommentRegExp = /\/(\/)?\s*\]/,
         templateCache = {},
-        defaultFuncs = {},
+        defaultFuncs = {
+            gt: function (a, b) {
+                return a > b;
+            },
+            gte: function (a, b) {
+                return a >= b;
+            },
+            lt: function (a, b) {
+                return a < b;
+            },
+            lte: function (a, b) {
+                return a <= b;
+            },
+            or: function (a, b) {
+                return !!(a || b);
+            },
+            and: function (a, b) {
+                return !!(a && b);
+            }
+        },
         attachData = true,
         dataIdCounter = 0,
         dataRegistry = {};
 
     function isArray(it) {
         return ostring.call(it) === "[object Array]";
     }
 
@@ -61,32 +73,51 @@ require.def("blade/jig", ["blade/object"
         return value ? parseInt(value, 10) : 0;
     }
 
     function getObject(name, data, options) {
         var brackRegExp = /\[([\w0-9\.'":]+)\]/,
             part = name,
             parent = data,
             match, pre, prop, obj, startIndex, endIndex, indices, result,
-            parenStart, parenEnd, func, funcName;
+            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;
         }
 
+        //If name is just an integer, just return it.
+        if (wordRegExp.test(name)) {
+            return strToInt(name);
+        }
+
         //First check for function call. Function must be globally visible.
         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);
             }
-            return func(getObject(name.substring(parenStart + 1, parenEnd), data, options));
+            arg = name.substring(parenStart + 1, parenEnd);
+            if (arg.indexOf(',')) {
+                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;
+            }
         }
 
         //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);
@@ -126,18 +157,29 @@ require.def("blade/jig", ["blade/object"
         return result;
     }
 
     commands = {
         '_default_': {
             doc: 'Property reference',
             action: function (args, data, options, children, render) {
                 var value = args[0] ? getObject(args[0], data, options) : data,
+                    comparison = args[1] ? getObject(args[1], data, options) : undefined,
                     i, text = '';
-                if (value === null || value === undefined || (isArray(value) && !value.length)) {
+
+                //If comparing to some other value, then the value is the data,
+                //and need to compute if the values compare.
+                if (args[1]) {
+                    comparison = value === comparison;
+                    value = data;
+                } else {
+                    //Just use the value, so the value is used in the comparison.
+                    comparison = value;
+                }
+                if (comparison === null || comparison === undefined || (isArray(comparison) && !comparison.length)) {
                     return '';
                 } else if (children) {
                     if (isArray(value)) {
                         for (i = 0; i < value.length; i++) {
                             text += render(children, value[i], options);
                         }
                     } else {
                         text = render(children, value, options);
@@ -146,18 +188,30 @@ require.def("blade/jig", ["blade/object"
                     text = value;
                 }
                 return text;
             }
         },
         '!': {
             doc: 'Not',
             action: function (args, data, options, children, render) {
-                var value = getObject(args[0], data, options);
-                if (children && !value) {
+                var value = getObject(args[0], data, options),
+                    comparison = args[1] ? getObject(args[1], data, options) : undefined;
+
+                //If comparing to some other value, then the value is the data,
+                //and need to compute if the values compare.
+                if (args[1]) {
+                    comparison = value === comparison;
+                    value = data;
+                } else {
+                    //Just use the value, so the value is used in the comparison.
+                    comparison = value;
+                }
+
+                if (children && !comparison) {
                     return render(children, data, options);
                 }
                 return '';
             }
         },
         '@': {
             doc: 'Template reference',
             action: function (args, data, options, children, render) {
@@ -190,17 +244,18 @@ require.def("blade/jig", ["blade/object"
     jig.htmlEscape = function (text) {
         return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
     };
 
     function compile(text, options) {
         var compiled = [],
             start = 0,
             useRawHtml = false,
-            segment, index, match, tag, command, args, lastArg, lastChar, children;
+            segment, index, match, tag, command, args, lastArg, lastChar,
+            children, i;
 
         while ((index = text.indexOf(options.startToken, start)) !== -1) {
             //Output any string that is before the template tag start
             if (index !== start) {
                 compiled.push(text.substring(start, index));
             }
 
             //Find the end of the token
@@ -217,16 +272,22 @@ require.def("blade/jig", ["blade/object"
                 start = index + match[0].length;
 
                 //Pull out the command
                 tag = text.substring(index + options.startToken.length, index + match[0].length - options.endToken.length).trim();
 
                 //decode in case the value was in an URL field, like an  href or an img src attribute
                 tag = decode(tag);
 
+                //if the command is commented out end block call, that messes with stuff,
+                //just throw to let the user know, otherwise browser can lock up.
+                if (badCommentRegExp.test(tag)) {
+                    throw new Error('blade/jig: end block tags should not be commented: ' + tag);
+                }
+
                 command = tag.charAt(0);
 
                 if (command && !options.propertyRegExp.test(command)) {
                     //Have a template command
                     tag = tag.substring(1).trim();
                 } else {
                     command = '_default_';
                     //Command could contain just the raw HTML indicator.
@@ -260,16 +321,27 @@ require.def("blade/jig", ["blade/object"
                     return compiled;
                 }
 
                 //If this defines a template, save it off,
                 //if a comment (starts with /), then ignore it.
                 if (command === '+') {
                     options.templates[args[0]] = children;
                 } else if (command !== '/') {
+                    //Adjust args if some end in commas, it means they are function
+                    //args.
+                    if (args.length > 1) {
+                        for (i = args.length - 1; i >= 0; i--) {
+                            if (args[i].charAt(args[i].length - 1) === ',') {
+                                args[i] = args[i] + args[i + 1];
+                                args.splice(i + 1, 1);
+                            }
+                        }
+                    }
+
                     compiled.push({
                         action: options.commands[command].action,
                         useRawHtml: useRawHtml,
                         args: args,
                         children: children
                     });
                 }
             }
@@ -344,20 +416,26 @@ require.def("blade/jig", ["blade/object"
      * definition.
      * @returns {String} the rendered template.
      */
     jig.render = function (compiled, data, options) {
         //Normalize options, filling in defaults.
         options = options || {};
         object.mixin(options, {
             templates: templateCache,
-            funcs: defaultFuncs,
             attachData: attachData
         });
 
+        //Mix in default funcs
+        if (options.funcs) {
+            object.mixin(options.funcs, defaultFuncs);
+        } else {
+            options.funcs = defaultFuncs;
+        }
+
         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
--- a/client/api/scripts/blade/object.js
+++ b/client/api/scripts/blade/object.js
@@ -1,10 +1,10 @@
 /**
- * @license blade/object Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
+ * @license blade/object Copyright (c) 2010, The Dojo Foundation All Rights Reserved.
  * Available via the MIT, GPL or new BSD license.
  * see: http://github.com/jrburke/blade for details
  */
 /*jslint plusplus: false */
 /*global require: false */
 
 "use strict";