roojs-ui.js
[roojs1] / roojs-debug.js
index dc1c61c..618ea92 100644 (file)
@@ -4097,7 +4097,7 @@ Roo.lib.Easing = {
 /**
  * @class Roo.DomHelper
  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
- * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
+ * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
  * @singleton
  */
 Roo.DomHelper = function(){
@@ -12149,6 +12149,449 @@ Roo.UpdateManager.BasicRenderer.prototype = {
     }
 };
 /*
+ * Based on:
+ * Roo JS
+ * (c)) Alan Knowles
+ * Licence : LGPL
+ */
+
+
+/**
+ * @class Roo.DomTemplate
+ * @extends Roo.Template
+ * An effort at a dom based template engine..
+ *
+ * Similar to XTemplate, except it uses dom parsing to create the template..
+ *
+ * Supported features:
+ *
+ *  Tags:
+
+<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;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
+        &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
+        &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
+        &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
+  
+</code></pre>
+ *      
+ */
+Roo.DomTemplate = function()
+{
+     Roo.DomTemplate.superclass.constructor.apply(this, arguments);
+    if (this.html) {
+        this.compile();
+    }
+};
+
+
+Roo.extend(Roo.DomTemplate, Roo.Template, {
+    /**
+     * id counter for sub templates.
+     */
+    id : 0,
+    /**
+     * flag to indicate if dom parser is inside a pre,
+     * it will strip whitespace if not.
+     */
+    inPre : false,
+    
+    /**
+     * The various sub templates
+     */
+    tpls : false,
+    
+    
+    
+    /**
+     *
+     * basic tag replacing syntax
+     * WORD:WORD()
+     *
+     * // you can fake an object call by doing this
+     *  x.t:(test,tesT) 
+     * 
+     */
+    re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\}|\%7D)/g,
+    //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
+    
+    iterChild : function (node, method) {
+        
+        var oldPre = this.inPre;
+        if (node.tagName == 'PRE') {
+            this.inPre = true;
+        }
+        for( var i = 0; i < node.childNodes.length; i++) {
+            method.call(this, node.childNodes[i]);
+        }
+        this.inPre = oldPre;
+    },
+    
+    
+    
+    /**
+     * 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;
+        
+        // covert the html into DOM...
+        
+        var div = document.createElement('div');
+        div.innerHTML =   this.html  ;
+        
+        this.tpls = [];
+        var _t = this;
+        this.iterChild(div, function(n) {_t.compileNode(n, true); });
+        
+        var tpls = this.tpls;
+        
+        // create a top level template from the snippet..
+        
+        //Roo.log(div.innerHTML);
+        
+        var tpl = {
+            uid : 'master',
+            id : this.id++,
+            attr : false,
+            value : false,
+            body : div.innerHTML,
+            
+            forCall : false,
+            execCall : false,
+            dom : div,
+            isTop : true
+            
+        };
+        tpls.unshift(tpl);
+        
+        
+        // compile them...
+        this.tpls = [];
+        Roo.each(tpls, function(tp){
+            this.compileTpl(tp);
+            this.tpls[tp.id] = tp;
+        }, this);
+        
+        this.master = tpls[0];
+        return this;
+        
+        
+    },
+    
+    compileNode : function(node, istop) {
+        // test for
+        //Roo.log(node);
+        
+        
+        // skip anything not a tag..
+        if (node.nodeType != 1) {
+            if (node.nodeType == 3 && !this.inPre) {
+                // reduce white space..
+                node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
+                
+            }
+            return;
+        }
+        
+        var tpl = {
+            uid : false,
+            id : false,
+            attr : false,
+            value : false,
+            body : '',
+            
+            forCall : false,
+            execCall : false,
+            dom : false,
+            isTop : istop
+            
+            
+        };
+        
+        
+        switch(true) {
+            case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
+            case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
+            case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
+            case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
+            // no default..
+        }
+        
+        
+        if (!tpl.attr) {
+            // just itterate children..
+            this.iterChild(node,this.compileNode);
+            return;
+        }
+        tpl.uid = this.id++;
+        tpl.value = node.getAttribute('roo-' +  tpl.attr);
+        node.removeAttribute('roo-'+ tpl.attr);
+        if (tpl.attr != 'name') {
+            var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
+            node.parentNode.replaceChild(placeholder,  node);
+        } else {
+            
+            var placeholder =  document.createElement('span');
+            placeholder.className = 'roo-tpl-' + tpl.value;
+            node.parentNode.replaceChild(placeholder,  node);
+        }
+        
+        // parent now sees '{domtplXXXX}
+        this.iterChild(node,this.compileNode);
+        
+        // we should now have node body...
+        var div = document.createElement('div');
+        div.appendChild(node);
+        tpl.dom = node;
+        // this has the unfortunate side effect of converting tagged attributes
+        // eg. href="{...}" into %7C...%7D
+        // this has been fixed by searching for those combo's although it's a bit hacky..
+        
+        
+        tpl.body = div.innerHTML;
+        
+        
+         
+        tpl.id = tpl.uid;
+        switch(tpl.attr) {
+            case 'for' :
+                switch (tpl.value) {
+                    case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
+                    case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
+                    default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
+                }
+                break;
+            
+            case 'exec':
+                tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
+                break;
+            
+            case 'if':     
+                tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
+                break;
+            
+            case 'name':
+                tpl.id  = tpl.value; // replace non characters???
+                break;
+            
+        }
+        
+        
+        this.tpls.push(tpl);
+        
+        
+        
+    },
+    
+    
+    
+    
+    /**
+     * Compile a segment of the template into a 'sub-template'
+     *
+     * 
+     * 
+     *
+     */
+    compileTpl : function(tpl)
+    {
+        var fm = Roo.util.Format;
+        var useF = this.disableFormats !== true;
+        
+        var sep = Roo.isGecko ? "+\n" : ",\n";
+        
+        var undef = function(str) {
+            Roo.debug && Roo.log("Property not found :"  + str);
+            return '';
+        };
+          
+        //Roo.log(tpl.body);
+        
+        
+        
+        var fn = function(m, lbrace, name, format, args)
+        {
+            //Roo.log("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'; 
+            }
+            if (format == 'raw' ) {
+                format = false;
+            }
+            
+            if(name.substr(0, 6) == 'domtpl'){
+                return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
+            }
+            
+            // 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 : "";
+                 
+                if(format.substr(0, 5) != "this."){
+                    format = "fm." + format + '(';
+                }else{
+                    format = 'this.call("'+ format.substr(5) + '", ';
+                    args = ", values";
+                }
+                
+                return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
+            }
+             
+            if (args.length) {
+                // called with xxyx.yuu:(test,test)
+                // change to ()
+                return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
+            }
+            // raw.. - :raw modifier..
+            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){  with(values) { return '" +
+                   tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
+                    "';};};";
+        }else{
+            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('');
+        }
+        
+        Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
+       
+        /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
+        eval(body);
+        
+        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.ifCall && !t.ifCall.call(this, values, parent)){
+                Roo.log('if call on ' + t.value + ' return false');
+                return '';
+            }
+        } catch(e) {
+            Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
+            Roo.log(values);
+          
+            return '';
+        }
+        try { 
+            
+            if(t.execCall && t.execCall.call(this, values, parent)){
+                return '';
+            }
+        } catch(e) {
+            Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
+            Roo.log(values);
+            return '';
+        }
+        
+        try {
+            var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
+            parent = t.target ? values : parent;
+            if(t.forCall && vs instanceof Array){
+                var buf = [];
+                for(var i = 0, len = vs.length; i < len; i++){
+                    try {
+                        buf[buf.length] = t.compiled.call(this, vs[i], parent);
+                    } catch (e) {
+                        Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
+                        Roo.log(e.body);
+                        //Roo.log(t.compiled);
+                        Roo.log(vs[i]);
+                    }   
+                }
+                return buf.join('');
+            }
+        } catch (e) {
+            Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
+            Roo.log(values);
+            return '';
+        }
+        try {
+            return t.compiled.call(this, vs, parent);
+        } catch (e) {
+            Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
+            Roo.log(e.body);
+            //Roo.log(t.compiled);
+            Roo.log(values);
+            return '';
+        }
+    },
+
+   
+
+    applyTemplate : function(values){
+        return this.master.compiled.call(this, values, {});
+        //var s = this.subs;
+    },
+
+    apply : function(){
+        return this.applyTemplate.apply(this, arguments);
+    }
+
+ });
+
+Roo.DomTemplate.from = function(el){
+    el = Roo.getDom(el);
+    return new Roo.Domtemplate(el.value || el.innerHTML);
+};/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -13142,17 +13585,47 @@ Roo.util.Format = function(){
          * @return {String} The formatted currency string
          */
         usMoney : function(v){
-            v = (Math.round((v-0)*100))/100;
-            v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
+            return '$' + Roo.util.Format.number(v);
+        },
+        
+        /**
+         * Format a number
+         * eventually this should probably emulate php's number_format
+         * @param {Number/String} value The numeric value to format
+         * @param {Number} decimals number of decimal places
+         * @return {String} The formatted currency string
+         */
+        number : function(v,decimals)
+        {
+            // multiply and round.
+            decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
+            var mul = Math.pow(10, decimals);
+            var zero = String(mul).substring(1);
+            v = (Math.round((v-0)*mul))/mul;
+            
+            // if it's '0' number.. then
+            
+            //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
             v = String(v);
             var ps = v.split('.');
             var whole = ps[0];
-            var sub = ps[1] ? '.'+ ps[1] : '.00';
+            
+            
             var r = /(\d+)(\d{3})/;
+            // add comma's
             while (r.test(whole)) {
                 whole = whole.replace(r, '$1' + ',' + '$2');
             }
-            return "$" + whole + sub ;
+            
+            
+            var sub = ps[1] ?
+                    // has decimals..
+                    (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
+                    // does not have decimals
+                    (decimals ? ('.' + zero) : '');
+            
+            
+            return whole + sub ;
         },
         
         /**
@@ -18464,6 +18937,7 @@ Roo.dd.DropTarget = function(el, config){
          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
          * overClass (if any) from the drop element.
+         * 
          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
          * @param {Event} e The event
          * @param {Object} data An object containing arbitrary data supplied by the drag source
@@ -24854,6 +25328,15 @@ Roo.extend(Roo.DatePicker, Roo.Component, {
      */
     setValue : function(value){
         var old = this.value;
+        
+        if (typeof(value) == 'string') {
+         
+            value = Date.parseDate(value, this.format);
+        }
+        if (!value) {
+            value = new Date();
+        }
+        
         this.value = value.clearTime(true);
         if(this.el){
             this.update(this.value);
@@ -24875,8 +25358,9 @@ Roo.extend(Roo.DatePicker, Roo.Component, {
         }
     },
 
-    // private
+    // privateval
     onRender : function(container, position){
+        
         var m = [
              '<table cellspacing="0">',
                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
@@ -33455,6 +33939,7 @@ Roo.tree.TreeDropZone = function(tree, config){
 
 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
     ddGroup : "TreeDD",
+    scroll:  true,
     
     expandDelay : 1000,
     
@@ -33679,7 +34164,7 @@ Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
             data.node.ui.highlight();
         }
         this.hideProxy();
-    }
+    } 
     
 });
 
@@ -33725,12 +34210,7 @@ Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
     
     onEndDrag : function(data, e){
         this.tree.fireEvent("enddrag", this.tree, data.node, e);
-        if (this.scroller !== false) {
-            Roo.log('clear scroller');
-            window.clearInterval(this.scroller);
-            this.scroller =false;
-            
-        }
+        
         
     },
     
@@ -33744,72 +34224,6 @@ Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
         var sm = this.tree.getSelectionModel();
         sm.clearSelections();
         sm.select(this.dragData.node);
-    },
-    autoScroll: function(x, y, h, w) {
-        Roo.log("drop zone - autoscroll called");
-        
-        Roo.log(this.scroll ? "scroll=y": "scroll=m" );
-        if (this.scroll) {
-            // The client height
-            var clientH = Roo.lib.Dom.getViewWidth();
-
-            // The client width
-            var clientW = Roo.lib.Dom.getViewHeight();
-
-            // The amt scrolled down
-            var st = this.DDM.getScrollTop();
-
-            // The amt scrolled right
-            var sl = this.DDM.getScrollLeft();
-
-            // Location of the bottom of the element
-            var bot = h + y;
-
-            // Location of the right of the element
-            var right = w + x;
-
-            // The distance from the cursor to the bottom of the visible area,
-            // adjusted so that we don't scroll if the cursor is beyond the
-            // element drag constraints
-            var toBot = (clientH + st - y - this.deltaY);
-
-            // The distance from the cursor to the right of the visible area
-            var toRight = (clientW + sl - x - this.deltaX);
-
-
-            // How close to the edge the cursor must be before we scroll
-            // var thresh = (document.all) ? 100 : 40;
-            var thresh = 40;
-
-            // How many pixels to scroll per autoscroll op.  This helps to reduce
-            // clunky scrolling. IE is more sensitive about this ... it needs this
-            // value to be higher.
-            var scrAmt = (document.all) ? 80 : 30;
-
-            // Scroll down if we are near the bottom of the visible page and the
-            // obj extends below the crease
-            if ( bot > clientH && toBot < thresh ) {
-                window.scrollTo(sl, st + scrAmt);
-            }
-
-            // Scroll up if the window is scrolled down and the top of the object
-            // goes above the top border
-            if ( y < st && st > 0 && y - st < thresh ) {
-                window.scrollTo(sl, st - scrAmt);
-            }
-
-            // Scroll right if the obj is beyond the right border and the cursor is
-            // near the border.
-            if ( right > clientW && toRight < thresh ) {
-                window.scrollTo(sl + scrAmt, st);
-            }
-
-            // Scroll left if the window has been scrolled to the right and the obj
-            // extends past the left border
-            if ( x < sl && sl > 0 && x - sl < thresh ) {
-                window.scrollTo(sl - scrAmt, st);
-            }
-        }
     }
 });
 }/*
@@ -35650,10 +36064,9 @@ Roo.menu.DateMenu = function(config){
      * @param {Date} date
      */
     this.relayEvents(di, ["select"]);
-
     this.on('beforeshow', function(){
         if(this.picker){
-            this.picker.hideMonthPicker(true);
+            this.picker.hideMonthPicker(false);
         }
     }, this);
 };
@@ -37271,7 +37684,7 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
     
 
     /**
-     * @cfg {bool} useIso
+     * @cfg {Boolean} useIso
      * if enabled, then the date field will use a hidden field to store the 
      * real value as iso formated date. default (false)
      */ 
@@ -37294,7 +37707,7 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
             this.el.dom.removeAttribute('name'); 
             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
                     'before', true);
-            this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
+            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
             // prevent input submission
             this.hiddenName = this.name;
         }
@@ -37307,6 +37720,7 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
     {
         value = this.formatDate(value);
         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
+            Roo.log('super failed');
             return false;
         }
         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
@@ -37315,6 +37729,7 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
         var svalue = value;
         value = this.parseDate(value);
         if(!value){
+            Roo.log('parse date failed' + svalue);
             this.markInvalid(String.format(this.invalidText, svalue, this.format));
             return false;
         }
@@ -37356,7 +37771,9 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
      */
     getValue : function(){
         
-        return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
+        return  this.hiddenField ?
+                this.hiddenField.value :
+                this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
     },
 
     /**
@@ -37385,6 +37802,10 @@ dateField.setValue('2006-5-4');
             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
         }
         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
+        // make sure the value field is always stored as a date..
+        this.value = this.parseDate(date);
+        
+        
     },
 
     // private
@@ -37393,6 +37814,9 @@ dateField.setValue('2006-5-4');
             return value;
         }
         var v = Date.parseDate(value, this.format);
+         if (!v && this.useIso) {
+            v = Date.parseDate(value, 'Y-m-d');
+        }
         if(!v && this.altFormats){
             if(!this.altFormatsArray){
                 this.altFormatsArray = this.altFormats.split("|");
@@ -37413,6 +37837,7 @@ dateField.setValue('2006-5-4');
     // private
     menuListeners : {
         select: function(m, d){
+            
             this.setValue(d);
             this.fireEvent('select', this, d);
         },
@@ -37445,7 +37870,7 @@ dateField.setValue('2006-5-4');
             disabledDatesText : this.disabledDatesText,
             disabledDays : this.disabledDays,
             disabledDaysText : this.disabledDaysText,
-            format : this.format,
+            format : this.useIso ? 'Y-m-d' : this.format,
             minText : String.format(this.minText, this.formatDate(this.minValue)),
             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
         });
@@ -37481,6 +37906,406 @@ dateField.setValue('2006-5-4');
  * <script type="text/javascript">
  */
  
+/**
+ * @class Roo.form.MonthField
+ * @extends Roo.form.TriggerField
+ * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
+* @constructor
+* Create a new MonthField
+* @param {Object} config
+ */
+Roo.form.MonthField = function(config){
+    
+    Roo.form.MonthField.superclass.constructor.call(this, config);
+    
+      this.addEvents({
+         
+        /**
+         * @event select
+         * Fires when a date is selected
+            * @param {Roo.form.MonthFieeld} combo This combo box
+            * @param {Date} date The date selected
+            */
+        'select' : true
+         
+    });
+    
+    
+    if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
+    if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
+    this.ddMatch = null;
+    if(this.disabledDates){
+        var dd = this.disabledDates;
+        var re = "(?:";
+        for(var i = 0; i < dd.length; i++){
+            re += dd[i];
+            if(i != dd.length-1) re += "|";
+        }
+        this.ddMatch = new RegExp(re + ")");
+    }
+};
+
+Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
+    /**
+     * @cfg {String} format
+     * The default date format string which can be overriden for localization support.  The format must be
+     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
+     */
+    format : "M Y",
+    /**
+     * @cfg {String} altFormats
+     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
+     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
+     */
+    altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
+    /**
+     * @cfg {Array} disabledDays
+     * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
+     */
+    disabledDays : [0,1,2,3,4,5,6],
+    /**
+     * @cfg {String} disabledDaysText
+     * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
+     */
+    disabledDaysText : "Disabled",
+    /**
+     * @cfg {Array} disabledDates
+     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
+     * expression so they are very powerful. Some examples:
+     * <ul>
+     * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
+     * <li>["03/08", "09/16"] would disable those days for every year</li>
+     * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
+     * <li>["03/../2006"] would disable every day in March 2006</li>
+     * <li>["^03"] would disable every day in every March</li>
+     * </ul>
+     * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
+     * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
+     */
+    disabledDates : null,
+    /**
+     * @cfg {String} disabledDatesText
+     * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
+     */
+    disabledDatesText : "Disabled",
+    /**
+     * @cfg {Date/String} minValue
+     * The minimum allowed date. Can be either a Javascript date object or a string date in a
+     * valid format (defaults to null).
+     */
+    minValue : null,
+    /**
+     * @cfg {Date/String} maxValue
+     * The maximum allowed date. Can be either a Javascript date object or a string date in a
+     * valid format (defaults to null).
+     */
+    maxValue : null,
+    /**
+     * @cfg {String} minText
+     * The error text to display when the date in the cell is before minValue (defaults to
+     * 'The date in this field must be after {minValue}').
+     */
+    minText : "The date in this field must be equal to or after {0}",
+    /**
+     * @cfg {String} maxTextf
+     * The error text to display when the date in the cell is after maxValue (defaults to
+     * 'The date in this field must be before {maxValue}').
+     */
+    maxText : "The date in this field must be equal to or before {0}",
+    /**
+     * @cfg {String} invalidText
+     * The error text to display when the date in the field is invalid (defaults to
+     * '{value} is not a valid date - it must be in the format {format}').
+     */
+    invalidText : "{0} is not a valid date - it must be in the format {1}",
+    /**
+     * @cfg {String} triggerClass
+     * An additional CSS class used to style the trigger button.  The trigger will always get the
+     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
+     * which displays a calendar icon).
+     */
+    triggerClass : 'x-form-date-trigger',
+    
+
+    /**
+     * @cfg {Boolean} useIso
+     * if enabled, then the date field will use a hidden field to store the 
+     * real value as iso formated date. default (true)
+     */ 
+    useIso : true,
+    /**
+     * @cfg {String/Object} autoCreate
+     * A DomHelper element spec, or true for a default element spec (defaults to
+     * {tag: "input", type: "text", size: "10", autocomplete: "off"})
+     */ 
+    // private
+    defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
+    
+    // private
+    hiddenField: false,
+    
+    hideMonthPicker : false,
+    
+    onRender : function(ct, position)
+    {
+        Roo.form.MonthField.superclass.onRender.call(this, ct, position);
+        if (this.useIso) {
+            this.el.dom.removeAttribute('name'); 
+            this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
+                    'before', true);
+            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
+            // prevent input submission
+            this.hiddenName = this.name;
+        }
+            
+            
+    },
+    
+    // private
+    validateValue : function(value)
+    {
+        value = this.formatDate(value);
+        if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
+            return false;
+        }
+        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
+             return true;
+        }
+        var svalue = value;
+        value = this.parseDate(value);
+        if(!value){
+            this.markInvalid(String.format(this.invalidText, svalue, this.format));
+            return false;
+        }
+        var time = value.getTime();
+        if(this.minValue && time < this.minValue.getTime()){
+            this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
+            return false;
+        }
+        if(this.maxValue && time > this.maxValue.getTime()){
+            this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
+            return false;
+        }
+        /*if(this.disabledDays){
+            var day = value.getDay();
+            for(var i = 0; i < this.disabledDays.length; i++) {
+               if(day === this.disabledDays[i]){
+                   this.markInvalid(this.disabledDaysText);
+                    return false;
+               }
+            }
+        }
+        */
+        var fvalue = this.formatDate(value);
+        /*if(this.ddMatch && this.ddMatch.test(fvalue)){
+            this.markInvalid(String.format(this.disabledDatesText, fvalue));
+            return false;
+        }
+        */
+        return true;
+    },
+
+    // private
+    // Provides logic to override the default TriggerField.validateBlur which just returns true
+    validateBlur : function(){
+        return !this.menu || !this.menu.isVisible();
+    },
+
+    /**
+     * Returns the current date value of the date field.
+     * @return {Date} The date value
+     */
+    getValue : function(){
+        
+        
+        
+        return  this.hiddenField ?
+                this.hiddenField.value :
+                this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
+    },
+
+    /**
+     * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
+     * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
+     * (the default format used is "m/d/y").
+     * <br />Usage:
+     * <pre><code>
+//All of these calls set the same date value (May 4, 2006)
+
+//Pass a date object:
+var dt = new Date('5/4/06');
+monthField.setValue(dt);
+
+//Pass a date string (default format):
+monthField.setValue('5/4/06');
+
+//Pass a date string (custom format):
+monthField.format = 'Y-m-d';
+monthField.setValue('2006-5-4');
+</code></pre>
+     * @param {String/Date} date The date or valid date string
+     */
+    setValue : function(date){
+        Roo.log('month setValue' + date);
+        // can only be first of month..
+        
+        var val = this.parseDate(date);
+        
+        if (this.hiddenField) {
+            this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
+        }
+        Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
+        this.value = this.parseDate(date);
+    },
+
+    // private
+    parseDate : function(value){
+        if(!value || value instanceof Date){
+            value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
+            return value;
+        }
+        var v = Date.parseDate(value, this.format);
+        if (!v && this.useIso) {
+            v = Date.parseDate(value, 'Y-m-d');
+        }
+        if (v) {
+            // 
+            v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
+        }
+        
+        
+        if(!v && this.altFormats){
+            if(!this.altFormatsArray){
+                this.altFormatsArray = this.altFormats.split("|");
+            }
+            for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
+                v = Date.parseDate(value, this.altFormatsArray[i]);
+            }
+        }
+        return v;
+    },
+
+    // private
+    formatDate : function(date, fmt){
+        return (!date || !(date instanceof Date)) ?
+               date : date.dateFormat(fmt || this.format);
+    },
+
+    // private
+    menuListeners : {
+        select: function(m, d){
+            this.setValue(d);
+            this.fireEvent('select', this, d);
+        },
+        show : function(){ // retain focus styling
+            this.onFocus();
+        },
+        hide : function(){
+            this.focus.defer(10, this);
+            var ml = this.menuListeners;
+            this.menu.un("select", ml.select,  this);
+            this.menu.un("show", ml.show,  this);
+            this.menu.un("hide", ml.hide,  this);
+        }
+    },
+    // private
+    // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
+    onTriggerClick : function(){
+        if(this.disabled){
+            return;
+        }
+        if(this.menu == null){
+            this.menu = new Roo.menu.DateMenu();
+           
+        }
+        
+        Roo.apply(this.menu.picker,  {
+            
+            showClear: this.allowBlank,
+            minDate : this.minValue,
+            maxDate : this.maxValue,
+            disabledDatesRE : this.ddMatch,
+            disabledDatesText : this.disabledDatesText,
+            
+            format : this.useIso ? 'Y-m-d' : this.format,
+            minText : String.format(this.minText, this.formatDate(this.minValue)),
+            maxText : String.format(this.maxText, this.formatDate(this.maxValue))
+            
+        });
+         this.menu.on(Roo.apply({}, this.menuListeners, {
+            scope:this
+        }));
+       
+        
+        var m = this.menu;
+        var p = m.picker;
+        
+        // hide month picker get's called when we called by 'before hide';
+        
+        var ignorehide = true;
+        p.hideMonthPicker  = function(disableAnim){
+            if (ignorehide) {
+                return;
+            }
+             if(this.monthPicker){
+                Roo.log("hideMonthPicker called");
+                if(disableAnim === true){
+                    this.monthPicker.hide();
+                }else{
+                    this.monthPicker.slideOut('t', {duration:.2});
+                    p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
+                    p.fireEvent("select", this, this.value);
+                    m.hide();
+                }
+            }
+        }
+        
+        Roo.log('picker set value');
+        Roo.log(this.getValue());
+        p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
+        m.show(this.el, 'tl-bl?');
+        ignorehide  = false;
+        // this will trigger hideMonthPicker..
+        
+        
+        // hidden the day picker
+        Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
+        
+        
+        
+      
+        
+        p.showMonthPicker.defer(100, p);
+    
+        
+       
+    },
+
+    beforeBlur : function(){
+        var v = this.parseDate(this.getRawValue());
+        if(v){
+            this.setValue(v);
+        }
+    }
+
+    /** @cfg {Boolean} grow @hide */
+    /** @cfg {Number} growMin @hide */
+    /** @cfg {Number} growMax @hide */
+    /**
+     * @hide
+     * @method autoSize
+     */
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
 
 /**
  * @class Roo.form.ComboBox
@@ -38202,6 +39027,9 @@ Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
         this.setValue(this.originalValue);
         this.clearInvalid();
         this.lastData = false;
+        if (this.view) {
+            this.view.clearSelections();
+        }
     },
     // private
     findRecord : function(prop, value){
@@ -40350,6 +41178,9 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
                 return;
             }
+            if (v.match(/^#/)) {
+                return;
+            }
             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
             node.removeAttribute(n);
             
@@ -40389,14 +41220,15 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
             //console.log(a);
             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
                 node.removeAttribute(a.name);
-                return;
+                continue;
             }
             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
                 cleanAttr(a.name,a.value); // fixme..
-                return;
+                continue;
             }
             if (a.name == 'style') {
                 cleanStyle(a.name,a.value);
+                continue;
             }
             /// clean up MS crap..
             // tecnically this should be a list of valid class'es..
@@ -40410,6 +41242,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
                 if (a.value.match(/body/)) {
                     node.className = '';
                 }
+                continue;
             }
             
             // style cleanup!?
@@ -42089,7 +42922,7 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
                 (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",
@@ -43028,6 +43861,7 @@ Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
                 }
                 if (!rdata || !rdata.success) {
                     Roo.log(rdata);
+                    Roo.MessageBox.alert(Roo.encode(rdata));
                     return;
                 }
                 var data = rdata.data;
@@ -47352,7 +48186,17 @@ Roo.ContentPanel = function(el, config, content){
         this.toolbar = new Roo.Toolbar(this.toolbar);
     }
     
+    // xtype created footer. - not sure if will work as we normally have to render first..
+    if (this.footer && !this.footer.el && this.footer.xtype) {
+        if (!this.wrapEl) {
+            this.wrapEl = this.el.wrap();
+        }
     
+        this.footer.container = this.wrapEl.createChild();
+         
+        this.footer = Roo.factory(this.footer, Roo);
+        
+    }
     
     if(this.resizeEl){
         this.resizeEl = Roo.get(this.resizeEl, true);
@@ -47540,7 +48384,9 @@ panel.load({
         return this.wrapEl || this.el;
     },
     
-    adjustForComponents : function(width, height){
+    adjustForComponents : function(width, height)
+    {
+        Roo.log('adjustForComponents ');
         if(this.resizeEl != this.el){
             width -= this.el.getFrameWidth('lr');
             height -= this.el.getFrameWidth('tb');
@@ -47550,6 +48396,15 @@ panel.load({
             height -= te.getHeight();
             te.setWidth(width);
         }
+        if(this.footer){
+            var te = this.footer.getEl();
+            Roo.log("footer:" + te.getHeight());
+            
+            height -= te.getHeight();
+            te.setWidth(width);
+        }
+        
+        
         if(this.adjustments){
             width += this.adjustments[0];
             height += this.adjustments[1];
@@ -47658,7 +48513,13 @@ layout.addxtype({
     addxtype : function(cfg) {
         // add form..
         if (cfg.xtype.match(/^Form$/)) {
-            var el = this.el.createChild();
+            
+            var el;
+            //if (this.footer) {
+            //    el = this.footer.container.insertSibling(false, 'before');
+            //} else {
+                el = this.el.createChild();
+            //}
 
             this.form = new  Roo.form.Form(cfg);
             
@@ -47984,12 +48845,14 @@ Roo.TreePanel = function(config){
         //console.log('render tree');
         this.tree.render();
     });
-    
-    this.on('resize',  function (cp, w, h) {
-            this.tree.innerCt.setWidth(w);
-            this.tree.innerCt.setHeight(h);
-            this.tree.innerCt.setStyle('overflow-y', 'auto');
-    });
+    // this should not be needed.. - it's actually the 'el' that resizes?
+    // actuall it breaks the containerScroll - dragging nodes auto scroll at top
+    
+    //this.on('resize',  function (cp, w, h) {
+    //        this.tree.innerCt.setWidth(w);
+    //        this.tree.innerCt.setHeight(h);
+    //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
+    //});