roojs-ui.js
[roojs1] / roojs-debug.js
index 00be1f7..9a90d06 100644 (file)
@@ -1774,11 +1774,28 @@ Date.prototype.add = function(interval, value){
  * <script type="text/javascript">
  */
 
+/**
+ * @class Roo.lib.Dom
+ * @static
+ * 
+ * Dom utils (from YIU afaik)
+ * 
+ **/
 Roo.lib.Dom = {
+    /**
+     * Get the view width
+     * @param {Boolean} full True will get the full document, otherwise it's the view width
+     * @return {Number} The width
+     */
+     
     getViewWidth : function(full) {
         return full ? this.getDocumentWidth() : this.getViewportWidth();
     },
-
+    /**
+     * Get the view height
+     * @param {Boolean} full True will get the full document, otherwise it's the view height
+     * @return {Number} The height
+     */
     getViewHeight : function(full) {
         return full ? this.getDocumentHeight() : this.getViewportHeight();
     },
@@ -23760,7 +23777,11 @@ Roo.extend(Roo.View, Roo.util.Observable, {
      * @cfg {String|Roo.Template} tpl The template used by this View 
      */
     tpl : false,
-    
+    /**
+     * @cfg {String} dataName the named area of the template to use as the data area
+     *                          Works with domtemplates roo-name="name"
+     */
+    dataName: false,
     /**
      * @cfg {String} selectedClass The css class to add to selected nodes
      */
@@ -23796,31 +23817,62 @@ Roo.extend(Roo.View, Roo.util.Observable, {
      */
     refresh : function(){
         var t = this.tpl;
+        
+        // if we are using something like 'domtemplate', then
+        // the what gets used is:
+        // t.applySubtemplate(NAME, data, wrapping data..)
+        // the outer template then get' applied with
+        //     the store 'extra data'
+        // and the body get's added to the
+        //      roo-name="data" node?
+        //      <span class='roo-tpl-{name}'></span> ?????
+        
+        
+        
         this.clearSelections();
         this.el.update("");
         var html = [];
         var records = this.store.getRange();
-        if(records.length < 1){
+        if(records.length < 1) {
+            
+            // is this valid??  = should it render a template??
+            
             this.el.update(this.emptyText);
             return;
         }
+        var el = this.el;
+        if (this.dataName) {
+            this.el.update(t.apply(this.store.meta)); //????
+            el = this.el.child('.roo-tpl-' + this.dataName);
+        }
+        
         for(var i = 0, len = records.length; i < len; i++){
             var data = this.prepareData(records[i].data, i, records[i]);
             this.fireEvent("preparedata", this, data, i, records[i]);
-            html[html.length] = t.apply(data);
+            html[html.length] = Roo.util.Format.trim(
+                this.dataName ?
+                    t.applySubtemplate(this.dataName, data, this.store.meta) :
+                    t.apply(data)
+            );
         }
-        this.el.update(html.join(""));
-        this.nodes = this.el.dom.childNodes;
+        
+        
+        
+        el.update(html.join(""));
+        this.nodes = el.dom.childNodes;
         this.updateIndexes(0);
     },
 
     /**
      * Function to override to reformat the data that is sent to
      * the template for each node.
+     * DEPRICATED - use the preparedata event handler.
      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
      * a JSON object for an UpdateManager bound view).
      */
-    prepareData : function(data){
+    prepareData : function(data, index, record)
+    {
+        this.fireEvent("preparedata", this, data, index, record);
         return data;
     },
 
@@ -23828,12 +23880,16 @@ Roo.extend(Roo.View, Roo.util.Observable, {
         this.clearSelections();
         var index = this.store.indexOf(record);
         var n = this.nodes[index];
-        this.tpl.insertBefore(n, this.prepareData(record.data));
+        this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
         n.parentNode.removeChild(n);
         this.updateIndexes(index, index);
     },
 
-    onAdd : function(ds, records, index){
+    
+    
+// --------- FIXME     
+    onAdd : function(ds, records, index)
+    {
         this.clearSelections();
         if(this.nodes.length == 0){
             this.refresh();
@@ -23841,10 +23897,11 @@ Roo.extend(Roo.View, Roo.util.Observable, {
         }
         var n = this.nodes[index];
         for(var i = 0, len = records.length; i < len; i++){
-            var d = this.prepareData(records[i].data);
+            var d = this.prepareData(records[i].data, i, records[i]);
             if(n){
                 this.tpl.insertBefore(n, d);
             }else{
+                
                 this.tpl.append(this.el, d);
             }
         }
@@ -23853,7 +23910,10 @@ Roo.extend(Roo.View, Roo.util.Observable, {
 
     onRemove : function(ds, record, index){
         this.clearSelections();
-        this.el.dom.removeChild(this.nodes[index]);
+        var el = this.dataName  ?
+            this.el.child('.roo-tpl-' + this.dataName) :
+            this.el; 
+        el.dom.removeChild(this.nodes[index]);
         this.updateIndexes(index);
     },
 
@@ -23906,7 +23966,10 @@ Roo.extend(Roo.View, Roo.util.Observable, {
      * @return {HTMLElement} The template node
      */
     findItemFromChild : function(node){
-        var el = this.el.dom;
+        var el = this.dataName  ?
+            this.el.child('.roo-tpl-' + this.dataName,true) :
+            this.el.dom; 
+        
         if(!node || node.parentNode == el){
                    return node;
            }
@@ -38397,6 +38460,401 @@ Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
      * @hide
      * @method autoSize
      */
+});/*
+ * Copyright(c) 2010-2012, Roo J Solutions Limited
+ *
+ * Licence LGPL
+ *
+ */
+
+/**
+ * @class Roo.form.ComboBoxArray
+ * @extends Roo.form.ComboBox
+ * A facebook style adder... for lists of email / people / countries  etc...
+ * pick multiple items from a combo box, and shows each one.
+ *
+ *  Fred [x]  Brian [x]  [Pick another |v]
+ *
+ *
+ *  For this to work: it needs various extra information
+ *    - normal combo problay has
+ *      name, hiddenName
+ *    + displayField, valueField
+ *
+ *    For our purpose...
+ *
+ *
+ *   If we change from 'extends' to wrapping...
+ *   
+ *  
+ *
+ * @constructor
+ * Create a new ComboBoxArray.
+ * @param {Object} config Configuration options
+ */
+
+Roo.form.ComboBoxArray = function(config)
+{
+    
+    Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
+    
+    this.items = new Roo.util.MixedCollection(false);
+    
+    // construct the child combo...
+    
+    
+    
+    
+   
+    
+}
+
+Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
+{ 
+    /**
+     * @cfg {Roo.form.Combo} combo The combo box that is wrapped
+     */
+    
+    lastData : false,
+    
+    // behavies liek a hiddne field
+    inputType:      'hidden',
+    /**
+     * @cfg {Number} width The width of the box that displays the selected element
+     */ 
+    width:          300,
+
+    
+    
+    /**
+     * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
+     */
+    name : false,
+    /**
+     * @cfg {String} name    The hidden name of the field, often contains an comma seperated list of names
+     */
+    hiddenName : false,
+    
+    
+    // private the array of items that are displayed..
+    items  : false,
+    // private - the hidden field el.
+    hiddenEl : false,
+    // private - the filed el..
+    el : false,
+    
+    //validateValue : function() { return true; }, // all values are ok!
+    //onAddClick: function() { },
+    
+    onRender : function(ct, position) 
+    {
+        
+        // create the standard hidden element
+        //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
+        
+        
+        // give fake names to child combo;
+        this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
+        this.combo.name = this.name? (this.name+'-subcombo') : this.name;
+        
+        this.combo = Roo.factory(this.combo, Roo.form);
+        this.combo.onRender(ct, position);
+        
+        // assigned so form know we need to do this..
+        this.store          = this.combo.store;
+        this.valueField     = this.combo.valueField;
+        this.displayField   = this.combo.displayField ;
+        
+        
+        this.combo.wrap.addClass('x-cbarray-grp');
+        
+        var cbwrap = this.combo.wrap.createChild(
+            {tag: 'div', cls: 'x-cbarray-cb'},
+            this.combo.el.dom
+        );
+        
+             
+        this.hiddenEl = this.combo.wrap.createChild({
+            tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
+        });
+        this.el = this.combo.wrap.createChild({
+            tag: 'input',  type:'hidden' , name: this.name, value : ''
+        });
+         //   this.el.dom.removeAttribute("name");
+        
+        
+        this.outerWrap = this.combo.wrap;
+        this.wrap = cbwrap;
+        
+        this.outerWrap.setWidth(this.width);
+        this.outerWrap.dom.removeChild(this.el.dom);
+        
+        this.wrap.dom.appendChild(this.el.dom);
+        this.outerWrap.dom.removeChild(this.combo.trigger.dom);
+        this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
+        
+        this.combo.trigger.setStyle('position','relative');
+        this.combo.trigger.setStyle('left', '0px');
+        this.combo.trigger.setStyle('top', '2px');
+        
+        this.combo.el.setStyle('vertical-align', 'text-bottom');
+        
+        //this.trigger.setStyle('vertical-align', 'top');
+        
+        // this should use the code from combo really... on('add' ....)
+        if (this.adder) {
+            
+        
+            this.adder = this.outerWrap.createChild(
+                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
+            var _t = this;
+            this.adder.on('click', function(e) {
+                _t.fireEvent('adderclick', this, e);
+            }, _t);
+        }
+        //var _t = this;
+        //this.adder.on('click', this.onAddClick, _t);
+        
+        
+        this.combo.on('select', function(cb, rec, ix) {
+            this.addItem(rec.data);
+            
+            cb.setValue('');
+            cb.el.dom.value = '';
+            //cb.lastData = rec.data;
+            // add to list
+            
+        }, this);
+        
+        
+    },
+    
+    
+    getName: function()
+    {
+        // returns hidden if it's set..
+        if (!this.rendered) {return ''};
+        return  this.hiddenName ? this.hiddenName : this.name;
+        
+    },
+    
+    
+    onResize: function(w, h){
+        
+        return;
+        // not sure if this is needed..
+        this.combo.onResize(w,h);
+        
+        if(typeof w != 'number'){
+            // we do not handle it!?!?
+            return;
+        }
+        var tw = this.combo.trigger.getWidth();
+        tw += this.addicon ? this.addicon.getWidth() : 0;
+        tw += this.editicon ? this.editicon.getWidth() : 0;
+        var x = w - tw;
+        this.combo.el.setWidth( this.combo.adjustWidth('input', x));
+            
+        this.combo.trigger.setStyle('left', '0px');
+        
+        if(this.list && this.listWidth === undefined){
+            var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
+            this.list.setWidth(lw);
+            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+        }
+        
+    
+        
+    },
+    
+    addItem: function(rec)
+    {
+        var idField = this.combo.valueField;
+        var displayField = this.combo.displayField;
+        if (this.items.indexOfKey(rec[idField]) > -1) {
+            //console.log("GOT " + rec.data.id);
+            return;
+        }
+        
+        var x = new Roo.form.ComboBoxArray.Item({
+            //id : rec[this.idField],
+            data : rec,
+            nameField : displayField ,
+            tipField : displayField ,
+            cb : this
+        });
+        // use the 
+        this.items.add(rec[idField],x);
+        // add it before the element..
+        this.updateHiddenEl();
+        x.render(this.outerWrap, this.wrap.dom);
+        // add the image handler..
+    },
+    
+    updateHiddenEl : function()
+    {
+        this.validate();
+        if (!this.hiddenEl) {
+            return;
+        }
+        var ar = [];
+        var idField = this.combo.valueField;
+        
+        this.items.each(function(f) {
+            ar.push(f.data[idField]);
+           
+        });
+        this.hiddenEl.dom.value = ar.join(',');
+        this.validate();
+    },
+    
+    reset : function()
+    {
+        Roo.form.ComboBoxArray.superclass.reset.call(this); 
+        this.items.each(function(f) {
+           f.remove(); 
+        });
+        if (this.hiddenEl) {
+            this.hiddenEl.dom.value = '';
+        }
+        
+    },
+    getValue: function()
+    {
+        return this.hiddenEl ? this.hiddenEl.dom.value : '';
+    },
+    setValue: function(v) // not a valid action - must use addItems..
+    {
+         
+        
+        if (this.store.isLocal) {
+            // then we can use the store to find the values..
+            // comma seperated at present.. this needs to allow JSON based encoding..
+            this.hiddenEl.value  = v;
+            var v_ar = [];
+            Roo.each(v.split(','), function(k) {
+                Roo.log("CHECK " + this.valueField + ',' + k);
+                var li = this.store.query(this.valueField, k);
+                if (!li.length) {
+                    return;
+                }
+                add = {};
+                add[this.valueField] = k;
+                add[this.displayField] = li.item(0).data[this.displayField];
+                
+                this.addItem(add);
+            }, this) 
+            
+                
+            
+        }
+        
+        
+    },
+    setFromData: function(v)
+    {
+        // this recieves an object, if setValues is called.
+        var keys = v[this.valueField].split(',');
+        var display = v[this.displayField].split(',');
+        for (var i = 0 ; i < keys.length; i++) {
+            
+            add = {};
+            add[this.valueField] = keys[i];
+            add[this.displayField] = display[i];
+            this.addItem(add);
+        }
+      
+        
+    },
+    
+    
+    validateValue : function(value){
+        return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
+        
+    }
+    
+});
+
+
+
+/**
+ * @class Roo.form.ComboBoxArray.Item
+ * @extends Roo.BoxComponent
+ * A selected item in the list
+ *  Fred [x]  Brian [x]  [Pick another |v]
+ * 
+ * @constructor
+ * Create a new item.
+ * @param {Object} config Configuration options
+ */
+Roo.form.ComboBoxArray.Item = function(config) {
+    config.id = Roo.id();
+    Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
+}
+
+Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
+    data : {},
+    cb: false,
+    nameField : false,
+    tipField : false,
+    
+    
+    defaultAutoCreate : {
+        tag: 'div',
+        cls: 'x-cbarray-item',
+        cn : [ 
+            { tag: 'div' },
+            {
+                tag: 'img',
+                width:16,
+                height : 16,
+                src : Roo.BLANK_IMAGE_URL ,
+                align: 'center'
+            }
+        ]
+        
+    },
+    
+    onRender : function(ct, position)
+    {
+        Roo.form.Field.superclass.onRender.call(this, ct, position);
+        
+        if(!this.el){
+            var cfg = this.getAutoCreate();
+            this.el = ct.createChild(cfg, position);
+        }
+        
+        this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
+        
+        this.el.child('div').dom.innerHTML = this.cb.renderer ? 
+            this.cb.renderer(this.data) :
+            String.format('{0}',this.data[this.nameField]);
+        
+            
+        this.el.child('div').dom.setAttribute('qtip',
+                        String.format('{0}',this.data[this.tipField])
+        );
+        
+        this.el.child('img').on('click', this.remove, this);
+        
+    },
+   
+    remove : function()
+    {
+        
+        this.cb.items.remove(this);
+        this.el.child('img').un('click', this.remove, this);
+        this.el.remove();
+        this.cb.updateHiddenEl();
+    }
+    
+    
 });/*
  * Based on:
  * Ext JS Library 1.1.1
@@ -38854,7 +39312,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
             //<style type="text/css">' +
             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
             //'</style>' +
-            ' </head><body></body></html>';
+            ' </head><body class="roo-htmleditor-body"></body></html>';
     },
 
     // private
@@ -40844,6 +41302,9 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         this.tb.el.show();
         this.buildFooter();
         this.footer.show();
+        editor.on('hide', function( ) { this.footer.hide() }, this);
+        editor.on('show', function( ) { this.footer.show() }, this);
+        
          
         this.rendered = true;
         
@@ -40929,32 +41390,42 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
             // update attributes
             if (this.tb.fields) {
                 this.tb.fields.each(function(e) {
-                   e.setValue(sel.getAttribute(e.name));
+                   e.setValue(sel.getAttribute(e.attrname));
                 });
             }
             
-            // update styles
-            var st = this.tb.fields.item(0);
-            st.store.removeAll();
-            var cn = sel.className.split(/\s+/);
+            var hasStyles = false;
+            for(var i in this.styles) {
+                hasStyles = true;
+                break;
+            }
             
-            var avs = [];
-            if (this.styles['*']) {
+            // update styles
+            if (hasStyles) { 
+                var st = this.tb.fields.item(0);
                 
-                Roo.each(this.styles['*'], function(v) {
-                    avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
-                });
-            }
-            if (this.styles[tn]) { 
-                Roo.each(this.styles[tn], function(v) {
-                    avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
-                });
+                st.store.removeAll();
+               
+                
+                var cn = sel.className.split(/\s+/);
+                
+                var avs = [];
+                if (this.styles['*']) {
+                    
+                    Roo.each(this.styles['*'], function(v) {
+                        avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
+                    });
+                }
+                if (this.styles[tn]) { 
+                    Roo.each(this.styles[tn], function(v) {
+                        avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
+                    });
+                }
+                
+                st.store.loadData(avs);
+                st.collapse();
+                st.setValue(cn);
             }
-            
-            st.store.loadData(avs);
-            st.collapse();
-            st.setValue(cn);
-            
             // flag our selected Node.
             this.tb.selectedNode = sel;
            
@@ -41043,7 +41514,8 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                     fields: ['val', 'selected'],
                     data : [] 
                 }),
-                name : 'className',
+                name : '-roo-edit-className',
+                attrname : 'className',
                 displayField:'val',
                 typeAhead: false,
                 mode: 'local',
@@ -41081,7 +41553,8 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                         fields: ['val'],
                         data : item.opts  
                     }),
-                    name : i,
+                    name : '-roo-edit-' + i,
+                    attrname : i,
                     displayField:'val',
                     typeAhead: false,
                     mode: 'local',
@@ -41092,7 +41565,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                     width: item.width ? item.width  : 130,
                     listeners : {
                         'select': function(c, r, i) {
-                            tb.selectedNode.setAttribute(c.name, r.get('val'));
+                            tb.selectedNode.setAttribute(c.attrname, r.get('val'));
                         }
                     }
 
@@ -41110,13 +41583,15 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                 continue;
             }
             tb.addField( new Roo.form.TextField({
-                name: i,
+                name: '-roo-edit-' + i,
+                attrname : i,
+                
                 width: item.width,
                 //allowBlank:true,
                 value: '',
                 listeners: {
                     'change' : function(f, nv, ov) {
-                        tb.selectedNode.setAttribute(f.name, nv);
+                        tb.selectedNode.setAttribute(f.attrname, nv);
                     }
                 }
             }));
@@ -41497,7 +41972,11 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
             // we have a scenario where updates need confirming.
             // eg. if a locking scenario exists..
             // we look for { errors : { needs_confirm : true }} in the response.
-            if (typeof(action.result.errors.needs_confirm) != 'undefined') {
+            if (
+                (typeof(action.result) != 'undefined')  &&
+                (typeof(action.result.errors) != 'undefined')  &&
+                (typeof(action.result.errors.needs_confirm) != 'undefined')
+           ){
                 var _t = this;
                 Roo.MessageBox.confirm(
                     "Change requires confirmation",
@@ -41522,7 +42001,7 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
                 Roo.MessageBox.alert("Error",
                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
                         action.result.errorMsg :
-                        "Saving Failed, please check your entries"
+                        "Saving Failed, please check your entries or try again"
                 );
             }
             
@@ -52329,7 +52808,7 @@ Roo.LoadMask.prototype = {
  * @extends Roo.Template
  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
 <pre><code>
-var t = new Roo.MasterTemplate(
+var t = new Roo.XTemplate(
        '&lt;select name="{name}"&gt;',
                '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
        '&lt;/select&gt;'
@@ -52341,20 +52820,25 @@ var t = new Roo.MasterTemplate(
  * Supported features:
  *
  *  Tags:
- *    {a_variable} - output encoded.
- *    {a_variable.format:("Y-m-d")} - call a method on the variable
- *    {a_variable:raw} - unencoded output
- *    {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
- *    {a_variable:this.method_on_template(...)} - call a method on the template object.
- *  
- *  Tpl:
- *      &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
- *      &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
- *      &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
- *
- *      &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
- *      &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
- *      
+
+<pre><code>
+      {a_variable} - output encoded.
+      {a_variable.format:("Y-m-d")} - call a method on the variable
+      {a_variable:raw} - unencoded output
+      {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
+      {a_variable:this.method_on_template(...)} - call a method on the template object.
+</code></pre>
+ *  The tpl tag:
+<pre><code>
+        &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
+        &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
+        &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
+        &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
+  
+        &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
+        &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
+</code></pre>
  *      
  */
 Roo.XTemplate = function()
@@ -52368,6 +52852,10 @@ Roo.XTemplate = function()
 
 Roo.extend(Roo.XTemplate, Roo.Template, {
 
+    /**
+     * The various sub templates
+     */
+    tpls : false,
     /**
      *
      * basic tag replacing syntax
@@ -52379,7 +52867,12 @@ Roo.extend(Roo.XTemplate, Roo.Template, {
      */
     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
 
-    
+    /**
+     * compile the template
+     *
+     * This is not recursive, so I'm not sure how nested templates are really going to be handled..
+     *
+     */
     compile: function()
     {
         var s = this.html;
@@ -52390,33 +52883,39 @@ Roo.extend(Roo.XTemplate, Roo.Template, {
             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
+            namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
             m,
             id     = 0,
             tpls   = [];
     
         while(true == !!(m = s.match(re))){
-            var m2   = m[0].match(nameRe),
-                m3   = m[0].match(ifRe),
-                m4   = m[0].match(execRe),
+            var forMatch   = m[0].match(nameRe),
+                ifMatch   = m[0].match(ifRe),
+                execMatch   = m[0].match(execRe),
+                namedMatch   = m[0].match(namedRe),
+                
                 exp  = null, 
                 fn   = null,
                 exec = null,
-                name = m2 && m2[1] ? m2[1] : '';
+                name = forMatch && forMatch[1] ? forMatch[1] : '';
                 
-            if (m3) {
+            if (ifMatch) {
                 // if - puts fn into test..
-                exp = m3 && m3[1] ? m3[1] : null;
+                exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
                 if(exp){
                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
                 }
             }
-            if (m4) {
+            
+            if (execMatch) {
                 // exec - calls a function... returns empty if true is  returned.
-                exp = m4 && m4[1] ? m4[1] : null;
+                exp = execMatch && execMatch[1] ? execMatch[1] : null;
                 if(exp){
                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
                 }
             }
+            
+            
             if (name) {
                 // for = 
                 switch(name){
@@ -52425,34 +52924,71 @@ Roo.extend(Roo.XTemplate, Roo.Template, {
                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
                 }
             }
+            var uid = namedMatch ? namedMatch[1] : id;
+            
+            
             tpls.push({
-                id:     id,
+                id:     namedMatch ? namedMatch[1] : id,
                 target: name,
                 exec:   exec,
                 test:   fn,
                 body:   m[1] || ''
             });
-            s = s.replace(m[0], '{xtpl'+ id + '}');
+            if (namedMatch) {
+                s = s.replace(m[0], '');
+            } else { 
+                s = s.replace(m[0], '{xtpl'+ id + '}');
+            }
             ++id;
         }
+        this.tpls = [];
         for(var i = tpls.length-1; i >= 0; --i){
             this.compileTpl(tpls[i]);
+            this.tpls[tpls[i].id] = tpls[i];
         }
         this.master = tpls[tpls.length-1];
-        this.tpls = tpls;
         return this;
     },
-    
+    /**
+     * same as applyTemplate, except it's done to one of the subTemplates
+     * when using named templates, you can do:
+     *
+     * var str = pl.applySubTemplate('your-name', values);
+     *
+     * 
+     * @param {Number} id of the template
+     * @param {Object} values to apply to template
+     * @param {Object} parent (normaly the instance of this object)
+     */
     applySubTemplate : function(id, values, parent)
     {
+        
+        
         var t = this.tpls[id];
+        
+        
         try { 
             if(t.test && !t.test.call(this, values, parent)){
                 return '';
             }
+        } catch(e) {
+            Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
+            Roo.log(e.toString());
+            Roo.log(t.test);
+            return ''
+        }
+        try { 
+            
             if(t.exec && t.exec.call(this, values, parent)){
                 return '';
             }
+        } catch(e) {
+            Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
+            Roo.log(e.toString());
+            Roo.log(t.exec);
+            return ''
+        }
+        try {
             var vs = t.target ? t.target.call(this, values, parent) : values;
             parent = t.target ? values : parent;
             if(t.target && vs instanceof Array){
@@ -52466,8 +53002,7 @@ Roo.extend(Roo.XTemplate, Roo.Template, {
         } catch (e) {
             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
             Roo.log(e.toString());
-            Roo.log(e);
-            Roo.log(t);
+            Roo.log(t.compiled);
             return '';
         }
     },
@@ -52477,7 +53012,15 @@ Roo.extend(Roo.XTemplate, Roo.Template, {
         var fm = Roo.util.Format;
         var useF = this.disableFormats !== true;
         var sep = Roo.isGecko ? "+" : ",";
-        var fn = function(m, name, format, args){
+        var undef = function(str) {
+            Roo.log("Property not found :"  + str);
+            return '';
+        };
+        
+        var fn = function(m, name, format, args)
+        {
+            //Roo.log(arguments);
+            args = args ? args.replace(/\\'/g,"'") : args;
             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
             if (typeof(format) == 'undefined') {
                 format= 'htmlEncode';
@@ -52490,12 +53033,23 @@ Roo.extend(Roo.XTemplate, Roo.Template, {
                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
             }
             
-            var v;
-            //if(name.indexOf('.') != -1){
-                v = name;
-            //}else{
-            //    v = "values['" + name + "']";
-            //}
+            // build an array of options to determine if value is undefined..
+            
+            // basically get 'xxxx.yyyy' then do
+            // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
+            //    (function () { Roo.log("Property not found"); return ''; })() :
+            //    ......
+            
+            var udef_ar = [];
+            var lookfor = '';
+            Roo.each(name.split('.'), function(st) {
+                lookfor += (lookfor.length ? '.': '') + st;
+                udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
+            });
+            
+            var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
+            
+            
             if(format && useF){
                 
                 args = args ? ',' + args : "";
@@ -52506,34 +53060,37 @@ Roo.extend(Roo.XTemplate, Roo.Template, {
                     format = 'this.call("'+ format.substr(5) + '", ';
                     args = ", values";
                 }
-                return "'"+ sep + format + v + args + ")"+sep+"'";
+                
+                return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
             }
              
             if (args.length) {
                 // called with xxyx.yuu:(test,test)
                 // change to ()
-                return "'"+ sep + "("+v+" === undefined ? '' : " + v + '(' +  args + "))"+sep+"'";
+                return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
             }
             // raw.. - :raw modifier..
-            return "'"+ sep + "("+v+" === undefined ? '' : " + v + ")"+sep+"'";
+            return "'"+ sep + udef_st  + name + ")"+sep+"'";
             
         };
         var body;
         // branched to use + in gecko and [].join() in others
         if(Roo.isGecko){
-            body = "tpl.compiled = function(values, parent){ Roo.log(values); with(values) { return '" +
+            body = "tpl.compiled = function(values, parent){  with(values) { return '" +
                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
                     "';};};";
         }else{
-            body = ["tpl.compiled = function(values, parent){ Roo.log(values); with (values) { return ['"];
-            body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
+            body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
+            body.push(tpl.body.replace(/(\r\n|\n)/g,
+                            '\\n').replace(/'/g, "\\'").replace(this.re, fn));
             body.push("'].join('');};};");
             body = body.join('');
         }
         
-        /** eval:var:zzzzzzz */
+        Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
+       
+        /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
         eval(body);
-        Roo.log(body.replace(/\\n/,'\n'));
         
         return this;
     },