roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13 /**
14  * @class Roo.ComponentMgr
15  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16  * @singleton
17  */
18 Roo.ComponentMgr = function(){
19     var all = new Roo.util.MixedCollection();
20
21     return {
22         /**
23          * Registers a component.
24          * @param {Roo.Component} c The component
25          */
26         register : function(c){
27             all.add(c);
28         },
29
30         /**
31          * Unregisters a component.
32          * @param {Roo.Component} c The component
33          */
34         unregister : function(c){
35             all.remove(c);
36         },
37
38         /**
39          * Returns a component by id
40          * @param {String} id The component id
41          */
42         get : function(id){
43             return all.get(id);
44         },
45
46         /**
47          * Registers a function that will be called when a specified component is added to ComponentMgr
48          * @param {String} id The component id
49          * @param {Funtction} fn The callback function
50          * @param {Object} scope The scope of the callback
51          */
52         onAvailable : function(id, fn, scope){
53             all.on("add", function(index, o){
54                 if(o.id == id){
55                     fn.call(scope || o, o);
56                     all.un("add", fn, scope);
57                 }
58             });
59         }
60     };
61 }();/*
62  * Based on:
63  * Ext JS Library 1.1.1
64  * Copyright(c) 2006-2007, Ext JS, LLC.
65  *
66  * Originally Released Under LGPL - original licence link has changed is not relivant.
67  *
68  * Fork - LGPL
69  * <script type="text/javascript">
70  */
71  
72 /**
73  * @class Roo.Component
74  * @extends Roo.util.Observable
75  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
76  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
77  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
78  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
79  * All visual components (widgets) that require rendering into a layout should subclass Component.
80  * @constructor
81  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
82  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
83  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
84  */
85 Roo.Component = function(config){
86     config = config || {};
87     if(config.tagName || config.dom || typeof config == "string"){ // element object
88         config = {el: config, id: config.id || config};
89     }
90     this.initialConfig = config;
91
92     Roo.apply(this, config);
93     this.addEvents({
94         /**
95          * @event disable
96          * Fires after the component is disabled.
97              * @param {Roo.Component} this
98              */
99         disable : true,
100         /**
101          * @event enable
102          * Fires after the component is enabled.
103              * @param {Roo.Component} this
104              */
105         enable : true,
106         /**
107          * @event beforeshow
108          * Fires before the component is shown.  Return false to stop the show.
109              * @param {Roo.Component} this
110              */
111         beforeshow : true,
112         /**
113          * @event show
114          * Fires after the component is shown.
115              * @param {Roo.Component} this
116              */
117         show : true,
118         /**
119          * @event beforehide
120          * Fires before the component is hidden. Return false to stop the hide.
121              * @param {Roo.Component} this
122              */
123         beforehide : true,
124         /**
125          * @event hide
126          * Fires after the component is hidden.
127              * @param {Roo.Component} this
128              */
129         hide : true,
130         /**
131          * @event beforerender
132          * Fires before the component is rendered. Return false to stop the render.
133              * @param {Roo.Component} this
134              */
135         beforerender : true,
136         /**
137          * @event render
138          * Fires after the component is rendered.
139              * @param {Roo.Component} this
140              */
141         render : true,
142         /**
143          * @event beforedestroy
144          * Fires before the component is destroyed. Return false to stop the destroy.
145              * @param {Roo.Component} this
146              */
147         beforedestroy : true,
148         /**
149          * @event destroy
150          * Fires after the component is destroyed.
151              * @param {Roo.Component} this
152              */
153         destroy : true
154     });
155     if(!this.id){
156         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
157     }
158     Roo.ComponentMgr.register(this);
159     Roo.Component.superclass.constructor.call(this);
160     this.initComponent();
161     if(this.renderTo){ // not supported by all components yet. use at your own risk!
162         this.render(this.renderTo);
163         delete this.renderTo;
164     }
165 };
166
167 // private
168 Roo.Component.AUTO_ID = 1000;
169
170 Roo.extend(Roo.Component, Roo.util.Observable, {
171     /**
172      * @property {Boolean} hidden
173      * true if this component is hidden. Read-only.
174      */
175     hidden : false,
176     /**
177      * true if this component is disabled. Read-only.
178      */
179     disabled : false,
180     /**
181      * true if this component has been rendered. Read-only.
182      */
183     rendered : false,
184     
185     /** @cfg {String} disableClass
186      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
187      */
188     disabledClass : "x-item-disabled",
189         /** @cfg {Boolean} allowDomMove
190          * Whether the component can move the Dom node when rendering (defaults to true).
191          */
192     allowDomMove : true,
193     /** @cfg {String} hideMode
194      * How this component should hidden. Supported values are
195      * "visibility" (css visibility), "offsets" (negative offset position) and
196      * "display" (css display) - defaults to "display".
197      */
198     hideMode: 'display',
199
200     // private
201     ctype : "Roo.Component",
202
203     /** @cfg {String} actionMode 
204      * which property holds the element that used for  hide() / show() / disable() / enable()
205      * default is 'el' 
206      */
207     actionMode : "el",
208
209     // private
210     getActionEl : function(){
211         return this[this.actionMode];
212     },
213
214     initComponent : Roo.emptyFn,
215     /**
216      * If this is a lazy rendering component, render it to its container element.
217      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
218      */
219     render : function(container, position){
220         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
221             if(!container && this.el){
222                 this.el = Roo.get(this.el);
223                 container = this.el.dom.parentNode;
224                 this.allowDomMove = false;
225             }
226             this.container = Roo.get(container);
227             this.rendered = true;
228             if(position !== undefined){
229                 if(typeof position == 'number'){
230                     position = this.container.dom.childNodes[position];
231                 }else{
232                     position = Roo.getDom(position);
233                 }
234             }
235             this.onRender(this.container, position || null);
236             if(this.cls){
237                 this.el.addClass(this.cls);
238                 delete this.cls;
239             }
240             if(this.style){
241                 this.el.applyStyles(this.style);
242                 delete this.style;
243             }
244             this.fireEvent("render", this);
245             this.afterRender(this.container);
246             if(this.hidden){
247                 this.hide();
248             }
249             if(this.disabled){
250                 this.disable();
251             }
252         }
253         return this;
254     },
255
256     // private
257     // default function is not really useful
258     onRender : function(ct, position){
259         if(this.el){
260             this.el = Roo.get(this.el);
261             if(this.allowDomMove !== false){
262                 ct.dom.insertBefore(this.el.dom, position);
263             }
264         }
265     },
266
267     // private
268     getAutoCreate : function(){
269         var cfg = typeof this.autoCreate == "object" ?
270                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
271         if(this.id && !cfg.id){
272             cfg.id = this.id;
273         }
274         return cfg;
275     },
276
277     // private
278     afterRender : Roo.emptyFn,
279
280     /**
281      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
282      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
283      */
284     destroy : function(){
285         if(this.fireEvent("beforedestroy", this) !== false){
286             this.purgeListeners();
287             this.beforeDestroy();
288             if(this.rendered){
289                 this.el.removeAllListeners();
290                 this.el.remove();
291                 if(this.actionMode == "container"){
292                     this.container.remove();
293                 }
294             }
295             this.onDestroy();
296             Roo.ComponentMgr.unregister(this);
297             this.fireEvent("destroy", this);
298         }
299     },
300
301         // private
302     beforeDestroy : function(){
303
304     },
305
306         // private
307         onDestroy : function(){
308
309     },
310
311     /**
312      * Returns the underlying {@link Roo.Element}.
313      * @return {Roo.Element} The element
314      */
315     getEl : function(){
316         return this.el;
317     },
318
319     /**
320      * Returns the id of this component.
321      * @return {String}
322      */
323     getId : function(){
324         return this.id;
325     },
326
327     /**
328      * Try to focus this component.
329      * @param {Boolean} selectText True to also select the text in this component (if applicable)
330      * @return {Roo.Component} this
331      */
332     focus : function(selectText){
333         if(this.rendered){
334             this.el.focus();
335             if(selectText === true){
336                 this.el.dom.select();
337             }
338         }
339         return this;
340     },
341
342     // private
343     blur : function(){
344         if(this.rendered){
345             this.el.blur();
346         }
347         return this;
348     },
349
350     /**
351      * Disable this component.
352      * @return {Roo.Component} this
353      */
354     disable : function(){
355         if(this.rendered){
356             this.onDisable();
357         }
358         this.disabled = true;
359         this.fireEvent("disable", this);
360         return this;
361     },
362
363         // private
364     onDisable : function(){
365         this.getActionEl().addClass(this.disabledClass);
366         this.el.dom.disabled = true;
367     },
368
369     /**
370      * Enable this component.
371      * @return {Roo.Component} this
372      */
373     enable : function(){
374         if(this.rendered){
375             this.onEnable();
376         }
377         this.disabled = false;
378         this.fireEvent("enable", this);
379         return this;
380     },
381
382         // private
383     onEnable : function(){
384         this.getActionEl().removeClass(this.disabledClass);
385         this.el.dom.disabled = false;
386     },
387
388     /**
389      * Convenience function for setting disabled/enabled by boolean.
390      * @param {Boolean} disabled
391      */
392     setDisabled : function(disabled){
393         this[disabled ? "disable" : "enable"]();
394     },
395
396     /**
397      * Show this component.
398      * @return {Roo.Component} this
399      */
400     show: function(){
401         if(this.fireEvent("beforeshow", this) !== false){
402             this.hidden = false;
403             if(this.rendered){
404                 this.onShow();
405             }
406             this.fireEvent("show", this);
407         }
408         return this;
409     },
410
411     // private
412     onShow : function(){
413         var ae = this.getActionEl();
414         if(this.hideMode == 'visibility'){
415             ae.dom.style.visibility = "visible";
416         }else if(this.hideMode == 'offsets'){
417             ae.removeClass('x-hidden');
418         }else{
419             ae.dom.style.display = "";
420         }
421     },
422
423     /**
424      * Hide this component.
425      * @return {Roo.Component} this
426      */
427     hide: function(){
428         if(this.fireEvent("beforehide", this) !== false){
429             this.hidden = true;
430             if(this.rendered){
431                 this.onHide();
432             }
433             this.fireEvent("hide", this);
434         }
435         return this;
436     },
437
438     // private
439     onHide : function(){
440         var ae = this.getActionEl();
441         if(this.hideMode == 'visibility'){
442             ae.dom.style.visibility = "hidden";
443         }else if(this.hideMode == 'offsets'){
444             ae.addClass('x-hidden');
445         }else{
446             ae.dom.style.display = "none";
447         }
448     },
449
450     /**
451      * Convenience function to hide or show this component by boolean.
452      * @param {Boolean} visible True to show, false to hide
453      * @return {Roo.Component} this
454      */
455     setVisible: function(visible){
456         if(visible) {
457             this.show();
458         }else{
459             this.hide();
460         }
461         return this;
462     },
463
464     /**
465      * Returns true if this component is visible.
466      */
467     isVisible : function(){
468         return this.getActionEl().isVisible();
469     },
470
471     cloneConfig : function(overrides){
472         overrides = overrides || {};
473         var id = overrides.id || Roo.id();
474         var cfg = Roo.applyIf(overrides, this.initialConfig);
475         cfg.id = id; // prevent dup id
476         return new this.constructor(cfg);
477     }
478 });/*
479  * Based on:
480  * Ext JS Library 1.1.1
481  * Copyright(c) 2006-2007, Ext JS, LLC.
482  *
483  * Originally Released Under LGPL - original licence link has changed is not relivant.
484  *
485  * Fork - LGPL
486  * <script type="text/javascript">
487  */
488  (function(){ 
489 /**
490  * @class Roo.Layer
491  * @extends Roo.Element
492  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
493  * automatic maintaining of shadow/shim positions.
494  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
495  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
496  * you can pass a string with a CSS class name. False turns off the shadow.
497  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
498  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
499  * @cfg {String} cls CSS class to add to the element
500  * @cfg {Number} zindex Starting z-index (defaults to 11000)
501  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
502  * @constructor
503  * @param {Object} config An object with config options.
504  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
505  */
506
507 Roo.Layer = function(config, existingEl){
508     config = config || {};
509     var dh = Roo.DomHelper;
510     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
511     if(existingEl){
512         this.dom = Roo.getDom(existingEl);
513     }
514     if(!this.dom){
515         var o = config.dh || {tag: "div", cls: "x-layer"};
516         this.dom = dh.append(pel, o);
517     }
518     if(config.cls){
519         this.addClass(config.cls);
520     }
521     this.constrain = config.constrain !== false;
522     this.visibilityMode = Roo.Element.VISIBILITY;
523     if(config.id){
524         this.id = this.dom.id = config.id;
525     }else{
526         this.id = Roo.id(this.dom);
527     }
528     this.zindex = config.zindex || this.getZIndex();
529     this.position("absolute", this.zindex);
530     if(config.shadow){
531         this.shadowOffset = config.shadowOffset || 4;
532         this.shadow = new Roo.Shadow({
533             offset : this.shadowOffset,
534             mode : config.shadow
535         });
536     }else{
537         this.shadowOffset = 0;
538     }
539     this.useShim = config.shim !== false && Roo.useShims;
540     this.useDisplay = config.useDisplay;
541     this.hide();
542 };
543
544 var supr = Roo.Element.prototype;
545
546 // shims are shared among layer to keep from having 100 iframes
547 var shims = [];
548
549 Roo.extend(Roo.Layer, Roo.Element, {
550
551     getZIndex : function(){
552         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
553     },
554
555     getShim : function(){
556         if(!this.useShim){
557             return null;
558         }
559         if(this.shim){
560             return this.shim;
561         }
562         var shim = shims.shift();
563         if(!shim){
564             shim = this.createShim();
565             shim.enableDisplayMode('block');
566             shim.dom.style.display = 'none';
567             shim.dom.style.visibility = 'visible';
568         }
569         var pn = this.dom.parentNode;
570         if(shim.dom.parentNode != pn){
571             pn.insertBefore(shim.dom, this.dom);
572         }
573         shim.setStyle('z-index', this.getZIndex()-2);
574         this.shim = shim;
575         return shim;
576     },
577
578     hideShim : function(){
579         if(this.shim){
580             this.shim.setDisplayed(false);
581             shims.push(this.shim);
582             delete this.shim;
583         }
584     },
585
586     disableShadow : function(){
587         if(this.shadow){
588             this.shadowDisabled = true;
589             this.shadow.hide();
590             this.lastShadowOffset = this.shadowOffset;
591             this.shadowOffset = 0;
592         }
593     },
594
595     enableShadow : function(show){
596         if(this.shadow){
597             this.shadowDisabled = false;
598             this.shadowOffset = this.lastShadowOffset;
599             delete this.lastShadowOffset;
600             if(show){
601                 this.sync(true);
602             }
603         }
604     },
605
606     // private
607     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
608     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
609     sync : function(doShow){
610         var sw = this.shadow;
611         if(!this.updating && this.isVisible() && (sw || this.useShim)){
612             var sh = this.getShim();
613
614             var w = this.getWidth(),
615                 h = this.getHeight();
616
617             var l = this.getLeft(true),
618                 t = this.getTop(true);
619
620             if(sw && !this.shadowDisabled){
621                 if(doShow && !sw.isVisible()){
622                     sw.show(this);
623                 }else{
624                     sw.realign(l, t, w, h);
625                 }
626                 if(sh){
627                     if(doShow){
628                        sh.show();
629                     }
630                     // fit the shim behind the shadow, so it is shimmed too
631                     var a = sw.adjusts, s = sh.dom.style;
632                     s.left = (Math.min(l, l+a.l))+"px";
633                     s.top = (Math.min(t, t+a.t))+"px";
634                     s.width = (w+a.w)+"px";
635                     s.height = (h+a.h)+"px";
636                 }
637             }else if(sh){
638                 if(doShow){
639                    sh.show();
640                 }
641                 sh.setSize(w, h);
642                 sh.setLeftTop(l, t);
643             }
644             
645         }
646     },
647
648     // private
649     destroy : function(){
650         this.hideShim();
651         if(this.shadow){
652             this.shadow.hide();
653         }
654         this.removeAllListeners();
655         var pn = this.dom.parentNode;
656         if(pn){
657             pn.removeChild(this.dom);
658         }
659         Roo.Element.uncache(this.id);
660     },
661
662     remove : function(){
663         this.destroy();
664     },
665
666     // private
667     beginUpdate : function(){
668         this.updating = true;
669     },
670
671     // private
672     endUpdate : function(){
673         this.updating = false;
674         this.sync(true);
675     },
676
677     // private
678     hideUnders : function(negOffset){
679         if(this.shadow){
680             this.shadow.hide();
681         }
682         this.hideShim();
683     },
684
685     // private
686     constrainXY : function(){
687         if(this.constrain){
688             var vw = Roo.lib.Dom.getViewWidth(),
689                 vh = Roo.lib.Dom.getViewHeight();
690             var s = Roo.get(document).getScroll();
691
692             var xy = this.getXY();
693             var x = xy[0], y = xy[1];   
694             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
695             // only move it if it needs it
696             var moved = false;
697             // first validate right/bottom
698             if((x + w) > vw+s.left){
699                 x = vw - w - this.shadowOffset;
700                 moved = true;
701             }
702             if((y + h) > vh+s.top){
703                 y = vh - h - this.shadowOffset;
704                 moved = true;
705             }
706             // then make sure top/left isn't negative
707             if(x < s.left){
708                 x = s.left;
709                 moved = true;
710             }
711             if(y < s.top){
712                 y = s.top;
713                 moved = true;
714             }
715             if(moved){
716                 if(this.avoidY){
717                     var ay = this.avoidY;
718                     if(y <= ay && (y+h) >= ay){
719                         y = ay-h-5;   
720                     }
721                 }
722                 xy = [x, y];
723                 this.storeXY(xy);
724                 supr.setXY.call(this, xy);
725                 this.sync();
726             }
727         }
728     },
729
730     isVisible : function(){
731         return this.visible;    
732     },
733
734     // private
735     showAction : function(){
736         this.visible = true; // track visibility to prevent getStyle calls
737         if(this.useDisplay === true){
738             this.setDisplayed("");
739         }else if(this.lastXY){
740             supr.setXY.call(this, this.lastXY);
741         }else if(this.lastLT){
742             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
743         }
744     },
745
746     // private
747     hideAction : function(){
748         this.visible = false;
749         if(this.useDisplay === true){
750             this.setDisplayed(false);
751         }else{
752             this.setLeftTop(-10000,-10000);
753         }
754     },
755
756     // overridden Element method
757     setVisible : function(v, a, d, c, e){
758         if(v){
759             this.showAction();
760         }
761         if(a && v){
762             var cb = function(){
763                 this.sync(true);
764                 if(c){
765                     c();
766                 }
767             }.createDelegate(this);
768             supr.setVisible.call(this, true, true, d, cb, e);
769         }else{
770             if(!v){
771                 this.hideUnders(true);
772             }
773             var cb = c;
774             if(a){
775                 cb = function(){
776                     this.hideAction();
777                     if(c){
778                         c();
779                     }
780                 }.createDelegate(this);
781             }
782             supr.setVisible.call(this, v, a, d, cb, e);
783             if(v){
784                 this.sync(true);
785             }else if(!a){
786                 this.hideAction();
787             }
788         }
789     },
790
791     storeXY : function(xy){
792         delete this.lastLT;
793         this.lastXY = xy;
794     },
795
796     storeLeftTop : function(left, top){
797         delete this.lastXY;
798         this.lastLT = [left, top];
799     },
800
801     // private
802     beforeFx : function(){
803         this.beforeAction();
804         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
805     },
806
807     // private
808     afterFx : function(){
809         Roo.Layer.superclass.afterFx.apply(this, arguments);
810         this.sync(this.isVisible());
811     },
812
813     // private
814     beforeAction : function(){
815         if(!this.updating && this.shadow){
816             this.shadow.hide();
817         }
818     },
819
820     // overridden Element method
821     setLeft : function(left){
822         this.storeLeftTop(left, this.getTop(true));
823         supr.setLeft.apply(this, arguments);
824         this.sync();
825     },
826
827     setTop : function(top){
828         this.storeLeftTop(this.getLeft(true), top);
829         supr.setTop.apply(this, arguments);
830         this.sync();
831     },
832
833     setLeftTop : function(left, top){
834         this.storeLeftTop(left, top);
835         supr.setLeftTop.apply(this, arguments);
836         this.sync();
837     },
838
839     setXY : function(xy, a, d, c, e){
840         this.fixDisplay();
841         this.beforeAction();
842         this.storeXY(xy);
843         var cb = this.createCB(c);
844         supr.setXY.call(this, xy, a, d, cb, e);
845         if(!a){
846             cb();
847         }
848     },
849
850     // private
851     createCB : function(c){
852         var el = this;
853         return function(){
854             el.constrainXY();
855             el.sync(true);
856             if(c){
857                 c();
858             }
859         };
860     },
861
862     // overridden Element method
863     setX : function(x, a, d, c, e){
864         this.setXY([x, this.getY()], a, d, c, e);
865     },
866
867     // overridden Element method
868     setY : function(y, a, d, c, e){
869         this.setXY([this.getX(), y], a, d, c, e);
870     },
871
872     // overridden Element method
873     setSize : function(w, h, a, d, c, e){
874         this.beforeAction();
875         var cb = this.createCB(c);
876         supr.setSize.call(this, w, h, a, d, cb, e);
877         if(!a){
878             cb();
879         }
880     },
881
882     // overridden Element method
883     setWidth : function(w, a, d, c, e){
884         this.beforeAction();
885         var cb = this.createCB(c);
886         supr.setWidth.call(this, w, a, d, cb, e);
887         if(!a){
888             cb();
889         }
890     },
891
892     // overridden Element method
893     setHeight : function(h, a, d, c, e){
894         this.beforeAction();
895         var cb = this.createCB(c);
896         supr.setHeight.call(this, h, a, d, cb, e);
897         if(!a){
898             cb();
899         }
900     },
901
902     // overridden Element method
903     setBounds : function(x, y, w, h, a, d, c, e){
904         this.beforeAction();
905         var cb = this.createCB(c);
906         if(!a){
907             this.storeXY([x, y]);
908             supr.setXY.call(this, [x, y]);
909             supr.setSize.call(this, w, h, a, d, cb, e);
910             cb();
911         }else{
912             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
913         }
914         return this;
915     },
916     
917     /**
918      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
919      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
920      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
921      * @param {Number} zindex The new z-index to set
922      * @return {this} The Layer
923      */
924     setZIndex : function(zindex){
925         this.zindex = zindex;
926         this.setStyle("z-index", zindex + 2);
927         if(this.shadow){
928             this.shadow.setZIndex(zindex + 1);
929         }
930         if(this.shim){
931             this.shim.setStyle("z-index", zindex);
932         }
933     }
934 });
935 })();/*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946
947 /**
948  * @class Roo.Shadow
949  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
950  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
951  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
952  * @constructor
953  * Create a new Shadow
954  * @param {Object} config The config object
955  */
956 Roo.Shadow = function(config){
957     Roo.apply(this, config);
958     if(typeof this.mode != "string"){
959         this.mode = this.defaultMode;
960     }
961     var o = this.offset, a = {h: 0};
962     var rad = Math.floor(this.offset/2);
963     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
964         case "drop":
965             a.w = 0;
966             a.l = a.t = o;
967             a.t -= 1;
968             if(Roo.isIE){
969                 a.l -= this.offset + rad;
970                 a.t -= this.offset + rad;
971                 a.w -= rad;
972                 a.h -= rad;
973                 a.t += 1;
974             }
975         break;
976         case "sides":
977             a.w = (o*2);
978             a.l = -o;
979             a.t = o-1;
980             if(Roo.isIE){
981                 a.l -= (this.offset - rad);
982                 a.t -= this.offset + rad;
983                 a.l += 1;
984                 a.w -= (this.offset - rad)*2;
985                 a.w -= rad + 1;
986                 a.h -= 1;
987             }
988         break;
989         case "frame":
990             a.w = a.h = (o*2);
991             a.l = a.t = -o;
992             a.t += 1;
993             a.h -= 2;
994             if(Roo.isIE){
995                 a.l -= (this.offset - rad);
996                 a.t -= (this.offset - rad);
997                 a.l += 1;
998                 a.w -= (this.offset + rad + 1);
999                 a.h -= (this.offset + rad);
1000                 a.h += 1;
1001             }
1002         break;
1003     };
1004
1005     this.adjusts = a;
1006 };
1007
1008 Roo.Shadow.prototype = {
1009     /**
1010      * @cfg {String} mode
1011      * The shadow display mode.  Supports the following options:<br />
1012      * sides: Shadow displays on both sides and bottom only<br />
1013      * frame: Shadow displays equally on all four sides<br />
1014      * drop: Traditional bottom-right drop shadow (default)
1015      */
1016     /**
1017      * @cfg {String} offset
1018      * The number of pixels to offset the shadow from the element (defaults to 4)
1019      */
1020     offset: 4,
1021
1022     // private
1023     defaultMode: "drop",
1024
1025     /**
1026      * Displays the shadow under the target element
1027      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
1028      */
1029     show : function(target){
1030         target = Roo.get(target);
1031         if(!this.el){
1032             this.el = Roo.Shadow.Pool.pull();
1033             if(this.el.dom.nextSibling != target.dom){
1034                 this.el.insertBefore(target);
1035             }
1036         }
1037         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
1038         if(Roo.isIE){
1039             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
1040         }
1041         this.realign(
1042             target.getLeft(true),
1043             target.getTop(true),
1044             target.getWidth(),
1045             target.getHeight()
1046         );
1047         this.el.dom.style.display = "block";
1048     },
1049
1050     /**
1051      * Returns true if the shadow is visible, else false
1052      */
1053     isVisible : function(){
1054         return this.el ? true : false;  
1055     },
1056
1057     /**
1058      * Direct alignment when values are already available. Show must be called at least once before
1059      * calling this method to ensure it is initialized.
1060      * @param {Number} left The target element left position
1061      * @param {Number} top The target element top position
1062      * @param {Number} width The target element width
1063      * @param {Number} height The target element height
1064      */
1065     realign : function(l, t, w, h){
1066         if(!this.el){
1067             return;
1068         }
1069         var a = this.adjusts, d = this.el.dom, s = d.style;
1070         var iea = 0;
1071         s.left = (l+a.l)+"px";
1072         s.top = (t+a.t)+"px";
1073         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
1074         if(s.width != sws || s.height != shs){
1075             s.width = sws;
1076             s.height = shs;
1077             if(!Roo.isIE){
1078                 var cn = d.childNodes;
1079                 var sww = Math.max(0, (sw-12))+"px";
1080                 cn[0].childNodes[1].style.width = sww;
1081                 cn[1].childNodes[1].style.width = sww;
1082                 cn[2].childNodes[1].style.width = sww;
1083                 cn[1].style.height = Math.max(0, (sh-12))+"px";
1084             }
1085         }
1086     },
1087
1088     /**
1089      * Hides this shadow
1090      */
1091     hide : function(){
1092         if(this.el){
1093             this.el.dom.style.display = "none";
1094             Roo.Shadow.Pool.push(this.el);
1095             delete this.el;
1096         }
1097     },
1098
1099     /**
1100      * Adjust the z-index of this shadow
1101      * @param {Number} zindex The new z-index
1102      */
1103     setZIndex : function(z){
1104         this.zIndex = z;
1105         if(this.el){
1106             this.el.setStyle("z-index", z);
1107         }
1108     }
1109 };
1110
1111 // Private utility class that manages the internal Shadow cache
1112 Roo.Shadow.Pool = function(){
1113     var p = [];
1114     var markup = Roo.isIE ?
1115                  '<div class="x-ie-shadow"></div>' :
1116                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
1117     return {
1118         pull : function(){
1119             var sh = p.shift();
1120             if(!sh){
1121                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
1122                 sh.autoBoxAdjust = false;
1123             }
1124             return sh;
1125         },
1126
1127         push : function(sh){
1128             p.push(sh);
1129         }
1130     };
1131 }();/*
1132  * Based on:
1133  * Ext JS Library 1.1.1
1134  * Copyright(c) 2006-2007, Ext JS, LLC.
1135  *
1136  * Originally Released Under LGPL - original licence link has changed is not relivant.
1137  *
1138  * Fork - LGPL
1139  * <script type="text/javascript">
1140  */
1141
1142 /**
1143  * @class Roo.BoxComponent
1144  * @extends Roo.Component
1145  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
1146  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
1147  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
1148  * layout containers.
1149  * @constructor
1150  * @param {Roo.Element/String/Object} config The configuration options.
1151  */
1152 Roo.BoxComponent = function(config){
1153     Roo.Component.call(this, config);
1154     this.addEvents({
1155         /**
1156          * @event resize
1157          * Fires after the component is resized.
1158              * @param {Roo.Component} this
1159              * @param {Number} adjWidth The box-adjusted width that was set
1160              * @param {Number} adjHeight The box-adjusted height that was set
1161              * @param {Number} rawWidth The width that was originally specified
1162              * @param {Number} rawHeight The height that was originally specified
1163              */
1164         resize : true,
1165         /**
1166          * @event move
1167          * Fires after the component is moved.
1168              * @param {Roo.Component} this
1169              * @param {Number} x The new x position
1170              * @param {Number} y The new y position
1171              */
1172         move : true
1173     });
1174 };
1175
1176 Roo.extend(Roo.BoxComponent, Roo.Component, {
1177     // private, set in afterRender to signify that the component has been rendered
1178     boxReady : false,
1179     // private, used to defer height settings to subclasses
1180     deferHeight: false,
1181     /** @cfg {Number} width
1182      * width (optional) size of component
1183      */
1184      /** @cfg {Number} height
1185      * height (optional) size of component
1186      */
1187      
1188     /**
1189      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
1190      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
1191      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
1192      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
1193      * @return {Roo.BoxComponent} this
1194      */
1195     setSize : function(w, h){
1196         // support for standard size objects
1197         if(typeof w == 'object'){
1198             h = w.height;
1199             w = w.width;
1200         }
1201         // not rendered
1202         if(!this.boxReady){
1203             this.width = w;
1204             this.height = h;
1205             return this;
1206         }
1207
1208         // prevent recalcs when not needed
1209         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
1210             return this;
1211         }
1212         this.lastSize = {width: w, height: h};
1213
1214         var adj = this.adjustSize(w, h);
1215         var aw = adj.width, ah = adj.height;
1216         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
1217             var rz = this.getResizeEl();
1218             if(!this.deferHeight && aw !== undefined && ah !== undefined){
1219                 rz.setSize(aw, ah);
1220             }else if(!this.deferHeight && ah !== undefined){
1221                 rz.setHeight(ah);
1222             }else if(aw !== undefined){
1223                 rz.setWidth(aw);
1224             }
1225             this.onResize(aw, ah, w, h);
1226             this.fireEvent('resize', this, aw, ah, w, h);
1227         }
1228         return this;
1229     },
1230
1231     /**
1232      * Gets the current size of the component's underlying element.
1233      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
1234      */
1235     getSize : function(){
1236         return this.el.getSize();
1237     },
1238
1239     /**
1240      * Gets the current XY position of the component's underlying element.
1241      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
1242      * @return {Array} The XY position of the element (e.g., [100, 200])
1243      */
1244     getPosition : function(local){
1245         if(local === true){
1246             return [this.el.getLeft(true), this.el.getTop(true)];
1247         }
1248         return this.xy || this.el.getXY();
1249     },
1250
1251     /**
1252      * Gets the current box measurements of the component's underlying element.
1253      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
1254      * @returns {Object} box An object in the format {x, y, width, height}
1255      */
1256     getBox : function(local){
1257         var s = this.el.getSize();
1258         if(local){
1259             s.x = this.el.getLeft(true);
1260             s.y = this.el.getTop(true);
1261         }else{
1262             var xy = this.xy || this.el.getXY();
1263             s.x = xy[0];
1264             s.y = xy[1];
1265         }
1266         return s;
1267     },
1268
1269     /**
1270      * Sets the current box measurements of the component's underlying element.
1271      * @param {Object} box An object in the format {x, y, width, height}
1272      * @returns {Roo.BoxComponent} this
1273      */
1274     updateBox : function(box){
1275         this.setSize(box.width, box.height);
1276         this.setPagePosition(box.x, box.y);
1277         return this;
1278     },
1279
1280     // protected
1281     getResizeEl : function(){
1282         return this.resizeEl || this.el;
1283     },
1284
1285     // protected
1286     getPositionEl : function(){
1287         return this.positionEl || this.el;
1288     },
1289
1290     /**
1291      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
1292      * This method fires the move event.
1293      * @param {Number} left The new left
1294      * @param {Number} top The new top
1295      * @returns {Roo.BoxComponent} this
1296      */
1297     setPosition : function(x, y){
1298         this.x = x;
1299         this.y = y;
1300         if(!this.boxReady){
1301             return this;
1302         }
1303         var adj = this.adjustPosition(x, y);
1304         var ax = adj.x, ay = adj.y;
1305
1306         var el = this.getPositionEl();
1307         if(ax !== undefined || ay !== undefined){
1308             if(ax !== undefined && ay !== undefined){
1309                 el.setLeftTop(ax, ay);
1310             }else if(ax !== undefined){
1311                 el.setLeft(ax);
1312             }else if(ay !== undefined){
1313                 el.setTop(ay);
1314             }
1315             this.onPosition(ax, ay);
1316             this.fireEvent('move', this, ax, ay);
1317         }
1318         return this;
1319     },
1320
1321     /**
1322      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
1323      * This method fires the move event.
1324      * @param {Number} x The new x position
1325      * @param {Number} y The new y position
1326      * @returns {Roo.BoxComponent} this
1327      */
1328     setPagePosition : function(x, y){
1329         this.pageX = x;
1330         this.pageY = y;
1331         if(!this.boxReady){
1332             return;
1333         }
1334         if(x === undefined || y === undefined){ // cannot translate undefined points
1335             return;
1336         }
1337         var p = this.el.translatePoints(x, y);
1338         this.setPosition(p.left, p.top);
1339         return this;
1340     },
1341
1342     // private
1343     onRender : function(ct, position){
1344         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
1345         if(this.resizeEl){
1346             this.resizeEl = Roo.get(this.resizeEl);
1347         }
1348         if(this.positionEl){
1349             this.positionEl = Roo.get(this.positionEl);
1350         }
1351     },
1352
1353     // private
1354     afterRender : function(){
1355         Roo.BoxComponent.superclass.afterRender.call(this);
1356         this.boxReady = true;
1357         this.setSize(this.width, this.height);
1358         if(this.x || this.y){
1359             this.setPosition(this.x, this.y);
1360         }
1361         if(this.pageX || this.pageY){
1362             this.setPagePosition(this.pageX, this.pageY);
1363         }
1364     },
1365
1366     /**
1367      * Force the component's size to recalculate based on the underlying element's current height and width.
1368      * @returns {Roo.BoxComponent} this
1369      */
1370     syncSize : function(){
1371         delete this.lastSize;
1372         this.setSize(this.el.getWidth(), this.el.getHeight());
1373         return this;
1374     },
1375
1376     /**
1377      * Called after the component is resized, this method is empty by default but can be implemented by any
1378      * subclass that needs to perform custom logic after a resize occurs.
1379      * @param {Number} adjWidth The box-adjusted width that was set
1380      * @param {Number} adjHeight The box-adjusted height that was set
1381      * @param {Number} rawWidth The width that was originally specified
1382      * @param {Number} rawHeight The height that was originally specified
1383      */
1384     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
1385
1386     },
1387
1388     /**
1389      * Called after the component is moved, this method is empty by default but can be implemented by any
1390      * subclass that needs to perform custom logic after a move occurs.
1391      * @param {Number} x The new x position
1392      * @param {Number} y The new y position
1393      */
1394     onPosition : function(x, y){
1395
1396     },
1397
1398     // private
1399     adjustSize : function(w, h){
1400         if(this.autoWidth){
1401             w = 'auto';
1402         }
1403         if(this.autoHeight){
1404             h = 'auto';
1405         }
1406         return {width : w, height: h};
1407     },
1408
1409     // private
1410     adjustPosition : function(x, y){
1411         return {x : x, y: y};
1412     }
1413 });/*
1414  * Based on:
1415  * Ext JS Library 1.1.1
1416  * Copyright(c) 2006-2007, Ext JS, LLC.
1417  *
1418  * Originally Released Under LGPL - original licence link has changed is not relivant.
1419  *
1420  * Fork - LGPL
1421  * <script type="text/javascript">
1422  */
1423
1424
1425 /**
1426  * @class Roo.SplitBar
1427  * @extends Roo.util.Observable
1428  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
1429  * <br><br>
1430  * Usage:
1431  * <pre><code>
1432 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
1433                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
1434 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
1435 split.minSize = 100;
1436 split.maxSize = 600;
1437 split.animate = true;
1438 split.on('moved', splitterMoved);
1439 </code></pre>
1440  * @constructor
1441  * Create a new SplitBar
1442  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
1443  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
1444  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
1445  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
1446                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
1447                         position of the SplitBar).
1448  */
1449 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
1450     
1451     /** @private */
1452     this.el = Roo.get(dragElement, true);
1453     this.el.dom.unselectable = "on";
1454     /** @private */
1455     this.resizingEl = Roo.get(resizingElement, true);
1456
1457     /**
1458      * @private
1459      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
1460      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
1461      * @type Number
1462      */
1463     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
1464     
1465     /**
1466      * The minimum size of the resizing element. (Defaults to 0)
1467      * @type Number
1468      */
1469     this.minSize = 0;
1470     
1471     /**
1472      * The maximum size of the resizing element. (Defaults to 2000)
1473      * @type Number
1474      */
1475     this.maxSize = 2000;
1476     
1477     /**
1478      * Whether to animate the transition to the new size
1479      * @type Boolean
1480      */
1481     this.animate = false;
1482     
1483     /**
1484      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
1485      * @type Boolean
1486      */
1487     this.useShim = false;
1488     
1489     /** @private */
1490     this.shim = null;
1491     
1492     if(!existingProxy){
1493         /** @private */
1494         this.proxy = Roo.SplitBar.createProxy(this.orientation);
1495     }else{
1496         this.proxy = Roo.get(existingProxy).dom;
1497     }
1498     /** @private */
1499     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
1500     
1501     /** @private */
1502     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
1503     
1504     /** @private */
1505     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
1506     
1507     /** @private */
1508     this.dragSpecs = {};
1509     
1510     /**
1511      * @private The adapter to use to positon and resize elements
1512      */
1513     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
1514     this.adapter.init(this);
1515     
1516     if(this.orientation == Roo.SplitBar.HORIZONTAL){
1517         /** @private */
1518         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
1519         this.el.addClass("x-splitbar-h");
1520     }else{
1521         /** @private */
1522         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
1523         this.el.addClass("x-splitbar-v");
1524     }
1525     
1526     this.addEvents({
1527         /**
1528          * @event resize
1529          * Fires when the splitter is moved (alias for {@link #event-moved})
1530          * @param {Roo.SplitBar} this
1531          * @param {Number} newSize the new width or height
1532          */
1533         "resize" : true,
1534         /**
1535          * @event moved
1536          * Fires when the splitter is moved
1537          * @param {Roo.SplitBar} this
1538          * @param {Number} newSize the new width or height
1539          */
1540         "moved" : true,
1541         /**
1542          * @event beforeresize
1543          * Fires before the splitter is dragged
1544          * @param {Roo.SplitBar} this
1545          */
1546         "beforeresize" : true,
1547
1548         "beforeapply" : true
1549     });
1550
1551     Roo.util.Observable.call(this);
1552 };
1553
1554 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
1555     onStartProxyDrag : function(x, y){
1556         this.fireEvent("beforeresize", this);
1557         if(!this.overlay){
1558             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
1559             o.unselectable();
1560             o.enableDisplayMode("block");
1561             // all splitbars share the same overlay
1562             Roo.SplitBar.prototype.overlay = o;
1563         }
1564         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1565         this.overlay.show();
1566         Roo.get(this.proxy).setDisplayed("block");
1567         var size = this.adapter.getElementSize(this);
1568         this.activeMinSize = this.getMinimumSize();;
1569         this.activeMaxSize = this.getMaximumSize();;
1570         var c1 = size - this.activeMinSize;
1571         var c2 = Math.max(this.activeMaxSize - size, 0);
1572         if(this.orientation == Roo.SplitBar.HORIZONTAL){
1573             this.dd.resetConstraints();
1574             this.dd.setXConstraint(
1575                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
1576                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
1577             );
1578             this.dd.setYConstraint(0, 0);
1579         }else{
1580             this.dd.resetConstraints();
1581             this.dd.setXConstraint(0, 0);
1582             this.dd.setYConstraint(
1583                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
1584                 this.placement == Roo.SplitBar.TOP ? c2 : c1
1585             );
1586          }
1587         this.dragSpecs.startSize = size;
1588         this.dragSpecs.startPoint = [x, y];
1589         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
1590     },
1591     
1592     /** 
1593      * @private Called after the drag operation by the DDProxy
1594      */
1595     onEndProxyDrag : function(e){
1596         Roo.get(this.proxy).setDisplayed(false);
1597         var endPoint = Roo.lib.Event.getXY(e);
1598         if(this.overlay){
1599             this.overlay.hide();
1600         }
1601         var newSize;
1602         if(this.orientation == Roo.SplitBar.HORIZONTAL){
1603             newSize = this.dragSpecs.startSize + 
1604                 (this.placement == Roo.SplitBar.LEFT ?
1605                     endPoint[0] - this.dragSpecs.startPoint[0] :
1606                     this.dragSpecs.startPoint[0] - endPoint[0]
1607                 );
1608         }else{
1609             newSize = this.dragSpecs.startSize + 
1610                 (this.placement == Roo.SplitBar.TOP ?
1611                     endPoint[1] - this.dragSpecs.startPoint[1] :
1612                     this.dragSpecs.startPoint[1] - endPoint[1]
1613                 );
1614         }
1615         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
1616         if(newSize != this.dragSpecs.startSize){
1617             if(this.fireEvent('beforeapply', this, newSize) !== false){
1618                 this.adapter.setElementSize(this, newSize);
1619                 this.fireEvent("moved", this, newSize);
1620                 this.fireEvent("resize", this, newSize);
1621             }
1622         }
1623     },
1624     
1625     /**
1626      * Get the adapter this SplitBar uses
1627      * @return The adapter object
1628      */
1629     getAdapter : function(){
1630         return this.adapter;
1631     },
1632     
1633     /**
1634      * Set the adapter this SplitBar uses
1635      * @param {Object} adapter A SplitBar adapter object
1636      */
1637     setAdapter : function(adapter){
1638         this.adapter = adapter;
1639         this.adapter.init(this);
1640     },
1641     
1642     /**
1643      * Gets the minimum size for the resizing element
1644      * @return {Number} The minimum size
1645      */
1646     getMinimumSize : function(){
1647         return this.minSize;
1648     },
1649     
1650     /**
1651      * Sets the minimum size for the resizing element
1652      * @param {Number} minSize The minimum size
1653      */
1654     setMinimumSize : function(minSize){
1655         this.minSize = minSize;
1656     },
1657     
1658     /**
1659      * Gets the maximum size for the resizing element
1660      * @return {Number} The maximum size
1661      */
1662     getMaximumSize : function(){
1663         return this.maxSize;
1664     },
1665     
1666     /**
1667      * Sets the maximum size for the resizing element
1668      * @param {Number} maxSize The maximum size
1669      */
1670     setMaximumSize : function(maxSize){
1671         this.maxSize = maxSize;
1672     },
1673     
1674     /**
1675      * Sets the initialize size for the resizing element
1676      * @param {Number} size The initial size
1677      */
1678     setCurrentSize : function(size){
1679         var oldAnimate = this.animate;
1680         this.animate = false;
1681         this.adapter.setElementSize(this, size);
1682         this.animate = oldAnimate;
1683     },
1684     
1685     /**
1686      * Destroy this splitbar. 
1687      * @param {Boolean} removeEl True to remove the element
1688      */
1689     destroy : function(removeEl){
1690         if(this.shim){
1691             this.shim.remove();
1692         }
1693         this.dd.unreg();
1694         this.proxy.parentNode.removeChild(this.proxy);
1695         if(removeEl){
1696             this.el.remove();
1697         }
1698     }
1699 });
1700
1701 /**
1702  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
1703  */
1704 Roo.SplitBar.createProxy = function(dir){
1705     var proxy = new Roo.Element(document.createElement("div"));
1706     proxy.unselectable();
1707     var cls = 'x-splitbar-proxy';
1708     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
1709     document.body.appendChild(proxy.dom);
1710     return proxy.dom;
1711 };
1712
1713 /** 
1714  * @class Roo.SplitBar.BasicLayoutAdapter
1715  * Default Adapter. It assumes the splitter and resizing element are not positioned
1716  * elements and only gets/sets the width of the element. Generally used for table based layouts.
1717  */
1718 Roo.SplitBar.BasicLayoutAdapter = function(){
1719 };
1720
1721 Roo.SplitBar.BasicLayoutAdapter.prototype = {
1722     // do nothing for now
1723     init : function(s){
1724     
1725     },
1726     /**
1727      * Called before drag operations to get the current size of the resizing element. 
1728      * @param {Roo.SplitBar} s The SplitBar using this adapter
1729      */
1730      getElementSize : function(s){
1731         if(s.orientation == Roo.SplitBar.HORIZONTAL){
1732             return s.resizingEl.getWidth();
1733         }else{
1734             return s.resizingEl.getHeight();
1735         }
1736     },
1737     
1738     /**
1739      * Called after drag operations to set the size of the resizing element.
1740      * @param {Roo.SplitBar} s The SplitBar using this adapter
1741      * @param {Number} newSize The new size to set
1742      * @param {Function} onComplete A function to be invoked when resizing is complete
1743      */
1744     setElementSize : function(s, newSize, onComplete){
1745         if(s.orientation == Roo.SplitBar.HORIZONTAL){
1746             if(!s.animate){
1747                 s.resizingEl.setWidth(newSize);
1748                 if(onComplete){
1749                     onComplete(s, newSize);
1750                 }
1751             }else{
1752                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
1753             }
1754         }else{
1755             
1756             if(!s.animate){
1757                 s.resizingEl.setHeight(newSize);
1758                 if(onComplete){
1759                     onComplete(s, newSize);
1760                 }
1761             }else{
1762                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
1763             }
1764         }
1765     }
1766 };
1767
1768 /** 
1769  *@class Roo.SplitBar.AbsoluteLayoutAdapter
1770  * @extends Roo.SplitBar.BasicLayoutAdapter
1771  * Adapter that  moves the splitter element to align with the resized sizing element. 
1772  * Used with an absolute positioned SplitBar.
1773  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
1774  * document.body, make sure you assign an id to the body element.
1775  */
1776 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
1777     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
1778     this.container = Roo.get(container);
1779 };
1780
1781 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
1782     init : function(s){
1783         this.basic.init(s);
1784     },
1785     
1786     getElementSize : function(s){
1787         return this.basic.getElementSize(s);
1788     },
1789     
1790     setElementSize : function(s, newSize, onComplete){
1791         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
1792     },
1793     
1794     moveSplitter : function(s){
1795         var yes = Roo.SplitBar;
1796         switch(s.placement){
1797             case yes.LEFT:
1798                 s.el.setX(s.resizingEl.getRight());
1799                 break;
1800             case yes.RIGHT:
1801                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
1802                 break;
1803             case yes.TOP:
1804                 s.el.setY(s.resizingEl.getBottom());
1805                 break;
1806             case yes.BOTTOM:
1807                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
1808                 break;
1809         }
1810     }
1811 };
1812
1813 /**
1814  * Orientation constant - Create a vertical SplitBar
1815  * @static
1816  * @type Number
1817  */
1818 Roo.SplitBar.VERTICAL = 1;
1819
1820 /**
1821  * Orientation constant - Create a horizontal SplitBar
1822  * @static
1823  * @type Number
1824  */
1825 Roo.SplitBar.HORIZONTAL = 2;
1826
1827 /**
1828  * Placement constant - The resizing element is to the left of the splitter element
1829  * @static
1830  * @type Number
1831  */
1832 Roo.SplitBar.LEFT = 1;
1833
1834 /**
1835  * Placement constant - The resizing element is to the right of the splitter element
1836  * @static
1837  * @type Number
1838  */
1839 Roo.SplitBar.RIGHT = 2;
1840
1841 /**
1842  * Placement constant - The resizing element is positioned above the splitter element
1843  * @static
1844  * @type Number
1845  */
1846 Roo.SplitBar.TOP = 3;
1847
1848 /**
1849  * Placement constant - The resizing element is positioned under splitter element
1850  * @static
1851  * @type Number
1852  */
1853 Roo.SplitBar.BOTTOM = 4;
1854 /*
1855  * Based on:
1856  * Ext JS Library 1.1.1
1857  * Copyright(c) 2006-2007, Ext JS, LLC.
1858  *
1859  * Originally Released Under LGPL - original licence link has changed is not relivant.
1860  *
1861  * Fork - LGPL
1862  * <script type="text/javascript">
1863  */
1864
1865 /**
1866  * @class Roo.View
1867  * @extends Roo.util.Observable
1868  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
1869  * This class also supports single and multi selection modes. <br>
1870  * Create a data model bound view:
1871  <pre><code>
1872  var store = new Roo.data.Store(...);
1873
1874  var view = new Roo.View("my-element",
1875  '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
1876  {
1877  singleSelect: true,
1878  selectedClass: "ydataview-selected",
1879  store: store
1880  });
1881
1882  // listen for node click?
1883  view.on("click", function(vw, index, node, e){
1884  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
1885  });
1886
1887  // load XML data
1888  dataModel.load("foobar.xml");
1889  </code></pre>
1890  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
1891  * <br><br>
1892  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
1893  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
1894  * @constructor
1895  * Create a new View
1896  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
1897  * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
1898  * @param {Object} config The config object
1899  */
1900 Roo.View = function(container, tpl, config){
1901     this.el = Roo.get(container);
1902     if(typeof tpl == "string"){
1903         tpl = new Roo.Template(tpl);
1904     }
1905     tpl.compile();
1906     /**
1907      * The template used by this View
1908      * @type {Roo.DomHelper.Template}
1909      */
1910     this.tpl = tpl;
1911
1912     Roo.apply(this, config);
1913
1914     /** @private */
1915     this.addEvents({
1916     /**
1917      * @event beforeclick
1918      * Fires before a click is processed. Returns false to cancel the default action.
1919      * @param {Roo.View} this
1920      * @param {Number} index The index of the target node
1921      * @param {HTMLElement} node The target node
1922      * @param {Roo.EventObject} e The raw event object
1923      */
1924         "beforeclick" : true,
1925     /**
1926      * @event click
1927      * Fires when a template node is clicked.
1928      * @param {Roo.View} this
1929      * @param {Number} index The index of the target node
1930      * @param {HTMLElement} node The target node
1931      * @param {Roo.EventObject} e The raw event object
1932      */
1933         "click" : true,
1934     /**
1935      * @event dblclick
1936      * Fires when a template node is double clicked.
1937      * @param {Roo.View} this
1938      * @param {Number} index The index of the target node
1939      * @param {HTMLElement} node The target node
1940      * @param {Roo.EventObject} e The raw event object
1941      */
1942         "dblclick" : true,
1943     /**
1944      * @event contextmenu
1945      * Fires when a template node is right clicked.
1946      * @param {Roo.View} this
1947      * @param {Number} index The index of the target node
1948      * @param {HTMLElement} node The target node
1949      * @param {Roo.EventObject} e The raw event object
1950      */
1951         "contextmenu" : true,
1952     /**
1953      * @event selectionchange
1954      * Fires when the selected nodes change.
1955      * @param {Roo.View} this
1956      * @param {Array} selections Array of the selected nodes
1957      */
1958         "selectionchange" : true,
1959
1960     /**
1961      * @event beforeselect
1962      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
1963      * @param {Roo.View} this
1964      * @param {HTMLElement} node The node to be selected
1965      * @param {Array} selections Array of currently selected nodes
1966      */
1967         "beforeselect" : true
1968     });
1969
1970     this.el.on({
1971         "click": this.onClick,
1972         "dblclick": this.onDblClick,
1973         "contextmenu": this.onContextMenu,
1974         scope:this
1975     });
1976
1977     this.selections = [];
1978     this.nodes = [];
1979     this.cmp = new Roo.CompositeElementLite([]);
1980     if(this.store){
1981         this.store = Roo.factory(this.store, Roo.data);
1982         this.setStore(this.store, true);
1983     }
1984     Roo.View.superclass.constructor.call(this);
1985 };
1986
1987 Roo.extend(Roo.View, Roo.util.Observable, {
1988     /**
1989      * The css class to add to selected nodes
1990      * @type {Roo.DomHelper.Template}
1991      */
1992     selectedClass : "x-view-selected",
1993     
1994     emptyText : "",
1995     /**
1996      * Returns the element this view is bound to.
1997      * @return {Roo.Element}
1998      */
1999     getEl : function(){
2000         return this.el;
2001     },
2002
2003     /**
2004      * Refreshes the view.
2005      */
2006     refresh : function(){
2007         var t = this.tpl;
2008         this.clearSelections();
2009         this.el.update("");
2010         var html = [];
2011         var records = this.store.getRange();
2012         if(records.length < 1){
2013             this.el.update(this.emptyText);
2014             return;
2015         }
2016         for(var i = 0, len = records.length; i < len; i++){
2017             var data = this.prepareData(records[i].data, i, records[i]);
2018             html[html.length] = t.apply(data);
2019         }
2020         this.el.update(html.join(""));
2021         this.nodes = this.el.dom.childNodes;
2022         this.updateIndexes(0);
2023     },
2024
2025     /**
2026      * Function to override to reformat the data that is sent to
2027      * the template for each node.
2028      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
2029      * a JSON object for an UpdateManager bound view).
2030      */
2031     prepareData : function(data){
2032         return data;
2033     },
2034
2035     onUpdate : function(ds, record){
2036         this.clearSelections();
2037         var index = this.store.indexOf(record);
2038         var n = this.nodes[index];
2039         this.tpl.insertBefore(n, this.prepareData(record.data));
2040         n.parentNode.removeChild(n);
2041         this.updateIndexes(index, index);
2042     },
2043
2044     onAdd : function(ds, records, index){
2045         this.clearSelections();
2046         if(this.nodes.length == 0){
2047             this.refresh();
2048             return;
2049         }
2050         var n = this.nodes[index];
2051         for(var i = 0, len = records.length; i < len; i++){
2052             var d = this.prepareData(records[i].data);
2053             if(n){
2054                 this.tpl.insertBefore(n, d);
2055             }else{
2056                 this.tpl.append(this.el, d);
2057             }
2058         }
2059         this.updateIndexes(index);
2060     },
2061
2062     onRemove : function(ds, record, index){
2063         this.clearSelections();
2064         this.el.dom.removeChild(this.nodes[index]);
2065         this.updateIndexes(index);
2066     },
2067
2068     /**
2069      * Refresh an individual node.
2070      * @param {Number} index
2071      */
2072     refreshNode : function(index){
2073         this.onUpdate(this.store, this.store.getAt(index));
2074     },
2075
2076     updateIndexes : function(startIndex, endIndex){
2077         var ns = this.nodes;
2078         startIndex = startIndex || 0;
2079         endIndex = endIndex || ns.length - 1;
2080         for(var i = startIndex; i <= endIndex; i++){
2081             ns[i].nodeIndex = i;
2082         }
2083     },
2084
2085     /**
2086      * Changes the data store this view uses and refresh the view.
2087      * @param {Store} store
2088      */
2089     setStore : function(store, initial){
2090         if(!initial && this.store){
2091             this.store.un("datachanged", this.refresh);
2092             this.store.un("add", this.onAdd);
2093             this.store.un("remove", this.onRemove);
2094             this.store.un("update", this.onUpdate);
2095             this.store.un("clear", this.refresh);
2096         }
2097         if(store){
2098           
2099             store.on("datachanged", this.refresh, this);
2100             store.on("add", this.onAdd, this);
2101             store.on("remove", this.onRemove, this);
2102             store.on("update", this.onUpdate, this);
2103             store.on("clear", this.refresh, this);
2104         }
2105         
2106         if(store){
2107             this.refresh();
2108         }
2109     },
2110
2111     /**
2112      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
2113      * @param {HTMLElement} node
2114      * @return {HTMLElement} The template node
2115      */
2116     findItemFromChild : function(node){
2117         var el = this.el.dom;
2118         if(!node || node.parentNode == el){
2119                     return node;
2120             }
2121             var p = node.parentNode;
2122             while(p && p != el){
2123             if(p.parentNode == el){
2124                 return p;
2125             }
2126             p = p.parentNode;
2127         }
2128             return null;
2129     },
2130
2131     /** @ignore */
2132     onClick : function(e){
2133         var item = this.findItemFromChild(e.getTarget());
2134         if(item){
2135             var index = this.indexOf(item);
2136             if(this.onItemClick(item, index, e) !== false){
2137                 this.fireEvent("click", this, index, item, e);
2138             }
2139         }else{
2140             this.clearSelections();
2141         }
2142     },
2143
2144     /** @ignore */
2145     onContextMenu : function(e){
2146         var item = this.findItemFromChild(e.getTarget());
2147         if(item){
2148             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
2149         }
2150     },
2151
2152     /** @ignore */
2153     onDblClick : function(e){
2154         var item = this.findItemFromChild(e.getTarget());
2155         if(item){
2156             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
2157         }
2158     },
2159
2160     onItemClick : function(item, index, e){
2161         if(this.fireEvent("beforeclick", this, index, item, e) === false){
2162             return false;
2163         }
2164         if(this.multiSelect || this.singleSelect){
2165             if(this.multiSelect && e.shiftKey && this.lastSelection){
2166                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
2167             }else{
2168                 this.select(item, this.multiSelect && e.ctrlKey);
2169                 this.lastSelection = item;
2170             }
2171             e.preventDefault();
2172         }
2173         return true;
2174     },
2175
2176     /**
2177      * Get the number of selected nodes.
2178      * @return {Number}
2179      */
2180     getSelectionCount : function(){
2181         return this.selections.length;
2182     },
2183
2184     /**
2185      * Get the currently selected nodes.
2186      * @return {Array} An array of HTMLElements
2187      */
2188     getSelectedNodes : function(){
2189         return this.selections;
2190     },
2191
2192     /**
2193      * Get the indexes of the selected nodes.
2194      * @return {Array}
2195      */
2196     getSelectedIndexes : function(){
2197         var indexes = [], s = this.selections;
2198         for(var i = 0, len = s.length; i < len; i++){
2199             indexes.push(s[i].nodeIndex);
2200         }
2201         return indexes;
2202     },
2203
2204     /**
2205      * Clear all selections
2206      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
2207      */
2208     clearSelections : function(suppressEvent){
2209         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
2210             this.cmp.elements = this.selections;
2211             this.cmp.removeClass(this.selectedClass);
2212             this.selections = [];
2213             if(!suppressEvent){
2214                 this.fireEvent("selectionchange", this, this.selections);
2215             }
2216         }
2217     },
2218
2219     /**
2220      * Returns true if the passed node is selected
2221      * @param {HTMLElement/Number} node The node or node index
2222      * @return {Boolean}
2223      */
2224     isSelected : function(node){
2225         var s = this.selections;
2226         if(s.length < 1){
2227             return false;
2228         }
2229         node = this.getNode(node);
2230         return s.indexOf(node) !== -1;
2231     },
2232
2233     /**
2234      * Selects nodes.
2235      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
2236      * @param {Boolean} keepExisting (optional) true to keep existing selections
2237      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
2238      */
2239     select : function(nodeInfo, keepExisting, suppressEvent){
2240         if(nodeInfo instanceof Array){
2241             if(!keepExisting){
2242                 this.clearSelections(true);
2243             }
2244             for(var i = 0, len = nodeInfo.length; i < len; i++){
2245                 this.select(nodeInfo[i], true, true);
2246             }
2247         } else{
2248             var node = this.getNode(nodeInfo);
2249             if(node && !this.isSelected(node)){
2250                 if(!keepExisting){
2251                     this.clearSelections(true);
2252                 }
2253                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
2254                     Roo.fly(node).addClass(this.selectedClass);
2255                     this.selections.push(node);
2256                     if(!suppressEvent){
2257                         this.fireEvent("selectionchange", this, this.selections);
2258                     }
2259                 }
2260             }
2261         }
2262     },
2263
2264     /**
2265      * Gets a template node.
2266      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
2267      * @return {HTMLElement} The node or null if it wasn't found
2268      */
2269     getNode : function(nodeInfo){
2270         if(typeof nodeInfo == "string"){
2271             return document.getElementById(nodeInfo);
2272         }else if(typeof nodeInfo == "number"){
2273             return this.nodes[nodeInfo];
2274         }
2275         return nodeInfo;
2276     },
2277
2278     /**
2279      * Gets a range template nodes.
2280      * @param {Number} startIndex
2281      * @param {Number} endIndex
2282      * @return {Array} An array of nodes
2283      */
2284     getNodes : function(start, end){
2285         var ns = this.nodes;
2286         start = start || 0;
2287         end = typeof end == "undefined" ? ns.length - 1 : end;
2288         var nodes = [];
2289         if(start <= end){
2290             for(var i = start; i <= end; i++){
2291                 nodes.push(ns[i]);
2292             }
2293         } else{
2294             for(var i = start; i >= end; i--){
2295                 nodes.push(ns[i]);
2296             }
2297         }
2298         return nodes;
2299     },
2300
2301     /**
2302      * Finds the index of the passed node
2303      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
2304      * @return {Number} The index of the node or -1
2305      */
2306     indexOf : function(node){
2307         node = this.getNode(node);
2308         if(typeof node.nodeIndex == "number"){
2309             return node.nodeIndex;
2310         }
2311         var ns = this.nodes;
2312         for(var i = 0, len = ns.length; i < len; i++){
2313             if(ns[i] == node){
2314                 return i;
2315             }
2316         }
2317         return -1;
2318     }
2319 });
2320 /*
2321  * Based on:
2322  * Ext JS Library 1.1.1
2323  * Copyright(c) 2006-2007, Ext JS, LLC.
2324  *
2325  * Originally Released Under LGPL - original licence link has changed is not relivant.
2326  *
2327  * Fork - LGPL
2328  * <script type="text/javascript">
2329  */
2330
2331 /**
2332  * @class Roo.JsonView
2333  * @extends Roo.View
2334  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
2335 <pre><code>
2336 var view = new Roo.JsonView("my-element",
2337     '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
2338     { multiSelect: true, jsonRoot: "data" }
2339 );
2340
2341 // listen for node click?
2342 view.on("click", function(vw, index, node, e){
2343     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
2344 });
2345
2346 // direct load of JSON data
2347 view.load("foobar.php");
2348
2349 // Example from my blog list
2350 var tpl = new Roo.Template(
2351     '&lt;div class="entry"&gt;' +
2352     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
2353     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
2354     "&lt;/div&gt;&lt;hr /&gt;"
2355 );
2356
2357 var moreView = new Roo.JsonView("entry-list", tpl, {
2358     jsonRoot: "posts"
2359 });
2360 moreView.on("beforerender", this.sortEntries, this);
2361 moreView.load({
2362     url: "/blog/get-posts.php",
2363     params: "allposts=true",
2364     text: "Loading Blog Entries..."
2365 });
2366 </code></pre>
2367  * @constructor
2368  * Create a new JsonView
2369  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
2370  * @param {Template} tpl The rendering template
2371  * @param {Object} config The config object
2372  */
2373 Roo.JsonView = function(container, tpl, config){
2374     Roo.JsonView.superclass.constructor.call(this, container, tpl, config);
2375
2376     var um = this.el.getUpdateManager();
2377     um.setRenderer(this);
2378     um.on("update", this.onLoad, this);
2379     um.on("failure", this.onLoadException, this);
2380
2381     /**
2382      * @event beforerender
2383      * Fires before rendering of the downloaded JSON data.
2384      * @param {Roo.JsonView} this
2385      * @param {Object} data The JSON data loaded
2386      */
2387     /**
2388      * @event load
2389      * Fires when data is loaded.
2390      * @param {Roo.JsonView} this
2391      * @param {Object} data The JSON data loaded
2392      * @param {Object} response The raw Connect response object
2393      */
2394     /**
2395      * @event loadexception
2396      * Fires when loading fails.
2397      * @param {Roo.JsonView} this
2398      * @param {Object} response The raw Connect response object
2399      */
2400     this.addEvents({
2401         'beforerender' : true,
2402         'load' : true,
2403         'loadexception' : true
2404     });
2405 };
2406 Roo.extend(Roo.JsonView, Roo.View, {
2407     /**
2408      * The root property in the loaded JSON object that contains the data
2409      * @type {String}
2410      */
2411     jsonRoot : "",
2412
2413     /**
2414      * Refreshes the view.
2415      */
2416     refresh : function(){
2417         this.clearSelections();
2418         this.el.update("");
2419         var html = [];
2420         var o = this.jsonData;
2421         if(o && o.length > 0){
2422             for(var i = 0, len = o.length; i < len; i++){
2423                 var data = this.prepareData(o[i], i, o);
2424                 html[html.length] = this.tpl.apply(data);
2425             }
2426         }else{
2427             html.push(this.emptyText);
2428         }
2429         this.el.update(html.join(""));
2430         this.nodes = this.el.dom.childNodes;
2431         this.updateIndexes(0);
2432     },
2433
2434     /**
2435      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
2436      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
2437      <pre><code>
2438      view.load({
2439          url: "your-url.php",
2440          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
2441          callback: yourFunction,
2442          scope: yourObject, //(optional scope)
2443          discardUrl: false,
2444          nocache: false,
2445          text: "Loading...",
2446          timeout: 30,
2447          scripts: false
2448      });
2449      </code></pre>
2450      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
2451      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
2452      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
2453      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
2454      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
2455      */
2456     load : function(){
2457         var um = this.el.getUpdateManager();
2458         um.update.apply(um, arguments);
2459     },
2460
2461     render : function(el, response){
2462         this.clearSelections();
2463         this.el.update("");
2464         var o;
2465         try{
2466             o = Roo.util.JSON.decode(response.responseText);
2467             if(this.jsonRoot){
2468                 
2469                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
2470             }
2471         } catch(e){
2472         }
2473         /**
2474          * The current JSON data or null
2475          */
2476         this.jsonData = o;
2477         this.beforeRender();
2478         this.refresh();
2479     },
2480
2481 /**
2482  * Get the number of records in the current JSON dataset
2483  * @return {Number}
2484  */
2485     getCount : function(){
2486         return this.jsonData ? this.jsonData.length : 0;
2487     },
2488
2489 /**
2490  * Returns the JSON object for the specified node(s)
2491  * @param {HTMLElement/Array} node The node or an array of nodes
2492  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
2493  * you get the JSON object for the node
2494  */
2495     getNodeData : function(node){
2496         if(node instanceof Array){
2497             var data = [];
2498             for(var i = 0, len = node.length; i < len; i++){
2499                 data.push(this.getNodeData(node[i]));
2500             }
2501             return data;
2502         }
2503         return this.jsonData[this.indexOf(node)] || null;
2504     },
2505
2506     beforeRender : function(){
2507         this.snapshot = this.jsonData;
2508         if(this.sortInfo){
2509             this.sort.apply(this, this.sortInfo);
2510         }
2511         this.fireEvent("beforerender", this, this.jsonData);
2512     },
2513
2514     onLoad : function(el, o){
2515         this.fireEvent("load", this, this.jsonData, o);
2516     },
2517
2518     onLoadException : function(el, o){
2519         this.fireEvent("loadexception", this, o);
2520     },
2521
2522 /**
2523  * Filter the data by a specific property.
2524  * @param {String} property A property on your JSON objects
2525  * @param {String/RegExp} value Either string that the property values
2526  * should start with, or a RegExp to test against the property
2527  */
2528     filter : function(property, value){
2529         if(this.jsonData){
2530             var data = [];
2531             var ss = this.snapshot;
2532             if(typeof value == "string"){
2533                 var vlen = value.length;
2534                 if(vlen == 0){
2535                     this.clearFilter();
2536                     return;
2537                 }
2538                 value = value.toLowerCase();
2539                 for(var i = 0, len = ss.length; i < len; i++){
2540                     var o = ss[i];
2541                     if(o[property].substr(0, vlen).toLowerCase() == value){
2542                         data.push(o);
2543                     }
2544                 }
2545             } else if(value.exec){ // regex?
2546                 for(var i = 0, len = ss.length; i < len; i++){
2547                     var o = ss[i];
2548                     if(value.test(o[property])){
2549                         data.push(o);
2550                     }
2551                 }
2552             } else{
2553                 return;
2554             }
2555             this.jsonData = data;
2556             this.refresh();
2557         }
2558     },
2559
2560 /**
2561  * Filter by a function. The passed function will be called with each
2562  * object in the current dataset. If the function returns true the value is kept,
2563  * otherwise it is filtered.
2564  * @param {Function} fn
2565  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
2566  */
2567     filterBy : function(fn, scope){
2568         if(this.jsonData){
2569             var data = [];
2570             var ss = this.snapshot;
2571             for(var i = 0, len = ss.length; i < len; i++){
2572                 var o = ss[i];
2573                 if(fn.call(scope || this, o)){
2574                     data.push(o);
2575                 }
2576             }
2577             this.jsonData = data;
2578             this.refresh();
2579         }
2580     },
2581
2582 /**
2583  * Clears the current filter.
2584  */
2585     clearFilter : function(){
2586         if(this.snapshot && this.jsonData != this.snapshot){
2587             this.jsonData = this.snapshot;
2588             this.refresh();
2589         }
2590     },
2591
2592
2593 /**
2594  * Sorts the data for this view and refreshes it.
2595  * @param {String} property A property on your JSON objects to sort on
2596  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
2597  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
2598  */
2599     sort : function(property, dir, sortType){
2600         this.sortInfo = Array.prototype.slice.call(arguments, 0);
2601         if(this.jsonData){
2602             var p = property;
2603             var dsc = dir && dir.toLowerCase() == "desc";
2604             var f = function(o1, o2){
2605                 var v1 = sortType ? sortType(o1[p]) : o1[p];
2606                 var v2 = sortType ? sortType(o2[p]) : o2[p];
2607                 ;
2608                 if(v1 < v2){
2609                     return dsc ? +1 : -1;
2610                 } else if(v1 > v2){
2611                     return dsc ? -1 : +1;
2612                 } else{
2613                     return 0;
2614                 }
2615             };
2616             this.jsonData.sort(f);
2617             this.refresh();
2618             if(this.jsonData != this.snapshot){
2619                 this.snapshot.sort(f);
2620             }
2621         }
2622     }
2623 });/*
2624  * Based on:
2625  * Ext JS Library 1.1.1
2626  * Copyright(c) 2006-2007, Ext JS, LLC.
2627  *
2628  * Originally Released Under LGPL - original licence link has changed is not relivant.
2629  *
2630  * Fork - LGPL
2631  * <script type="text/javascript">
2632  */
2633  
2634
2635 /**
2636  * @class Roo.ColorPalette
2637  * @extends Roo.Component
2638  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
2639  * Here's an example of typical usage:
2640  * <pre><code>
2641 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
2642 cp.render('my-div');
2643
2644 cp.on('select', function(palette, selColor){
2645     // do something with selColor
2646 });
2647 </code></pre>
2648  * @constructor
2649  * Create a new ColorPalette
2650  * @param {Object} config The config object
2651  */
2652 Roo.ColorPalette = function(config){
2653     Roo.ColorPalette.superclass.constructor.call(this, config);
2654     this.addEvents({
2655         /**
2656              * @event select
2657              * Fires when a color is selected
2658              * @param {ColorPalette} this
2659              * @param {String} color The 6-digit color hex code (without the # symbol)
2660              */
2661         select: true
2662     });
2663
2664     if(this.handler){
2665         this.on("select", this.handler, this.scope, true);
2666     }
2667 };
2668 Roo.extend(Roo.ColorPalette, Roo.Component, {
2669     /**
2670      * @cfg {String} itemCls
2671      * The CSS class to apply to the containing element (defaults to "x-color-palette")
2672      */
2673     itemCls : "x-color-palette",
2674     /**
2675      * @cfg {String} value
2676      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
2677      * the hex codes are case-sensitive.
2678      */
2679     value : null,
2680     clickEvent:'click',
2681     // private
2682     ctype: "Roo.ColorPalette",
2683
2684     /**
2685      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
2686      */
2687     allowReselect : false,
2688
2689     /**
2690      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
2691      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
2692      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
2693      * of colors with the width setting until the box is symmetrical.</p>
2694      * <p>You can override individual colors if needed:</p>
2695      * <pre><code>
2696 var cp = new Roo.ColorPalette();
2697 cp.colors[0] = "FF0000";  // change the first box to red
2698 </code></pre>
2699
2700 Or you can provide a custom array of your own for complete control:
2701 <pre><code>
2702 var cp = new Roo.ColorPalette();
2703 cp.colors = ["000000", "993300", "333300"];
2704 </code></pre>
2705      * @type Array
2706      */
2707     colors : [
2708         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
2709         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
2710         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
2711         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
2712         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
2713     ],
2714
2715     // private
2716     onRender : function(container, position){
2717         var t = new Roo.MasterTemplate(
2718             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
2719         );
2720         var c = this.colors;
2721         for(var i = 0, len = c.length; i < len; i++){
2722             t.add([c[i]]);
2723         }
2724         var el = document.createElement("div");
2725         el.className = this.itemCls;
2726         t.overwrite(el);
2727         container.dom.insertBefore(el, position);
2728         this.el = Roo.get(el);
2729         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
2730         if(this.clickEvent != 'click'){
2731             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
2732         }
2733     },
2734
2735     // private
2736     afterRender : function(){
2737         Roo.ColorPalette.superclass.afterRender.call(this);
2738         if(this.value){
2739             var s = this.value;
2740             this.value = null;
2741             this.select(s);
2742         }
2743     },
2744
2745     // private
2746     handleClick : function(e, t){
2747         e.preventDefault();
2748         if(!this.disabled){
2749             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
2750             this.select(c.toUpperCase());
2751         }
2752     },
2753
2754     /**
2755      * Selects the specified color in the palette (fires the select event)
2756      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
2757      */
2758     select : function(color){
2759         color = color.replace("#", "");
2760         if(color != this.value || this.allowReselect){
2761             var el = this.el;
2762             if(this.value){
2763                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
2764             }
2765             el.child("a.color-"+color).addClass("x-color-palette-sel");
2766             this.value = color;
2767             this.fireEvent("select", this, color);
2768         }
2769     }
2770 });/*
2771  * Based on:
2772  * Ext JS Library 1.1.1
2773  * Copyright(c) 2006-2007, Ext JS, LLC.
2774  *
2775  * Originally Released Under LGPL - original licence link has changed is not relivant.
2776  *
2777  * Fork - LGPL
2778  * <script type="text/javascript">
2779  */
2780  
2781 /**
2782  * @class Roo.DatePicker
2783  * @extends Roo.Component
2784  * Simple date picker class.
2785  * @constructor
2786  * Create a new DatePicker
2787  * @param {Object} config The config object
2788  */
2789 Roo.DatePicker = function(config){
2790     Roo.DatePicker.superclass.constructor.call(this, config);
2791
2792     this.value = config && config.value ?
2793                  config.value.clearTime() : new Date().clearTime();
2794
2795     this.addEvents({
2796         /**
2797              * @event select
2798              * Fires when a date is selected
2799              * @param {DatePicker} this
2800              * @param {Date} date The selected date
2801              */
2802         select: true
2803     });
2804
2805     if(this.handler){
2806         this.on("select", this.handler,  this.scope || this);
2807     }
2808     // build the disabledDatesRE
2809     if(!this.disabledDatesRE && this.disabledDates){
2810         var dd = this.disabledDates;
2811         var re = "(?:";
2812         for(var i = 0; i < dd.length; i++){
2813             re += dd[i];
2814             if(i != dd.length-1) re += "|";
2815         }
2816         this.disabledDatesRE = new RegExp(re + ")");
2817     }
2818 };
2819
2820 Roo.extend(Roo.DatePicker, Roo.Component, {
2821     /**
2822      * @cfg {String} todayText
2823      * The text to display on the button that selects the current date (defaults to "Today")
2824      */
2825     todayText : "Today",
2826     /**
2827      * @cfg {String} okText
2828      * The text to display on the ok button
2829      */
2830     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
2831     /**
2832      * @cfg {String} cancelText
2833      * The text to display on the cancel button
2834      */
2835     cancelText : "Cancel",
2836     /**
2837      * @cfg {String} todayTip
2838      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
2839      */
2840     todayTip : "{0} (Spacebar)",
2841     /**
2842      * @cfg {Date} minDate
2843      * Minimum allowable date (JavaScript date object, defaults to null)
2844      */
2845     minDate : null,
2846     /**
2847      * @cfg {Date} maxDate
2848      * Maximum allowable date (JavaScript date object, defaults to null)
2849      */
2850     maxDate : null,
2851     /**
2852      * @cfg {String} minText
2853      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
2854      */
2855     minText : "This date is before the minimum date",
2856     /**
2857      * @cfg {String} maxText
2858      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
2859      */
2860     maxText : "This date is after the maximum date",
2861     /**
2862      * @cfg {String} format
2863      * The default date format string which can be overriden for localization support.  The format must be
2864      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
2865      */
2866     format : "m/d/y",
2867     /**
2868      * @cfg {Array} disabledDays
2869      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
2870      */
2871     disabledDays : null,
2872     /**
2873      * @cfg {String} disabledDaysText
2874      * The tooltip to display when the date falls on a disabled day (defaults to "")
2875      */
2876     disabledDaysText : "",
2877     /**
2878      * @cfg {RegExp} disabledDatesRE
2879      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
2880      */
2881     disabledDatesRE : null,
2882     /**
2883      * @cfg {String} disabledDatesText
2884      * The tooltip text to display when the date falls on a disabled date (defaults to "")
2885      */
2886     disabledDatesText : "",
2887     /**
2888      * @cfg {Boolean} constrainToViewport
2889      * True to constrain the date picker to the viewport (defaults to true)
2890      */
2891     constrainToViewport : true,
2892     /**
2893      * @cfg {Array} monthNames
2894      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
2895      */
2896     monthNames : Date.monthNames,
2897     /**
2898      * @cfg {Array} dayNames
2899      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
2900      */
2901     dayNames : Date.dayNames,
2902     /**
2903      * @cfg {String} nextText
2904      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
2905      */
2906     nextText: 'Next Month (Control+Right)',
2907     /**
2908      * @cfg {String} prevText
2909      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
2910      */
2911     prevText: 'Previous Month (Control+Left)',
2912     /**
2913      * @cfg {String} monthYearText
2914      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
2915      */
2916     monthYearText: 'Choose a month (Control+Up/Down to move years)',
2917     /**
2918      * @cfg {Number} startDay
2919      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
2920      */
2921     startDay : 0,
2922     /**
2923      * @cfg {Bool} showClear
2924      * Show a clear button (usefull for date form elements that can be blank.)
2925      */
2926     
2927     showClear: false,
2928     
2929     /**
2930      * Sets the value of the date field
2931      * @param {Date} value The date to set
2932      */
2933     setValue : function(value){
2934         var old = this.value;
2935         this.value = value.clearTime(true);
2936         if(this.el){
2937             this.update(this.value);
2938         }
2939     },
2940
2941     /**
2942      * Gets the current selected value of the date field
2943      * @return {Date} The selected date
2944      */
2945     getValue : function(){
2946         return this.value;
2947     },
2948
2949     // private
2950     focus : function(){
2951         if(this.el){
2952             this.update(this.activeDate);
2953         }
2954     },
2955
2956     // private
2957     onRender : function(container, position){
2958         var m = [
2959              '<table cellspacing="0">',
2960                 '<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>',
2961                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
2962         var dn = this.dayNames;
2963         for(var i = 0; i < 7; i++){
2964             var d = this.startDay+i;
2965             if(d > 6){
2966                 d = d-7;
2967             }
2968             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
2969         }
2970         m[m.length] = "</tr></thead><tbody><tr>";
2971         for(var i = 0; i < 42; i++) {
2972             if(i % 7 == 0 && i != 0){
2973                 m[m.length] = "</tr><tr>";
2974             }
2975             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
2976         }
2977         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
2978             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
2979
2980         var el = document.createElement("div");
2981         el.className = "x-date-picker";
2982         el.innerHTML = m.join("");
2983
2984         container.dom.insertBefore(el, position);
2985
2986         this.el = Roo.get(el);
2987         this.eventEl = Roo.get(el.firstChild);
2988
2989         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
2990             handler: this.showPrevMonth,
2991             scope: this,
2992             preventDefault:true,
2993             stopDefault:true
2994         });
2995
2996         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
2997             handler: this.showNextMonth,
2998             scope: this,
2999             preventDefault:true,
3000             stopDefault:true
3001         });
3002
3003         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
3004
3005         this.monthPicker = this.el.down('div.x-date-mp');
3006         this.monthPicker.enableDisplayMode('block');
3007         
3008         var kn = new Roo.KeyNav(this.eventEl, {
3009             "left" : function(e){
3010                 e.ctrlKey ?
3011                     this.showPrevMonth() :
3012                     this.update(this.activeDate.add("d", -1));
3013             },
3014
3015             "right" : function(e){
3016                 e.ctrlKey ?
3017                     this.showNextMonth() :
3018                     this.update(this.activeDate.add("d", 1));
3019             },
3020
3021             "up" : function(e){
3022                 e.ctrlKey ?
3023                     this.showNextYear() :
3024                     this.update(this.activeDate.add("d", -7));
3025             },
3026
3027             "down" : function(e){
3028                 e.ctrlKey ?
3029                     this.showPrevYear() :
3030                     this.update(this.activeDate.add("d", 7));
3031             },
3032
3033             "pageUp" : function(e){
3034                 this.showNextMonth();
3035             },
3036
3037             "pageDown" : function(e){
3038                 this.showPrevMonth();
3039             },
3040
3041             "enter" : function(e){
3042                 e.stopPropagation();
3043                 return true;
3044             },
3045
3046             scope : this
3047         });
3048
3049         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
3050
3051         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
3052
3053         this.el.unselectable();
3054         
3055         this.cells = this.el.select("table.x-date-inner tbody td");
3056         this.textNodes = this.el.query("table.x-date-inner tbody span");
3057
3058         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
3059             text: "&#160;",
3060             tooltip: this.monthYearText
3061         });
3062
3063         this.mbtn.on('click', this.showMonthPicker, this);
3064         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
3065
3066
3067         var today = (new Date()).dateFormat(this.format);
3068         
3069         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
3070         baseTb.add({
3071             text: String.format(this.todayText, today),
3072             tooltip: String.format(this.todayTip, today),
3073             handler: this.selectToday,
3074             scope: this
3075         });
3076         
3077         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
3078             
3079         //});
3080         if (this.showClear) {
3081             
3082             baseTb.add( new Roo.Toolbar.Fill());
3083             baseTb.add({
3084                 text: '&#160;',
3085                 cls: 'x-btn-icon x-btn-clear',
3086                 handler: function() {
3087                     //this.value = '';
3088                     this.fireEvent("select", this, '');
3089                 },
3090                 scope: this
3091             });
3092         }
3093         
3094         
3095         if(Roo.isIE){
3096             this.el.repaint();
3097         }
3098         this.update(this.value);
3099     },
3100
3101     createMonthPicker : function(){
3102         if(!this.monthPicker.dom.firstChild){
3103             var buf = ['<table border="0" cellspacing="0">'];
3104             for(var i = 0; i < 6; i++){
3105                 buf.push(
3106                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
3107                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
3108                     i == 0 ?
3109                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
3110                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
3111                 );
3112             }
3113             buf.push(
3114                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
3115                     this.okText,
3116                     '</button><button type="button" class="x-date-mp-cancel">',
3117                     this.cancelText,
3118                     '</button></td></tr>',
3119                 '</table>'
3120             );
3121             this.monthPicker.update(buf.join(''));
3122             this.monthPicker.on('click', this.onMonthClick, this);
3123             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
3124
3125             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
3126             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
3127
3128             this.mpMonths.each(function(m, a, i){
3129                 i += 1;
3130                 if((i%2) == 0){
3131                     m.dom.xmonth = 5 + Math.round(i * .5);
3132                 }else{
3133                     m.dom.xmonth = Math.round((i-1) * .5);
3134                 }
3135             });
3136         }
3137     },
3138
3139     showMonthPicker : function(){
3140         this.createMonthPicker();
3141         var size = this.el.getSize();
3142         this.monthPicker.setSize(size);
3143         this.monthPicker.child('table').setSize(size);
3144
3145         this.mpSelMonth = (this.activeDate || this.value).getMonth();
3146         this.updateMPMonth(this.mpSelMonth);
3147         this.mpSelYear = (this.activeDate || this.value).getFullYear();
3148         this.updateMPYear(this.mpSelYear);
3149
3150         this.monthPicker.slideIn('t', {duration:.2});
3151     },
3152
3153     updateMPYear : function(y){
3154         this.mpyear = y;
3155         var ys = this.mpYears.elements;
3156         for(var i = 1; i <= 10; i++){
3157             var td = ys[i-1], y2;
3158             if((i%2) == 0){
3159                 y2 = y + Math.round(i * .5);
3160                 td.firstChild.innerHTML = y2;
3161                 td.xyear = y2;
3162             }else{
3163                 y2 = y - (5-Math.round(i * .5));
3164                 td.firstChild.innerHTML = y2;
3165                 td.xyear = y2;
3166             }
3167             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
3168         }
3169     },
3170
3171     updateMPMonth : function(sm){
3172         this.mpMonths.each(function(m, a, i){
3173             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
3174         });
3175     },
3176
3177     selectMPMonth: function(m){
3178         
3179     },
3180
3181     onMonthClick : function(e, t){
3182         e.stopEvent();
3183         var el = new Roo.Element(t), pn;
3184         if(el.is('button.x-date-mp-cancel')){
3185             this.hideMonthPicker();
3186         }
3187         else if(el.is('button.x-date-mp-ok')){
3188             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
3189             this.hideMonthPicker();
3190         }
3191         else if(pn = el.up('td.x-date-mp-month', 2)){
3192             this.mpMonths.removeClass('x-date-mp-sel');
3193             pn.addClass('x-date-mp-sel');
3194             this.mpSelMonth = pn.dom.xmonth;
3195         }
3196         else if(pn = el.up('td.x-date-mp-year', 2)){
3197             this.mpYears.removeClass('x-date-mp-sel');
3198             pn.addClass('x-date-mp-sel');
3199             this.mpSelYear = pn.dom.xyear;
3200         }
3201         else if(el.is('a.x-date-mp-prev')){
3202             this.updateMPYear(this.mpyear-10);
3203         }
3204         else if(el.is('a.x-date-mp-next')){
3205             this.updateMPYear(this.mpyear+10);
3206         }
3207     },
3208
3209     onMonthDblClick : function(e, t){
3210         e.stopEvent();
3211         var el = new Roo.Element(t), pn;
3212         if(pn = el.up('td.x-date-mp-month', 2)){
3213             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
3214             this.hideMonthPicker();
3215         }
3216         else if(pn = el.up('td.x-date-mp-year', 2)){
3217             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
3218             this.hideMonthPicker();
3219         }
3220     },
3221
3222     hideMonthPicker : function(disableAnim){
3223         if(this.monthPicker){
3224             if(disableAnim === true){
3225                 this.monthPicker.hide();
3226             }else{
3227                 this.monthPicker.slideOut('t', {duration:.2});
3228             }
3229         }
3230     },
3231
3232     // private
3233     showPrevMonth : function(e){
3234         this.update(this.activeDate.add("mo", -1));
3235     },
3236
3237     // private
3238     showNextMonth : function(e){
3239         this.update(this.activeDate.add("mo", 1));
3240     },
3241
3242     // private
3243     showPrevYear : function(){
3244         this.update(this.activeDate.add("y", -1));
3245     },
3246
3247     // private
3248     showNextYear : function(){
3249         this.update(this.activeDate.add("y", 1));
3250     },
3251
3252     // private
3253     handleMouseWheel : function(e){
3254         var delta = e.getWheelDelta();
3255         if(delta > 0){
3256             this.showPrevMonth();
3257             e.stopEvent();
3258         } else if(delta < 0){
3259             this.showNextMonth();
3260             e.stopEvent();
3261         }
3262     },
3263
3264     // private
3265     handleDateClick : function(e, t){
3266         e.stopEvent();
3267         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
3268             this.setValue(new Date(t.dateValue));
3269             this.fireEvent("select", this, this.value);
3270         }
3271     },
3272
3273     // private
3274     selectToday : function(){
3275         this.setValue(new Date().clearTime());
3276         this.fireEvent("select", this, this.value);
3277     },
3278
3279     // private
3280     update : function(date){
3281         var vd = this.activeDate;
3282         this.activeDate = date;
3283         if(vd && this.el){
3284             var t = date.getTime();
3285             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
3286                 this.cells.removeClass("x-date-selected");
3287                 this.cells.each(function(c){
3288                    if(c.dom.firstChild.dateValue == t){
3289                        c.addClass("x-date-selected");
3290                        setTimeout(function(){
3291                             try{c.dom.firstChild.focus();}catch(e){}
3292                        }, 50);
3293                        return false;
3294                    }
3295                 });
3296                 return;
3297             }
3298         }
3299         var days = date.getDaysInMonth();
3300         var firstOfMonth = date.getFirstDateOfMonth();
3301         var startingPos = firstOfMonth.getDay()-this.startDay;
3302
3303         if(startingPos <= this.startDay){
3304             startingPos += 7;
3305         }
3306
3307         var pm = date.add("mo", -1);
3308         var prevStart = pm.getDaysInMonth()-startingPos;
3309
3310         var cells = this.cells.elements;
3311         var textEls = this.textNodes;
3312         days += startingPos;
3313
3314         // convert everything to numbers so it's fast
3315         var day = 86400000;
3316         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
3317         var today = new Date().clearTime().getTime();
3318         var sel = date.clearTime().getTime();
3319         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
3320         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
3321         var ddMatch = this.disabledDatesRE;
3322         var ddText = this.disabledDatesText;
3323         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
3324         var ddaysText = this.disabledDaysText;
3325         var format = this.format;
3326
3327         var setCellClass = function(cal, cell){
3328             cell.title = "";
3329             var t = d.getTime();
3330             cell.firstChild.dateValue = t;
3331             if(t == today){
3332                 cell.className += " x-date-today";
3333                 cell.title = cal.todayText;
3334             }
3335             if(t == sel){
3336                 cell.className += " x-date-selected";
3337                 setTimeout(function(){
3338                     try{cell.firstChild.focus();}catch(e){}
3339                 }, 50);
3340             }
3341             // disabling
3342             if(t < min) {
3343                 cell.className = " x-date-disabled";
3344                 cell.title = cal.minText;
3345                 return;
3346             }
3347             if(t > max) {
3348                 cell.className = " x-date-disabled";
3349                 cell.title = cal.maxText;
3350                 return;
3351             }
3352             if(ddays){
3353                 if(ddays.indexOf(d.getDay()) != -1){
3354                     cell.title = ddaysText;
3355                     cell.className = " x-date-disabled";
3356                 }
3357             }
3358             if(ddMatch && format){
3359                 var fvalue = d.dateFormat(format);
3360                 if(ddMatch.test(fvalue)){
3361                     cell.title = ddText.replace("%0", fvalue);
3362                     cell.className = " x-date-disabled";
3363                 }
3364             }
3365         };
3366
3367         var i = 0;
3368         for(; i < startingPos; i++) {
3369             textEls[i].innerHTML = (++prevStart);
3370             d.setDate(d.getDate()+1);
3371             cells[i].className = "x-date-prevday";
3372             setCellClass(this, cells[i]);
3373         }
3374         for(; i < days; i++){
3375             intDay = i - startingPos + 1;
3376             textEls[i].innerHTML = (intDay);
3377             d.setDate(d.getDate()+1);
3378             cells[i].className = "x-date-active";
3379             setCellClass(this, cells[i]);
3380         }
3381         var extraDays = 0;
3382         for(; i < 42; i++) {
3383              textEls[i].innerHTML = (++extraDays);
3384              d.setDate(d.getDate()+1);
3385              cells[i].className = "x-date-nextday";
3386              setCellClass(this, cells[i]);
3387         }
3388
3389         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
3390
3391         if(!this.internalRender){
3392             var main = this.el.dom.firstChild;
3393             var w = main.offsetWidth;
3394             this.el.setWidth(w + this.el.getBorderWidth("lr"));
3395             Roo.fly(main).setWidth(w);
3396             this.internalRender = true;
3397             // opera does not respect the auto grow header center column
3398             // then, after it gets a width opera refuses to recalculate
3399             // without a second pass
3400             if(Roo.isOpera && !this.secondPass){
3401                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
3402                 this.secondPass = true;
3403                 this.update.defer(10, this, [date]);
3404             }
3405         }
3406     }
3407 });/*
3408  * Based on:
3409  * Ext JS Library 1.1.1
3410  * Copyright(c) 2006-2007, Ext JS, LLC.
3411  *
3412  * Originally Released Under LGPL - original licence link has changed is not relivant.
3413  *
3414  * Fork - LGPL
3415  * <script type="text/javascript">
3416  */
3417 /**
3418  * @class Roo.TabPanel
3419  * @extends Roo.util.Observable
3420  * A lightweight tab container.
3421  * <br><br>
3422  * Usage:
3423  * <pre><code>
3424 // basic tabs 1, built from existing content
3425 var tabs = new Roo.TabPanel("tabs1");
3426 tabs.addTab("script", "View Script");
3427 tabs.addTab("markup", "View Markup");
3428 tabs.activate("script");
3429
3430 // more advanced tabs, built from javascript
3431 var jtabs = new Roo.TabPanel("jtabs");
3432 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
3433
3434 // set up the UpdateManager
3435 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
3436 var updater = tab2.getUpdateManager();
3437 updater.setDefaultUrl("ajax1.htm");
3438 tab2.on('activate', updater.refresh, updater, true);
3439
3440 // Use setUrl for Ajax loading
3441 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
3442 tab3.setUrl("ajax2.htm", null, true);
3443
3444 // Disabled tab
3445 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
3446 tab4.disable();
3447
3448 jtabs.activate("jtabs-1");
3449  * </code></pre>
3450  * @constructor
3451  * Create a new TabPanel.
3452  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
3453  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
3454  */
3455 Roo.TabPanel = function(container, config){
3456     /**
3457     * The container element for this TabPanel.
3458     * @type Roo.Element
3459     */
3460     this.el = Roo.get(container, true);
3461     if(config){
3462         if(typeof config == "boolean"){
3463             this.tabPosition = config ? "bottom" : "top";
3464         }else{
3465             Roo.apply(this, config);
3466         }
3467     }
3468     if(this.tabPosition == "bottom"){
3469         this.bodyEl = Roo.get(this.createBody(this.el.dom));
3470         this.el.addClass("x-tabs-bottom");
3471     }
3472     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
3473     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
3474     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
3475     if(Roo.isIE){
3476         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
3477     }
3478     if(this.tabPosition != "bottom"){
3479     /** The body element that contains {@link Roo.TabPanelItem} bodies.
3480      * @type Roo.Element
3481      */
3482       this.bodyEl = Roo.get(this.createBody(this.el.dom));
3483       this.el.addClass("x-tabs-top");
3484     }
3485     this.items = [];
3486
3487     this.bodyEl.setStyle("position", "relative");
3488
3489     this.active = null;
3490     this.activateDelegate = this.activate.createDelegate(this);
3491
3492     this.addEvents({
3493         /**
3494          * @event tabchange
3495          * Fires when the active tab changes
3496          * @param {Roo.TabPanel} this
3497          * @param {Roo.TabPanelItem} activePanel The new active tab
3498          */
3499         "tabchange": true,
3500         /**
3501          * @event beforetabchange
3502          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
3503          * @param {Roo.TabPanel} this
3504          * @param {Object} e Set cancel to true on this object to cancel the tab change
3505          * @param {Roo.TabPanelItem} tab The tab being changed to
3506          */
3507         "beforetabchange" : true
3508     });
3509
3510     Roo.EventManager.onWindowResize(this.onResize, this);
3511     this.cpad = this.el.getPadding("lr");
3512     this.hiddenCount = 0;
3513
3514     Roo.TabPanel.superclass.constructor.call(this);
3515 };
3516
3517 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
3518         /*
3519          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
3520          */
3521     tabPosition : "top",
3522         /*
3523          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
3524          */
3525     currentTabWidth : 0,
3526         /*
3527          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
3528          */
3529     minTabWidth : 40,
3530         /*
3531          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
3532          */
3533     maxTabWidth : 250,
3534         /*
3535          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
3536          */
3537     preferredTabWidth : 175,
3538         /*
3539          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
3540          */
3541     resizeTabs : false,
3542         /*
3543          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
3544          */
3545     monitorResize : true,
3546
3547     /**
3548      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
3549      * @param {String} id The id of the div to use <b>or create</b>
3550      * @param {String} text The text for the tab
3551      * @param {String} content (optional) Content to put in the TabPanelItem body
3552      * @param {Boolean} closable (optional) True to create a close icon on the tab
3553      * @return {Roo.TabPanelItem} The created TabPanelItem
3554      */
3555     addTab : function(id, text, content, closable){
3556         var item = new Roo.TabPanelItem(this, id, text, closable);
3557         this.addTabItem(item);
3558         if(content){
3559             item.setContent(content);
3560         }
3561         return item;
3562     },
3563
3564     /**
3565      * Returns the {@link Roo.TabPanelItem} with the specified id/index
3566      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
3567      * @return {Roo.TabPanelItem}
3568      */
3569     getTab : function(id){
3570         return this.items[id];
3571     },
3572
3573     /**
3574      * Hides the {@link Roo.TabPanelItem} with the specified id/index
3575      * @param {String/Number} id The id or index of the TabPanelItem to hide.
3576      */
3577     hideTab : function(id){
3578         var t = this.items[id];
3579         if(!t.isHidden()){
3580            t.setHidden(true);
3581            this.hiddenCount++;
3582            this.autoSizeTabs();
3583         }
3584     },
3585
3586     /**
3587      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
3588      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
3589      */
3590     unhideTab : function(id){
3591         var t = this.items[id];
3592         if(t.isHidden()){
3593            t.setHidden(false);
3594            this.hiddenCount--;
3595            this.autoSizeTabs();
3596         }
3597     },
3598
3599     /**
3600      * Adds an existing {@link Roo.TabPanelItem}.
3601      * @param {Roo.TabPanelItem} item The TabPanelItem to add
3602      */
3603     addTabItem : function(item){
3604         this.items[item.id] = item;
3605         this.items.push(item);
3606         if(this.resizeTabs){
3607            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
3608            this.autoSizeTabs();
3609         }else{
3610             item.autoSize();
3611         }
3612     },
3613
3614     /**
3615      * Removes a {@link Roo.TabPanelItem}.
3616      * @param {String/Number} id The id or index of the TabPanelItem to remove.
3617      */
3618     removeTab : function(id){
3619         var items = this.items;
3620         var tab = items[id];
3621         if(!tab) return;
3622         var index = items.indexOf(tab);
3623         if(this.active == tab && items.length > 1){
3624             var newTab = this.getNextAvailable(index);
3625             if(newTab)newTab.activate();
3626         }
3627         this.stripEl.dom.removeChild(tab.pnode.dom);
3628         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
3629             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
3630         }
3631         items.splice(index, 1);
3632         delete this.items[tab.id];
3633         tab.fireEvent("close", tab);
3634         tab.purgeListeners();
3635         this.autoSizeTabs();
3636     },
3637
3638     getNextAvailable : function(start){
3639         var items = this.items;
3640         var index = start;
3641         // look for a next tab that will slide over to
3642         // replace the one being removed
3643         while(index < items.length){
3644             var item = items[++index];
3645             if(item && !item.isHidden()){
3646                 return item;
3647             }
3648         }
3649         // if one isn't found select the previous tab (on the left)
3650         index = start;
3651         while(index >= 0){
3652             var item = items[--index];
3653             if(item && !item.isHidden()){
3654                 return item;
3655             }
3656         }
3657         return null;
3658     },
3659
3660     /**
3661      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
3662      * @param {String/Number} id The id or index of the TabPanelItem to disable.
3663      */
3664     disableTab : function(id){
3665         var tab = this.items[id];
3666         if(tab && this.active != tab){
3667             tab.disable();
3668         }
3669     },
3670
3671     /**
3672      * Enables a {@link Roo.TabPanelItem} that is disabled.
3673      * @param {String/Number} id The id or index of the TabPanelItem to enable.
3674      */
3675     enableTab : function(id){
3676         var tab = this.items[id];
3677         tab.enable();
3678     },
3679
3680     /**
3681      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
3682      * @param {String/Number} id The id or index of the TabPanelItem to activate.
3683      * @return {Roo.TabPanelItem} The TabPanelItem.
3684      */
3685     activate : function(id){
3686         var tab = this.items[id];
3687         if(!tab){
3688             return null;
3689         }
3690         if(tab == this.active || tab.disabled){
3691             return tab;
3692         }
3693         var e = {};
3694         this.fireEvent("beforetabchange", this, e, tab);
3695         if(e.cancel !== true && !tab.disabled){
3696             if(this.active){
3697                 this.active.hide();
3698             }
3699             this.active = this.items[id];
3700             this.active.show();
3701             this.fireEvent("tabchange", this, this.active);
3702         }
3703         return tab;
3704     },
3705
3706     /**
3707      * Gets the active {@link Roo.TabPanelItem}.
3708      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
3709      */
3710     getActiveTab : function(){
3711         return this.active;
3712     },
3713
3714     /**
3715      * Updates the tab body element to fit the height of the container element
3716      * for overflow scrolling
3717      * @param {Number} targetHeight (optional) Override the starting height from the elements height
3718      */
3719     syncHeight : function(targetHeight){
3720         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
3721         var bm = this.bodyEl.getMargins();
3722         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
3723         this.bodyEl.setHeight(newHeight);
3724         return newHeight;
3725     },
3726
3727     onResize : function(){
3728         if(this.monitorResize){
3729             this.autoSizeTabs();
3730         }
3731     },
3732
3733     /**
3734      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
3735      */
3736     beginUpdate : function(){
3737         this.updating = true;
3738     },
3739
3740     /**
3741      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
3742      */
3743     endUpdate : function(){
3744         this.updating = false;
3745         this.autoSizeTabs();
3746     },
3747
3748     /**
3749      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
3750      */
3751     autoSizeTabs : function(){
3752         var count = this.items.length;
3753         var vcount = count - this.hiddenCount;
3754         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
3755         var w = Math.max(this.el.getWidth() - this.cpad, 10);
3756         var availWidth = Math.floor(w / vcount);
3757         var b = this.stripBody;
3758         if(b.getWidth() > w){
3759             var tabs = this.items;
3760             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
3761             if(availWidth < this.minTabWidth){
3762                 /*if(!this.sleft){    // incomplete scrolling code
3763                     this.createScrollButtons();
3764                 }
3765                 this.showScroll();
3766                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
3767             }
3768         }else{
3769             if(this.currentTabWidth < this.preferredTabWidth){
3770                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
3771             }
3772         }
3773     },
3774
3775     /**
3776      * Returns the number of tabs in this TabPanel.
3777      * @return {Number}
3778      */
3779      getCount : function(){
3780          return this.items.length;
3781      },
3782
3783     /**
3784      * Resizes all the tabs to the passed width
3785      * @param {Number} The new width
3786      */
3787     setTabWidth : function(width){
3788         this.currentTabWidth = width;
3789         for(var i = 0, len = this.items.length; i < len; i++) {
3790                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
3791         }
3792     },
3793
3794     /**
3795      * Destroys this TabPanel
3796      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
3797      */
3798     destroy : function(removeEl){
3799         Roo.EventManager.removeResizeListener(this.onResize, this);
3800         for(var i = 0, len = this.items.length; i < len; i++){
3801             this.items[i].purgeListeners();
3802         }
3803         if(removeEl === true){
3804             this.el.update("");
3805             this.el.remove();
3806         }
3807     }
3808 });
3809
3810 /**
3811  * @class Roo.TabPanelItem
3812  * @extends Roo.util.Observable
3813  * Represents an individual item (tab plus body) in a TabPanel.
3814  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
3815  * @param {String} id The id of this TabPanelItem
3816  * @param {String} text The text for the tab of this TabPanelItem
3817  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
3818  */
3819 Roo.TabPanelItem = function(tabPanel, id, text, closable){
3820     /**
3821      * The {@link Roo.TabPanel} this TabPanelItem belongs to
3822      * @type Roo.TabPanel
3823      */
3824     this.tabPanel = tabPanel;
3825     /**
3826      * The id for this TabPanelItem
3827      * @type String
3828      */
3829     this.id = id;
3830     /** @private */
3831     this.disabled = false;
3832     /** @private */
3833     this.text = text;
3834     /** @private */
3835     this.loaded = false;
3836     this.closable = closable;
3837
3838     /**
3839      * The body element for this TabPanelItem.
3840      * @type Roo.Element
3841      */
3842     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
3843     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
3844     this.bodyEl.setStyle("display", "block");
3845     this.bodyEl.setStyle("zoom", "1");
3846     this.hideAction();
3847
3848     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
3849     /** @private */
3850     this.el = Roo.get(els.el, true);
3851     this.inner = Roo.get(els.inner, true);
3852     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
3853     this.pnode = Roo.get(els.el.parentNode, true);
3854     this.el.on("mousedown", this.onTabMouseDown, this);
3855     this.el.on("click", this.onTabClick, this);
3856     /** @private */
3857     if(closable){
3858         var c = Roo.get(els.close, true);
3859         c.dom.title = this.closeText;
3860         c.addClassOnOver("close-over");
3861         c.on("click", this.closeClick, this);
3862      }
3863
3864     this.addEvents({
3865          /**
3866          * @event activate
3867          * Fires when this tab becomes the active tab.
3868          * @param {Roo.TabPanel} tabPanel The parent TabPanel
3869          * @param {Roo.TabPanelItem} this
3870          */
3871         "activate": true,
3872         /**
3873          * @event beforeclose
3874          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
3875          * @param {Roo.TabPanelItem} this
3876          * @param {Object} e Set cancel to true on this object to cancel the close.
3877          */
3878         "beforeclose": true,
3879         /**
3880          * @event close
3881          * Fires when this tab is closed.
3882          * @param {Roo.TabPanelItem} this
3883          */
3884          "close": true,
3885         /**
3886          * @event deactivate
3887          * Fires when this tab is no longer the active tab.
3888          * @param {Roo.TabPanel} tabPanel The parent TabPanel
3889          * @param {Roo.TabPanelItem} this
3890          */
3891          "deactivate" : true
3892     });
3893     this.hidden = false;
3894
3895     Roo.TabPanelItem.superclass.constructor.call(this);
3896 };
3897
3898 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
3899     purgeListeners : function(){
3900        Roo.util.Observable.prototype.purgeListeners.call(this);
3901        this.el.removeAllListeners();
3902     },
3903     /**
3904      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
3905      */
3906     show : function(){
3907         this.pnode.addClass("on");
3908         this.showAction();
3909         if(Roo.isOpera){
3910             this.tabPanel.stripWrap.repaint();
3911         }
3912         this.fireEvent("activate", this.tabPanel, this);
3913     },
3914
3915     /**
3916      * Returns true if this tab is the active tab.
3917      * @return {Boolean}
3918      */
3919     isActive : function(){
3920         return this.tabPanel.getActiveTab() == this;
3921     },
3922
3923     /**
3924      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
3925      */
3926     hide : function(){
3927         this.pnode.removeClass("on");
3928         this.hideAction();
3929         this.fireEvent("deactivate", this.tabPanel, this);
3930     },
3931
3932     hideAction : function(){
3933         this.bodyEl.hide();
3934         this.bodyEl.setStyle("position", "absolute");
3935         this.bodyEl.setLeft("-20000px");
3936         this.bodyEl.setTop("-20000px");
3937     },
3938
3939     showAction : function(){
3940         this.bodyEl.setStyle("position", "relative");
3941         this.bodyEl.setTop("");
3942         this.bodyEl.setLeft("");
3943         this.bodyEl.show();
3944     },
3945
3946     /**
3947      * Set the tooltip for the tab.
3948      * @param {String} tooltip The tab's tooltip
3949      */
3950     setTooltip : function(text){
3951         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
3952             this.textEl.dom.qtip = text;
3953             this.textEl.dom.removeAttribute('title');
3954         }else{
3955             this.textEl.dom.title = text;
3956         }
3957     },
3958
3959     onTabClick : function(e){
3960         e.preventDefault();
3961         this.tabPanel.activate(this.id);
3962     },
3963
3964     onTabMouseDown : function(e){
3965         e.preventDefault();
3966         this.tabPanel.activate(this.id);
3967     },
3968
3969     getWidth : function(){
3970         return this.inner.getWidth();
3971     },
3972
3973     setWidth : function(width){
3974         var iwidth = width - this.pnode.getPadding("lr");
3975         this.inner.setWidth(iwidth);
3976         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
3977         this.pnode.setWidth(width);
3978     },
3979
3980     /**
3981      * Show or hide the tab
3982      * @param {Boolean} hidden True to hide or false to show.
3983      */
3984     setHidden : function(hidden){
3985         this.hidden = hidden;
3986         this.pnode.setStyle("display", hidden ? "none" : "");
3987     },
3988
3989     /**
3990      * Returns true if this tab is "hidden"
3991      * @return {Boolean}
3992      */
3993     isHidden : function(){
3994         return this.hidden;
3995     },
3996
3997     /**
3998      * Returns the text for this tab
3999      * @return {String}
4000      */
4001     getText : function(){
4002         return this.text;
4003     },
4004
4005     autoSize : function(){
4006         //this.el.beginMeasure();
4007         this.textEl.setWidth(1);
4008         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
4009         //this.el.endMeasure();
4010     },
4011
4012     /**
4013      * Sets the text for the tab (Note: this also sets the tooltip text)
4014      * @param {String} text The tab's text and tooltip
4015      */
4016     setText : function(text){
4017         this.text = text;
4018         this.textEl.update(text);
4019         this.setTooltip(text);
4020         if(!this.tabPanel.resizeTabs){
4021             this.autoSize();
4022         }
4023     },
4024     /**
4025      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
4026      */
4027     activate : function(){
4028         this.tabPanel.activate(this.id);
4029     },
4030
4031     /**
4032      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
4033      */
4034     disable : function(){
4035         if(this.tabPanel.active != this){
4036             this.disabled = true;
4037             this.pnode.addClass("disabled");
4038         }
4039     },
4040
4041     /**
4042      * Enables this TabPanelItem if it was previously disabled.
4043      */
4044     enable : function(){
4045         this.disabled = false;
4046         this.pnode.removeClass("disabled");
4047     },
4048
4049     /**
4050      * Sets the content for this TabPanelItem.
4051      * @param {String} content The content
4052      * @param {Boolean} loadScripts true to look for and load scripts
4053      */
4054     setContent : function(content, loadScripts){
4055         this.bodyEl.update(content, loadScripts);
4056     },
4057
4058     /**
4059      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
4060      * @return {Roo.UpdateManager} The UpdateManager
4061      */
4062     getUpdateManager : function(){
4063         return this.bodyEl.getUpdateManager();
4064     },
4065
4066     /**
4067      * Set a URL to be used to load the content for this TabPanelItem.
4068      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
4069      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
4070      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
4071      * @return {Roo.UpdateManager} The UpdateManager
4072      */
4073     setUrl : function(url, params, loadOnce){
4074         if(this.refreshDelegate){
4075             this.un('activate', this.refreshDelegate);
4076         }
4077         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
4078         this.on("activate", this.refreshDelegate);
4079         return this.bodyEl.getUpdateManager();
4080     },
4081
4082     /** @private */
4083     _handleRefresh : function(url, params, loadOnce){
4084         if(!loadOnce || !this.loaded){
4085             var updater = this.bodyEl.getUpdateManager();
4086             updater.update(url, params, this._setLoaded.createDelegate(this));
4087         }
4088     },
4089
4090     /**
4091      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
4092      *   Will fail silently if the setUrl method has not been called.
4093      *   This does not activate the panel, just updates its content.
4094      */
4095     refresh : function(){
4096         if(this.refreshDelegate){
4097            this.loaded = false;
4098            this.refreshDelegate();
4099         }
4100     },
4101
4102     /** @private */
4103     _setLoaded : function(){
4104         this.loaded = true;
4105     },
4106
4107     /** @private */
4108     closeClick : function(e){
4109         var o = {};
4110         e.stopEvent();
4111         this.fireEvent("beforeclose", this, o);
4112         if(o.cancel !== true){
4113             this.tabPanel.removeTab(this.id);
4114         }
4115     },
4116     /**
4117      * The text displayed in the tooltip for the close icon.
4118      * @type String
4119      */
4120     closeText : "Close this tab"
4121 });
4122
4123 /** @private */
4124 Roo.TabPanel.prototype.createStrip = function(container){
4125     var strip = document.createElement("div");
4126     strip.className = "x-tabs-wrap";
4127     container.appendChild(strip);
4128     return strip;
4129 };
4130 /** @private */
4131 Roo.TabPanel.prototype.createStripList = function(strip){
4132     // div wrapper for retard IE
4133     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
4134     return strip.firstChild.firstChild.firstChild.firstChild;
4135 };
4136 /** @private */
4137 Roo.TabPanel.prototype.createBody = function(container){
4138     var body = document.createElement("div");
4139     Roo.id(body, "tab-body");
4140     Roo.fly(body).addClass("x-tabs-body");
4141     container.appendChild(body);
4142     return body;
4143 };
4144 /** @private */
4145 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
4146     var body = Roo.getDom(id);
4147     if(!body){
4148         body = document.createElement("div");
4149         body.id = id;
4150     }
4151     Roo.fly(body).addClass("x-tabs-item-body");
4152     bodyEl.insertBefore(body, bodyEl.firstChild);
4153     return body;
4154 };
4155 /** @private */
4156 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
4157     var td = document.createElement("td");
4158     stripEl.appendChild(td);
4159     if(closable){
4160         td.className = "x-tabs-closable";
4161         if(!this.closeTpl){
4162             this.closeTpl = new Roo.Template(
4163                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
4164                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
4165                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
4166             );
4167         }
4168         var el = this.closeTpl.overwrite(td, {"text": text});
4169         var close = el.getElementsByTagName("div")[0];
4170         var inner = el.getElementsByTagName("em")[0];
4171         return {"el": el, "close": close, "inner": inner};
4172     } else {
4173         if(!this.tabTpl){
4174             this.tabTpl = new Roo.Template(
4175                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
4176                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
4177             );
4178         }
4179         var el = this.tabTpl.overwrite(td, {"text": text});
4180         var inner = el.getElementsByTagName("em")[0];
4181         return {"el": el, "inner": inner};
4182     }
4183 };/*
4184  * Based on:
4185  * Ext JS Library 1.1.1
4186  * Copyright(c) 2006-2007, Ext JS, LLC.
4187  *
4188  * Originally Released Under LGPL - original licence link has changed is not relivant.
4189  *
4190  * Fork - LGPL
4191  * <script type="text/javascript">
4192  */
4193
4194 /**
4195  * @class Roo.Button
4196  * @extends Roo.util.Observable
4197  * Simple Button class
4198  * @cfg {String} text The button text
4199  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
4200  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
4201  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
4202  * @cfg {Object} scope The scope of the handler
4203  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
4204  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
4205  * @cfg {Boolean} hidden True to start hidden (defaults to false)
4206  * @cfg {Boolean} disabled True to start disabled (defaults to false)
4207  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
4208  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
4209    applies if enableToggle = true)
4210  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
4211  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
4212   an {@link Roo.util.ClickRepeater} config object (defaults to false).
4213  * @constructor
4214  * Create a new button
4215  * @param {Object} config The config object
4216  */
4217 Roo.Button = function(renderTo, config)
4218 {
4219     if (!config) {
4220         config = renderTo;
4221         renderTo = config.renderTo || false;
4222     }
4223     
4224     Roo.apply(this, config);
4225     this.addEvents({
4226         /**
4227              * @event click
4228              * Fires when this button is clicked
4229              * @param {Button} this
4230              * @param {EventObject} e The click event
4231              */
4232             "click" : true,
4233         /**
4234              * @event toggle
4235              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
4236              * @param {Button} this
4237              * @param {Boolean} pressed
4238              */
4239             "toggle" : true,
4240         /**
4241              * @event mouseover
4242              * Fires when the mouse hovers over the button
4243              * @param {Button} this
4244              * @param {Event} e The event object
4245              */
4246         'mouseover' : true,
4247         /**
4248              * @event mouseout
4249              * Fires when the mouse exits the button
4250              * @param {Button} this
4251              * @param {Event} e The event object
4252              */
4253         'mouseout': true,
4254          /**
4255              * @event render
4256              * Fires when the button is rendered
4257              * @param {Button} this
4258              */
4259         'render': true
4260     });
4261     if(this.menu){
4262         this.menu = Roo.menu.MenuMgr.get(this.menu);
4263     }
4264     if(renderTo){
4265         this.render(renderTo);
4266     }
4267     
4268     Roo.util.Observable.call(this);
4269 };
4270
4271 Roo.extend(Roo.Button, Roo.util.Observable, {
4272     /**
4273      * 
4274      */
4275     
4276     /**
4277      * Read-only. True if this button is hidden
4278      * @type Boolean
4279      */
4280     hidden : false,
4281     /**
4282      * Read-only. True if this button is disabled
4283      * @type Boolean
4284      */
4285     disabled : false,
4286     /**
4287      * Read-only. True if this button is pressed (only if enableToggle = true)
4288      * @type Boolean
4289      */
4290     pressed : false,
4291
4292     /**
4293      * @cfg {Number} tabIndex 
4294      * The DOM tabIndex for this button (defaults to undefined)
4295      */
4296     tabIndex : undefined,
4297
4298     /**
4299      * @cfg {Boolean} enableToggle
4300      * True to enable pressed/not pressed toggling (defaults to false)
4301      */
4302     enableToggle: false,
4303     /**
4304      * @cfg {Mixed} menu
4305      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
4306      */
4307     menu : undefined,
4308     /**
4309      * @cfg {String} menuAlign
4310      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
4311      */
4312     menuAlign : "tl-bl?",
4313
4314     /**
4315      * @cfg {String} iconCls
4316      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
4317      */
4318     iconCls : undefined,
4319     /**
4320      * @cfg {String} type
4321      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
4322      */
4323     type : 'button',
4324
4325     // private
4326     menuClassTarget: 'tr',
4327
4328     /**
4329      * @cfg {String} clickEvent
4330      * The type of event to map to the button's event handler (defaults to 'click')
4331      */
4332     clickEvent : 'click',
4333
4334     /**
4335      * @cfg {Boolean} handleMouseEvents
4336      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
4337      */
4338     handleMouseEvents : true,
4339
4340     /**
4341      * @cfg {String} tooltipType
4342      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
4343      */
4344     tooltipType : 'qtip',
4345
4346     /**
4347      * @cfg {String} cls
4348      * A CSS class to apply to the button's main element.
4349      */
4350     
4351     /**
4352      * @cfg {Roo.Template} template (Optional)
4353      * An {@link Roo.Template} with which to create the Button's main element. This Template must
4354      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
4355      * require code modifications if required elements (e.g. a button) aren't present.
4356      */
4357
4358     // private
4359     render : function(renderTo){
4360         var btn;
4361         if(this.hideParent){
4362             this.parentEl = Roo.get(renderTo);
4363         }
4364         if(!this.dhconfig){
4365             if(!this.template){
4366                 if(!Roo.Button.buttonTemplate){
4367                     // hideous table template
4368                     Roo.Button.buttonTemplate = new Roo.Template(
4369                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
4370                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
4371                         "</tr></tbody></table>");
4372                 }
4373                 this.template = Roo.Button.buttonTemplate;
4374             }
4375             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
4376             var btnEl = btn.child("button:first");
4377             btnEl.on('focus', this.onFocus, this);
4378             btnEl.on('blur', this.onBlur, this);
4379             if(this.cls){
4380                 btn.addClass(this.cls);
4381             }
4382             if(this.icon){
4383                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
4384             }
4385             if(this.iconCls){
4386                 btnEl.addClass(this.iconCls);
4387                 if(!this.cls){
4388                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
4389                 }
4390             }
4391             if(this.tabIndex !== undefined){
4392                 btnEl.dom.tabIndex = this.tabIndex;
4393             }
4394             if(this.tooltip){
4395                 if(typeof this.tooltip == 'object'){
4396                     Roo.QuickTips.tips(Roo.apply({
4397                           target: btnEl.id
4398                     }, this.tooltip));
4399                 } else {
4400                     btnEl.dom[this.tooltipType] = this.tooltip;
4401                 }
4402             }
4403         }else{
4404             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
4405         }
4406         this.el = btn;
4407         if(this.id){
4408             this.el.dom.id = this.el.id = this.id;
4409         }
4410         if(this.menu){
4411             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
4412             this.menu.on("show", this.onMenuShow, this);
4413             this.menu.on("hide", this.onMenuHide, this);
4414         }
4415         btn.addClass("x-btn");
4416         if(Roo.isIE && !Roo.isIE7){
4417             this.autoWidth.defer(1, this);
4418         }else{
4419             this.autoWidth();
4420         }
4421         if(this.handleMouseEvents){
4422             btn.on("mouseover", this.onMouseOver, this);
4423             btn.on("mouseout", this.onMouseOut, this);
4424             btn.on("mousedown", this.onMouseDown, this);
4425         }
4426         btn.on(this.clickEvent, this.onClick, this);
4427         //btn.on("mouseup", this.onMouseUp, this);
4428         if(this.hidden){
4429             this.hide();
4430         }
4431         if(this.disabled){
4432             this.disable();
4433         }
4434         Roo.ButtonToggleMgr.register(this);
4435         if(this.pressed){
4436             this.el.addClass("x-btn-pressed");
4437         }
4438         if(this.repeat){
4439             var repeater = new Roo.util.ClickRepeater(btn,
4440                 typeof this.repeat == "object" ? this.repeat : {}
4441             );
4442             repeater.on("click", this.onClick,  this);
4443         }
4444         this.fireEvent('render', this);
4445         
4446     },
4447     /**
4448      * Returns the button's underlying element
4449      * @return {Roo.Element} The element
4450      */
4451     getEl : function(){
4452         return this.el;  
4453     },
4454     
4455     /**
4456      * Destroys this Button and removes any listeners.
4457      */
4458     destroy : function(){
4459         Roo.ButtonToggleMgr.unregister(this);
4460         this.el.removeAllListeners();
4461         this.purgeListeners();
4462         this.el.remove();
4463     },
4464
4465     // private
4466     autoWidth : function(){
4467         if(this.el){
4468             this.el.setWidth("auto");
4469             if(Roo.isIE7 && Roo.isStrict){
4470                 var ib = this.el.child('button');
4471                 if(ib && ib.getWidth() > 20){
4472                     ib.clip();
4473                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
4474                 }
4475             }
4476             if(this.minWidth){
4477                 if(this.hidden){
4478                     this.el.beginMeasure();
4479                 }
4480                 if(this.el.getWidth() < this.minWidth){
4481                     this.el.setWidth(this.minWidth);
4482                 }
4483                 if(this.hidden){
4484                     this.el.endMeasure();
4485                 }
4486             }
4487         }
4488     },
4489
4490     /**
4491      * Assigns this button's click handler
4492      * @param {Function} handler The function to call when the button is clicked
4493      * @param {Object} scope (optional) Scope for the function passed in
4494      */
4495     setHandler : function(handler, scope){
4496         this.handler = handler;
4497         this.scope = scope;  
4498     },
4499     
4500     /**
4501      * Sets this button's text
4502      * @param {String} text The button text
4503      */
4504     setText : function(text){
4505         this.text = text;
4506         if(this.el){
4507             this.el.child("td.x-btn-center button.x-btn-text").update(text);
4508         }
4509         this.autoWidth();
4510     },
4511     
4512     /**
4513      * Gets the text for this button
4514      * @return {String} The button text
4515      */
4516     getText : function(){
4517         return this.text;  
4518     },
4519     
4520     /**
4521      * Show this button
4522      */
4523     show: function(){
4524         this.hidden = false;
4525         if(this.el){
4526             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
4527         }
4528     },
4529     
4530     /**
4531      * Hide this button
4532      */
4533     hide: function(){
4534         this.hidden = true;
4535         if(this.el){
4536             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
4537         }
4538     },
4539     
4540     /**
4541      * Convenience function for boolean show/hide
4542      * @param {Boolean} visible True to show, false to hide
4543      */
4544     setVisible: function(visible){
4545         if(visible) {
4546             this.show();
4547         }else{
4548             this.hide();
4549         }
4550     },
4551     
4552     /**
4553      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
4554      * @param {Boolean} state (optional) Force a particular state
4555      */
4556     toggle : function(state){
4557         state = state === undefined ? !this.pressed : state;
4558         if(state != this.pressed){
4559             if(state){
4560                 this.el.addClass("x-btn-pressed");
4561                 this.pressed = true;
4562                 this.fireEvent("toggle", this, true);
4563             }else{
4564                 this.el.removeClass("x-btn-pressed");
4565                 this.pressed = false;
4566                 this.fireEvent("toggle", this, false);
4567             }
4568             if(this.toggleHandler){
4569                 this.toggleHandler.call(this.scope || this, this, state);
4570             }
4571         }
4572     },
4573     
4574     /**
4575      * Focus the button
4576      */
4577     focus : function(){
4578         this.el.child('button:first').focus();
4579     },
4580     
4581     /**
4582      * Disable this button
4583      */
4584     disable : function(){
4585         if(this.el){
4586             this.el.addClass("x-btn-disabled");
4587         }
4588         this.disabled = true;
4589     },
4590     
4591     /**
4592      * Enable this button
4593      */
4594     enable : function(){
4595         if(this.el){
4596             this.el.removeClass("x-btn-disabled");
4597         }
4598         this.disabled = false;
4599     },
4600
4601     /**
4602      * Convenience function for boolean enable/disable
4603      * @param {Boolean} enabled True to enable, false to disable
4604      */
4605     setDisabled : function(v){
4606         this[v !== true ? "enable" : "disable"]();
4607     },
4608
4609     // private
4610     onClick : function(e){
4611         if(e){
4612             e.preventDefault();
4613         }
4614         if(e.button != 0){
4615             return;
4616         }
4617         if(!this.disabled){
4618             if(this.enableToggle){
4619                 this.toggle();
4620             }
4621             if(this.menu && !this.menu.isVisible()){
4622                 this.menu.show(this.el, this.menuAlign);
4623             }
4624             this.fireEvent("click", this, e);
4625             if(this.handler){
4626                 this.el.removeClass("x-btn-over");
4627                 this.handler.call(this.scope || this, this, e);
4628             }
4629         }
4630     },
4631     // private
4632     onMouseOver : function(e){
4633         if(!this.disabled){
4634             this.el.addClass("x-btn-over");
4635             this.fireEvent('mouseover', this, e);
4636         }
4637     },
4638     // private
4639     onMouseOut : function(e){
4640         if(!e.within(this.el,  true)){
4641             this.el.removeClass("x-btn-over");
4642             this.fireEvent('mouseout', this, e);
4643         }
4644     },
4645     // private
4646     onFocus : function(e){
4647         if(!this.disabled){
4648             this.el.addClass("x-btn-focus");
4649         }
4650     },
4651     // private
4652     onBlur : function(e){
4653         this.el.removeClass("x-btn-focus");
4654     },
4655     // private
4656     onMouseDown : function(e){
4657         if(!this.disabled && e.button == 0){
4658             this.el.addClass("x-btn-click");
4659             Roo.get(document).on('mouseup', this.onMouseUp, this);
4660         }
4661     },
4662     // private
4663     onMouseUp : function(e){
4664         if(e.button == 0){
4665             this.el.removeClass("x-btn-click");
4666             Roo.get(document).un('mouseup', this.onMouseUp, this);
4667         }
4668     },
4669     // private
4670     onMenuShow : function(e){
4671         this.el.addClass("x-btn-menu-active");
4672     },
4673     // private
4674     onMenuHide : function(e){
4675         this.el.removeClass("x-btn-menu-active");
4676     }   
4677 });
4678
4679 // Private utility class used by Button
4680 Roo.ButtonToggleMgr = function(){
4681    var groups = {};
4682    
4683    function toggleGroup(btn, state){
4684        if(state){
4685            var g = groups[btn.toggleGroup];
4686            for(var i = 0, l = g.length; i < l; i++){
4687                if(g[i] != btn){
4688                    g[i].toggle(false);
4689                }
4690            }
4691        }
4692    }
4693    
4694    return {
4695        register : function(btn){
4696            if(!btn.toggleGroup){
4697                return;
4698            }
4699            var g = groups[btn.toggleGroup];
4700            if(!g){
4701                g = groups[btn.toggleGroup] = [];
4702            }
4703            g.push(btn);
4704            btn.on("toggle", toggleGroup);
4705        },
4706        
4707        unregister : function(btn){
4708            if(!btn.toggleGroup){
4709                return;
4710            }
4711            var g = groups[btn.toggleGroup];
4712            if(g){
4713                g.remove(btn);
4714                btn.un("toggle", toggleGroup);
4715            }
4716        }
4717    };
4718 }();/*
4719  * Based on:
4720  * Ext JS Library 1.1.1
4721  * Copyright(c) 2006-2007, Ext JS, LLC.
4722  *
4723  * Originally Released Under LGPL - original licence link has changed is not relivant.
4724  *
4725  * Fork - LGPL
4726  * <script type="text/javascript">
4727  */
4728  
4729 /**
4730  * @class Roo.SplitButton
4731  * @extends Roo.Button
4732  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
4733  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
4734  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
4735  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
4736  * @cfg {String} arrowTooltip The title attribute of the arrow
4737  * @constructor
4738  * Create a new menu button
4739  * @param {String/HTMLElement/Element} renderTo The element to append the button to
4740  * @param {Object} config The config object
4741  */
4742 Roo.SplitButton = function(renderTo, config){
4743     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
4744     /**
4745      * @event arrowclick
4746      * Fires when this button's arrow is clicked
4747      * @param {SplitButton} this
4748      * @param {EventObject} e The click event
4749      */
4750     this.addEvents({"arrowclick":true});
4751 };
4752
4753 Roo.extend(Roo.SplitButton, Roo.Button, {
4754     render : function(renderTo){
4755         // this is one sweet looking template!
4756         var tpl = new Roo.Template(
4757             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
4758             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
4759             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
4760             "</tbody></table></td><td>",
4761             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
4762             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
4763             "</tbody></table></td></tr></table>"
4764         );
4765         var btn = tpl.append(renderTo, [this.text, this.type], true);
4766         var btnEl = btn.child("button");
4767         if(this.cls){
4768             btn.addClass(this.cls);
4769         }
4770         if(this.icon){
4771             btnEl.setStyle('background-image', 'url(' +this.icon +')');
4772         }
4773         if(this.iconCls){
4774             btnEl.addClass(this.iconCls);
4775             if(!this.cls){
4776                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
4777             }
4778         }
4779         this.el = btn;
4780         if(this.handleMouseEvents){
4781             btn.on("mouseover", this.onMouseOver, this);
4782             btn.on("mouseout", this.onMouseOut, this);
4783             btn.on("mousedown", this.onMouseDown, this);
4784             btn.on("mouseup", this.onMouseUp, this);
4785         }
4786         btn.on(this.clickEvent, this.onClick, this);
4787         if(this.tooltip){
4788             if(typeof this.tooltip == 'object'){
4789                 Roo.QuickTips.tips(Roo.apply({
4790                       target: btnEl.id
4791                 }, this.tooltip));
4792             } else {
4793                 btnEl.dom[this.tooltipType] = this.tooltip;
4794             }
4795         }
4796         if(this.arrowTooltip){
4797             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
4798         }
4799         if(this.hidden){
4800             this.hide();
4801         }
4802         if(this.disabled){
4803             this.disable();
4804         }
4805         if(this.pressed){
4806             this.el.addClass("x-btn-pressed");
4807         }
4808         if(Roo.isIE && !Roo.isIE7){
4809             this.autoWidth.defer(1, this);
4810         }else{
4811             this.autoWidth();
4812         }
4813         if(this.menu){
4814             this.menu.on("show", this.onMenuShow, this);
4815             this.menu.on("hide", this.onMenuHide, this);
4816         }
4817         this.fireEvent('render', this);
4818     },
4819
4820     // private
4821     autoWidth : function(){
4822         if(this.el){
4823             var tbl = this.el.child("table:first");
4824             var tbl2 = this.el.child("table:last");
4825             this.el.setWidth("auto");
4826             tbl.setWidth("auto");
4827             if(Roo.isIE7 && Roo.isStrict){
4828                 var ib = this.el.child('button:first');
4829                 if(ib && ib.getWidth() > 20){
4830                     ib.clip();
4831                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
4832                 }
4833             }
4834             if(this.minWidth){
4835                 if(this.hidden){
4836                     this.el.beginMeasure();
4837                 }
4838                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
4839                     tbl.setWidth(this.minWidth-tbl2.getWidth());
4840                 }
4841                 if(this.hidden){
4842                     this.el.endMeasure();
4843                 }
4844             }
4845             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
4846         } 
4847     },
4848     /**
4849      * Sets this button's click handler
4850      * @param {Function} handler The function to call when the button is clicked
4851      * @param {Object} scope (optional) Scope for the function passed above
4852      */
4853     setHandler : function(handler, scope){
4854         this.handler = handler;
4855         this.scope = scope;  
4856     },
4857     
4858     /**
4859      * Sets this button's arrow click handler
4860      * @param {Function} handler The function to call when the arrow is clicked
4861      * @param {Object} scope (optional) Scope for the function passed above
4862      */
4863     setArrowHandler : function(handler, scope){
4864         this.arrowHandler = handler;
4865         this.scope = scope;  
4866     },
4867     
4868     /**
4869      * Focus the button
4870      */
4871     focus : function(){
4872         if(this.el){
4873             this.el.child("button:first").focus();
4874         }
4875     },
4876
4877     // private
4878     onClick : function(e){
4879         e.preventDefault();
4880         if(!this.disabled){
4881             if(e.getTarget(".x-btn-menu-arrow-wrap")){
4882                 if(this.menu && !this.menu.isVisible()){
4883                     this.menu.show(this.el, this.menuAlign);
4884                 }
4885                 this.fireEvent("arrowclick", this, e);
4886                 if(this.arrowHandler){
4887                     this.arrowHandler.call(this.scope || this, this, e);
4888                 }
4889             }else{
4890                 this.fireEvent("click", this, e);
4891                 if(this.handler){
4892                     this.handler.call(this.scope || this, this, e);
4893                 }
4894             }
4895         }
4896     },
4897     // private
4898     onMouseDown : function(e){
4899         if(!this.disabled){
4900             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
4901         }
4902     },
4903     // private
4904     onMouseUp : function(e){
4905         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
4906     }   
4907 });
4908
4909
4910 // backwards compat
4911 Roo.MenuButton = Roo.SplitButton;/*
4912  * Based on:
4913  * Ext JS Library 1.1.1
4914  * Copyright(c) 2006-2007, Ext JS, LLC.
4915  *
4916  * Originally Released Under LGPL - original licence link has changed is not relivant.
4917  *
4918  * Fork - LGPL
4919  * <script type="text/javascript">
4920  */
4921
4922 /**
4923  * @class Roo.Toolbar
4924  * Basic Toolbar class.
4925  * @constructor
4926  * Creates a new Toolbar
4927  * @param {Object} config The config object
4928  */ 
4929 Roo.Toolbar = function(container, buttons, config)
4930 {
4931     /// old consturctor format still supported..
4932     if(container instanceof Array){ // omit the container for later rendering
4933         buttons = container;
4934         config = buttons;
4935         container = null;
4936     }
4937     if (typeof(container) == 'object' && container.xtype) {
4938         config = container;
4939         container = config.container;
4940         buttons = config.buttons; // not really - use items!!
4941     }
4942     var xitems = [];
4943     if (config && config.items) {
4944         xitems = config.items;
4945         delete config.items;
4946     }
4947     Roo.apply(this, config);
4948     this.buttons = buttons;
4949     
4950     if(container){
4951         this.render(container);
4952     }
4953     Roo.each(xitems, function(b) {
4954         this.add(b);
4955     }, this);
4956     
4957 };
4958
4959 Roo.Toolbar.prototype = {
4960     /**
4961      * @cfg {Roo.data.Store} items
4962      * array of button configs or elements to add
4963      */
4964     
4965     /**
4966      * @cfg {String/HTMLElement/Element} container
4967      * The id or element that will contain the toolbar
4968      */
4969     // private
4970     render : function(ct){
4971         this.el = Roo.get(ct);
4972         if(this.cls){
4973             this.el.addClass(this.cls);
4974         }
4975         // using a table allows for vertical alignment
4976         // 100% width is needed by Safari...
4977         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
4978         this.tr = this.el.child("tr", true);
4979         var autoId = 0;
4980         this.items = new Roo.util.MixedCollection(false, function(o){
4981             return o.id || ("item" + (++autoId));
4982         });
4983         if(this.buttons){
4984             this.add.apply(this, this.buttons);
4985             delete this.buttons;
4986         }
4987     },
4988
4989     /**
4990      * Adds element(s) to the toolbar -- this function takes a variable number of 
4991      * arguments of mixed type and adds them to the toolbar.
4992      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
4993      * <ul>
4994      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
4995      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
4996      * <li>Field: Any form field (equivalent to {@link #addField})</li>
4997      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
4998      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
4999      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
5000      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
5001      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
5002      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
5003      * </ul>
5004      * @param {Mixed} arg2
5005      * @param {Mixed} etc.
5006      */
5007     add : function(){
5008         var a = arguments, l = a.length;
5009         for(var i = 0; i < l; i++){
5010             this._add(a[i]);
5011         }
5012     },
5013     // private..
5014     _add : function(el) {
5015         
5016         if (el.xtype) {
5017             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
5018         }
5019         
5020         if (el.applyTo){ // some kind of form field
5021             return this.addField(el);
5022         } 
5023         if (el.render){ // some kind of Toolbar.Item
5024             return this.addItem(el);
5025         }
5026         if (typeof el == "string"){ // string
5027             if(el == "separator" || el == "-"){
5028                 return this.addSeparator();
5029             }
5030             if (el == " "){
5031                 return this.addSpacer();
5032             }
5033             if(el == "->"){
5034                 return this.addFill();
5035             }
5036             return this.addText(el);
5037             
5038         }
5039         if(el.tagName){ // element
5040             return this.addElement(el);
5041         }
5042         if(typeof el == "object"){ // must be button config?
5043             return this.addButton(el);
5044         }
5045         // and now what?!?!
5046         return false;
5047         
5048     },
5049     
5050     /**
5051      * Add an Xtype element
5052      * @param {Object} xtype Xtype Object
5053      * @return {Object} created Object
5054      */
5055     addxtype : function(e){
5056         return this.add(e);  
5057     },
5058     
5059     /**
5060      * Returns the Element for this toolbar.
5061      * @return {Roo.Element}
5062      */
5063     getEl : function(){
5064         return this.el;  
5065     },
5066     
5067     /**
5068      * Adds a separator
5069      * @return {Roo.Toolbar.Item} The separator item
5070      */
5071     addSeparator : function(){
5072         return this.addItem(new Roo.Toolbar.Separator());
5073     },
5074
5075     /**
5076      * Adds a spacer element
5077      * @return {Roo.Toolbar.Spacer} The spacer item
5078      */
5079     addSpacer : function(){
5080         return this.addItem(new Roo.Toolbar.Spacer());
5081     },
5082
5083     /**
5084      * Adds a fill element that forces subsequent additions to the right side of the toolbar
5085      * @return {Roo.Toolbar.Fill} The fill item
5086      */
5087     addFill : function(){
5088         return this.addItem(new Roo.Toolbar.Fill());
5089     },
5090
5091     /**
5092      * Adds any standard HTML element to the toolbar
5093      * @param {String/HTMLElement/Element} el The element or id of the element to add
5094      * @return {Roo.Toolbar.Item} The element's item
5095      */
5096     addElement : function(el){
5097         return this.addItem(new Roo.Toolbar.Item(el));
5098     },
5099     /**
5100      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
5101      * @type Roo.util.MixedCollection  
5102      */
5103     items : false,
5104      
5105     /**
5106      * Adds any Toolbar.Item or subclass
5107      * @param {Roo.Toolbar.Item} item
5108      * @return {Roo.Toolbar.Item} The item
5109      */
5110     addItem : function(item){
5111         var td = this.nextBlock();
5112         item.render(td);
5113         this.items.add(item);
5114         return item;
5115     },
5116     
5117     /**
5118      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
5119      * @param {Object/Array} config A button config or array of configs
5120      * @return {Roo.Toolbar.Button/Array}
5121      */
5122     addButton : function(config){
5123         if(config instanceof Array){
5124             var buttons = [];
5125             for(var i = 0, len = config.length; i < len; i++) {
5126                 buttons.push(this.addButton(config[i]));
5127             }
5128             return buttons;
5129         }
5130         var b = config;
5131         if(!(config instanceof Roo.Toolbar.Button)){
5132             b = config.split ?
5133                 new Roo.Toolbar.SplitButton(config) :
5134                 new Roo.Toolbar.Button(config);
5135         }
5136         var td = this.nextBlock();
5137         b.render(td);
5138         this.items.add(b);
5139         return b;
5140     },
5141     
5142     /**
5143      * Adds text to the toolbar
5144      * @param {String} text The text to add
5145      * @return {Roo.Toolbar.Item} The element's item
5146      */
5147     addText : function(text){
5148         return this.addItem(new Roo.Toolbar.TextItem(text));
5149     },
5150     
5151     /**
5152      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
5153      * @param {Number} index The index where the item is to be inserted
5154      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
5155      * @return {Roo.Toolbar.Button/Item}
5156      */
5157     insertButton : function(index, item){
5158         if(item instanceof Array){
5159             var buttons = [];
5160             for(var i = 0, len = item.length; i < len; i++) {
5161                buttons.push(this.insertButton(index + i, item[i]));
5162             }
5163             return buttons;
5164         }
5165         if (!(item instanceof Roo.Toolbar.Button)){
5166            item = new Roo.Toolbar.Button(item);
5167         }
5168         var td = document.createElement("td");
5169         this.tr.insertBefore(td, this.tr.childNodes[index]);
5170         item.render(td);
5171         this.items.insert(index, item);
5172         return item;
5173     },
5174     
5175     /**
5176      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
5177      * @param {Object} config
5178      * @return {Roo.Toolbar.Item} The element's item
5179      */
5180     addDom : function(config, returnEl){
5181         var td = this.nextBlock();
5182         Roo.DomHelper.overwrite(td, config);
5183         var ti = new Roo.Toolbar.Item(td.firstChild);
5184         ti.render(td);
5185         this.items.add(ti);
5186         return ti;
5187     },
5188
5189     /**
5190      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
5191      * @type Roo.util.MixedCollection  
5192      */
5193     fields : false,
5194     
5195     /**
5196      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
5197      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
5198      * @param {Roo.form.Field} field
5199      * @return {Roo.ToolbarItem}
5200      */
5201      
5202       
5203     addField : function(field) {
5204         if (!this.fields) {
5205             var autoId = 0;
5206             this.fields = new Roo.util.MixedCollection(false, function(o){
5207                 return o.id || ("item" + (++autoId));
5208             });
5209
5210         }
5211         
5212         var td = this.nextBlock();
5213         field.render(td);
5214         var ti = new Roo.Toolbar.Item(td.firstChild);
5215         ti.render(td);
5216         this.items.add(ti);
5217         this.fields.add(field);
5218         return ti;
5219     },
5220     /**
5221      * Hide the toolbar
5222      * @method hide
5223      */
5224      
5225       
5226     hide : function()
5227     {
5228         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
5229         this.el.child('div').hide();
5230     },
5231     /**
5232      * Show the toolbar
5233      * @method show
5234      */
5235     show : function()
5236     {
5237         this.el.child('div').show();
5238     },
5239       
5240     // private
5241     nextBlock : function(){
5242         var td = document.createElement("td");
5243         this.tr.appendChild(td);
5244         return td;
5245     },
5246
5247     // private
5248     destroy : function(){
5249         if(this.items){ // rendered?
5250             Roo.destroy.apply(Roo, this.items.items);
5251         }
5252         if(this.fields){ // rendered?
5253             Roo.destroy.apply(Roo, this.fields.items);
5254         }
5255         Roo.Element.uncache(this.el, this.tr);
5256     }
5257 };
5258
5259 /**
5260  * @class Roo.Toolbar.Item
5261  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
5262  * @constructor
5263  * Creates a new Item
5264  * @param {HTMLElement} el 
5265  */
5266 Roo.Toolbar.Item = function(el){
5267     this.el = Roo.getDom(el);
5268     this.id = Roo.id(this.el);
5269     this.hidden = false;
5270 };
5271
5272 Roo.Toolbar.Item.prototype = {
5273     
5274     /**
5275      * Get this item's HTML Element
5276      * @return {HTMLElement}
5277      */
5278     getEl : function(){
5279        return this.el;  
5280     },
5281
5282     // private
5283     render : function(td){
5284         this.td = td;
5285         td.appendChild(this.el);
5286     },
5287     
5288     /**
5289      * Removes and destroys this item.
5290      */
5291     destroy : function(){
5292         this.td.parentNode.removeChild(this.td);
5293     },
5294     
5295     /**
5296      * Shows this item.
5297      */
5298     show: function(){
5299         this.hidden = false;
5300         this.td.style.display = "";
5301     },
5302     
5303     /**
5304      * Hides this item.
5305      */
5306     hide: function(){
5307         this.hidden = true;
5308         this.td.style.display = "none";
5309     },
5310     
5311     /**
5312      * Convenience function for boolean show/hide.
5313      * @param {Boolean} visible true to show/false to hide
5314      */
5315     setVisible: function(visible){
5316         if(visible) {
5317             this.show();
5318         }else{
5319             this.hide();
5320         }
5321     },
5322     
5323     /**
5324      * Try to focus this item.
5325      */
5326     focus : function(){
5327         Roo.fly(this.el).focus();
5328     },
5329     
5330     /**
5331      * Disables this item.
5332      */
5333     disable : function(){
5334         Roo.fly(this.td).addClass("x-item-disabled");
5335         this.disabled = true;
5336         this.el.disabled = true;
5337     },
5338     
5339     /**
5340      * Enables this item.
5341      */
5342     enable : function(){
5343         Roo.fly(this.td).removeClass("x-item-disabled");
5344         this.disabled = false;
5345         this.el.disabled = false;
5346     }
5347 };
5348
5349
5350 /**
5351  * @class Roo.Toolbar.Separator
5352  * @extends Roo.Toolbar.Item
5353  * A simple toolbar separator class
5354  * @constructor
5355  * Creates a new Separator
5356  */
5357 Roo.Toolbar.Separator = function(){
5358     var s = document.createElement("span");
5359     s.className = "ytb-sep";
5360     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
5361 };
5362 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
5363     enable:Roo.emptyFn,
5364     disable:Roo.emptyFn,
5365     focus:Roo.emptyFn
5366 });
5367
5368 /**
5369  * @class Roo.Toolbar.Spacer
5370  * @extends Roo.Toolbar.Item
5371  * A simple element that adds extra horizontal space to a toolbar.
5372  * @constructor
5373  * Creates a new Spacer
5374  */
5375 Roo.Toolbar.Spacer = function(){
5376     var s = document.createElement("div");
5377     s.className = "ytb-spacer";
5378     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
5379 };
5380 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
5381     enable:Roo.emptyFn,
5382     disable:Roo.emptyFn,
5383     focus:Roo.emptyFn
5384 });
5385
5386 /**
5387  * @class Roo.Toolbar.Fill
5388  * @extends Roo.Toolbar.Spacer
5389  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
5390  * @constructor
5391  * Creates a new Spacer
5392  */
5393 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
5394     // private
5395     render : function(td){
5396         td.style.width = '100%';
5397         Roo.Toolbar.Fill.superclass.render.call(this, td);
5398     }
5399 });
5400
5401 /**
5402  * @class Roo.Toolbar.TextItem
5403  * @extends Roo.Toolbar.Item
5404  * A simple class that renders text directly into a toolbar.
5405  * @constructor
5406  * Creates a new TextItem
5407  * @param {String} text
5408  */
5409 Roo.Toolbar.TextItem = function(text){
5410     if (typeof(text) == 'object') {
5411         text = text.text;
5412     }
5413     var s = document.createElement("span");
5414     s.className = "ytb-text";
5415     s.innerHTML = text;
5416     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
5417 };
5418 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
5419     enable:Roo.emptyFn,
5420     disable:Roo.emptyFn,
5421     focus:Roo.emptyFn
5422 });
5423
5424 /**
5425  * @class Roo.Toolbar.Button
5426  * @extends Roo.Button
5427  * A button that renders into a toolbar.
5428  * @constructor
5429  * Creates a new Button
5430  * @param {Object} config A standard {@link Roo.Button} config object
5431  */
5432 Roo.Toolbar.Button = function(config){
5433     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
5434 };
5435 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
5436     render : function(td){
5437         this.td = td;
5438         Roo.Toolbar.Button.superclass.render.call(this, td);
5439     },
5440     
5441     /**
5442      * Removes and destroys this button
5443      */
5444     destroy : function(){
5445         Roo.Toolbar.Button.superclass.destroy.call(this);
5446         this.td.parentNode.removeChild(this.td);
5447     },
5448     
5449     /**
5450      * Shows this button
5451      */
5452     show: function(){
5453         this.hidden = false;
5454         this.td.style.display = "";
5455     },
5456     
5457     /**
5458      * Hides this button
5459      */
5460     hide: function(){
5461         this.hidden = true;
5462         this.td.style.display = "none";
5463     },
5464
5465     /**
5466      * Disables this item
5467      */
5468     disable : function(){
5469         Roo.fly(this.td).addClass("x-item-disabled");
5470         this.disabled = true;
5471     },
5472
5473     /**
5474      * Enables this item
5475      */
5476     enable : function(){
5477         Roo.fly(this.td).removeClass("x-item-disabled");
5478         this.disabled = false;
5479     }
5480 });
5481 // backwards compat
5482 Roo.ToolbarButton = Roo.Toolbar.Button;
5483
5484 /**
5485  * @class Roo.Toolbar.SplitButton
5486  * @extends Roo.SplitButton
5487  * A menu button that renders into a toolbar.
5488  * @constructor
5489  * Creates a new SplitButton
5490  * @param {Object} config A standard {@link Roo.SplitButton} config object
5491  */
5492 Roo.Toolbar.SplitButton = function(config){
5493     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
5494 };
5495 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
5496     render : function(td){
5497         this.td = td;
5498         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
5499     },
5500     
5501     /**
5502      * Removes and destroys this button
5503      */
5504     destroy : function(){
5505         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
5506         this.td.parentNode.removeChild(this.td);
5507     },
5508     
5509     /**
5510      * Shows this button
5511      */
5512     show: function(){
5513         this.hidden = false;
5514         this.td.style.display = "";
5515     },
5516     
5517     /**
5518      * Hides this button
5519      */
5520     hide: function(){
5521         this.hidden = true;
5522         this.td.style.display = "none";
5523     }
5524 });
5525
5526 // backwards compat
5527 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
5528  * Based on:
5529  * Ext JS Library 1.1.1
5530  * Copyright(c) 2006-2007, Ext JS, LLC.
5531  *
5532  * Originally Released Under LGPL - original licence link has changed is not relivant.
5533  *
5534  * Fork - LGPL
5535  * <script type="text/javascript">
5536  */
5537  
5538 /**
5539  * @class Roo.PagingToolbar
5540  * @extends Roo.Toolbar
5541  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
5542  * @constructor
5543  * Create a new PagingToolbar
5544  * @param {Object} config The config object
5545  */
5546 Roo.PagingToolbar = function(el, ds, config)
5547 {
5548     // old args format still supported... - xtype is prefered..
5549     if (typeof(el) == 'object' && el.xtype) {
5550         // created from xtype...
5551         config = el;
5552         ds = el.dataSource;
5553         el = config.container;
5554     }
5555     
5556     
5557     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
5558     this.ds = ds;
5559     this.cursor = 0;
5560     this.renderButtons(this.el);
5561     this.bind(ds);
5562 };
5563
5564 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
5565     /**
5566      * @cfg {Roo.data.Store} dataSource
5567      * The underlying data store providing the paged data
5568      */
5569     /**
5570      * @cfg {String/HTMLElement/Element} container
5571      * container The id or element that will contain the toolbar
5572      */
5573     /**
5574      * @cfg {Boolean} displayInfo
5575      * True to display the displayMsg (defaults to false)
5576      */
5577     /**
5578      * @cfg {Number} pageSize
5579      * The number of records to display per page (defaults to 20)
5580      */
5581     pageSize: 20,
5582     /**
5583      * @cfg {String} displayMsg
5584      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
5585      */
5586     displayMsg : 'Displaying {0} - {1} of {2}',
5587     /**
5588      * @cfg {String} emptyMsg
5589      * The message to display when no records are found (defaults to "No data to display")
5590      */
5591     emptyMsg : 'No data to display',
5592     /**
5593      * Customizable piece of the default paging text (defaults to "Page")
5594      * @type String
5595      */
5596     beforePageText : "Page",
5597     /**
5598      * Customizable piece of the default paging text (defaults to "of %0")
5599      * @type String
5600      */
5601     afterPageText : "of {0}",
5602     /**
5603      * Customizable piece of the default paging text (defaults to "First Page")
5604      * @type String
5605      */
5606     firstText : "First Page",
5607     /**
5608      * Customizable piece of the default paging text (defaults to "Previous Page")
5609      * @type String
5610      */
5611     prevText : "Previous Page",
5612     /**
5613      * Customizable piece of the default paging text (defaults to "Next Page")
5614      * @type String
5615      */
5616     nextText : "Next Page",
5617     /**
5618      * Customizable piece of the default paging text (defaults to "Last Page")
5619      * @type String
5620      */
5621     lastText : "Last Page",
5622     /**
5623      * Customizable piece of the default paging text (defaults to "Refresh")
5624      * @type String
5625      */
5626     refreshText : "Refresh",
5627
5628     // private
5629     renderButtons : function(el){
5630         Roo.PagingToolbar.superclass.render.call(this, el);
5631         this.first = this.addButton({
5632             tooltip: this.firstText,
5633             cls: "x-btn-icon x-grid-page-first",
5634             disabled: true,
5635             handler: this.onClick.createDelegate(this, ["first"])
5636         });
5637         this.prev = this.addButton({
5638             tooltip: this.prevText,
5639             cls: "x-btn-icon x-grid-page-prev",
5640             disabled: true,
5641             handler: this.onClick.createDelegate(this, ["prev"])
5642         });
5643         this.addSeparator();
5644         this.add(this.beforePageText);
5645         this.field = Roo.get(this.addDom({
5646            tag: "input",
5647            type: "text",
5648            size: "3",
5649            value: "1",
5650            cls: "x-grid-page-number"
5651         }).el);
5652         this.field.on("keydown", this.onPagingKeydown, this);
5653         this.field.on("focus", function(){this.dom.select();});
5654         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
5655         this.field.setHeight(18);
5656         this.addSeparator();
5657         this.next = this.addButton({
5658             tooltip: this.nextText,
5659             cls: "x-btn-icon x-grid-page-next",
5660             disabled: true,
5661             handler: this.onClick.createDelegate(this, ["next"])
5662         });
5663         this.last = this.addButton({
5664             tooltip: this.lastText,
5665             cls: "x-btn-icon x-grid-page-last",
5666             disabled: true,
5667             handler: this.onClick.createDelegate(this, ["last"])
5668         });
5669         this.addSeparator();
5670         this.loading = this.addButton({
5671             tooltip: this.refreshText,
5672             cls: "x-btn-icon x-grid-loading",
5673             handler: this.onClick.createDelegate(this, ["refresh"])
5674         });
5675
5676         if(this.displayInfo){
5677             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
5678         }
5679     },
5680
5681     // private
5682     updateInfo : function(){
5683         if(this.displayEl){
5684             var count = this.ds.getCount();
5685             var msg = count == 0 ?
5686                 this.emptyMsg :
5687                 String.format(
5688                     this.displayMsg,
5689                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
5690                 );
5691             this.displayEl.update(msg);
5692         }
5693     },
5694
5695     // private
5696     onLoad : function(ds, r, o){
5697        this.cursor = o.params ? o.params.start : 0;
5698        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
5699
5700        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
5701        this.field.dom.value = ap;
5702        this.first.setDisabled(ap == 1);
5703        this.prev.setDisabled(ap == 1);
5704        this.next.setDisabled(ap == ps);
5705        this.last.setDisabled(ap == ps);
5706        this.loading.enable();
5707        this.updateInfo();
5708     },
5709
5710     // private
5711     getPageData : function(){
5712         var total = this.ds.getTotalCount();
5713         return {
5714             total : total,
5715             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
5716             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
5717         };
5718     },
5719
5720     // private
5721     onLoadError : function(){
5722         this.loading.enable();
5723     },
5724
5725     // private
5726     onPagingKeydown : function(e){
5727         var k = e.getKey();
5728         var d = this.getPageData();
5729         if(k == e.RETURN){
5730             var v = this.field.dom.value, pageNum;
5731             if(!v || isNaN(pageNum = parseInt(v, 10))){
5732                 this.field.dom.value = d.activePage;
5733                 return;
5734             }
5735             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
5736             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
5737             e.stopEvent();
5738         }
5739         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
5740         {
5741           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
5742           this.field.dom.value = pageNum;
5743           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
5744           e.stopEvent();
5745         }
5746         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
5747         {
5748           var v = this.field.dom.value, pageNum; 
5749           var increment = (e.shiftKey) ? 10 : 1;
5750           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
5751             increment *= -1;
5752           if(!v || isNaN(pageNum = parseInt(v, 10))) {
5753             this.field.dom.value = d.activePage;
5754             return;
5755           }
5756           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
5757           {
5758             this.field.dom.value = parseInt(v, 10) + increment;
5759             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
5760             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
5761           }
5762           e.stopEvent();
5763         }
5764     },
5765
5766     // private
5767     beforeLoad : function(){
5768         if(this.loading){
5769             this.loading.disable();
5770         }
5771     },
5772
5773     // private
5774     onClick : function(which){
5775         var ds = this.ds;
5776         switch(which){
5777             case "first":
5778                 ds.load({params:{start: 0, limit: this.pageSize}});
5779             break;
5780             case "prev":
5781                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
5782             break;
5783             case "next":
5784                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
5785             break;
5786             case "last":
5787                 var total = ds.getTotalCount();
5788                 var extra = total % this.pageSize;
5789                 var lastStart = extra ? (total - extra) : total-this.pageSize;
5790                 ds.load({params:{start: lastStart, limit: this.pageSize}});
5791             break;
5792             case "refresh":
5793                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
5794             break;
5795         }
5796     },
5797
5798     /**
5799      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
5800      * @param {Roo.data.Store} store The data store to unbind
5801      */
5802     unbind : function(ds){
5803         ds.un("beforeload", this.beforeLoad, this);
5804         ds.un("load", this.onLoad, this);
5805         ds.un("loadexception", this.onLoadError, this);
5806         ds.un("remove", this.updateInfo, this);
5807         ds.un("add", this.updateInfo, this);
5808         this.ds = undefined;
5809     },
5810
5811     /**
5812      * Binds the paging toolbar to the specified {@link Roo.data.Store}
5813      * @param {Roo.data.Store} store The data store to bind
5814      */
5815     bind : function(ds){
5816         ds.on("beforeload", this.beforeLoad, this);
5817         ds.on("load", this.onLoad, this);
5818         ds.on("loadexception", this.onLoadError, this);
5819         ds.on("remove", this.updateInfo, this);
5820         ds.on("add", this.updateInfo, this);
5821         this.ds = ds;
5822     }
5823 });/*
5824  * Based on:
5825  * Ext JS Library 1.1.1
5826  * Copyright(c) 2006-2007, Ext JS, LLC.
5827  *
5828  * Originally Released Under LGPL - original licence link has changed is not relivant.
5829  *
5830  * Fork - LGPL
5831  * <script type="text/javascript">
5832  */
5833
5834 /**
5835  * @class Roo.Resizable
5836  * @extends Roo.util.Observable
5837  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
5838  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
5839  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
5840  * the element will be wrapped for you automatically.</p>
5841  * <p>Here is the list of valid resize handles:</p>
5842  * <pre>
5843 Value   Description
5844 ------  -------------------
5845  'n'     north
5846  's'     south
5847  'e'     east
5848  'w'     west
5849  'nw'    northwest
5850  'sw'    southwest
5851  'se'    southeast
5852  'ne'    northeast
5853  'all'   all
5854 </pre>
5855  * <p>Here's an example showing the creation of a typical Resizable:</p>
5856  * <pre><code>
5857 var resizer = new Roo.Resizable("element-id", {
5858     handles: 'all',
5859     minWidth: 200,
5860     minHeight: 100,
5861     maxWidth: 500,
5862     maxHeight: 400,
5863     pinned: true
5864 });
5865 resizer.on("resize", myHandler);
5866 </code></pre>
5867  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
5868  * resizer.east.setDisplayed(false);</p>
5869  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
5870  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
5871  * resize operation's new size (defaults to [0, 0])
5872  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
5873  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
5874  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
5875  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
5876  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
5877  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
5878  * @cfg {Number} width The width of the element in pixels (defaults to null)
5879  * @cfg {Number} height The height of the element in pixels (defaults to null)
5880  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
5881  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
5882  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
5883  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
5884  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
5885  * in favor of the handles config option (defaults to false)
5886  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
5887  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
5888  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
5889  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
5890  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
5891  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
5892  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
5893  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
5894  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
5895  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
5896  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
5897  * @constructor
5898  * Create a new resizable component
5899  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
5900  * @param {Object} config configuration options
5901   */
5902 Roo.Resizable = function(el, config){
5903     this.el = Roo.get(el);
5904
5905     if(config && config.wrap){
5906         config.resizeChild = this.el;
5907         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
5908         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
5909         this.el.setStyle("overflow", "hidden");
5910         this.el.setPositioning(config.resizeChild.getPositioning());
5911         config.resizeChild.clearPositioning();
5912         if(!config.width || !config.height){
5913             var csize = config.resizeChild.getSize();
5914             this.el.setSize(csize.width, csize.height);
5915         }
5916         if(config.pinned && !config.adjustments){
5917             config.adjustments = "auto";
5918         }
5919     }
5920
5921     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
5922     this.proxy.unselectable();
5923     this.proxy.enableDisplayMode('block');
5924
5925     Roo.apply(this, config);
5926
5927     if(this.pinned){
5928         this.disableTrackOver = true;
5929         this.el.addClass("x-resizable-pinned");
5930     }
5931     // if the element isn't positioned, make it relative
5932     var position = this.el.getStyle("position");
5933     if(position != "absolute" && position != "fixed"){
5934         this.el.setStyle("position", "relative");
5935     }
5936     if(!this.handles){ // no handles passed, must be legacy style
5937         this.handles = 's,e,se';
5938         if(this.multiDirectional){
5939             this.handles += ',n,w';
5940         }
5941     }
5942     if(this.handles == "all"){
5943         this.handles = "n s e w ne nw se sw";
5944     }
5945     var hs = this.handles.split(/\s*?[,;]\s*?| /);
5946     var ps = Roo.Resizable.positions;
5947     for(var i = 0, len = hs.length; i < len; i++){
5948         if(hs[i] && ps[hs[i]]){
5949             var pos = ps[hs[i]];
5950             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
5951         }
5952     }
5953     // legacy
5954     this.corner = this.southeast;
5955
5956     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
5957         this.updateBox = true;
5958     }
5959
5960     this.activeHandle = null;
5961
5962     if(this.resizeChild){
5963         if(typeof this.resizeChild == "boolean"){
5964             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
5965         }else{
5966             this.resizeChild = Roo.get(this.resizeChild, true);
5967         }
5968     }
5969
5970     if(this.adjustments == "auto"){
5971         var rc = this.resizeChild;
5972         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
5973         if(rc && (hw || hn)){
5974             rc.position("relative");
5975             rc.setLeft(hw ? hw.el.getWidth() : 0);
5976             rc.setTop(hn ? hn.el.getHeight() : 0);
5977         }
5978         this.adjustments = [
5979             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
5980             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
5981         ];
5982     }
5983
5984     if(this.draggable){
5985         this.dd = this.dynamic ?
5986             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
5987         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
5988     }
5989
5990     // public events
5991     this.addEvents({
5992         /**
5993          * @event beforeresize
5994          * Fired before resize is allowed. Set enabled to false to cancel resize.
5995          * @param {Roo.Resizable} this
5996          * @param {Roo.EventObject} e The mousedown event
5997          */
5998         "beforeresize" : true,
5999         /**
6000          * @event resize
6001          * Fired after a resize.
6002          * @param {Roo.Resizable} this
6003          * @param {Number} width The new width
6004          * @param {Number} height The new height
6005          * @param {Roo.EventObject} e The mouseup event
6006          */
6007         "resize" : true
6008     });
6009
6010     if(this.width !== null && this.height !== null){
6011         this.resizeTo(this.width, this.height);
6012     }else{
6013         this.updateChildSize();
6014     }
6015     if(Roo.isIE){
6016         this.el.dom.style.zoom = 1;
6017     }
6018     Roo.Resizable.superclass.constructor.call(this);
6019 };
6020
6021 Roo.extend(Roo.Resizable, Roo.util.Observable, {
6022         resizeChild : false,
6023         adjustments : [0, 0],
6024         minWidth : 5,
6025         minHeight : 5,
6026         maxWidth : 10000,
6027         maxHeight : 10000,
6028         enabled : true,
6029         animate : false,
6030         duration : .35,
6031         dynamic : false,
6032         handles : false,
6033         multiDirectional : false,
6034         disableTrackOver : false,
6035         easing : 'easeOutStrong',
6036         widthIncrement : 0,
6037         heightIncrement : 0,
6038         pinned : false,
6039         width : null,
6040         height : null,
6041         preserveRatio : false,
6042         transparent: false,
6043         minX: 0,
6044         minY: 0,
6045         draggable: false,
6046
6047         /**
6048          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
6049          */
6050         constrainTo: undefined,
6051         /**
6052          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
6053          */
6054         resizeRegion: undefined,
6055
6056
6057     /**
6058      * Perform a manual resize
6059      * @param {Number} width
6060      * @param {Number} height
6061      */
6062     resizeTo : function(width, height){
6063         this.el.setSize(width, height);
6064         this.updateChildSize();
6065         this.fireEvent("resize", this, width, height, null);
6066     },
6067
6068     // private
6069     startSizing : function(e, handle){
6070         this.fireEvent("beforeresize", this, e);
6071         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
6072
6073             if(!this.overlay){
6074                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
6075                 this.overlay.unselectable();
6076                 this.overlay.enableDisplayMode("block");
6077                 this.overlay.on("mousemove", this.onMouseMove, this);
6078                 this.overlay.on("mouseup", this.onMouseUp, this);
6079             }
6080             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
6081
6082             this.resizing = true;
6083             this.startBox = this.el.getBox();
6084             this.startPoint = e.getXY();
6085             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
6086                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
6087
6088             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
6089             this.overlay.show();
6090
6091             if(this.constrainTo) {
6092                 var ct = Roo.get(this.constrainTo);
6093                 this.resizeRegion = ct.getRegion().adjust(
6094                     ct.getFrameWidth('t'),
6095                     ct.getFrameWidth('l'),
6096                     -ct.getFrameWidth('b'),
6097                     -ct.getFrameWidth('r')
6098                 );
6099             }
6100
6101             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
6102             this.proxy.show();
6103             this.proxy.setBox(this.startBox);
6104             if(!this.dynamic){
6105                 this.proxy.setStyle('visibility', 'visible');
6106             }
6107         }
6108     },
6109
6110     // private
6111     onMouseDown : function(handle, e){
6112         if(this.enabled){
6113             e.stopEvent();
6114             this.activeHandle = handle;
6115             this.startSizing(e, handle);
6116         }
6117     },
6118
6119     // private
6120     onMouseUp : function(e){
6121         var size = this.resizeElement();
6122         this.resizing = false;
6123         this.handleOut();
6124         this.overlay.hide();
6125         this.proxy.hide();
6126         this.fireEvent("resize", this, size.width, size.height, e);
6127     },
6128
6129     // private
6130     updateChildSize : function(){
6131         if(this.resizeChild){
6132             var el = this.el;
6133             var child = this.resizeChild;
6134             var adj = this.adjustments;
6135             if(el.dom.offsetWidth){
6136                 var b = el.getSize(true);
6137                 child.setSize(b.width+adj[0], b.height+adj[1]);
6138             }
6139             // Second call here for IE
6140             // The first call enables instant resizing and
6141             // the second call corrects scroll bars if they
6142             // exist
6143             if(Roo.isIE){
6144                 setTimeout(function(){
6145                     if(el.dom.offsetWidth){
6146                         var b = el.getSize(true);
6147                         child.setSize(b.width+adj[0], b.height+adj[1]);
6148                     }
6149                 }, 10);
6150             }
6151         }
6152     },
6153
6154     // private
6155     snap : function(value, inc, min){
6156         if(!inc || !value) return value;
6157         var newValue = value;
6158         var m = value % inc;
6159         if(m > 0){
6160             if(m > (inc/2)){
6161                 newValue = value + (inc-m);
6162             }else{
6163                 newValue = value - m;
6164             }
6165         }
6166         return Math.max(min, newValue);
6167     },
6168
6169     // private
6170     resizeElement : function(){
6171         var box = this.proxy.getBox();
6172         if(this.updateBox){
6173             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
6174         }else{
6175             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
6176         }
6177         this.updateChildSize();
6178         if(!this.dynamic){
6179             this.proxy.hide();
6180         }
6181         return box;
6182     },
6183
6184     // private
6185     constrain : function(v, diff, m, mx){
6186         if(v - diff < m){
6187             diff = v - m;
6188         }else if(v - diff > mx){
6189             diff = mx - v;
6190         }
6191         return diff;
6192     },
6193
6194     // private
6195     onMouseMove : function(e){
6196         if(this.enabled){
6197             try{// try catch so if something goes wrong the user doesn't get hung
6198
6199             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
6200                 return;
6201             }
6202
6203             //var curXY = this.startPoint;
6204             var curSize = this.curSize || this.startBox;
6205             var x = this.startBox.x, y = this.startBox.y;
6206             var ox = x, oy = y;
6207             var w = curSize.width, h = curSize.height;
6208             var ow = w, oh = h;
6209             var mw = this.minWidth, mh = this.minHeight;
6210             var mxw = this.maxWidth, mxh = this.maxHeight;
6211             var wi = this.widthIncrement;
6212             var hi = this.heightIncrement;
6213
6214             var eventXY = e.getXY();
6215             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
6216             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
6217
6218             var pos = this.activeHandle.position;
6219
6220             switch(pos){
6221                 case "east":
6222                     w += diffX;
6223                     w = Math.min(Math.max(mw, w), mxw);
6224                     break;
6225                 case "south":
6226                     h += diffY;
6227                     h = Math.min(Math.max(mh, h), mxh);
6228                     break;
6229                 case "southeast":
6230                     w += diffX;
6231                     h += diffY;
6232                     w = Math.min(Math.max(mw, w), mxw);
6233                     h = Math.min(Math.max(mh, h), mxh);
6234                     break;
6235                 case "north":
6236                     diffY = this.constrain(h, diffY, mh, mxh);
6237                     y += diffY;
6238                     h -= diffY;
6239                     break;
6240                 case "west":
6241                     diffX = this.constrain(w, diffX, mw, mxw);
6242                     x += diffX;
6243                     w -= diffX;
6244                     break;
6245                 case "northeast":
6246                     w += diffX;
6247                     w = Math.min(Math.max(mw, w), mxw);
6248                     diffY = this.constrain(h, diffY, mh, mxh);
6249                     y += diffY;
6250                     h -= diffY;
6251                     break;
6252                 case "northwest":
6253                     diffX = this.constrain(w, diffX, mw, mxw);
6254                     diffY = this.constrain(h, diffY, mh, mxh);
6255                     y += diffY;
6256                     h -= diffY;
6257                     x += diffX;
6258                     w -= diffX;
6259                     break;
6260                case "southwest":
6261                     diffX = this.constrain(w, diffX, mw, mxw);
6262                     h += diffY;
6263                     h = Math.min(Math.max(mh, h), mxh);
6264                     x += diffX;
6265                     w -= diffX;
6266                     break;
6267             }
6268
6269             var sw = this.snap(w, wi, mw);
6270             var sh = this.snap(h, hi, mh);
6271             if(sw != w || sh != h){
6272                 switch(pos){
6273                     case "northeast":
6274                         y -= sh - h;
6275                     break;
6276                     case "north":
6277                         y -= sh - h;
6278                         break;
6279                     case "southwest":
6280                         x -= sw - w;
6281                     break;
6282                     case "west":
6283                         x -= sw - w;
6284                         break;
6285                     case "northwest":
6286                         x -= sw - w;
6287                         y -= sh - h;
6288                     break;
6289                 }
6290                 w = sw;
6291                 h = sh;
6292             }
6293
6294             if(this.preserveRatio){
6295                 switch(pos){
6296                     case "southeast":
6297                     case "east":
6298                         h = oh * (w/ow);
6299                         h = Math.min(Math.max(mh, h), mxh);
6300                         w = ow * (h/oh);
6301                        break;
6302                     case "south":
6303                         w = ow * (h/oh);
6304                         w = Math.min(Math.max(mw, w), mxw);
6305                         h = oh * (w/ow);
6306                         break;
6307                     case "northeast":
6308                         w = ow * (h/oh);
6309                         w = Math.min(Math.max(mw, w), mxw);
6310                         h = oh * (w/ow);
6311                     break;
6312                     case "north":
6313                         var tw = w;
6314                         w = ow * (h/oh);
6315                         w = Math.min(Math.max(mw, w), mxw);
6316                         h = oh * (w/ow);
6317                         x += (tw - w) / 2;
6318                         break;
6319                     case "southwest":
6320                         h = oh * (w/ow);
6321                         h = Math.min(Math.max(mh, h), mxh);
6322                         var tw = w;
6323                         w = ow * (h/oh);
6324                         x += tw - w;
6325                         break;
6326                     case "west":
6327                         var th = h;
6328                         h = oh * (w/ow);
6329                         h = Math.min(Math.max(mh, h), mxh);
6330                         y += (th - h) / 2;
6331                         var tw = w;
6332                         w = ow * (h/oh);
6333                         x += tw - w;
6334                        break;
6335                     case "northwest":
6336                         var tw = w;
6337                         var th = h;
6338                         h = oh * (w/ow);
6339                         h = Math.min(Math.max(mh, h), mxh);
6340                         w = ow * (h/oh);
6341                         y += th - h;
6342                          x += tw - w;
6343                        break;
6344
6345                 }
6346             }
6347             this.proxy.setBounds(x, y, w, h);
6348             if(this.dynamic){
6349                 this.resizeElement();
6350             }
6351             }catch(e){}
6352         }
6353     },
6354
6355     // private
6356     handleOver : function(){
6357         if(this.enabled){
6358             this.el.addClass("x-resizable-over");
6359         }
6360     },
6361
6362     // private
6363     handleOut : function(){
6364         if(!this.resizing){
6365             this.el.removeClass("x-resizable-over");
6366         }
6367     },
6368
6369     /**
6370      * Returns the element this component is bound to.
6371      * @return {Roo.Element}
6372      */
6373     getEl : function(){
6374         return this.el;
6375     },
6376
6377     /**
6378      * Returns the resizeChild element (or null).
6379      * @return {Roo.Element}
6380      */
6381     getResizeChild : function(){
6382         return this.resizeChild;
6383     },
6384
6385     /**
6386      * Destroys this resizable. If the element was wrapped and
6387      * removeEl is not true then the element remains.
6388      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
6389      */
6390     destroy : function(removeEl){
6391         this.proxy.remove();
6392         if(this.overlay){
6393             this.overlay.removeAllListeners();
6394             this.overlay.remove();
6395         }
6396         var ps = Roo.Resizable.positions;
6397         for(var k in ps){
6398             if(typeof ps[k] != "function" && this[ps[k]]){
6399                 var h = this[ps[k]];
6400                 h.el.removeAllListeners();
6401                 h.el.remove();
6402             }
6403         }
6404         if(removeEl){
6405             this.el.update("");
6406             this.el.remove();
6407         }
6408     }
6409 });
6410
6411 // private
6412 // hash to map config positions to true positions
6413 Roo.Resizable.positions = {
6414     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
6415 };
6416
6417 // private
6418 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
6419     if(!this.tpl){
6420         // only initialize the template if resizable is used
6421         var tpl = Roo.DomHelper.createTemplate(
6422             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
6423         );
6424         tpl.compile();
6425         Roo.Resizable.Handle.prototype.tpl = tpl;
6426     }
6427     this.position = pos;
6428     this.rz = rz;
6429     this.el = this.tpl.append(rz.el.dom, [this.position], true);
6430     this.el.unselectable();
6431     if(transparent){
6432         this.el.setOpacity(0);
6433     }
6434     this.el.on("mousedown", this.onMouseDown, this);
6435     if(!disableTrackOver){
6436         this.el.on("mouseover", this.onMouseOver, this);
6437         this.el.on("mouseout", this.onMouseOut, this);
6438     }
6439 };
6440
6441 // private
6442 Roo.Resizable.Handle.prototype = {
6443     afterResize : function(rz){
6444         // do nothing
6445     },
6446     // private
6447     onMouseDown : function(e){
6448         this.rz.onMouseDown(this, e);
6449     },
6450     // private
6451     onMouseOver : function(e){
6452         this.rz.handleOver(this, e);
6453     },
6454     // private
6455     onMouseOut : function(e){
6456         this.rz.handleOut(this, e);
6457     }
6458 };/*
6459  * Based on:
6460  * Ext JS Library 1.1.1
6461  * Copyright(c) 2006-2007, Ext JS, LLC.
6462  *
6463  * Originally Released Under LGPL - original licence link has changed is not relivant.
6464  *
6465  * Fork - LGPL
6466  * <script type="text/javascript">
6467  */
6468
6469 /**
6470  * @class Roo.Editor
6471  * @extends Roo.Component
6472  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
6473  * @constructor
6474  * Create a new Editor
6475  * @param {Roo.form.Field} field The Field object (or descendant)
6476  * @param {Object} config The config object
6477  */
6478 Roo.Editor = function(field, config){
6479     Roo.Editor.superclass.constructor.call(this, config);
6480     this.field = field;
6481     this.addEvents({
6482         /**
6483              * @event beforestartedit
6484              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
6485              * false from the handler of this event.
6486              * @param {Editor} this
6487              * @param {Roo.Element} boundEl The underlying element bound to this editor
6488              * @param {Mixed} value The field value being set
6489              */
6490         "beforestartedit" : true,
6491         /**
6492              * @event startedit
6493              * Fires when this editor is displayed
6494              * @param {Roo.Element} boundEl The underlying element bound to this editor
6495              * @param {Mixed} value The starting field value
6496              */
6497         "startedit" : true,
6498         /**
6499              * @event beforecomplete
6500              * Fires after a change has been made to the field, but before the change is reflected in the underlying
6501              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
6502              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
6503              * event will not fire since no edit actually occurred.
6504              * @param {Editor} this
6505              * @param {Mixed} value The current field value
6506              * @param {Mixed} startValue The original field value
6507              */
6508         "beforecomplete" : true,
6509         /**
6510              * @event complete
6511              * Fires after editing is complete and any changed value has been written to the underlying field.
6512              * @param {Editor} this
6513              * @param {Mixed} value The current field value
6514              * @param {Mixed} startValue The original field value
6515              */
6516         "complete" : true,
6517         /**
6518          * @event specialkey
6519          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6520          * {@link Roo.EventObject#getKey} to determine which key was pressed.
6521          * @param {Roo.form.Field} this
6522          * @param {Roo.EventObject} e The event object
6523          */
6524         "specialkey" : true
6525     });
6526 };
6527
6528 Roo.extend(Roo.Editor, Roo.Component, {
6529     /**
6530      * @cfg {Boolean/String} autosize
6531      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
6532      * or "height" to adopt the height only (defaults to false)
6533      */
6534     /**
6535      * @cfg {Boolean} revertInvalid
6536      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
6537      * validation fails (defaults to true)
6538      */
6539     /**
6540      * @cfg {Boolean} ignoreNoChange
6541      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
6542      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
6543      * will never be ignored.
6544      */
6545     /**
6546      * @cfg {Boolean} hideEl
6547      * False to keep the bound element visible while the editor is displayed (defaults to true)
6548      */
6549     /**
6550      * @cfg {Mixed} value
6551      * The data value of the underlying field (defaults to "")
6552      */
6553     value : "",
6554     /**
6555      * @cfg {String} alignment
6556      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
6557      */
6558     alignment: "c-c?",
6559     /**
6560      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
6561      * for bottom-right shadow (defaults to "frame")
6562      */
6563     shadow : "frame",
6564     /**
6565      * @cfg {Boolean} constrain True to constrain the editor to the viewport
6566      */
6567     constrain : false,
6568     /**
6569      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
6570      */
6571     completeOnEnter : false,
6572     /**
6573      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
6574      */
6575     cancelOnEsc : false,
6576     /**
6577      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
6578      */
6579     updateEl : false,
6580
6581     // private
6582     onRender : function(ct, position){
6583         this.el = new Roo.Layer({
6584             shadow: this.shadow,
6585             cls: "x-editor",
6586             parentEl : ct,
6587             shim : this.shim,
6588             shadowOffset:4,
6589             id: this.id,
6590             constrain: this.constrain
6591         });
6592         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
6593         if(this.field.msgTarget != 'title'){
6594             this.field.msgTarget = 'qtip';
6595         }
6596         this.field.render(this.el);
6597         if(Roo.isGecko){
6598             this.field.el.dom.setAttribute('autocomplete', 'off');
6599         }
6600         this.field.on("specialkey", this.onSpecialKey, this);
6601         if(this.swallowKeys){
6602             this.field.el.swallowEvent(['keydown','keypress']);
6603         }
6604         this.field.show();
6605         this.field.on("blur", this.onBlur, this);
6606         if(this.field.grow){
6607             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
6608         }
6609     },
6610
6611     onSpecialKey : function(field, e){
6612         if(this.completeOnEnter && e.getKey() == e.ENTER){
6613             e.stopEvent();
6614             this.completeEdit();
6615         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
6616             this.cancelEdit();
6617         }else{
6618             this.fireEvent('specialkey', field, e);
6619         }
6620     },
6621
6622     /**
6623      * Starts the editing process and shows the editor.
6624      * @param {String/HTMLElement/Element} el The element to edit
6625      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
6626       * to the innerHTML of el.
6627      */
6628     startEdit : function(el, value){
6629         if(this.editing){
6630             this.completeEdit();
6631         }
6632         this.boundEl = Roo.get(el);
6633         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
6634         if(!this.rendered){
6635             this.render(this.parentEl || document.body);
6636         }
6637         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
6638             return;
6639         }
6640         this.startValue = v;
6641         this.field.setValue(v);
6642         if(this.autoSize){
6643             var sz = this.boundEl.getSize();
6644             switch(this.autoSize){
6645                 case "width":
6646                 this.setSize(sz.width,  "");
6647                 break;
6648                 case "height":
6649                 this.setSize("",  sz.height);
6650                 break;
6651                 default:
6652                 this.setSize(sz.width,  sz.height);
6653             }
6654         }
6655         this.el.alignTo(this.boundEl, this.alignment);
6656         this.editing = true;
6657         if(Roo.QuickTips){
6658             Roo.QuickTips.disable();
6659         }
6660         this.show();
6661     },
6662
6663     /**
6664      * Sets the height and width of this editor.
6665      * @param {Number} width The new width
6666      * @param {Number} height The new height
6667      */
6668     setSize : function(w, h){
6669         this.field.setSize(w, h);
6670         if(this.el){
6671             this.el.sync();
6672         }
6673     },
6674
6675     /**
6676      * Realigns the editor to the bound field based on the current alignment config value.
6677      */
6678     realign : function(){
6679         this.el.alignTo(this.boundEl, this.alignment);
6680     },
6681
6682     /**
6683      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
6684      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
6685      */
6686     completeEdit : function(remainVisible){
6687         if(!this.editing){
6688             return;
6689         }
6690         var v = this.getValue();
6691         if(this.revertInvalid !== false && !this.field.isValid()){
6692             v = this.startValue;
6693             this.cancelEdit(true);
6694         }
6695         if(String(v) === String(this.startValue) && this.ignoreNoChange){
6696             this.editing = false;
6697             this.hide();
6698             return;
6699         }
6700         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
6701             this.editing = false;
6702             if(this.updateEl && this.boundEl){
6703                 this.boundEl.update(v);
6704             }
6705             if(remainVisible !== true){
6706                 this.hide();
6707             }
6708             this.fireEvent("complete", this, v, this.startValue);
6709         }
6710     },
6711
6712     // private
6713     onShow : function(){
6714         this.el.show();
6715         if(this.hideEl !== false){
6716             this.boundEl.hide();
6717         }
6718         this.field.show();
6719         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
6720             this.fixIEFocus = true;
6721             this.deferredFocus.defer(50, this);
6722         }else{
6723             this.field.focus();
6724         }
6725         this.fireEvent("startedit", this.boundEl, this.startValue);
6726     },
6727
6728     deferredFocus : function(){
6729         if(this.editing){
6730             this.field.focus();
6731         }
6732     },
6733
6734     /**
6735      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
6736      * reverted to the original starting value.
6737      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
6738      * cancel (defaults to false)
6739      */
6740     cancelEdit : function(remainVisible){
6741         if(this.editing){
6742             this.setValue(this.startValue);
6743             if(remainVisible !== true){
6744                 this.hide();
6745             }
6746         }
6747     },
6748
6749     // private
6750     onBlur : function(){
6751         if(this.allowBlur !== true && this.editing){
6752             this.completeEdit();
6753         }
6754     },
6755
6756     // private
6757     onHide : function(){
6758         if(this.editing){
6759             this.completeEdit();
6760             return;
6761         }
6762         this.field.blur();
6763         if(this.field.collapse){
6764             this.field.collapse();
6765         }
6766         this.el.hide();
6767         if(this.hideEl !== false){
6768             this.boundEl.show();
6769         }
6770         if(Roo.QuickTips){
6771             Roo.QuickTips.enable();
6772         }
6773     },
6774
6775     /**
6776      * Sets the data value of the editor
6777      * @param {Mixed} value Any valid value supported by the underlying field
6778      */
6779     setValue : function(v){
6780         this.field.setValue(v);
6781     },
6782
6783     /**
6784      * Gets the data value of the editor
6785      * @return {Mixed} The data value
6786      */
6787     getValue : function(){
6788         return this.field.getValue();
6789     }
6790 });/*
6791  * Based on:
6792  * Ext JS Library 1.1.1
6793  * Copyright(c) 2006-2007, Ext JS, LLC.
6794  *
6795  * Originally Released Under LGPL - original licence link has changed is not relivant.
6796  *
6797  * Fork - LGPL
6798  * <script type="text/javascript">
6799  */
6800  
6801 /**
6802  * @class Roo.BasicDialog
6803  * @extends Roo.util.Observable
6804  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
6805  * <pre><code>
6806 var dlg = new Roo.BasicDialog("my-dlg", {
6807     height: 200,
6808     width: 300,
6809     minHeight: 100,
6810     minWidth: 150,
6811     modal: true,
6812     proxyDrag: true,
6813     shadow: true
6814 });
6815 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
6816 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
6817 dlg.addButton('Cancel', dlg.hide, dlg);
6818 dlg.show();
6819 </code></pre>
6820   <b>A Dialog should always be a direct child of the body element.</b>
6821  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
6822  * @cfg {String} title Default text to display in the title bar (defaults to null)
6823  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
6824  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
6825  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
6826  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
6827  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
6828  * (defaults to null with no animation)
6829  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
6830  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
6831  * property for valid values (defaults to 'all')
6832  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
6833  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
6834  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
6835  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
6836  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
6837  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
6838  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
6839  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
6840  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
6841  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
6842  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
6843  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
6844  * draggable = true (defaults to false)
6845  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
6846  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
6847  * shadow (defaults to false)
6848  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
6849  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
6850  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
6851  * @cfg {Array} buttons Array of buttons
6852  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
6853  * @constructor
6854  * Create a new BasicDialog.
6855  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
6856  * @param {Object} config Configuration options
6857  */
6858 Roo.BasicDialog = function(el, config){
6859     this.el = Roo.get(el);
6860     var dh = Roo.DomHelper;
6861     if(!this.el && config && config.autoCreate){
6862         if(typeof config.autoCreate == "object"){
6863             if(!config.autoCreate.id){
6864                 config.autoCreate.id = el;
6865             }
6866             this.el = dh.append(document.body,
6867                         config.autoCreate, true);
6868         }else{
6869             this.el = dh.append(document.body,
6870                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
6871         }
6872     }
6873     el = this.el;
6874     el.setDisplayed(true);
6875     el.hide = this.hideAction;
6876     this.id = el.id;
6877     el.addClass("x-dlg");
6878
6879     Roo.apply(this, config);
6880
6881     this.proxy = el.createProxy("x-dlg-proxy");
6882     this.proxy.hide = this.hideAction;
6883     this.proxy.setOpacity(.5);
6884     this.proxy.hide();
6885
6886     if(config.width){
6887         el.setWidth(config.width);
6888     }
6889     if(config.height){
6890         el.setHeight(config.height);
6891     }
6892     this.size = el.getSize();
6893     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
6894         this.xy = [config.x,config.y];
6895     }else{
6896         this.xy = el.getCenterXY(true);
6897     }
6898     /** The header element @type Roo.Element */
6899     this.header = el.child("> .x-dlg-hd");
6900     /** The body element @type Roo.Element */
6901     this.body = el.child("> .x-dlg-bd");
6902     /** The footer element @type Roo.Element */
6903     this.footer = el.child("> .x-dlg-ft");
6904
6905     if(!this.header){
6906         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
6907     }
6908     if(!this.body){
6909         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
6910     }
6911
6912     this.header.unselectable();
6913     if(this.title){
6914         this.header.update(this.title);
6915     }
6916     // this element allows the dialog to be focused for keyboard event
6917     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
6918     this.focusEl.swallowEvent("click", true);
6919
6920     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
6921
6922     // wrap the body and footer for special rendering
6923     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
6924     if(this.footer){
6925         this.bwrap.dom.appendChild(this.footer.dom);
6926     }
6927
6928     this.bg = this.el.createChild({
6929         tag: "div", cls:"x-dlg-bg",
6930         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
6931     });
6932     this.centerBg = this.bg.child("div.x-dlg-bg-center");
6933
6934
6935     if(this.autoScroll !== false && !this.autoTabs){
6936         this.body.setStyle("overflow", "auto");
6937     }
6938
6939     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
6940
6941     if(this.closable !== false){
6942         this.el.addClass("x-dlg-closable");
6943         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
6944         this.close.on("click", this.closeClick, this);
6945         this.close.addClassOnOver("x-dlg-close-over");
6946     }
6947     if(this.collapsible !== false){
6948         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
6949         this.collapseBtn.on("click", this.collapseClick, this);
6950         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
6951         this.header.on("dblclick", this.collapseClick, this);
6952     }
6953     if(this.resizable !== false){
6954         this.el.addClass("x-dlg-resizable");
6955         this.resizer = new Roo.Resizable(el, {
6956             minWidth: this.minWidth || 80,
6957             minHeight:this.minHeight || 80,
6958             handles: this.resizeHandles || "all",
6959             pinned: true
6960         });
6961         this.resizer.on("beforeresize", this.beforeResize, this);
6962         this.resizer.on("resize", this.onResize, this);
6963     }
6964     if(this.draggable !== false){
6965         el.addClass("x-dlg-draggable");
6966         if (!this.proxyDrag) {
6967             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
6968         }
6969         else {
6970             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
6971         }
6972         dd.setHandleElId(this.header.id);
6973         dd.endDrag = this.endMove.createDelegate(this);
6974         dd.startDrag = this.startMove.createDelegate(this);
6975         dd.onDrag = this.onDrag.createDelegate(this);
6976         dd.scroll = false;
6977         this.dd = dd;
6978     }
6979     if(this.modal){
6980         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
6981         this.mask.enableDisplayMode("block");
6982         this.mask.hide();
6983         this.el.addClass("x-dlg-modal");
6984     }
6985     if(this.shadow){
6986         this.shadow = new Roo.Shadow({
6987             mode : typeof this.shadow == "string" ? this.shadow : "sides",
6988             offset : this.shadowOffset
6989         });
6990     }else{
6991         this.shadowOffset = 0;
6992     }
6993     if(Roo.useShims && this.shim !== false){
6994         this.shim = this.el.createShim();
6995         this.shim.hide = this.hideAction;
6996         this.shim.hide();
6997     }else{
6998         this.shim = false;
6999     }
7000     if(this.autoTabs){
7001         this.initTabs();
7002     }
7003     if (this.buttons) { 
7004         var bts= this.buttons;
7005         this.buttons = [];
7006         Roo.each(bts, function(b) {
7007             this.addButton(b);
7008         }, this);
7009     }
7010     
7011     
7012     this.addEvents({
7013         /**
7014          * @event keydown
7015          * Fires when a key is pressed
7016          * @param {Roo.BasicDialog} this
7017          * @param {Roo.EventObject} e
7018          */
7019         "keydown" : true,
7020         /**
7021          * @event move
7022          * Fires when this dialog is moved by the user.
7023          * @param {Roo.BasicDialog} this
7024          * @param {Number} x The new page X
7025          * @param {Number} y The new page Y
7026          */
7027         "move" : true,
7028         /**
7029          * @event resize
7030          * Fires when this dialog is resized by the user.
7031          * @param {Roo.BasicDialog} this
7032          * @param {Number} width The new width
7033          * @param {Number} height The new height
7034          */
7035         "resize" : true,
7036         /**
7037          * @event beforehide
7038          * Fires before this dialog is hidden.
7039          * @param {Roo.BasicDialog} this
7040          */
7041         "beforehide" : true,
7042         /**
7043          * @event hide
7044          * Fires when this dialog is hidden.
7045          * @param {Roo.BasicDialog} this
7046          */
7047         "hide" : true,
7048         /**
7049          * @event beforeshow
7050          * Fires before this dialog is shown.
7051          * @param {Roo.BasicDialog} this
7052          */
7053         "beforeshow" : true,
7054         /**
7055          * @event show
7056          * Fires when this dialog is shown.
7057          * @param {Roo.BasicDialog} this
7058          */
7059         "show" : true
7060     });
7061     el.on("keydown", this.onKeyDown, this);
7062     el.on("mousedown", this.toFront, this);
7063     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
7064     this.el.hide();
7065     Roo.DialogManager.register(this);
7066     Roo.BasicDialog.superclass.constructor.call(this);
7067 };
7068
7069 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
7070     shadowOffset: Roo.isIE ? 6 : 5,
7071     minHeight: 80,
7072     minWidth: 200,
7073     minButtonWidth: 75,
7074     defaultButton: null,
7075     buttonAlign: "right",
7076     tabTag: 'div',
7077     firstShow: true,
7078
7079     /**
7080      * Sets the dialog title text
7081      * @param {String} text The title text to display
7082      * @return {Roo.BasicDialog} this
7083      */
7084     setTitle : function(text){
7085         this.header.update(text);
7086         return this;
7087     },
7088
7089     // private
7090     closeClick : function(){
7091         this.hide();
7092     },
7093
7094     // private
7095     collapseClick : function(){
7096         this[this.collapsed ? "expand" : "collapse"]();
7097     },
7098
7099     /**
7100      * Collapses the dialog to its minimized state (only the title bar is visible).
7101      * Equivalent to the user clicking the collapse dialog button.
7102      */
7103     collapse : function(){
7104         if(!this.collapsed){
7105             this.collapsed = true;
7106             this.el.addClass("x-dlg-collapsed");
7107             this.restoreHeight = this.el.getHeight();
7108             this.resizeTo(this.el.getWidth(), this.header.getHeight());
7109         }
7110     },
7111
7112     /**
7113      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
7114      * clicking the expand dialog button.
7115      */
7116     expand : function(){
7117         if(this.collapsed){
7118             this.collapsed = false;
7119             this.el.removeClass("x-dlg-collapsed");
7120             this.resizeTo(this.el.getWidth(), this.restoreHeight);
7121         }
7122     },
7123
7124     /**
7125      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
7126      * @return {Roo.TabPanel} The tabs component
7127      */
7128     initTabs : function(){
7129         var tabs = this.getTabs();
7130         while(tabs.getTab(0)){
7131             tabs.removeTab(0);
7132         }
7133         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
7134             var dom = el.dom;
7135             tabs.addTab(Roo.id(dom), dom.title);
7136             dom.title = "";
7137         });
7138         tabs.activate(0);
7139         return tabs;
7140     },
7141
7142     // private
7143     beforeResize : function(){
7144         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
7145     },
7146
7147     // private
7148     onResize : function(){
7149         this.refreshSize();
7150         this.syncBodyHeight();
7151         this.adjustAssets();
7152         this.focus();
7153         this.fireEvent("resize", this, this.size.width, this.size.height);
7154     },
7155
7156     // private
7157     onKeyDown : function(e){
7158         if(this.isVisible()){
7159             this.fireEvent("keydown", this, e);
7160         }
7161     },
7162
7163     /**
7164      * Resizes the dialog.
7165      * @param {Number} width
7166      * @param {Number} height
7167      * @return {Roo.BasicDialog} this
7168      */
7169     resizeTo : function(width, height){
7170         this.el.setSize(width, height);
7171         this.size = {width: width, height: height};
7172         this.syncBodyHeight();
7173         if(this.fixedcenter){
7174             this.center();
7175         }
7176         if(this.isVisible()){
7177             this.constrainXY();
7178             this.adjustAssets();
7179         }
7180         this.fireEvent("resize", this, width, height);
7181         return this;
7182     },
7183
7184
7185     /**
7186      * Resizes the dialog to fit the specified content size.
7187      * @param {Number} width
7188      * @param {Number} height
7189      * @return {Roo.BasicDialog} this
7190      */
7191     setContentSize : function(w, h){
7192         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
7193         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
7194         //if(!this.el.isBorderBox()){
7195             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
7196             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
7197         //}
7198         if(this.tabs){
7199             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
7200             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
7201         }
7202         this.resizeTo(w, h);
7203         return this;
7204     },
7205
7206     /**
7207      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
7208      * executed in response to a particular key being pressed while the dialog is active.
7209      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
7210      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
7211      * @param {Function} fn The function to call
7212      * @param {Object} scope (optional) The scope of the function
7213      * @return {Roo.BasicDialog} this
7214      */
7215     addKeyListener : function(key, fn, scope){
7216         var keyCode, shift, ctrl, alt;
7217         if(typeof key == "object" && !(key instanceof Array)){
7218             keyCode = key["key"];
7219             shift = key["shift"];
7220             ctrl = key["ctrl"];
7221             alt = key["alt"];
7222         }else{
7223             keyCode = key;
7224         }
7225         var handler = function(dlg, e){
7226             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
7227                 var k = e.getKey();
7228                 if(keyCode instanceof Array){
7229                     for(var i = 0, len = keyCode.length; i < len; i++){
7230                         if(keyCode[i] == k){
7231                           fn.call(scope || window, dlg, k, e);
7232                           return;
7233                         }
7234                     }
7235                 }else{
7236                     if(k == keyCode){
7237                         fn.call(scope || window, dlg, k, e);
7238                     }
7239                 }
7240             }
7241         };
7242         this.on("keydown", handler);
7243         return this;
7244     },
7245
7246     /**
7247      * Returns the TabPanel component (creates it if it doesn't exist).
7248      * Note: If you wish to simply check for the existence of tabs without creating them,
7249      * check for a null 'tabs' property.
7250      * @return {Roo.TabPanel} The tabs component
7251      */
7252     getTabs : function(){
7253         if(!this.tabs){
7254             this.el.addClass("x-dlg-auto-tabs");
7255             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
7256             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
7257         }
7258         return this.tabs;
7259     },
7260
7261     /**
7262      * Adds a button to the footer section of the dialog.
7263      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
7264      * object or a valid Roo.DomHelper element config
7265      * @param {Function} handler The function called when the button is clicked
7266      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
7267      * @return {Roo.Button} The new button
7268      */
7269     addButton : function(config, handler, scope){
7270         var dh = Roo.DomHelper;
7271         if(!this.footer){
7272             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
7273         }
7274         if(!this.btnContainer){
7275             var tb = this.footer.createChild({
7276
7277                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
7278                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
7279             }, null, true);
7280             this.btnContainer = tb.firstChild.firstChild.firstChild;
7281         }
7282         var bconfig = {
7283             handler: handler,
7284             scope: scope,
7285             minWidth: this.minButtonWidth,
7286             hideParent:true
7287         };
7288         if(typeof config == "string"){
7289             bconfig.text = config;
7290         }else{
7291             if(config.tag){
7292                 bconfig.dhconfig = config;
7293             }else{
7294                 Roo.apply(bconfig, config);
7295             }
7296         }
7297         var fc = false;
7298         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
7299             bconfig.position = Math.max(0, bconfig.position);
7300             fc = this.btnContainer.childNodes[bconfig.position];
7301         }
7302          
7303         var btn = new Roo.Button(
7304             fc ? 
7305                 this.btnContainer.insertBefore(document.createElement("td"),fc)
7306                 : this.btnContainer.appendChild(document.createElement("td")),
7307             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
7308             bconfig
7309         );
7310         this.syncBodyHeight();
7311         if(!this.buttons){
7312             /**
7313              * Array of all the buttons that have been added to this dialog via addButton
7314              * @type Array
7315              */
7316             this.buttons = [];
7317         }
7318         this.buttons.push(btn);
7319         return btn;
7320     },
7321
7322     /**
7323      * Sets the default button to be focused when the dialog is displayed.
7324      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
7325      * @return {Roo.BasicDialog} this
7326      */
7327     setDefaultButton : function(btn){
7328         this.defaultButton = btn;
7329         return this;
7330     },
7331
7332     // private
7333     getHeaderFooterHeight : function(safe){
7334         var height = 0;
7335         if(this.header){
7336            height += this.header.getHeight();
7337         }
7338         if(this.footer){
7339            var fm = this.footer.getMargins();
7340             height += (this.footer.getHeight()+fm.top+fm.bottom);
7341         }
7342         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
7343         height += this.centerBg.getPadding("tb");
7344         return height;
7345     },
7346
7347     // private
7348     syncBodyHeight : function(){
7349         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
7350         var height = this.size.height - this.getHeaderFooterHeight(false);
7351         bd.setHeight(height-bd.getMargins("tb"));
7352         var hh = this.header.getHeight();
7353         var h = this.size.height-hh;
7354         cb.setHeight(h);
7355         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
7356         bw.setHeight(h-cb.getPadding("tb"));
7357         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
7358         bd.setWidth(bw.getWidth(true));
7359         if(this.tabs){
7360             this.tabs.syncHeight();
7361             if(Roo.isIE){
7362                 this.tabs.el.repaint();
7363             }
7364         }
7365     },
7366
7367     /**
7368      * Restores the previous state of the dialog if Roo.state is configured.
7369      * @return {Roo.BasicDialog} this
7370      */
7371     restoreState : function(){
7372         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
7373         if(box && box.width){
7374             this.xy = [box.x, box.y];
7375             this.resizeTo(box.width, box.height);
7376         }
7377         return this;
7378     },
7379
7380     // private
7381     beforeShow : function(){
7382         this.expand();
7383         if(this.fixedcenter){
7384             this.xy = this.el.getCenterXY(true);
7385         }
7386         if(this.modal){
7387             Roo.get(document.body).addClass("x-body-masked");
7388             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
7389             this.mask.show();
7390         }
7391         this.constrainXY();
7392     },
7393
7394     // private
7395     animShow : function(){
7396         var b = Roo.get(this.animateTarget, true).getBox();
7397         this.proxy.setSize(b.width, b.height);
7398         this.proxy.setLocation(b.x, b.y);
7399         this.proxy.show();
7400         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
7401                     true, .35, this.showEl.createDelegate(this));
7402     },
7403
7404     /**
7405      * Shows the dialog.
7406      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
7407      * @return {Roo.BasicDialog} this
7408      */
7409     show : function(animateTarget){
7410         if (this.fireEvent("beforeshow", this) === false){
7411             return;
7412         }
7413         if(this.syncHeightBeforeShow){
7414             this.syncBodyHeight();
7415         }else if(this.firstShow){
7416             this.firstShow = false;
7417             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
7418         }
7419         this.animateTarget = animateTarget || this.animateTarget;
7420         if(!this.el.isVisible()){
7421             this.beforeShow();
7422             if(this.animateTarget){
7423                 this.animShow();
7424             }else{
7425                 this.showEl();
7426             }
7427         }
7428         return this;
7429     },
7430
7431     // private
7432     showEl : function(){
7433         this.proxy.hide();
7434         this.el.setXY(this.xy);
7435         this.el.show();
7436         this.adjustAssets(true);
7437         this.toFront();
7438         this.focus();
7439         // IE peekaboo bug - fix found by Dave Fenwick
7440         if(Roo.isIE){
7441             this.el.repaint();
7442         }
7443         this.fireEvent("show", this);
7444     },
7445
7446     /**
7447      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
7448      * dialog itself will receive focus.
7449      */
7450     focus : function(){
7451         if(this.defaultButton){
7452             this.defaultButton.focus();
7453         }else{
7454             this.focusEl.focus();
7455         }
7456     },
7457
7458     // private
7459     constrainXY : function(){
7460         if(this.constraintoviewport !== false){
7461             if(!this.viewSize){
7462                 if(this.container){
7463                     var s = this.container.getSize();
7464                     this.viewSize = [s.width, s.height];
7465                 }else{
7466                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
7467                 }
7468             }
7469             var s = Roo.get(this.container||document).getScroll();
7470
7471             var x = this.xy[0], y = this.xy[1];
7472             var w = this.size.width, h = this.size.height;
7473             var vw = this.viewSize[0], vh = this.viewSize[1];
7474             // only move it if it needs it
7475             var moved = false;
7476             // first validate right/bottom
7477             if(x + w > vw+s.left){
7478                 x = vw - w;
7479                 moved = true;
7480             }
7481             if(y + h > vh+s.top){
7482                 y = vh - h;
7483                 moved = true;
7484             }
7485             // then make sure top/left isn't negative
7486             if(x < s.left){
7487                 x = s.left;
7488                 moved = true;
7489             }
7490             if(y < s.top){
7491                 y = s.top;
7492                 moved = true;
7493             }
7494             if(moved){
7495                 // cache xy
7496                 this.xy = [x, y];
7497                 if(this.isVisible()){
7498                     this.el.setLocation(x, y);
7499                     this.adjustAssets();
7500                 }
7501             }
7502         }
7503     },
7504
7505     // private
7506     onDrag : function(){
7507         if(!this.proxyDrag){
7508             this.xy = this.el.getXY();
7509             this.adjustAssets();
7510         }
7511     },
7512
7513     // private
7514     adjustAssets : function(doShow){
7515         var x = this.xy[0], y = this.xy[1];
7516         var w = this.size.width, h = this.size.height;
7517         if(doShow === true){
7518             if(this.shadow){
7519                 this.shadow.show(this.el);
7520             }
7521             if(this.shim){
7522                 this.shim.show();
7523             }
7524         }
7525         if(this.shadow && this.shadow.isVisible()){
7526             this.shadow.show(this.el);
7527         }
7528         if(this.shim && this.shim.isVisible()){
7529             this.shim.setBounds(x, y, w, h);
7530         }
7531     },
7532
7533     // private
7534     adjustViewport : function(w, h){
7535         if(!w || !h){
7536             w = Roo.lib.Dom.getViewWidth();
7537             h = Roo.lib.Dom.getViewHeight();
7538         }
7539         // cache the size
7540         this.viewSize = [w, h];
7541         if(this.modal && this.mask.isVisible()){
7542             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
7543             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
7544         }
7545         if(this.isVisible()){
7546             this.constrainXY();
7547         }
7548     },
7549
7550     /**
7551      * Destroys this dialog and all its supporting elements (including any tabs, shim,
7552      * shadow, proxy, mask, etc.)  Also removes all event listeners.
7553      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
7554      */
7555     destroy : function(removeEl){
7556         if(this.isVisible()){
7557             this.animateTarget = null;
7558             this.hide();
7559         }
7560         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
7561         if(this.tabs){
7562             this.tabs.destroy(removeEl);
7563         }
7564         Roo.destroy(
7565              this.shim,
7566              this.proxy,
7567              this.resizer,
7568              this.close,
7569              this.mask
7570         );
7571         if(this.dd){
7572             this.dd.unreg();
7573         }
7574         if(this.buttons){
7575            for(var i = 0, len = this.buttons.length; i < len; i++){
7576                this.buttons[i].destroy();
7577            }
7578         }
7579         this.el.removeAllListeners();
7580         if(removeEl === true){
7581             this.el.update("");
7582             this.el.remove();
7583         }
7584         Roo.DialogManager.unregister(this);
7585     },
7586
7587     // private
7588     startMove : function(){
7589         if(this.proxyDrag){
7590             this.proxy.show();
7591         }
7592         if(this.constraintoviewport !== false){
7593             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
7594         }
7595     },
7596
7597     // private
7598     endMove : function(){
7599         if(!this.proxyDrag){
7600             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
7601         }else{
7602             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
7603             this.proxy.hide();
7604         }
7605         this.refreshSize();
7606         this.adjustAssets();
7607         this.focus();
7608         this.fireEvent("move", this, this.xy[0], this.xy[1]);
7609     },
7610
7611     /**
7612      * Brings this dialog to the front of any other visible dialogs
7613      * @return {Roo.BasicDialog} this
7614      */
7615     toFront : function(){
7616         Roo.DialogManager.bringToFront(this);
7617         return this;
7618     },
7619
7620     /**
7621      * Sends this dialog to the back (under) of any other visible dialogs
7622      * @return {Roo.BasicDialog} this
7623      */
7624     toBack : function(){
7625         Roo.DialogManager.sendToBack(this);
7626         return this;
7627     },
7628
7629     /**
7630      * Centers this dialog in the viewport
7631      * @return {Roo.BasicDialog} this
7632      */
7633     center : function(){
7634         var xy = this.el.getCenterXY(true);
7635         this.moveTo(xy[0], xy[1]);
7636         return this;
7637     },
7638
7639     /**
7640      * Moves the dialog's top-left corner to the specified point
7641      * @param {Number} x
7642      * @param {Number} y
7643      * @return {Roo.BasicDialog} this
7644      */
7645     moveTo : function(x, y){
7646         this.xy = [x,y];
7647         if(this.isVisible()){
7648             this.el.setXY(this.xy);
7649             this.adjustAssets();
7650         }
7651         return this;
7652     },
7653
7654     /**
7655      * Aligns the dialog to the specified element
7656      * @param {String/HTMLElement/Roo.Element} element The element to align to.
7657      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
7658      * @param {Array} offsets (optional) Offset the positioning by [x, y]
7659      * @return {Roo.BasicDialog} this
7660      */
7661     alignTo : function(element, position, offsets){
7662         this.xy = this.el.getAlignToXY(element, position, offsets);
7663         if(this.isVisible()){
7664             this.el.setXY(this.xy);
7665             this.adjustAssets();
7666         }
7667         return this;
7668     },
7669
7670     /**
7671      * Anchors an element to another element and realigns it when the window is resized.
7672      * @param {String/HTMLElement/Roo.Element} element The element to align to.
7673      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
7674      * @param {Array} offsets (optional) Offset the positioning by [x, y]
7675      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
7676      * is a number, it is used as the buffer delay (defaults to 50ms).
7677      * @return {Roo.BasicDialog} this
7678      */
7679     anchorTo : function(el, alignment, offsets, monitorScroll){
7680         var action = function(){
7681             this.alignTo(el, alignment, offsets);
7682         };
7683         Roo.EventManager.onWindowResize(action, this);
7684         var tm = typeof monitorScroll;
7685         if(tm != 'undefined'){
7686             Roo.EventManager.on(window, 'scroll', action, this,
7687                 {buffer: tm == 'number' ? monitorScroll : 50});
7688         }
7689         action.call(this);
7690         return this;
7691     },
7692
7693     /**
7694      * Returns true if the dialog is visible
7695      * @return {Boolean}
7696      */
7697     isVisible : function(){
7698         return this.el.isVisible();
7699     },
7700
7701     // private
7702     animHide : function(callback){
7703         var b = Roo.get(this.animateTarget).getBox();
7704         this.proxy.show();
7705         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
7706         this.el.hide();
7707         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
7708                     this.hideEl.createDelegate(this, [callback]));
7709     },
7710
7711     /**
7712      * Hides the dialog.
7713      * @param {Function} callback (optional) Function to call when the dialog is hidden
7714      * @return {Roo.BasicDialog} this
7715      */
7716     hide : function(callback){
7717         if (this.fireEvent("beforehide", this) === false){
7718             return;
7719         }
7720         if(this.shadow){
7721             this.shadow.hide();
7722         }
7723         if(this.shim) {
7724           this.shim.hide();
7725         }
7726         if(this.animateTarget){
7727            this.animHide(callback);
7728         }else{
7729             this.el.hide();
7730             this.hideEl(callback);
7731         }
7732         return this;
7733     },
7734
7735     // private
7736     hideEl : function(callback){
7737         this.proxy.hide();
7738         if(this.modal){
7739             this.mask.hide();
7740             Roo.get(document.body).removeClass("x-body-masked");
7741         }
7742         this.fireEvent("hide", this);
7743         if(typeof callback == "function"){
7744             callback();
7745         }
7746     },
7747
7748     // private
7749     hideAction : function(){
7750         this.setLeft("-10000px");
7751         this.setTop("-10000px");
7752         this.setStyle("visibility", "hidden");
7753     },
7754
7755     // private
7756     refreshSize : function(){
7757         this.size = this.el.getSize();
7758         this.xy = this.el.getXY();
7759         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
7760     },
7761
7762     // private
7763     // z-index is managed by the DialogManager and may be overwritten at any time
7764     setZIndex : function(index){
7765         if(this.modal){
7766             this.mask.setStyle("z-index", index);
7767         }
7768         if(this.shim){
7769             this.shim.setStyle("z-index", ++index);
7770         }
7771         if(this.shadow){
7772             this.shadow.setZIndex(++index);
7773         }
7774         this.el.setStyle("z-index", ++index);
7775         if(this.proxy){
7776             this.proxy.setStyle("z-index", ++index);
7777         }
7778         if(this.resizer){
7779             this.resizer.proxy.setStyle("z-index", ++index);
7780         }
7781
7782         this.lastZIndex = index;
7783     },
7784
7785     /**
7786      * Returns the element for this dialog
7787      * @return {Roo.Element} The underlying dialog Element
7788      */
7789     getEl : function(){
7790         return this.el;
7791     }
7792 });
7793
7794 /**
7795  * @class Roo.DialogManager
7796  * Provides global access to BasicDialogs that have been created and
7797  * support for z-indexing (layering) multiple open dialogs.
7798  */
7799 Roo.DialogManager = function(){
7800     var list = {};
7801     var accessList = [];
7802     var front = null;
7803
7804     // private
7805     var sortDialogs = function(d1, d2){
7806         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
7807     };
7808
7809     // private
7810     var orderDialogs = function(){
7811         accessList.sort(sortDialogs);
7812         var seed = Roo.DialogManager.zseed;
7813         for(var i = 0, len = accessList.length; i < len; i++){
7814             var dlg = accessList[i];
7815             if(dlg){
7816                 dlg.setZIndex(seed + (i*10));
7817             }
7818         }
7819     };
7820
7821     return {
7822         /**
7823          * The starting z-index for BasicDialogs (defaults to 9000)
7824          * @type Number The z-index value
7825          */
7826         zseed : 9000,
7827
7828         // private
7829         register : function(dlg){
7830             list[dlg.id] = dlg;
7831             accessList.push(dlg);
7832         },
7833
7834         // private
7835         unregister : function(dlg){
7836             delete list[dlg.id];
7837             var i=0;
7838             var len=0;
7839             if(!accessList.indexOf){
7840                 for(  i = 0, len = accessList.length; i < len; i++){
7841                     if(accessList[i] == dlg){
7842                         accessList.splice(i, 1);
7843                         return;
7844                     }
7845                 }
7846             }else{
7847                  i = accessList.indexOf(dlg);
7848                 if(i != -1){
7849                     accessList.splice(i, 1);
7850                 }
7851             }
7852         },
7853
7854         /**
7855          * Gets a registered dialog by id
7856          * @param {String/Object} id The id of the dialog or a dialog
7857          * @return {Roo.BasicDialog} this
7858          */
7859         get : function(id){
7860             return typeof id == "object" ? id : list[id];
7861         },
7862
7863         /**
7864          * Brings the specified dialog to the front
7865          * @param {String/Object} dlg The id of the dialog or a dialog
7866          * @return {Roo.BasicDialog} this
7867          */
7868         bringToFront : function(dlg){
7869             dlg = this.get(dlg);
7870             if(dlg != front){
7871                 front = dlg;
7872                 dlg._lastAccess = new Date().getTime();
7873                 orderDialogs();
7874             }
7875             return dlg;
7876         },
7877
7878         /**
7879          * Sends the specified dialog to the back
7880          * @param {String/Object} dlg The id of the dialog or a dialog
7881          * @return {Roo.BasicDialog} this
7882          */
7883         sendToBack : function(dlg){
7884             dlg = this.get(dlg);
7885             dlg._lastAccess = -(new Date().getTime());
7886             orderDialogs();
7887             return dlg;
7888         },
7889
7890         /**
7891          * Hides all dialogs
7892          */
7893         hideAll : function(){
7894             for(var id in list){
7895                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
7896                     list[id].hide();
7897                 }
7898             }
7899         }
7900     };
7901 }();
7902
7903 /**
7904  * @class Roo.LayoutDialog
7905  * @extends Roo.BasicDialog
7906  * Dialog which provides adjustments for working with a layout in a Dialog.
7907  * Add your necessary layout config options to the dialog's config.<br>
7908  * Example usage (including a nested layout):
7909  * <pre><code>
7910 if(!dialog){
7911     dialog = new Roo.LayoutDialog("download-dlg", {
7912         modal: true,
7913         width:600,
7914         height:450,
7915         shadow:true,
7916         minWidth:500,
7917         minHeight:350,
7918         autoTabs:true,
7919         proxyDrag:true,
7920         // layout config merges with the dialog config
7921         center:{
7922             tabPosition: "top",
7923             alwaysShowTabs: true
7924         }
7925     });
7926     dialog.addKeyListener(27, dialog.hide, dialog);
7927     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
7928     dialog.addButton("Build It!", this.getDownload, this);
7929
7930     // we can even add nested layouts
7931     var innerLayout = new Roo.BorderLayout("dl-inner", {
7932         east: {
7933             initialSize: 200,
7934             autoScroll:true,
7935             split:true
7936         },
7937         center: {
7938             autoScroll:true
7939         }
7940     });
7941     innerLayout.beginUpdate();
7942     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
7943     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
7944     innerLayout.endUpdate(true);
7945
7946     var layout = dialog.getLayout();
7947     layout.beginUpdate();
7948     layout.add("center", new Roo.ContentPanel("standard-panel",
7949                         {title: "Download the Source", fitToFrame:true}));
7950     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
7951                {title: "Build your own roo.js"}));
7952     layout.getRegion("center").showPanel(sp);
7953     layout.endUpdate();
7954 }
7955 </code></pre>
7956     * @constructor
7957     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
7958     * @param {Object} config configuration options
7959   */
7960 Roo.LayoutDialog = function(el, cfg){
7961     
7962     var config=  cfg;
7963     if (typeof(cfg) == 'undefined') {
7964         config = Roo.apply({}, el);
7965         el = Roo.get( document.documentElement || document.body).createChild();
7966         //config.autoCreate = true;
7967     }
7968     
7969     
7970     config.autoTabs = false;
7971     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
7972     this.body.setStyle({overflow:"hidden", position:"relative"});
7973     this.layout = new Roo.BorderLayout(this.body.dom, config);
7974     this.layout.monitorWindowResize = false;
7975     this.el.addClass("x-dlg-auto-layout");
7976     // fix case when center region overwrites center function
7977     this.center = Roo.BasicDialog.prototype.center;
7978     this.on("show", this.layout.layout, this.layout, true);
7979     if (config.items) {
7980         var xitems = config.items;
7981         delete config.items;
7982         Roo.each(xitems, this.addxtype, this);
7983     }
7984     
7985     
7986 };
7987 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
7988     /**
7989      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
7990      * @deprecated
7991      */
7992     endUpdate : function(){
7993         this.layout.endUpdate();
7994     },
7995
7996     /**
7997      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
7998      *  @deprecated
7999      */
8000     beginUpdate : function(){
8001         this.layout.beginUpdate();
8002     },
8003
8004     /**
8005      * Get the BorderLayout for this dialog
8006      * @return {Roo.BorderLayout}
8007      */
8008     getLayout : function(){
8009         return this.layout;
8010     },
8011
8012     showEl : function(){
8013         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
8014         if(Roo.isIE7){
8015             this.layout.layout();
8016         }
8017     },
8018
8019     // private
8020     // Use the syncHeightBeforeShow config option to control this automatically
8021     syncBodyHeight : function(){
8022         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
8023         if(this.layout){this.layout.layout();}
8024     },
8025     
8026       /**
8027      * Add an xtype element (actually adds to the layout.)
8028      * @return {Object} xdata xtype object data.
8029      */
8030     
8031     addxtype : function(c) {
8032         return this.layout.addxtype(c);
8033     }
8034 });/*
8035  * Based on:
8036  * Ext JS Library 1.1.1
8037  * Copyright(c) 2006-2007, Ext JS, LLC.
8038  *
8039  * Originally Released Under LGPL - original licence link has changed is not relivant.
8040  *
8041  * Fork - LGPL
8042  * <script type="text/javascript">
8043  */
8044  
8045 /**
8046  * @class Roo.MessageBox
8047  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
8048  * Example usage:
8049  *<pre><code>
8050 // Basic alert:
8051 Roo.Msg.alert('Status', 'Changes saved successfully.');
8052
8053 // Prompt for user data:
8054 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
8055     if (btn == 'ok'){
8056         // process text value...
8057     }
8058 });
8059
8060 // Show a dialog using config options:
8061 Roo.Msg.show({
8062    title:'Save Changes?',
8063    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
8064    buttons: Roo.Msg.YESNOCANCEL,
8065    fn: processResult,
8066    animEl: 'elId'
8067 });
8068 </code></pre>
8069  * @singleton
8070  */
8071 Roo.MessageBox = function(){
8072     var dlg, opt, mask, waitTimer;
8073     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
8074     var buttons, activeTextEl, bwidth;
8075
8076     // private
8077     var handleButton = function(button){
8078         dlg.hide();
8079         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
8080     };
8081
8082     // private
8083     var handleHide = function(){
8084         if(opt && opt.cls){
8085             dlg.el.removeClass(opt.cls);
8086         }
8087         if(waitTimer){
8088             Roo.TaskMgr.stop(waitTimer);
8089             waitTimer = null;
8090         }
8091     };
8092
8093     // private
8094     var updateButtons = function(b){
8095         var width = 0;
8096         if(!b){
8097             buttons["ok"].hide();
8098             buttons["cancel"].hide();
8099             buttons["yes"].hide();
8100             buttons["no"].hide();
8101             dlg.footer.dom.style.display = 'none';
8102             return width;
8103         }
8104         dlg.footer.dom.style.display = '';
8105         for(var k in buttons){
8106             if(typeof buttons[k] != "function"){
8107                 if(b[k]){
8108                     buttons[k].show();
8109                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
8110                     width += buttons[k].el.getWidth()+15;
8111                 }else{
8112                     buttons[k].hide();
8113                 }
8114             }
8115         }
8116         return width;
8117     };
8118
8119     // private
8120     var handleEsc = function(d, k, e){
8121         if(opt && opt.closable !== false){
8122             dlg.hide();
8123         }
8124         if(e){
8125             e.stopEvent();
8126         }
8127     };
8128
8129     return {
8130         /**
8131          * Returns a reference to the underlying {@link Roo.BasicDialog} element
8132          * @return {Roo.BasicDialog} The BasicDialog element
8133          */
8134         getDialog : function(){
8135            if(!dlg){
8136                 dlg = new Roo.BasicDialog("x-msg-box", {
8137                     autoCreate : true,
8138                     shadow: true,
8139                     draggable: true,
8140                     resizable:false,
8141                     constraintoviewport:false,
8142                     fixedcenter:true,
8143                     collapsible : false,
8144                     shim:true,
8145                     modal: true,
8146                     width:400, height:100,
8147                     buttonAlign:"center",
8148                     closeClick : function(){
8149                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
8150                             handleButton("no");
8151                         }else{
8152                             handleButton("cancel");
8153                         }
8154                     }
8155                 });
8156                 dlg.on("hide", handleHide);
8157                 mask = dlg.mask;
8158                 dlg.addKeyListener(27, handleEsc);
8159                 buttons = {};
8160                 var bt = this.buttonText;
8161                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
8162                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
8163                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
8164                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
8165                 bodyEl = dlg.body.createChild({
8166
8167                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
8168                 });
8169                 msgEl = bodyEl.dom.firstChild;
8170                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
8171                 textboxEl.enableDisplayMode();
8172                 textboxEl.addKeyListener([10,13], function(){
8173                     if(dlg.isVisible() && opt && opt.buttons){
8174                         if(opt.buttons.ok){
8175                             handleButton("ok");
8176                         }else if(opt.buttons.yes){
8177                             handleButton("yes");
8178                         }
8179                     }
8180                 });
8181                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
8182                 textareaEl.enableDisplayMode();
8183                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
8184                 progressEl.enableDisplayMode();
8185                 var pf = progressEl.dom.firstChild;
8186                 if (pf) {
8187                     pp = Roo.get(pf.firstChild);
8188                     pp.setHeight(pf.offsetHeight);
8189                 }
8190                 
8191             }
8192             return dlg;
8193         },
8194
8195         /**
8196          * Updates the message box body text
8197          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
8198          * the XHTML-compliant non-breaking space character '&amp;#160;')
8199          * @return {Roo.MessageBox} This message box
8200          */
8201         updateText : function(text){
8202             if(!dlg.isVisible() && !opt.width){
8203                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
8204             }
8205             msgEl.innerHTML = text || '&#160;';
8206             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
8207                         Math.max(opt.minWidth || this.minWidth, bwidth));
8208             if(opt.prompt){
8209                 activeTextEl.setWidth(w);
8210             }
8211             if(dlg.isVisible()){
8212                 dlg.fixedcenter = false;
8213             }
8214             dlg.setContentSize(w, bodyEl.getHeight());
8215             if(dlg.isVisible()){
8216                 dlg.fixedcenter = true;
8217             }
8218             return this;
8219         },
8220
8221         /**
8222          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
8223          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
8224          * @param {Number} value Any number between 0 and 1 (e.g., .5)
8225          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
8226          * @return {Roo.MessageBox} This message box
8227          */
8228         updateProgress : function(value, text){
8229             if(text){
8230                 this.updateText(text);
8231             }
8232             if (pp) { // weird bug on my firefox - for some reason this is not defined
8233                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
8234             }
8235             return this;
8236         },        
8237
8238         /**
8239          * Returns true if the message box is currently displayed
8240          * @return {Boolean} True if the message box is visible, else false
8241          */
8242         isVisible : function(){
8243             return dlg && dlg.isVisible();  
8244         },
8245
8246         /**
8247          * Hides the message box if it is displayed
8248          */
8249         hide : function(){
8250             if(this.isVisible()){
8251                 dlg.hide();
8252             }  
8253         },
8254
8255         /**
8256          * Displays a new message box, or reinitializes an existing message box, based on the config options
8257          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
8258          * The following config object properties are supported:
8259          * <pre>
8260 Property    Type             Description
8261 ----------  ---------------  ------------------------------------------------------------------------------------
8262 animEl            String/Element   An id or Element from which the message box should animate as it opens and
8263                                    closes (defaults to undefined)
8264 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
8265                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
8266 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
8267                                    progress and wait dialogs will ignore this property and always hide the
8268                                    close button as they can only be closed programmatically.
8269 cls               String           A custom CSS class to apply to the message box element
8270 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
8271                                    displayed (defaults to 75)
8272 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
8273                                    function will be btn (the name of the button that was clicked, if applicable,
8274                                    e.g. "ok"), and text (the value of the active text field, if applicable).
8275                                    Progress and wait dialogs will ignore this option since they do not respond to
8276                                    user actions and can only be closed programmatically, so any required function
8277                                    should be called by the same code after it closes the dialog.
8278 icon              String           A CSS class that provides a background image to be used as an icon for
8279                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
8280 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
8281 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
8282 modal             Boolean          False to allow user interaction with the page while the message box is
8283                                    displayed (defaults to true)
8284 msg               String           A string that will replace the existing message box body text (defaults
8285                                    to the XHTML-compliant non-breaking space character '&#160;')
8286 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
8287 progress          Boolean          True to display a progress bar (defaults to false)
8288 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
8289 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
8290 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
8291 title             String           The title text
8292 value             String           The string value to set into the active textbox element if displayed
8293 wait              Boolean          True to display a progress bar (defaults to false)
8294 width             Number           The width of the dialog in pixels
8295 </pre>
8296          *
8297          * Example usage:
8298          * <pre><code>
8299 Roo.Msg.show({
8300    title: 'Address',
8301    msg: 'Please enter your address:',
8302    width: 300,
8303    buttons: Roo.MessageBox.OKCANCEL,
8304    multiline: true,
8305    fn: saveAddress,
8306    animEl: 'addAddressBtn'
8307 });
8308 </code></pre>
8309          * @param {Object} config Configuration options
8310          * @return {Roo.MessageBox} This message box
8311          */
8312         show : function(options){
8313             if(this.isVisible()){
8314                 this.hide();
8315             }
8316             var d = this.getDialog();
8317             opt = options;
8318             d.setTitle(opt.title || "&#160;");
8319             d.close.setDisplayed(opt.closable !== false);
8320             activeTextEl = textboxEl;
8321             opt.prompt = opt.prompt || (opt.multiline ? true : false);
8322             if(opt.prompt){
8323                 if(opt.multiline){
8324                     textboxEl.hide();
8325                     textareaEl.show();
8326                     textareaEl.setHeight(typeof opt.multiline == "number" ?
8327                         opt.multiline : this.defaultTextHeight);
8328                     activeTextEl = textareaEl;
8329                 }else{
8330                     textboxEl.show();
8331                     textareaEl.hide();
8332                 }
8333             }else{
8334                 textboxEl.hide();
8335                 textareaEl.hide();
8336             }
8337             progressEl.setDisplayed(opt.progress === true);
8338             this.updateProgress(0);
8339             activeTextEl.dom.value = opt.value || "";
8340             if(opt.prompt){
8341                 dlg.setDefaultButton(activeTextEl);
8342             }else{
8343                 var bs = opt.buttons;
8344                 var db = null;
8345                 if(bs && bs.ok){
8346                     db = buttons["ok"];
8347                 }else if(bs && bs.yes){
8348                     db = buttons["yes"];
8349                 }
8350                 dlg.setDefaultButton(db);
8351             }
8352             bwidth = updateButtons(opt.buttons);
8353             this.updateText(opt.msg);
8354             if(opt.cls){
8355                 d.el.addClass(opt.cls);
8356             }
8357             d.proxyDrag = opt.proxyDrag === true;
8358             d.modal = opt.modal !== false;
8359             d.mask = opt.modal !== false ? mask : false;
8360             if(!d.isVisible()){
8361                 // force it to the end of the z-index stack so it gets a cursor in FF
8362                 document.body.appendChild(dlg.el.dom);
8363                 d.animateTarget = null;
8364                 d.show(options.animEl);
8365             }
8366             return this;
8367         },
8368
8369         /**
8370          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
8371          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
8372          * and closing the message box when the process is complete.
8373          * @param {String} title The title bar text
8374          * @param {String} msg The message box body text
8375          * @return {Roo.MessageBox} This message box
8376          */
8377         progress : function(title, msg){
8378             this.show({
8379                 title : title,
8380                 msg : msg,
8381                 buttons: false,
8382                 progress:true,
8383                 closable:false,
8384                 minWidth: this.minProgressWidth,
8385                 modal : true
8386             });
8387             return this;
8388         },
8389
8390         /**
8391          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
8392          * If a callback function is passed it will be called after the user clicks the button, and the
8393          * id of the button that was clicked will be passed as the only parameter to the callback
8394          * (could also be the top-right close button).
8395          * @param {String} title The title bar text
8396          * @param {String} msg The message box body text
8397          * @param {Function} fn (optional) The callback function invoked after the message box is closed
8398          * @param {Object} scope (optional) The scope of the callback function
8399          * @return {Roo.MessageBox} This message box
8400          */
8401         alert : function(title, msg, fn, scope){
8402             this.show({
8403                 title : title,
8404                 msg : msg,
8405                 buttons: this.OK,
8406                 fn: fn,
8407                 scope : scope,
8408                 modal : true
8409             });
8410             return this;
8411         },
8412
8413         /**
8414          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
8415          * interaction while waiting for a long-running process to complete that does not have defined intervals.
8416          * You are responsible for closing the message box when the process is complete.
8417          * @param {String} msg The message box body text
8418          * @param {String} title (optional) The title bar text
8419          * @return {Roo.MessageBox} This message box
8420          */
8421         wait : function(msg, title){
8422             this.show({
8423                 title : title,
8424                 msg : msg,
8425                 buttons: false,
8426                 closable:false,
8427                 progress:true,
8428                 modal:true,
8429                 width:300,
8430                 wait:true
8431             });
8432             waitTimer = Roo.TaskMgr.start({
8433                 run: function(i){
8434                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
8435                 },
8436                 interval: 1000
8437             });
8438             return this;
8439         },
8440
8441         /**
8442          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
8443          * If a callback function is passed it will be called after the user clicks either button, and the id of the
8444          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
8445          * @param {String} title The title bar text
8446          * @param {String} msg The message box body text
8447          * @param {Function} fn (optional) The callback function invoked after the message box is closed
8448          * @param {Object} scope (optional) The scope of the callback function
8449          * @return {Roo.MessageBox} This message box
8450          */
8451         confirm : function(title, msg, fn, scope){
8452             this.show({
8453                 title : title,
8454                 msg : msg,
8455                 buttons: this.YESNO,
8456                 fn: fn,
8457                 scope : scope,
8458                 modal : true
8459             });
8460             return this;
8461         },
8462
8463         /**
8464          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
8465          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
8466          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
8467          * (could also be the top-right close button) and the text that was entered will be passed as the two
8468          * parameters to the callback.
8469          * @param {String} title The title bar text
8470          * @param {String} msg The message box body text
8471          * @param {Function} fn (optional) The callback function invoked after the message box is closed
8472          * @param {Object} scope (optional) The scope of the callback function
8473          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
8474          * property, or the height in pixels to create the textbox (defaults to false / single-line)
8475          * @return {Roo.MessageBox} This message box
8476          */
8477         prompt : function(title, msg, fn, scope, multiline){
8478             this.show({
8479                 title : title,
8480                 msg : msg,
8481                 buttons: this.OKCANCEL,
8482                 fn: fn,
8483                 minWidth:250,
8484                 scope : scope,
8485                 prompt:true,
8486                 multiline: multiline,
8487                 modal : true
8488             });
8489             return this;
8490         },
8491
8492         /**
8493          * Button config that displays a single OK button
8494          * @type Object
8495          */
8496         OK : {ok:true},
8497         /**
8498          * Button config that displays Yes and No buttons
8499          * @type Object
8500          */
8501         YESNO : {yes:true, no:true},
8502         /**
8503          * Button config that displays OK and Cancel buttons
8504          * @type Object
8505          */
8506         OKCANCEL : {ok:true, cancel:true},
8507         /**
8508          * Button config that displays Yes, No and Cancel buttons
8509          * @type Object
8510          */
8511         YESNOCANCEL : {yes:true, no:true, cancel:true},
8512
8513         /**
8514          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
8515          * @type Number
8516          */
8517         defaultTextHeight : 75,
8518         /**
8519          * The maximum width in pixels of the message box (defaults to 600)
8520          * @type Number
8521          */
8522         maxWidth : 600,
8523         /**
8524          * The minimum width in pixels of the message box (defaults to 100)
8525          * @type Number
8526          */
8527         minWidth : 100,
8528         /**
8529          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
8530          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
8531          * @type Number
8532          */
8533         minProgressWidth : 250,
8534         /**
8535          * An object containing the default button text strings that can be overriden for localized language support.
8536          * Supported properties are: ok, cancel, yes and no.
8537          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
8538          * @type Object
8539          */
8540         buttonText : {
8541             ok : "OK",
8542             cancel : "Cancel",
8543             yes : "Yes",
8544             no : "No"
8545         }
8546     };
8547 }();
8548
8549 /**
8550  * Shorthand for {@link Roo.MessageBox}
8551  */
8552 Roo.Msg = Roo.MessageBox;/*
8553  * Based on:
8554  * Ext JS Library 1.1.1
8555  * Copyright(c) 2006-2007, Ext JS, LLC.
8556  *
8557  * Originally Released Under LGPL - original licence link has changed is not relivant.
8558  *
8559  * Fork - LGPL
8560  * <script type="text/javascript">
8561  */
8562 /**
8563  * @class Roo.QuickTips
8564  * Provides attractive and customizable tooltips for any element.
8565  * @singleton
8566  */
8567 Roo.QuickTips = function(){
8568     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
8569     var ce, bd, xy, dd;
8570     var visible = false, disabled = true, inited = false;
8571     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
8572     
8573     var onOver = function(e){
8574         if(disabled){
8575             return;
8576         }
8577         var t = e.getTarget();
8578         if(!t || t.nodeType !== 1 || t == document || t == document.body){
8579             return;
8580         }
8581         if(ce && t == ce.el){
8582             clearTimeout(hideProc);
8583             return;
8584         }
8585         if(t && tagEls[t.id]){
8586             tagEls[t.id].el = t;
8587             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
8588             return;
8589         }
8590         var ttp, et = Roo.fly(t);
8591         var ns = cfg.namespace;
8592         if(tm.interceptTitles && t.title){
8593             ttp = t.title;
8594             t.qtip = ttp;
8595             t.removeAttribute("title");
8596             e.preventDefault();
8597         }else{
8598             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
8599         }
8600         if(ttp){
8601             showProc = show.defer(tm.showDelay, tm, [{
8602                 el: t, 
8603                 text: ttp, 
8604                 width: et.getAttributeNS(ns, cfg.width),
8605                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
8606                 title: et.getAttributeNS(ns, cfg.title),
8607                     cls: et.getAttributeNS(ns, cfg.cls)
8608             }]);
8609         }
8610     };
8611     
8612     var onOut = function(e){
8613         clearTimeout(showProc);
8614         var t = e.getTarget();
8615         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
8616             hideProc = setTimeout(hide, tm.hideDelay);
8617         }
8618     };
8619     
8620     var onMove = function(e){
8621         if(disabled){
8622             return;
8623         }
8624         xy = e.getXY();
8625         xy[1] += 18;
8626         if(tm.trackMouse && ce){
8627             el.setXY(xy);
8628         }
8629     };
8630     
8631     var onDown = function(e){
8632         clearTimeout(showProc);
8633         clearTimeout(hideProc);
8634         if(!e.within(el)){
8635             if(tm.hideOnClick){
8636                 hide();
8637                 tm.disable();
8638                 tm.enable.defer(100, tm);
8639             }
8640         }
8641     };
8642     
8643     var getPad = function(){
8644         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
8645     };
8646
8647     var show = function(o){
8648         if(disabled){
8649             return;
8650         }
8651         clearTimeout(dismissProc);
8652         ce = o;
8653         if(removeCls){ // in case manually hidden
8654             el.removeClass(removeCls);
8655             removeCls = null;
8656         }
8657         if(ce.cls){
8658             el.addClass(ce.cls);
8659             removeCls = ce.cls;
8660         }
8661         if(ce.title){
8662             tipTitle.update(ce.title);
8663             tipTitle.show();
8664         }else{
8665             tipTitle.update('');
8666             tipTitle.hide();
8667         }
8668         el.dom.style.width  = tm.maxWidth+'px';
8669         //tipBody.dom.style.width = '';
8670         tipBodyText.update(o.text);
8671         var p = getPad(), w = ce.width;
8672         if(!w){
8673             var td = tipBodyText.dom;
8674             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
8675             if(aw > tm.maxWidth){
8676                 w = tm.maxWidth;
8677             }else if(aw < tm.minWidth){
8678                 w = tm.minWidth;
8679             }else{
8680                 w = aw;
8681             }
8682         }
8683         //tipBody.setWidth(w);
8684         el.setWidth(parseInt(w, 10) + p);
8685         if(ce.autoHide === false){
8686             close.setDisplayed(true);
8687             if(dd){
8688                 dd.unlock();
8689             }
8690         }else{
8691             close.setDisplayed(false);
8692             if(dd){
8693                 dd.lock();
8694             }
8695         }
8696         if(xy){
8697             el.avoidY = xy[1]-18;
8698             el.setXY(xy);
8699         }
8700         if(tm.animate){
8701             el.setOpacity(.1);
8702             el.setStyle("visibility", "visible");
8703             el.fadeIn({callback: afterShow});
8704         }else{
8705             afterShow();
8706         }
8707     };
8708     
8709     var afterShow = function(){
8710         if(ce){
8711             el.show();
8712             esc.enable();
8713             if(tm.autoDismiss && ce.autoHide !== false){
8714                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
8715             }
8716         }
8717     };
8718     
8719     var hide = function(noanim){
8720         clearTimeout(dismissProc);
8721         clearTimeout(hideProc);
8722         ce = null;
8723         if(el.isVisible()){
8724             esc.disable();
8725             if(noanim !== true && tm.animate){
8726                 el.fadeOut({callback: afterHide});
8727             }else{
8728                 afterHide();
8729             } 
8730         }
8731     };
8732     
8733     var afterHide = function(){
8734         el.hide();
8735         if(removeCls){
8736             el.removeClass(removeCls);
8737             removeCls = null;
8738         }
8739     };
8740     
8741     return {
8742         /**
8743         * @cfg {Number} minWidth
8744         * The minimum width of the quick tip (defaults to 40)
8745         */
8746        minWidth : 40,
8747         /**
8748         * @cfg {Number} maxWidth
8749         * The maximum width of the quick tip (defaults to 300)
8750         */
8751        maxWidth : 300,
8752         /**
8753         * @cfg {Boolean} interceptTitles
8754         * True to automatically use the element's DOM title value if available (defaults to false)
8755         */
8756        interceptTitles : false,
8757         /**
8758         * @cfg {Boolean} trackMouse
8759         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
8760         */
8761        trackMouse : false,
8762         /**
8763         * @cfg {Boolean} hideOnClick
8764         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
8765         */
8766        hideOnClick : true,
8767         /**
8768         * @cfg {Number} showDelay
8769         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
8770         */
8771        showDelay : 500,
8772         /**
8773         * @cfg {Number} hideDelay
8774         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
8775         */
8776        hideDelay : 200,
8777         /**
8778         * @cfg {Boolean} autoHide
8779         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
8780         * Used in conjunction with hideDelay.
8781         */
8782        autoHide : true,
8783         /**
8784         * @cfg {Boolean}
8785         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
8786         * (defaults to true).  Used in conjunction with autoDismissDelay.
8787         */
8788        autoDismiss : true,
8789         /**
8790         * @cfg {Number}
8791         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
8792         */
8793        autoDismissDelay : 5000,
8794        /**
8795         * @cfg {Boolean} animate
8796         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
8797         */
8798        animate : false,
8799
8800        /**
8801         * @cfg {String} title
8802         * Title text to display (defaults to '').  This can be any valid HTML markup.
8803         */
8804         title: '',
8805        /**
8806         * @cfg {String} text
8807         * Body text to display (defaults to '').  This can be any valid HTML markup.
8808         */
8809         text : '',
8810        /**
8811         * @cfg {String} cls
8812         * A CSS class to apply to the base quick tip element (defaults to '').
8813         */
8814         cls : '',
8815        /**
8816         * @cfg {Number} width
8817         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
8818         * minWidth or maxWidth.
8819         */
8820         width : null,
8821
8822     /**
8823      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
8824      * or display QuickTips in a page.
8825      */
8826        init : function(){
8827           tm = Roo.QuickTips;
8828           cfg = tm.tagConfig;
8829           if(!inited){
8830               if(!Roo.isReady){ // allow calling of init() before onReady
8831                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
8832                   return;
8833               }
8834               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
8835               el.fxDefaults = {stopFx: true};
8836               // maximum custom styling
8837               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
8838               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
8839               tipTitle = el.child('h3');
8840               tipTitle.enableDisplayMode("block");
8841               tipBody = el.child('div.x-tip-bd');
8842               tipBodyText = el.child('div.x-tip-bd-inner');
8843               //bdLeft = el.child('div.x-tip-bd-left');
8844               //bdRight = el.child('div.x-tip-bd-right');
8845               close = el.child('div.x-tip-close');
8846               close.enableDisplayMode("block");
8847               close.on("click", hide);
8848               var d = Roo.get(document);
8849               d.on("mousedown", onDown);
8850               d.on("mouseover", onOver);
8851               d.on("mouseout", onOut);
8852               d.on("mousemove", onMove);
8853               esc = d.addKeyListener(27, hide);
8854               esc.disable();
8855               if(Roo.dd.DD){
8856                   dd = el.initDD("default", null, {
8857                       onDrag : function(){
8858                           el.sync();  
8859                       }
8860                   });
8861                   dd.setHandleElId(tipTitle.id);
8862                   dd.lock();
8863               }
8864               inited = true;
8865           }
8866           this.enable(); 
8867        },
8868
8869     /**
8870      * Configures a new quick tip instance and assigns it to a target element.  The following config options
8871      * are supported:
8872      * <pre>
8873 Property    Type                   Description
8874 ----------  ---------------------  ------------------------------------------------------------------------
8875 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
8876      * </ul>
8877      * @param {Object} config The config object
8878      */
8879        register : function(config){
8880            var cs = config instanceof Array ? config : arguments;
8881            for(var i = 0, len = cs.length; i < len; i++) {
8882                var c = cs[i];
8883                var target = c.target;
8884                if(target){
8885                    if(target instanceof Array){
8886                        for(var j = 0, jlen = target.length; j < jlen; j++){
8887                            tagEls[target[j]] = c;
8888                        }
8889                    }else{
8890                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
8891                    }
8892                }
8893            }
8894        },
8895
8896     /**
8897      * Removes this quick tip from its element and destroys it.
8898      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
8899      */
8900        unregister : function(el){
8901            delete tagEls[Roo.id(el)];
8902        },
8903
8904     /**
8905      * Enable this quick tip.
8906      */
8907        enable : function(){
8908            if(inited && disabled){
8909                locks.pop();
8910                if(locks.length < 1){
8911                    disabled = false;
8912                }
8913            }
8914        },
8915
8916     /**
8917      * Disable this quick tip.
8918      */
8919        disable : function(){
8920           disabled = true;
8921           clearTimeout(showProc);
8922           clearTimeout(hideProc);
8923           clearTimeout(dismissProc);
8924           if(ce){
8925               hide(true);
8926           }
8927           locks.push(1);
8928        },
8929
8930     /**
8931      * Returns true if the quick tip is enabled, else false.
8932      */
8933        isEnabled : function(){
8934             return !disabled;
8935        },
8936
8937         // private
8938        tagConfig : {
8939            namespace : "ext",
8940            attribute : "qtip",
8941            width : "width",
8942            target : "target",
8943            title : "qtitle",
8944            hide : "hide",
8945            cls : "qclass"
8946        }
8947    };
8948 }();
8949
8950 // backwards compat
8951 Roo.QuickTips.tips = Roo.QuickTips.register;/*
8952  * Based on:
8953  * Ext JS Library 1.1.1
8954  * Copyright(c) 2006-2007, Ext JS, LLC.
8955  *
8956  * Originally Released Under LGPL - original licence link has changed is not relivant.
8957  *
8958  * Fork - LGPL
8959  * <script type="text/javascript">
8960  */
8961  
8962
8963 /**
8964  * @class Roo.tree.TreePanel
8965  * @extends Roo.data.Tree
8966
8967  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
8968  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
8969  * @cfg {Boolean} enableDD true to enable drag and drop
8970  * @cfg {Boolean} enableDrag true to enable just drag
8971  * @cfg {Boolean} enableDrop true to enable just drop
8972  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
8973  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
8974  * @cfg {String} ddGroup The DD group this TreePanel belongs to
8975  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
8976  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
8977  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
8978  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
8979  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
8980  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
8981  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
8982  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
8983  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
8984  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
8985  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
8986  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
8987  * 
8988  * @constructor
8989  * @param {String/HTMLElement/Element} el The container element
8990  * @param {Object} config
8991  */
8992 Roo.tree.TreePanel = function(el, config){
8993     var root = false;
8994     var loader = false;
8995     if (config.root) {
8996         root = config.root;
8997         delete config.root;
8998     }
8999     if (config.loader) {
9000         loader = config.loader;
9001         delete config.loader;
9002     }
9003     
9004     Roo.apply(this, config);
9005     Roo.tree.TreePanel.superclass.constructor.call(this);
9006     this.el = Roo.get(el);
9007     this.el.addClass('x-tree');
9008     //console.log(root);
9009     if (root) {
9010         this.setRootNode( Roo.factory(root, Roo.tree));
9011     }
9012     if (loader) {
9013         this.loader = Roo.factory(loader, Roo.tree);
9014     }
9015    /**
9016     * Read-only. The id of the container element becomes this TreePanel's id.
9017     */
9018    this.id = this.el.id;
9019    this.addEvents({
9020         /**
9021         * @event beforeload
9022         * Fires before a node is loaded, return false to cancel
9023         * @param {Node} node The node being loaded
9024         */
9025         "beforeload" : true,
9026         /**
9027         * @event load
9028         * Fires when a node is loaded
9029         * @param {Node} node The node that was loaded
9030         */
9031         "load" : true,
9032         /**
9033         * @event textchange
9034         * Fires when the text for a node is changed
9035         * @param {Node} node The node
9036         * @param {String} text The new text
9037         * @param {String} oldText The old text
9038         */
9039         "textchange" : true,
9040         /**
9041         * @event beforeexpand
9042         * Fires before a node is expanded, return false to cancel.
9043         * @param {Node} node The node
9044         * @param {Boolean} deep
9045         * @param {Boolean} anim
9046         */
9047         "beforeexpand" : true,
9048         /**
9049         * @event beforecollapse
9050         * Fires before a node is collapsed, return false to cancel.
9051         * @param {Node} node The node
9052         * @param {Boolean} deep
9053         * @param {Boolean} anim
9054         */
9055         "beforecollapse" : true,
9056         /**
9057         * @event expand
9058         * Fires when a node is expanded
9059         * @param {Node} node The node
9060         */
9061         "expand" : true,
9062         /**
9063         * @event disabledchange
9064         * Fires when the disabled status of a node changes
9065         * @param {Node} node The node
9066         * @param {Boolean} disabled
9067         */
9068         "disabledchange" : true,
9069         /**
9070         * @event collapse
9071         * Fires when a node is collapsed
9072         * @param {Node} node The node
9073         */
9074         "collapse" : true,
9075         /**
9076         * @event beforeclick
9077         * Fires before click processing on a node. Return false to cancel the default action.
9078         * @param {Node} node The node
9079         * @param {Roo.EventObject} e The event object
9080         */
9081         "beforeclick":true,
9082         /**
9083         * @event checkchange
9084         * Fires when a node with a checkbox's checked property changes
9085         * @param {Node} this This node
9086         * @param {Boolean} checked
9087         */
9088         "checkchange":true,
9089         /**
9090         * @event click
9091         * Fires when a node is clicked
9092         * @param {Node} node The node
9093         * @param {Roo.EventObject} e The event object
9094         */
9095         "click":true,
9096         /**
9097         * @event dblclick
9098         * Fires when a node is double clicked
9099         * @param {Node} node The node
9100         * @param {Roo.EventObject} e The event object
9101         */
9102         "dblclick":true,
9103         /**
9104         * @event contextmenu
9105         * Fires when a node is right clicked
9106         * @param {Node} node The node
9107         * @param {Roo.EventObject} e The event object
9108         */
9109         "contextmenu":true,
9110         /**
9111         * @event beforechildrenrendered
9112         * Fires right before the child nodes for a node are rendered
9113         * @param {Node} node The node
9114         */
9115         "beforechildrenrendered":true,
9116        /**
9117              * @event startdrag
9118              * Fires when a node starts being dragged
9119              * @param {Roo.tree.TreePanel} this
9120              * @param {Roo.tree.TreeNode} node
9121              * @param {event} e The raw browser event
9122              */ 
9123             "startdrag" : true,
9124             /**
9125              * @event enddrag
9126              * Fires when a drag operation is complete
9127              * @param {Roo.tree.TreePanel} this
9128              * @param {Roo.tree.TreeNode} node
9129              * @param {event} e The raw browser event
9130              */
9131             "enddrag" : true,
9132             /**
9133              * @event dragdrop
9134              * Fires when a dragged node is dropped on a valid DD target
9135              * @param {Roo.tree.TreePanel} this
9136              * @param {Roo.tree.TreeNode} node
9137              * @param {DD} dd The dd it was dropped on
9138              * @param {event} e The raw browser event
9139              */
9140             "dragdrop" : true,
9141             /**
9142              * @event beforenodedrop
9143              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
9144              * passed to handlers has the following properties:<br />
9145              * <ul style="padding:5px;padding-left:16px;">
9146              * <li>tree - The TreePanel</li>
9147              * <li>target - The node being targeted for the drop</li>
9148              * <li>data - The drag data from the drag source</li>
9149              * <li>point - The point of the drop - append, above or below</li>
9150              * <li>source - The drag source</li>
9151              * <li>rawEvent - Raw mouse event</li>
9152              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
9153              * to be inserted by setting them on this object.</li>
9154              * <li>cancel - Set this to true to cancel the drop.</li>
9155              * </ul>
9156              * @param {Object} dropEvent
9157              */
9158             "beforenodedrop" : true,
9159             /**
9160              * @event nodedrop
9161              * Fires after a DD object is dropped on a node in this tree. The dropEvent
9162              * passed to handlers has the following properties:<br />
9163              * <ul style="padding:5px;padding-left:16px;">
9164              * <li>tree - The TreePanel</li>
9165              * <li>target - The node being targeted for the drop</li>
9166              * <li>data - The drag data from the drag source</li>
9167              * <li>point - The point of the drop - append, above or below</li>
9168              * <li>source - The drag source</li>
9169              * <li>rawEvent - Raw mouse event</li>
9170              * <li>dropNode - Dropped node(s).</li>
9171              * </ul>
9172              * @param {Object} dropEvent
9173              */
9174             "nodedrop" : true,
9175              /**
9176              * @event nodedragover
9177              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
9178              * passed to handlers has the following properties:<br />
9179              * <ul style="padding:5px;padding-left:16px;">
9180              * <li>tree - The TreePanel</li>
9181              * <li>target - The node being targeted for the drop</li>
9182              * <li>data - The drag data from the drag source</li>
9183              * <li>point - The point of the drop - append, above or below</li>
9184              * <li>source - The drag source</li>
9185              * <li>rawEvent - Raw mouse event</li>
9186              * <li>dropNode - Drop node(s) provided by the source.</li>
9187              * <li>cancel - Set this to true to signal drop not allowed.</li>
9188              * </ul>
9189              * @param {Object} dragOverEvent
9190              */
9191             "nodedragover" : true
9192         
9193    });
9194    if(this.singleExpand){
9195        this.on("beforeexpand", this.restrictExpand, this);
9196    }
9197 };
9198 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
9199     rootVisible : true,
9200     animate: Roo.enableFx,
9201     lines : true,
9202     enableDD : false,
9203     hlDrop : Roo.enableFx,
9204   
9205     renderer: false,
9206     
9207     rendererTip: false,
9208     // private
9209     restrictExpand : function(node){
9210         var p = node.parentNode;
9211         if(p){
9212             if(p.expandedChild && p.expandedChild.parentNode == p){
9213                 p.expandedChild.collapse();
9214             }
9215             p.expandedChild = node;
9216         }
9217     },
9218
9219     // private override
9220     setRootNode : function(node){
9221         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
9222         if(!this.rootVisible){
9223             node.ui = new Roo.tree.RootTreeNodeUI(node);
9224         }
9225         return node;
9226     },
9227
9228     /**
9229      * Returns the container element for this TreePanel
9230      */
9231     getEl : function(){
9232         return this.el;
9233     },
9234
9235     /**
9236      * Returns the default TreeLoader for this TreePanel
9237      */
9238     getLoader : function(){
9239         return this.loader;
9240     },
9241
9242     /**
9243      * Expand all nodes
9244      */
9245     expandAll : function(){
9246         this.root.expand(true);
9247     },
9248
9249     /**
9250      * Collapse all nodes
9251      */
9252     collapseAll : function(){
9253         this.root.collapse(true);
9254     },
9255
9256     /**
9257      * Returns the selection model used by this TreePanel
9258      */
9259     getSelectionModel : function(){
9260         if(!this.selModel){
9261             this.selModel = new Roo.tree.DefaultSelectionModel();
9262         }
9263         return this.selModel;
9264     },
9265
9266     /**
9267      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
9268      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
9269      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
9270      * @return {Array}
9271      */
9272     getChecked : function(a, startNode){
9273         startNode = startNode || this.root;
9274         var r = [];
9275         var f = function(){
9276             if(this.attributes.checked){
9277                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
9278             }
9279         }
9280         startNode.cascade(f);
9281         return r;
9282     },
9283
9284     /**
9285      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
9286      * @param {String} path
9287      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
9288      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
9289      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
9290      */
9291     expandPath : function(path, attr, callback){
9292         attr = attr || "id";
9293         var keys = path.split(this.pathSeparator);
9294         var curNode = this.root;
9295         if(curNode.attributes[attr] != keys[1]){ // invalid root
9296             if(callback){
9297                 callback(false, null);
9298             }
9299             return;
9300         }
9301         var index = 1;
9302         var f = function(){
9303             if(++index == keys.length){
9304                 if(callback){
9305                     callback(true, curNode);
9306                 }
9307                 return;
9308             }
9309             var c = curNode.findChild(attr, keys[index]);
9310             if(!c){
9311                 if(callback){
9312                     callback(false, curNode);
9313                 }
9314                 return;
9315             }
9316             curNode = c;
9317             c.expand(false, false, f);
9318         };
9319         curNode.expand(false, false, f);
9320     },
9321
9322     /**
9323      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
9324      * @param {String} path
9325      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
9326      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
9327      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
9328      */
9329     selectPath : function(path, attr, callback){
9330         attr = attr || "id";
9331         var keys = path.split(this.pathSeparator);
9332         var v = keys.pop();
9333         if(keys.length > 0){
9334             var f = function(success, node){
9335                 if(success && node){
9336                     var n = node.findChild(attr, v);
9337                     if(n){
9338                         n.select();
9339                         if(callback){
9340                             callback(true, n);
9341                         }
9342                     }else if(callback){
9343                         callback(false, n);
9344                     }
9345                 }else{
9346                     if(callback){
9347                         callback(false, n);
9348                     }
9349                 }
9350             };
9351             this.expandPath(keys.join(this.pathSeparator), attr, f);
9352         }else{
9353             this.root.select();
9354             if(callback){
9355                 callback(true, this.root);
9356             }
9357         }
9358     },
9359
9360     getTreeEl : function(){
9361         return this.el;
9362     },
9363
9364     /**
9365      * Trigger rendering of this TreePanel
9366      */
9367     render : function(){
9368         if (this.innerCt) {
9369             return this; // stop it rendering more than once!!
9370         }
9371         
9372         this.innerCt = this.el.createChild({tag:"ul",
9373                cls:"x-tree-root-ct " +
9374                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
9375
9376         if(this.containerScroll){
9377             Roo.dd.ScrollManager.register(this.el);
9378         }
9379         if((this.enableDD || this.enableDrop) && !this.dropZone){
9380            /**
9381             * The dropZone used by this tree if drop is enabled
9382             * @type Roo.tree.TreeDropZone
9383             */
9384              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
9385                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
9386            });
9387         }
9388         if((this.enableDD || this.enableDrag) && !this.dragZone){
9389            /**
9390             * The dragZone used by this tree if drag is enabled
9391             * @type Roo.tree.TreeDragZone
9392             */
9393             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
9394                ddGroup: this.ddGroup || "TreeDD",
9395                scroll: this.ddScroll
9396            });
9397         }
9398         this.getSelectionModel().init(this);
9399         if (!this.root) {
9400             console.log("ROOT not set in tree");
9401             return;
9402         }
9403         this.root.render();
9404         if(!this.rootVisible){
9405             this.root.renderChildren();
9406         }
9407         return this;
9408     }
9409 });/*
9410  * Based on:
9411  * Ext JS Library 1.1.1
9412  * Copyright(c) 2006-2007, Ext JS, LLC.
9413  *
9414  * Originally Released Under LGPL - original licence link has changed is not relivant.
9415  *
9416  * Fork - LGPL
9417  * <script type="text/javascript">
9418  */
9419  
9420
9421 /**
9422  * @class Roo.tree.DefaultSelectionModel
9423  * @extends Roo.util.Observable
9424  * The default single selection for a TreePanel.
9425  */
9426 Roo.tree.DefaultSelectionModel = function(){
9427    this.selNode = null;
9428    
9429    this.addEvents({
9430        /**
9431         * @event selectionchange
9432         * Fires when the selected node changes
9433         * @param {DefaultSelectionModel} this
9434         * @param {TreeNode} node the new selection
9435         */
9436        "selectionchange" : true,
9437
9438        /**
9439         * @event beforeselect
9440         * Fires before the selected node changes, return false to cancel the change
9441         * @param {DefaultSelectionModel} this
9442         * @param {TreeNode} node the new selection
9443         * @param {TreeNode} node the old selection
9444         */
9445        "beforeselect" : true
9446    });
9447 };
9448
9449 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
9450     init : function(tree){
9451         this.tree = tree;
9452         tree.getTreeEl().on("keydown", this.onKeyDown, this);
9453         tree.on("click", this.onNodeClick, this);
9454     },
9455     
9456     onNodeClick : function(node, e){
9457         if (e.ctrlKey && this.selNode == node)  {
9458             this.unselect(node);
9459             return;
9460         }
9461         this.select(node);
9462     },
9463     
9464     /**
9465      * Select a node.
9466      * @param {TreeNode} node The node to select
9467      * @return {TreeNode} The selected node
9468      */
9469     select : function(node){
9470         var last = this.selNode;
9471         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
9472             if(last){
9473                 last.ui.onSelectedChange(false);
9474             }
9475             this.selNode = node;
9476             node.ui.onSelectedChange(true);
9477             this.fireEvent("selectionchange", this, node, last);
9478         }
9479         return node;
9480     },
9481     
9482     /**
9483      * Deselect a node.
9484      * @param {TreeNode} node The node to unselect
9485      */
9486     unselect : function(node){
9487         if(this.selNode == node){
9488             this.clearSelections();
9489         }    
9490     },
9491     
9492     /**
9493      * Clear all selections
9494      */
9495     clearSelections : function(){
9496         var n = this.selNode;
9497         if(n){
9498             n.ui.onSelectedChange(false);
9499             this.selNode = null;
9500             this.fireEvent("selectionchange", this, null);
9501         }
9502         return n;
9503     },
9504     
9505     /**
9506      * Get the selected node
9507      * @return {TreeNode} The selected node
9508      */
9509     getSelectedNode : function(){
9510         return this.selNode;    
9511     },
9512     
9513     /**
9514      * Returns true if the node is selected
9515      * @param {TreeNode} node The node to check
9516      * @return {Boolean}
9517      */
9518     isSelected : function(node){
9519         return this.selNode == node;  
9520     },
9521
9522     /**
9523      * Selects the node above the selected node in the tree, intelligently walking the nodes
9524      * @return TreeNode The new selection
9525      */
9526     selectPrevious : function(){
9527         var s = this.selNode || this.lastSelNode;
9528         if(!s){
9529             return null;
9530         }
9531         var ps = s.previousSibling;
9532         if(ps){
9533             if(!ps.isExpanded() || ps.childNodes.length < 1){
9534                 return this.select(ps);
9535             } else{
9536                 var lc = ps.lastChild;
9537                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
9538                     lc = lc.lastChild;
9539                 }
9540                 return this.select(lc);
9541             }
9542         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
9543             return this.select(s.parentNode);
9544         }
9545         return null;
9546     },
9547
9548     /**
9549      * Selects the node above the selected node in the tree, intelligently walking the nodes
9550      * @return TreeNode The new selection
9551      */
9552     selectNext : function(){
9553         var s = this.selNode || this.lastSelNode;
9554         if(!s){
9555             return null;
9556         }
9557         if(s.firstChild && s.isExpanded()){
9558              return this.select(s.firstChild);
9559          }else if(s.nextSibling){
9560              return this.select(s.nextSibling);
9561          }else if(s.parentNode){
9562             var newS = null;
9563             s.parentNode.bubble(function(){
9564                 if(this.nextSibling){
9565                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
9566                     return false;
9567                 }
9568             });
9569             return newS;
9570          }
9571         return null;
9572     },
9573
9574     onKeyDown : function(e){
9575         var s = this.selNode || this.lastSelNode;
9576         // undesirable, but required
9577         var sm = this;
9578         if(!s){
9579             return;
9580         }
9581         var k = e.getKey();
9582         switch(k){
9583              case e.DOWN:
9584                  e.stopEvent();
9585                  this.selectNext();
9586              break;
9587              case e.UP:
9588                  e.stopEvent();
9589                  this.selectPrevious();
9590              break;
9591              case e.RIGHT:
9592                  e.preventDefault();
9593                  if(s.hasChildNodes()){
9594                      if(!s.isExpanded()){
9595                          s.expand();
9596                      }else if(s.firstChild){
9597                          this.select(s.firstChild, e);
9598                      }
9599                  }
9600              break;
9601              case e.LEFT:
9602                  e.preventDefault();
9603                  if(s.hasChildNodes() && s.isExpanded()){
9604                      s.collapse();
9605                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
9606                      this.select(s.parentNode, e);
9607                  }
9608              break;
9609         };
9610     }
9611 });
9612
9613 /**
9614  * @class Roo.tree.MultiSelectionModel
9615  * @extends Roo.util.Observable
9616  * Multi selection for a TreePanel.
9617  */
9618 Roo.tree.MultiSelectionModel = function(){
9619    this.selNodes = [];
9620    this.selMap = {};
9621    this.addEvents({
9622        /**
9623         * @event selectionchange
9624         * Fires when the selected nodes change
9625         * @param {MultiSelectionModel} this
9626         * @param {Array} nodes Array of the selected nodes
9627         */
9628        "selectionchange" : true
9629    });
9630 };
9631
9632 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
9633     init : function(tree){
9634         this.tree = tree;
9635         tree.getTreeEl().on("keydown", this.onKeyDown, this);
9636         tree.on("click", this.onNodeClick, this);
9637     },
9638     
9639     onNodeClick : function(node, e){
9640         this.select(node, e, e.ctrlKey);
9641     },
9642     
9643     /**
9644      * Select a node.
9645      * @param {TreeNode} node The node to select
9646      * @param {EventObject} e (optional) An event associated with the selection
9647      * @param {Boolean} keepExisting True to retain existing selections
9648      * @return {TreeNode} The selected node
9649      */
9650     select : function(node, e, keepExisting){
9651         if(keepExisting !== true){
9652             this.clearSelections(true);
9653         }
9654         if(this.isSelected(node)){
9655             this.lastSelNode = node;
9656             return node;
9657         }
9658         this.selNodes.push(node);
9659         this.selMap[node.id] = node;
9660         this.lastSelNode = node;
9661         node.ui.onSelectedChange(true);
9662         this.fireEvent("selectionchange", this, this.selNodes);
9663         return node;
9664     },
9665     
9666     /**
9667      * Deselect a node.
9668      * @param {TreeNode} node The node to unselect
9669      */
9670     unselect : function(node){
9671         if(this.selMap[node.id]){
9672             node.ui.onSelectedChange(false);
9673             var sn = this.selNodes;
9674             var index = -1;
9675             if(sn.indexOf){
9676                 index = sn.indexOf(node);
9677             }else{
9678                 for(var i = 0, len = sn.length; i < len; i++){
9679                     if(sn[i] == node){
9680                         index = i;
9681                         break;
9682                     }
9683                 }
9684             }
9685             if(index != -1){
9686                 this.selNodes.splice(index, 1);
9687             }
9688             delete this.selMap[node.id];
9689             this.fireEvent("selectionchange", this, this.selNodes);
9690         }
9691     },
9692     
9693     /**
9694      * Clear all selections
9695      */
9696     clearSelections : function(suppressEvent){
9697         var sn = this.selNodes;
9698         if(sn.length > 0){
9699             for(var i = 0, len = sn.length; i < len; i++){
9700                 sn[i].ui.onSelectedChange(false);
9701             }
9702             this.selNodes = [];
9703             this.selMap = {};
9704             if(suppressEvent !== true){
9705                 this.fireEvent("selectionchange", this, this.selNodes);
9706             }
9707         }
9708     },
9709     
9710     /**
9711      * Returns true if the node is selected
9712      * @param {TreeNode} node The node to check
9713      * @return {Boolean}
9714      */
9715     isSelected : function(node){
9716         return this.selMap[node.id] ? true : false;  
9717     },
9718     
9719     /**
9720      * Returns an array of the selected nodes
9721      * @return {Array}
9722      */
9723     getSelectedNodes : function(){
9724         return this.selNodes;    
9725     },
9726
9727     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
9728
9729     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
9730
9731     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
9732 });/*
9733  * Based on:
9734  * Ext JS Library 1.1.1
9735  * Copyright(c) 2006-2007, Ext JS, LLC.
9736  *
9737  * Originally Released Under LGPL - original licence link has changed is not relivant.
9738  *
9739  * Fork - LGPL
9740  * <script type="text/javascript">
9741  */
9742  
9743 /**
9744  * @class Roo.tree.TreeNode
9745  * @extends Roo.data.Node
9746  * @cfg {String} text The text for this node
9747  * @cfg {Boolean} expanded true to start the node expanded
9748  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
9749  * @cfg {Boolean} allowDrop false if this node cannot be drop on
9750  * @cfg {Boolean} disabled true to start the node disabled
9751  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
9752  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
9753  * @cfg {String} cls A css class to be added to the node
9754  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
9755  * @cfg {String} href URL of the link used for the node (defaults to #)
9756  * @cfg {String} hrefTarget target frame for the link
9757  * @cfg {String} qtip An Ext QuickTip for the node
9758  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
9759  * @cfg {Boolean} singleClickExpand True for single click expand on this node
9760  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
9761  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
9762  * (defaults to undefined with no checkbox rendered)
9763  * @constructor
9764  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
9765  */
9766 Roo.tree.TreeNode = function(attributes){
9767     attributes = attributes || {};
9768     if(typeof attributes == "string"){
9769         attributes = {text: attributes};
9770     }
9771     this.childrenRendered = false;
9772     this.rendered = false;
9773     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
9774     this.expanded = attributes.expanded === true;
9775     this.isTarget = attributes.isTarget !== false;
9776     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
9777     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
9778
9779     /**
9780      * Read-only. The text for this node. To change it use setText().
9781      * @type String
9782      */
9783     this.text = attributes.text;
9784     /**
9785      * True if this node is disabled.
9786      * @type Boolean
9787      */
9788     this.disabled = attributes.disabled === true;
9789
9790     this.addEvents({
9791         /**
9792         * @event textchange
9793         * Fires when the text for this node is changed
9794         * @param {Node} this This node
9795         * @param {String} text The new text
9796         * @param {String} oldText The old text
9797         */
9798         "textchange" : true,
9799         /**
9800         * @event beforeexpand
9801         * Fires before this node is expanded, return false to cancel.
9802         * @param {Node} this This node
9803         * @param {Boolean} deep
9804         * @param {Boolean} anim
9805         */
9806         "beforeexpand" : true,
9807         /**
9808         * @event beforecollapse
9809         * Fires before this node is collapsed, return false to cancel.
9810         * @param {Node} this This node
9811         * @param {Boolean} deep
9812         * @param {Boolean} anim
9813         */
9814         "beforecollapse" : true,
9815         /**
9816         * @event expand
9817         * Fires when this node is expanded
9818         * @param {Node} this This node
9819         */
9820         "expand" : true,
9821         /**
9822         * @event disabledchange
9823         * Fires when the disabled status of this node changes
9824         * @param {Node} this This node
9825         * @param {Boolean} disabled
9826         */
9827         "disabledchange" : true,
9828         /**
9829         * @event collapse
9830         * Fires when this node is collapsed
9831         * @param {Node} this This node
9832         */
9833         "collapse" : true,
9834         /**
9835         * @event beforeclick
9836         * Fires before click processing. Return false to cancel the default action.
9837         * @param {Node} this This node
9838         * @param {Roo.EventObject} e The event object
9839         */
9840         "beforeclick":true,
9841         /**
9842         * @event checkchange
9843         * Fires when a node with a checkbox's checked property changes
9844         * @param {Node} this This node
9845         * @param {Boolean} checked
9846         */
9847         "checkchange":true,
9848         /**
9849         * @event click
9850         * Fires when this node is clicked
9851         * @param {Node} this This node
9852         * @param {Roo.EventObject} e The event object
9853         */
9854         "click":true,
9855         /**
9856         * @event dblclick
9857         * Fires when this node is double clicked
9858         * @param {Node} this This node
9859         * @param {Roo.EventObject} e The event object
9860         */
9861         "dblclick":true,
9862         /**
9863         * @event contextmenu
9864         * Fires when this node is right clicked
9865         * @param {Node} this This node
9866         * @param {Roo.EventObject} e The event object
9867         */
9868         "contextmenu":true,
9869         /**
9870         * @event beforechildrenrendered
9871         * Fires right before the child nodes for this node are rendered
9872         * @param {Node} this This node
9873         */
9874         "beforechildrenrendered":true
9875     });
9876
9877     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
9878
9879     /**
9880      * Read-only. The UI for this node
9881      * @type TreeNodeUI
9882      */
9883     this.ui = new uiClass(this);
9884 };
9885 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
9886     preventHScroll: true,
9887     /**
9888      * Returns true if this node is expanded
9889      * @return {Boolean}
9890      */
9891     isExpanded : function(){
9892         return this.expanded;
9893     },
9894
9895     /**
9896      * Returns the UI object for this node
9897      * @return {TreeNodeUI}
9898      */
9899     getUI : function(){
9900         return this.ui;
9901     },
9902
9903     // private override
9904     setFirstChild : function(node){
9905         var of = this.firstChild;
9906         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
9907         if(this.childrenRendered && of && node != of){
9908             of.renderIndent(true, true);
9909         }
9910         if(this.rendered){
9911             this.renderIndent(true, true);
9912         }
9913     },
9914
9915     // private override
9916     setLastChild : function(node){
9917         var ol = this.lastChild;
9918         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
9919         if(this.childrenRendered && ol && node != ol){
9920             ol.renderIndent(true, true);
9921         }
9922         if(this.rendered){
9923             this.renderIndent(true, true);
9924         }
9925     },
9926
9927     // these methods are overridden to provide lazy rendering support
9928     // private override
9929     appendChild : function(){
9930         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
9931         if(node && this.childrenRendered){
9932             node.render();
9933         }
9934         this.ui.updateExpandIcon();
9935         return node;
9936     },
9937
9938     // private override
9939     removeChild : function(node){
9940         this.ownerTree.getSelectionModel().unselect(node);
9941         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
9942         // if it's been rendered remove dom node
9943         if(this.childrenRendered){
9944             node.ui.remove();
9945         }
9946         if(this.childNodes.length < 1){
9947             this.collapse(false, false);
9948         }else{
9949             this.ui.updateExpandIcon();
9950         }
9951         if(!this.firstChild) {
9952             this.childrenRendered = false;
9953         }
9954         return node;
9955     },
9956
9957     // private override
9958     insertBefore : function(node, refNode){
9959         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
9960         if(newNode && refNode && this.childrenRendered){
9961             node.render();
9962         }
9963         this.ui.updateExpandIcon();
9964         return newNode;
9965     },
9966
9967     /**
9968      * Sets the text for this node
9969      * @param {String} text
9970      */
9971     setText : function(text){
9972         var oldText = this.text;
9973         this.text = text;
9974         this.attributes.text = text;
9975         if(this.rendered){ // event without subscribing
9976             this.ui.onTextChange(this, text, oldText);
9977         }
9978         this.fireEvent("textchange", this, text, oldText);
9979     },
9980
9981     /**
9982      * Triggers selection of this node
9983      */
9984     select : function(){
9985         this.getOwnerTree().getSelectionModel().select(this);
9986     },
9987
9988     /**
9989      * Triggers deselection of this node
9990      */
9991     unselect : function(){
9992         this.getOwnerTree().getSelectionModel().unselect(this);
9993     },
9994
9995     /**
9996      * Returns true if this node is selected
9997      * @return {Boolean}
9998      */
9999     isSelected : function(){
10000         return this.getOwnerTree().getSelectionModel().isSelected(this);
10001     },
10002
10003     /**
10004      * Expand this node.
10005      * @param {Boolean} deep (optional) True to expand all children as well
10006      * @param {Boolean} anim (optional) false to cancel the default animation
10007      * @param {Function} callback (optional) A callback to be called when
10008      * expanding this node completes (does not wait for deep expand to complete).
10009      * Called with 1 parameter, this node.
10010      */
10011     expand : function(deep, anim, callback){
10012         if(!this.expanded){
10013             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
10014                 return;
10015             }
10016             if(!this.childrenRendered){
10017                 this.renderChildren();
10018             }
10019             this.expanded = true;
10020             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
10021                 this.ui.animExpand(function(){
10022                     this.fireEvent("expand", this);
10023                     if(typeof callback == "function"){
10024                         callback(this);
10025                     }
10026                     if(deep === true){
10027                         this.expandChildNodes(true);
10028                     }
10029                 }.createDelegate(this));
10030                 return;
10031             }else{
10032                 this.ui.expand();
10033                 this.fireEvent("expand", this);
10034                 if(typeof callback == "function"){
10035                     callback(this);
10036                 }
10037             }
10038         }else{
10039            if(typeof callback == "function"){
10040                callback(this);
10041            }
10042         }
10043         if(deep === true){
10044             this.expandChildNodes(true);
10045         }
10046     },
10047
10048     isHiddenRoot : function(){
10049         return this.isRoot && !this.getOwnerTree().rootVisible;
10050     },
10051
10052     /**
10053      * Collapse this node.
10054      * @param {Boolean} deep (optional) True to collapse all children as well
10055      * @param {Boolean} anim (optional) false to cancel the default animation
10056      */
10057     collapse : function(deep, anim){
10058         if(this.expanded && !this.isHiddenRoot()){
10059             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
10060                 return;
10061             }
10062             this.expanded = false;
10063             if((this.getOwnerTree().animate && anim !== false) || anim){
10064                 this.ui.animCollapse(function(){
10065                     this.fireEvent("collapse", this);
10066                     if(deep === true){
10067                         this.collapseChildNodes(true);
10068                     }
10069                 }.createDelegate(this));
10070                 return;
10071             }else{
10072                 this.ui.collapse();
10073                 this.fireEvent("collapse", this);
10074             }
10075         }
10076         if(deep === true){
10077             var cs = this.childNodes;
10078             for(var i = 0, len = cs.length; i < len; i++) {
10079                 cs[i].collapse(true, false);
10080             }
10081         }
10082     },
10083
10084     // private
10085     delayedExpand : function(delay){
10086         if(!this.expandProcId){
10087             this.expandProcId = this.expand.defer(delay, this);
10088         }
10089     },
10090
10091     // private
10092     cancelExpand : function(){
10093         if(this.expandProcId){
10094             clearTimeout(this.expandProcId);
10095         }
10096         this.expandProcId = false;
10097     },
10098
10099     /**
10100      * Toggles expanded/collapsed state of the node
10101      */
10102     toggle : function(){
10103         if(this.expanded){
10104             this.collapse();
10105         }else{
10106             this.expand();
10107         }
10108     },
10109
10110     /**
10111      * Ensures all parent nodes are expanded
10112      */
10113     ensureVisible : function(callback){
10114         var tree = this.getOwnerTree();
10115         tree.expandPath(this.parentNode.getPath(), false, function(){
10116             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
10117             Roo.callback(callback);
10118         }.createDelegate(this));
10119     },
10120
10121     /**
10122      * Expand all child nodes
10123      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
10124      */
10125     expandChildNodes : function(deep){
10126         var cs = this.childNodes;
10127         for(var i = 0, len = cs.length; i < len; i++) {
10128                 cs[i].expand(deep);
10129         }
10130     },
10131
10132     /**
10133      * Collapse all child nodes
10134      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
10135      */
10136     collapseChildNodes : function(deep){
10137         var cs = this.childNodes;
10138         for(var i = 0, len = cs.length; i < len; i++) {
10139                 cs[i].collapse(deep);
10140         }
10141     },
10142
10143     /**
10144      * Disables this node
10145      */
10146     disable : function(){
10147         this.disabled = true;
10148         this.unselect();
10149         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
10150             this.ui.onDisableChange(this, true);
10151         }
10152         this.fireEvent("disabledchange", this, true);
10153     },
10154
10155     /**
10156      * Enables this node
10157      */
10158     enable : function(){
10159         this.disabled = false;
10160         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
10161             this.ui.onDisableChange(this, false);
10162         }
10163         this.fireEvent("disabledchange", this, false);
10164     },
10165
10166     // private
10167     renderChildren : function(suppressEvent){
10168         if(suppressEvent !== false){
10169             this.fireEvent("beforechildrenrendered", this);
10170         }
10171         var cs = this.childNodes;
10172         for(var i = 0, len = cs.length; i < len; i++){
10173             cs[i].render(true);
10174         }
10175         this.childrenRendered = true;
10176     },
10177
10178     // private
10179     sort : function(fn, scope){
10180         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
10181         if(this.childrenRendered){
10182             var cs = this.childNodes;
10183             for(var i = 0, len = cs.length; i < len; i++){
10184                 cs[i].render(true);
10185             }
10186         }
10187     },
10188
10189     // private
10190     render : function(bulkRender){
10191         this.ui.render(bulkRender);
10192         if(!this.rendered){
10193             this.rendered = true;
10194             if(this.expanded){
10195                 this.expanded = false;
10196                 this.expand(false, false);
10197             }
10198         }
10199     },
10200
10201     // private
10202     renderIndent : function(deep, refresh){
10203         if(refresh){
10204             this.ui.childIndent = null;
10205         }
10206         this.ui.renderIndent();
10207         if(deep === true && this.childrenRendered){
10208             var cs = this.childNodes;
10209             for(var i = 0, len = cs.length; i < len; i++){
10210                 cs[i].renderIndent(true, refresh);
10211             }
10212         }
10213     }
10214 });/*
10215  * Based on:
10216  * Ext JS Library 1.1.1
10217  * Copyright(c) 2006-2007, Ext JS, LLC.
10218  *
10219  * Originally Released Under LGPL - original licence link has changed is not relivant.
10220  *
10221  * Fork - LGPL
10222  * <script type="text/javascript">
10223  */
10224  
10225 /**
10226  * @class Roo.tree.AsyncTreeNode
10227  * @extends Roo.tree.TreeNode
10228  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
10229  * @constructor
10230  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
10231  */
10232  Roo.tree.AsyncTreeNode = function(config){
10233     this.loaded = false;
10234     this.loading = false;
10235     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
10236     /**
10237     * @event beforeload
10238     * Fires before this node is loaded, return false to cancel
10239     * @param {Node} this This node
10240     */
10241     this.addEvents({'beforeload':true, 'load': true});
10242     /**
10243     * @event load
10244     * Fires when this node is loaded
10245     * @param {Node} this This node
10246     */
10247     /**
10248      * The loader used by this node (defaults to using the tree's defined loader)
10249      * @type TreeLoader
10250      * @property loader
10251      */
10252 };
10253 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
10254     expand : function(deep, anim, callback){
10255         if(this.loading){ // if an async load is already running, waiting til it's done
10256             var timer;
10257             var f = function(){
10258                 if(!this.loading){ // done loading
10259                     clearInterval(timer);
10260                     this.expand(deep, anim, callback);
10261                 }
10262             }.createDelegate(this);
10263             timer = setInterval(f, 200);
10264             return;
10265         }
10266         if(!this.loaded){
10267             if(this.fireEvent("beforeload", this) === false){
10268                 return;
10269             }
10270             this.loading = true;
10271             this.ui.beforeLoad(this);
10272             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
10273             if(loader){
10274                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
10275                 return;
10276             }
10277         }
10278         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
10279     },
10280     
10281     /**
10282      * Returns true if this node is currently loading
10283      * @return {Boolean}
10284      */
10285     isLoading : function(){
10286         return this.loading;  
10287     },
10288     
10289     loadComplete : function(deep, anim, callback){
10290         this.loading = false;
10291         this.loaded = true;
10292         this.ui.afterLoad(this);
10293         this.fireEvent("load", this);
10294         this.expand(deep, anim, callback);
10295     },
10296     
10297     /**
10298      * Returns true if this node has been loaded
10299      * @return {Boolean}
10300      */
10301     isLoaded : function(){
10302         return this.loaded;
10303     },
10304     
10305     hasChildNodes : function(){
10306         if(!this.isLeaf() && !this.loaded){
10307             return true;
10308         }else{
10309             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
10310         }
10311     },
10312
10313     /**
10314      * Trigger a reload for this node
10315      * @param {Function} callback
10316      */
10317     reload : function(callback){
10318         this.collapse(false, false);
10319         while(this.firstChild){
10320             this.removeChild(this.firstChild);
10321         }
10322         this.childrenRendered = false;
10323         this.loaded = false;
10324         if(this.isHiddenRoot()){
10325             this.expanded = false;
10326         }
10327         this.expand(false, false, callback);
10328     }
10329 });/*
10330  * Based on:
10331  * Ext JS Library 1.1.1
10332  * Copyright(c) 2006-2007, Ext JS, LLC.
10333  *
10334  * Originally Released Under LGPL - original licence link has changed is not relivant.
10335  *
10336  * Fork - LGPL
10337  * <script type="text/javascript">
10338  */
10339  
10340 /**
10341  * @class Roo.tree.TreeNodeUI
10342  * @constructor
10343  * @param {Object} node The node to render
10344  * The TreeNode UI implementation is separate from the
10345  * tree implementation. Unless you are customizing the tree UI,
10346  * you should never have to use this directly.
10347  */
10348 Roo.tree.TreeNodeUI = function(node){
10349     this.node = node;
10350     this.rendered = false;
10351     this.animating = false;
10352     this.emptyIcon = Roo.BLANK_IMAGE_URL;
10353 };
10354
10355 Roo.tree.TreeNodeUI.prototype = {
10356     removeChild : function(node){
10357         if(this.rendered){
10358             this.ctNode.removeChild(node.ui.getEl());
10359         }
10360     },
10361
10362     beforeLoad : function(){
10363          this.addClass("x-tree-node-loading");
10364     },
10365
10366     afterLoad : function(){
10367          this.removeClass("x-tree-node-loading");
10368     },
10369
10370     onTextChange : function(node, text, oldText){
10371         if(this.rendered){
10372             this.textNode.innerHTML = text;
10373         }
10374     },
10375
10376     onDisableChange : function(node, state){
10377         this.disabled = state;
10378         if(state){
10379             this.addClass("x-tree-node-disabled");
10380         }else{
10381             this.removeClass("x-tree-node-disabled");
10382         }
10383     },
10384
10385     onSelectedChange : function(state){
10386         if(state){
10387             this.focus();
10388             this.addClass("x-tree-selected");
10389         }else{
10390             //this.blur();
10391             this.removeClass("x-tree-selected");
10392         }
10393     },
10394
10395     onMove : function(tree, node, oldParent, newParent, index, refNode){
10396         this.childIndent = null;
10397         if(this.rendered){
10398             var targetNode = newParent.ui.getContainer();
10399             if(!targetNode){//target not rendered
10400                 this.holder = document.createElement("div");
10401                 this.holder.appendChild(this.wrap);
10402                 return;
10403             }
10404             var insertBefore = refNode ? refNode.ui.getEl() : null;
10405             if(insertBefore){
10406                 targetNode.insertBefore(this.wrap, insertBefore);
10407             }else{
10408                 targetNode.appendChild(this.wrap);
10409             }
10410             this.node.renderIndent(true);
10411         }
10412     },
10413
10414     addClass : function(cls){
10415         if(this.elNode){
10416             Roo.fly(this.elNode).addClass(cls);
10417         }
10418     },
10419
10420     removeClass : function(cls){
10421         if(this.elNode){
10422             Roo.fly(this.elNode).removeClass(cls);
10423         }
10424     },
10425
10426     remove : function(){
10427         if(this.rendered){
10428             this.holder = document.createElement("div");
10429             this.holder.appendChild(this.wrap);
10430         }
10431     },
10432
10433     fireEvent : function(){
10434         return this.node.fireEvent.apply(this.node, arguments);
10435     },
10436
10437     initEvents : function(){
10438         this.node.on("move", this.onMove, this);
10439         var E = Roo.EventManager;
10440         var a = this.anchor;
10441
10442         var el = Roo.fly(a, '_treeui');
10443
10444         if(Roo.isOpera){ // opera render bug ignores the CSS
10445             el.setStyle("text-decoration", "none");
10446         }
10447
10448         el.on("click", this.onClick, this);
10449         el.on("dblclick", this.onDblClick, this);
10450
10451         if(this.checkbox){
10452             Roo.EventManager.on(this.checkbox,
10453                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
10454         }
10455
10456         el.on("contextmenu", this.onContextMenu, this);
10457
10458         var icon = Roo.fly(this.iconNode);
10459         icon.on("click", this.onClick, this);
10460         icon.on("dblclick", this.onDblClick, this);
10461         icon.on("contextmenu", this.onContextMenu, this);
10462         E.on(this.ecNode, "click", this.ecClick, this, true);
10463
10464         if(this.node.disabled){
10465             this.addClass("x-tree-node-disabled");
10466         }
10467         if(this.node.hidden){
10468             this.addClass("x-tree-node-disabled");
10469         }
10470         var ot = this.node.getOwnerTree();
10471         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
10472         if(dd && (!this.node.isRoot || ot.rootVisible)){
10473             Roo.dd.Registry.register(this.elNode, {
10474                 node: this.node,
10475                 handles: this.getDDHandles(),
10476                 isHandle: false
10477             });
10478         }
10479     },
10480
10481     getDDHandles : function(){
10482         return [this.iconNode, this.textNode];
10483     },
10484
10485     hide : function(){
10486         if(this.rendered){
10487             this.wrap.style.display = "none";
10488         }
10489     },
10490
10491     show : function(){
10492         if(this.rendered){
10493             this.wrap.style.display = "";
10494         }
10495     },
10496
10497     onContextMenu : function(e){
10498         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
10499             e.preventDefault();
10500             this.focus();
10501             this.fireEvent("contextmenu", this.node, e);
10502         }
10503     },
10504
10505     onClick : function(e){
10506         if(this.dropping){
10507             e.stopEvent();
10508             return;
10509         }
10510         if(this.fireEvent("beforeclick", this.node, e) !== false){
10511             if(!this.disabled && this.node.attributes.href){
10512                 this.fireEvent("click", this.node, e);
10513                 return;
10514             }
10515             e.preventDefault();
10516             if(this.disabled){
10517                 return;
10518             }
10519
10520             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
10521                 this.node.toggle();
10522             }
10523
10524             this.fireEvent("click", this.node, e);
10525         }else{
10526             e.stopEvent();
10527         }
10528     },
10529
10530     onDblClick : function(e){
10531         e.preventDefault();
10532         if(this.disabled){
10533             return;
10534         }
10535         if(this.checkbox){
10536             this.toggleCheck();
10537         }
10538         if(!this.animating && this.node.hasChildNodes()){
10539             this.node.toggle();
10540         }
10541         this.fireEvent("dblclick", this.node, e);
10542     },
10543
10544     onCheckChange : function(){
10545         var checked = this.checkbox.checked;
10546         this.node.attributes.checked = checked;
10547         this.fireEvent('checkchange', this.node, checked);
10548     },
10549
10550     ecClick : function(e){
10551         if(!this.animating && this.node.hasChildNodes()){
10552             this.node.toggle();
10553         }
10554     },
10555
10556     startDrop : function(){
10557         this.dropping = true;
10558     },
10559
10560     // delayed drop so the click event doesn't get fired on a drop
10561     endDrop : function(){
10562        setTimeout(function(){
10563            this.dropping = false;
10564        }.createDelegate(this), 50);
10565     },
10566
10567     expand : function(){
10568         this.updateExpandIcon();
10569         this.ctNode.style.display = "";
10570     },
10571
10572     focus : function(){
10573         if(!this.node.preventHScroll){
10574             try{this.anchor.focus();
10575             }catch(e){}
10576         }else if(!Roo.isIE){
10577             try{
10578                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
10579                 var l = noscroll.scrollLeft;
10580                 this.anchor.focus();
10581                 noscroll.scrollLeft = l;
10582             }catch(e){}
10583         }
10584     },
10585
10586     toggleCheck : function(value){
10587         var cb = this.checkbox;
10588         if(cb){
10589             cb.checked = (value === undefined ? !cb.checked : value);
10590         }
10591     },
10592
10593     blur : function(){
10594         try{
10595             this.anchor.blur();
10596         }catch(e){}
10597     },
10598
10599     animExpand : function(callback){
10600         var ct = Roo.get(this.ctNode);
10601         ct.stopFx();
10602         if(!this.node.hasChildNodes()){
10603             this.updateExpandIcon();
10604             this.ctNode.style.display = "";
10605             Roo.callback(callback);
10606             return;
10607         }
10608         this.animating = true;
10609         this.updateExpandIcon();
10610
10611         ct.slideIn('t', {
10612            callback : function(){
10613                this.animating = false;
10614                Roo.callback(callback);
10615             },
10616             scope: this,
10617             duration: this.node.ownerTree.duration || .25
10618         });
10619     },
10620
10621     highlight : function(){
10622         var tree = this.node.getOwnerTree();
10623         Roo.fly(this.wrap).highlight(
10624             tree.hlColor || "C3DAF9",
10625             {endColor: tree.hlBaseColor}
10626         );
10627     },
10628
10629     collapse : function(){
10630         this.updateExpandIcon();
10631         this.ctNode.style.display = "none";
10632     },
10633
10634     animCollapse : function(callback){
10635         var ct = Roo.get(this.ctNode);
10636         ct.enableDisplayMode('block');
10637         ct.stopFx();
10638
10639         this.animating = true;
10640         this.updateExpandIcon();
10641
10642         ct.slideOut('t', {
10643             callback : function(){
10644                this.animating = false;
10645                Roo.callback(callback);
10646             },
10647             scope: this,
10648             duration: this.node.ownerTree.duration || .25
10649         });
10650     },
10651
10652     getContainer : function(){
10653         return this.ctNode;
10654     },
10655
10656     getEl : function(){
10657         return this.wrap;
10658     },
10659
10660     appendDDGhost : function(ghostNode){
10661         ghostNode.appendChild(this.elNode.cloneNode(true));
10662     },
10663
10664     getDDRepairXY : function(){
10665         return Roo.lib.Dom.getXY(this.iconNode);
10666     },
10667
10668     onRender : function(){
10669         this.render();
10670     },
10671
10672     render : function(bulkRender){
10673         var n = this.node, a = n.attributes;
10674         var targetNode = n.parentNode ?
10675               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
10676
10677         if(!this.rendered){
10678             this.rendered = true;
10679
10680             this.renderElements(n, a, targetNode, bulkRender);
10681
10682             if(a.qtip){
10683                if(this.textNode.setAttributeNS){
10684                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
10685                    if(a.qtipTitle){
10686                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
10687                    }
10688                }else{
10689                    this.textNode.setAttribute("ext:qtip", a.qtip);
10690                    if(a.qtipTitle){
10691                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
10692                    }
10693                }
10694             }else if(a.qtipCfg){
10695                 a.qtipCfg.target = Roo.id(this.textNode);
10696                 Roo.QuickTips.register(a.qtipCfg);
10697             }
10698             this.initEvents();
10699             if(!this.node.expanded){
10700                 this.updateExpandIcon();
10701             }
10702         }else{
10703             if(bulkRender === true) {
10704                 targetNode.appendChild(this.wrap);
10705             }
10706         }
10707     },
10708
10709     renderElements : function(n, a, targetNode, bulkRender){
10710         // add some indent caching, this helps performance when rendering a large tree
10711         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
10712         var t = n.getOwnerTree();
10713         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
10714         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
10715         var cb = typeof a.checked == 'boolean';
10716         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
10717         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
10718             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
10719             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
10720             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
10721             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
10722             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
10723              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
10724                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
10725             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
10726             "</li>"];
10727
10728         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
10729             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
10730                                 n.nextSibling.ui.getEl(), buf.join(""));
10731         }else{
10732             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
10733         }
10734
10735         this.elNode = this.wrap.childNodes[0];
10736         this.ctNode = this.wrap.childNodes[1];
10737         var cs = this.elNode.childNodes;
10738         this.indentNode = cs[0];
10739         this.ecNode = cs[1];
10740         this.iconNode = cs[2];
10741         var index = 3;
10742         if(cb){
10743             this.checkbox = cs[3];
10744             index++;
10745         }
10746         this.anchor = cs[index];
10747         this.textNode = cs[index].firstChild;
10748     },
10749
10750     getAnchor : function(){
10751         return this.anchor;
10752     },
10753
10754     getTextEl : function(){
10755         return this.textNode;
10756     },
10757
10758     getIconEl : function(){
10759         return this.iconNode;
10760     },
10761
10762     isChecked : function(){
10763         return this.checkbox ? this.checkbox.checked : false;
10764     },
10765
10766     updateExpandIcon : function(){
10767         if(this.rendered){
10768             var n = this.node, c1, c2;
10769             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
10770             var hasChild = n.hasChildNodes();
10771             if(hasChild){
10772                 if(n.expanded){
10773                     cls += "-minus";
10774                     c1 = "x-tree-node-collapsed";
10775                     c2 = "x-tree-node-expanded";
10776                 }else{
10777                     cls += "-plus";
10778                     c1 = "x-tree-node-expanded";
10779                     c2 = "x-tree-node-collapsed";
10780                 }
10781                 if(this.wasLeaf){
10782                     this.removeClass("x-tree-node-leaf");
10783                     this.wasLeaf = false;
10784                 }
10785                 if(this.c1 != c1 || this.c2 != c2){
10786                     Roo.fly(this.elNode).replaceClass(c1, c2);
10787                     this.c1 = c1; this.c2 = c2;
10788                 }
10789             }else{
10790                 if(!this.wasLeaf){
10791                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
10792                     delete this.c1;
10793                     delete this.c2;
10794                     this.wasLeaf = true;
10795                 }
10796             }
10797             var ecc = "x-tree-ec-icon "+cls;
10798             if(this.ecc != ecc){
10799                 this.ecNode.className = ecc;
10800                 this.ecc = ecc;
10801             }
10802         }
10803     },
10804
10805     getChildIndent : function(){
10806         if(!this.childIndent){
10807             var buf = [];
10808             var p = this.node;
10809             while(p){
10810                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
10811                     if(!p.isLast()) {
10812                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
10813                     } else {
10814                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
10815                     }
10816                 }
10817                 p = p.parentNode;
10818             }
10819             this.childIndent = buf.join("");
10820         }
10821         return this.childIndent;
10822     },
10823
10824     renderIndent : function(){
10825         if(this.rendered){
10826             var indent = "";
10827             var p = this.node.parentNode;
10828             if(p){
10829                 indent = p.ui.getChildIndent();
10830             }
10831             if(this.indentMarkup != indent){ // don't rerender if not required
10832                 this.indentNode.innerHTML = indent;
10833                 this.indentMarkup = indent;
10834             }
10835             this.updateExpandIcon();
10836         }
10837     }
10838 };
10839
10840 Roo.tree.RootTreeNodeUI = function(){
10841     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
10842 };
10843 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
10844     render : function(){
10845         if(!this.rendered){
10846             var targetNode = this.node.ownerTree.innerCt.dom;
10847             this.node.expanded = true;
10848             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
10849             this.wrap = this.ctNode = targetNode.firstChild;
10850         }
10851     },
10852     collapse : function(){
10853     },
10854     expand : function(){
10855     }
10856 });/*
10857  * Based on:
10858  * Ext JS Library 1.1.1
10859  * Copyright(c) 2006-2007, Ext JS, LLC.
10860  *
10861  * Originally Released Under LGPL - original licence link has changed is not relivant.
10862  *
10863  * Fork - LGPL
10864  * <script type="text/javascript">
10865  */
10866 /**
10867  * @class Roo.tree.TreeLoader
10868  * @extends Roo.util.Observable
10869  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
10870  * nodes from a specified URL. The response must be a javascript Array definition
10871  * who's elements are node definition objects. eg:
10872  * <pre><code>
10873    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
10874     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
10875 </code></pre>
10876  * <br><br>
10877  * A server request is sent, and child nodes are loaded only when a node is expanded.
10878  * The loading node's id is passed to the server under the parameter name "node" to
10879  * enable the server to produce the correct child nodes.
10880  * <br><br>
10881  * To pass extra parameters, an event handler may be attached to the "beforeload"
10882  * event, and the parameters specified in the TreeLoader's baseParams property:
10883  * <pre><code>
10884     myTreeLoader.on("beforeload", function(treeLoader, node) {
10885         this.baseParams.category = node.attributes.category;
10886     }, this);
10887 </code></pre><
10888  * This would pass an HTTP parameter called "category" to the server containing
10889  * the value of the Node's "category" attribute.
10890  * @constructor
10891  * Creates a new Treeloader.
10892  * @param {Object} config A config object containing config properties.
10893  */
10894 Roo.tree.TreeLoader = function(config){
10895     this.baseParams = {};
10896     this.requestMethod = "POST";
10897     Roo.apply(this, config);
10898
10899     this.addEvents({
10900     
10901         /**
10902          * @event beforeload
10903          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
10904          * @param {Object} This TreeLoader object.
10905          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
10906          * @param {Object} callback The callback function specified in the {@link #load} call.
10907          */
10908         beforeload : true,
10909         /**
10910          * @event load
10911          * Fires when the node has been successfuly loaded.
10912          * @param {Object} This TreeLoader object.
10913          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
10914          * @param {Object} response The response object containing the data from the server.
10915          */
10916         load : true,
10917         /**
10918          * @event loadexception
10919          * Fires if the network request failed.
10920          * @param {Object} This TreeLoader object.
10921          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
10922          * @param {Object} response The response object containing the data from the server.
10923          */
10924         loadexception : true,
10925         /**
10926          * @event create
10927          * Fires before a node is created, enabling you to return custom Node types 
10928          * @param {Object} This TreeLoader object.
10929          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
10930          */
10931         create : true
10932     });
10933
10934     Roo.tree.TreeLoader.superclass.constructor.call(this);
10935 };
10936
10937 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
10938     /**
10939     * @cfg {String} dataUrl The URL from which to request a Json string which
10940     * specifies an array of node definition object representing the child nodes
10941     * to be loaded.
10942     */
10943     /**
10944     * @cfg {Object} baseParams (optional) An object containing properties which
10945     * specify HTTP parameters to be passed to each request for child nodes.
10946     */
10947     /**
10948     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
10949     * created by this loader. If the attributes sent by the server have an attribute in this object,
10950     * they take priority.
10951     */
10952     /**
10953     * @cfg {Object} uiProviders (optional) An object containing properties which
10954     * 
10955     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
10956     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
10957     * <i>uiProvider</i> attribute of a returned child node is a string rather
10958     * than a reference to a TreeNodeUI implementation, this that string value
10959     * is used as a property name in the uiProviders object. You can define the provider named
10960     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
10961     */
10962     uiProviders : {},
10963
10964     /**
10965     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
10966     * child nodes before loading.
10967     */
10968     clearOnLoad : true,
10969
10970     /**
10971     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
10972     * property on loading, rather than expecting an array. (eg. more compatible to a standard
10973     * Grid query { data : [ .....] }
10974     */
10975     
10976     root : false,
10977      /**
10978     * @cfg {String} queryParam (optional) 
10979     * Name of the query as it will be passed on the querystring (defaults to 'node')
10980     * eg. the request will be ?node=[id]
10981     */
10982     
10983     
10984     queryParam: false,
10985     
10986     /**
10987      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
10988      * This is called automatically when a node is expanded, but may be used to reload
10989      * a node (or append new children if the {@link #clearOnLoad} option is false.)
10990      * @param {Roo.tree.TreeNode} node
10991      * @param {Function} callback
10992      */
10993     load : function(node, callback){
10994         if(this.clearOnLoad){
10995             while(node.firstChild){
10996                 node.removeChild(node.firstChild);
10997             }
10998         }
10999         if(node.attributes.children){ // preloaded json children
11000             var cs = node.attributes.children;
11001             for(var i = 0, len = cs.length; i < len; i++){
11002                 node.appendChild(this.createNode(cs[i]));
11003             }
11004             if(typeof callback == "function"){
11005                 callback();
11006             }
11007         }else if(this.dataUrl){
11008             this.requestData(node, callback);
11009         }
11010     },
11011
11012     getParams: function(node){
11013         var buf = [], bp = this.baseParams;
11014         for(var key in bp){
11015             if(typeof bp[key] != "function"){
11016                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
11017             }
11018         }
11019         var n = this.queryParam === false ? 'node' : this.queryParam;
11020         buf.push(n + "=", encodeURIComponent(node.id));
11021         return buf.join("");
11022     },
11023
11024     requestData : function(node, callback){
11025         if(this.fireEvent("beforeload", this, node, callback) !== false){
11026             this.transId = Roo.Ajax.request({
11027                 method:this.requestMethod,
11028                 url: this.dataUrl||this.url,
11029                 success: this.handleResponse,
11030                 failure: this.handleFailure,
11031                 scope: this,
11032                 argument: {callback: callback, node: node},
11033                 params: this.getParams(node)
11034             });
11035         }else{
11036             // if the load is cancelled, make sure we notify
11037             // the node that we are done
11038             if(typeof callback == "function"){
11039                 callback();
11040             }
11041         }
11042     },
11043
11044     isLoading : function(){
11045         return this.transId ? true : false;
11046     },
11047
11048     abort : function(){
11049         if(this.isLoading()){
11050             Roo.Ajax.abort(this.transId);
11051         }
11052     },
11053
11054     // private
11055     createNode : function(attr){
11056         // apply baseAttrs, nice idea Corey!
11057         if(this.baseAttrs){
11058             Roo.applyIf(attr, this.baseAttrs);
11059         }
11060         if(this.applyLoader !== false){
11061             attr.loader = this;
11062         }
11063         // uiProvider = depreciated..
11064         
11065         if(typeof(attr.uiProvider) == 'string'){
11066            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
11067                 /**  eval:var:attr */ eval(attr.uiProvider);
11068         }
11069         if(typeof(this.uiProviders['default']) != 'undefined') {
11070             attr.uiProvider = this.uiProviders['default'];
11071         }
11072         
11073         this.fireEvent('create', this, attr);
11074         
11075         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
11076         return(attr.leaf ?
11077                         new Roo.tree.TreeNode(attr) :
11078                         new Roo.tree.AsyncTreeNode(attr));
11079     },
11080
11081     processResponse : function(response, node, callback){
11082         var json = response.responseText;
11083         try {
11084             
11085             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
11086             if (this.root !== false) {
11087                 o = o[this.root];
11088             }
11089             
11090             for(var i = 0, len = o.length; i < len; i++){
11091                 var n = this.createNode(o[i]);
11092                 if(n){
11093                     node.appendChild(n);
11094                 }
11095             }
11096             if(typeof callback == "function"){
11097                 callback(this, node);
11098             }
11099         }catch(e){
11100             this.handleFailure(response);
11101         }
11102     },
11103
11104     handleResponse : function(response){
11105         this.transId = false;
11106         var a = response.argument;
11107         this.processResponse(response, a.node, a.callback);
11108         this.fireEvent("load", this, a.node, response);
11109     },
11110
11111     handleFailure : function(response){
11112         this.transId = false;
11113         var a = response.argument;
11114         this.fireEvent("loadexception", this, a.node, response);
11115         if(typeof a.callback == "function"){
11116             a.callback(this, a.node);
11117         }
11118     }
11119 });/*
11120  * Based on:
11121  * Ext JS Library 1.1.1
11122  * Copyright(c) 2006-2007, Ext JS, LLC.
11123  *
11124  * Originally Released Under LGPL - original licence link has changed is not relivant.
11125  *
11126  * Fork - LGPL
11127  * <script type="text/javascript">
11128  */
11129
11130 /**
11131 * @class Roo.tree.TreeFilter
11132 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
11133 * @param {TreePanel} tree
11134 * @param {Object} config (optional)
11135  */
11136 Roo.tree.TreeFilter = function(tree, config){
11137     this.tree = tree;
11138     this.filtered = {};
11139     Roo.apply(this, config);
11140 };
11141
11142 Roo.tree.TreeFilter.prototype = {
11143     clearBlank:false,
11144     reverse:false,
11145     autoClear:false,
11146     remove:false,
11147
11148      /**
11149      * Filter the data by a specific attribute.
11150      * @param {String/RegExp} value Either string that the attribute value
11151      * should start with or a RegExp to test against the attribute
11152      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
11153      * @param {TreeNode} startNode (optional) The node to start the filter at.
11154      */
11155     filter : function(value, attr, startNode){
11156         attr = attr || "text";
11157         var f;
11158         if(typeof value == "string"){
11159             var vlen = value.length;
11160             // auto clear empty filter
11161             if(vlen == 0 && this.clearBlank){
11162                 this.clear();
11163                 return;
11164             }
11165             value = value.toLowerCase();
11166             f = function(n){
11167                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
11168             };
11169         }else if(value.exec){ // regex?
11170             f = function(n){
11171                 return value.test(n.attributes[attr]);
11172             };
11173         }else{
11174             throw 'Illegal filter type, must be string or regex';
11175         }
11176         this.filterBy(f, null, startNode);
11177         },
11178
11179     /**
11180      * Filter by a function. The passed function will be called with each
11181      * node in the tree (or from the startNode). If the function returns true, the node is kept
11182      * otherwise it is filtered. If a node is filtered, its children are also filtered.
11183      * @param {Function} fn The filter function
11184      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
11185      */
11186     filterBy : function(fn, scope, startNode){
11187         startNode = startNode || this.tree.root;
11188         if(this.autoClear){
11189             this.clear();
11190         }
11191         var af = this.filtered, rv = this.reverse;
11192         var f = function(n){
11193             if(n == startNode){
11194                 return true;
11195             }
11196             if(af[n.id]){
11197                 return false;
11198             }
11199             var m = fn.call(scope || n, n);
11200             if(!m || rv){
11201                 af[n.id] = n;
11202                 n.ui.hide();
11203                 return false;
11204             }
11205             return true;
11206         };
11207         startNode.cascade(f);
11208         if(this.remove){
11209            for(var id in af){
11210                if(typeof id != "function"){
11211                    var n = af[id];
11212                    if(n && n.parentNode){
11213                        n.parentNode.removeChild(n);
11214                    }
11215                }
11216            }
11217         }
11218     },
11219
11220     /**
11221      * Clears the current filter. Note: with the "remove" option
11222      * set a filter cannot be cleared.
11223      */
11224     clear : function(){
11225         var t = this.tree;
11226         var af = this.filtered;
11227         for(var id in af){
11228             if(typeof id != "function"){
11229                 var n = af[id];
11230                 if(n){
11231                     n.ui.show();
11232                 }
11233             }
11234         }
11235         this.filtered = {};
11236     }
11237 };
11238 /*
11239  * Based on:
11240  * Ext JS Library 1.1.1
11241  * Copyright(c) 2006-2007, Ext JS, LLC.
11242  *
11243  * Originally Released Under LGPL - original licence link has changed is not relivant.
11244  *
11245  * Fork - LGPL
11246  * <script type="text/javascript">
11247  */
11248  
11249
11250 /**
11251  * @class Roo.tree.TreeSorter
11252  * Provides sorting of nodes in a TreePanel
11253  * 
11254  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
11255  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
11256  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
11257  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
11258  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
11259  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
11260  * @constructor
11261  * @param {TreePanel} tree
11262  * @param {Object} config
11263  */
11264 Roo.tree.TreeSorter = function(tree, config){
11265     Roo.apply(this, config);
11266     tree.on("beforechildrenrendered", this.doSort, this);
11267     tree.on("append", this.updateSort, this);
11268     tree.on("insert", this.updateSort, this);
11269     
11270     var dsc = this.dir && this.dir.toLowerCase() == "desc";
11271     var p = this.property || "text";
11272     var sortType = this.sortType;
11273     var fs = this.folderSort;
11274     var cs = this.caseSensitive === true;
11275     var leafAttr = this.leafAttr || 'leaf';
11276
11277     this.sortFn = function(n1, n2){
11278         if(fs){
11279             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
11280                 return 1;
11281             }
11282             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
11283                 return -1;
11284             }
11285         }
11286         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
11287         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
11288         if(v1 < v2){
11289                         return dsc ? +1 : -1;
11290                 }else if(v1 > v2){
11291                         return dsc ? -1 : +1;
11292         }else{
11293                 return 0;
11294         }
11295     };
11296 };
11297
11298 Roo.tree.TreeSorter.prototype = {
11299     doSort : function(node){
11300         node.sort(this.sortFn);
11301     },
11302     
11303     compareNodes : function(n1, n2){
11304         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
11305     },
11306     
11307     updateSort : function(tree, node){
11308         if(node.childrenRendered){
11309             this.doSort.defer(1, this, [node]);
11310         }
11311     }
11312 };/*
11313  * Based on:
11314  * Ext JS Library 1.1.1
11315  * Copyright(c) 2006-2007, Ext JS, LLC.
11316  *
11317  * Originally Released Under LGPL - original licence link has changed is not relivant.
11318  *
11319  * Fork - LGPL
11320  * <script type="text/javascript">
11321  */
11322
11323 if(Roo.dd.DropZone){
11324     
11325 Roo.tree.TreeDropZone = function(tree, config){
11326     this.allowParentInsert = false;
11327     this.allowContainerDrop = false;
11328     this.appendOnly = false;
11329     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
11330     this.tree = tree;
11331     this.lastInsertClass = "x-tree-no-status";
11332     this.dragOverData = {};
11333 };
11334
11335 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
11336     ddGroup : "TreeDD",
11337     
11338     expandDelay : 1000,
11339     
11340     expandNode : function(node){
11341         if(node.hasChildNodes() && !node.isExpanded()){
11342             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
11343         }
11344     },
11345     
11346     queueExpand : function(node){
11347         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
11348     },
11349     
11350     cancelExpand : function(){
11351         if(this.expandProcId){
11352             clearTimeout(this.expandProcId);
11353             this.expandProcId = false;
11354         }
11355     },
11356     
11357     isValidDropPoint : function(n, pt, dd, e, data){
11358         if(!n || !data){ return false; }
11359         var targetNode = n.node;
11360         var dropNode = data.node;
11361         // default drop rules
11362         if(!(targetNode && targetNode.isTarget && pt)){
11363             return false;
11364         }
11365         if(pt == "append" && targetNode.allowChildren === false){
11366             return false;
11367         }
11368         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
11369             return false;
11370         }
11371         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
11372             return false;
11373         }
11374         // reuse the object
11375         var overEvent = this.dragOverData;
11376         overEvent.tree = this.tree;
11377         overEvent.target = targetNode;
11378         overEvent.data = data;
11379         overEvent.point = pt;
11380         overEvent.source = dd;
11381         overEvent.rawEvent = e;
11382         overEvent.dropNode = dropNode;
11383         overEvent.cancel = false;  
11384         var result = this.tree.fireEvent("nodedragover", overEvent);
11385         return overEvent.cancel === false && result !== false;
11386     },
11387     
11388     getDropPoint : function(e, n, dd){
11389         var tn = n.node;
11390         if(tn.isRoot){
11391             return tn.allowChildren !== false ? "append" : false; // always append for root
11392         }
11393         var dragEl = n.ddel;
11394         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
11395         var y = Roo.lib.Event.getPageY(e);
11396         var noAppend = tn.allowChildren === false || tn.isLeaf();
11397         if(this.appendOnly || tn.parentNode.allowChildren === false){
11398             return noAppend ? false : "append";
11399         }
11400         var noBelow = false;
11401         if(!this.allowParentInsert){
11402             noBelow = tn.hasChildNodes() && tn.isExpanded();
11403         }
11404         var q = (b - t) / (noAppend ? 2 : 3);
11405         if(y >= t && y < (t + q)){
11406             return "above";
11407         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
11408             return "below";
11409         }else{
11410             return "append";
11411         }
11412     },
11413     
11414     onNodeEnter : function(n, dd, e, data){
11415         this.cancelExpand();
11416     },
11417     
11418     onNodeOver : function(n, dd, e, data){
11419         var pt = this.getDropPoint(e, n, dd);
11420         var node = n.node;
11421         
11422         // auto node expand check
11423         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
11424             this.queueExpand(node);
11425         }else if(pt != "append"){
11426             this.cancelExpand();
11427         }
11428         
11429         // set the insert point style on the target node
11430         var returnCls = this.dropNotAllowed;
11431         if(this.isValidDropPoint(n, pt, dd, e, data)){
11432            if(pt){
11433                var el = n.ddel;
11434                var cls;
11435                if(pt == "above"){
11436                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
11437                    cls = "x-tree-drag-insert-above";
11438                }else if(pt == "below"){
11439                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
11440                    cls = "x-tree-drag-insert-below";
11441                }else{
11442                    returnCls = "x-tree-drop-ok-append";
11443                    cls = "x-tree-drag-append";
11444                }
11445                if(this.lastInsertClass != cls){
11446                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
11447                    this.lastInsertClass = cls;
11448                }
11449            }
11450        }
11451        return returnCls;
11452     },
11453     
11454     onNodeOut : function(n, dd, e, data){
11455         this.cancelExpand();
11456         this.removeDropIndicators(n);
11457     },
11458     
11459     onNodeDrop : function(n, dd, e, data){
11460         var point = this.getDropPoint(e, n, dd);
11461         var targetNode = n.node;
11462         targetNode.ui.startDrop();
11463         if(!this.isValidDropPoint(n, point, dd, e, data)){
11464             targetNode.ui.endDrop();
11465             return false;
11466         }
11467         // first try to find the drop node
11468         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
11469         var dropEvent = {
11470             tree : this.tree,
11471             target: targetNode,
11472             data: data,
11473             point: point,
11474             source: dd,
11475             rawEvent: e,
11476             dropNode: dropNode,
11477             cancel: !dropNode   
11478         };
11479         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
11480         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
11481             targetNode.ui.endDrop();
11482             return false;
11483         }
11484         // allow target changing
11485         targetNode = dropEvent.target;
11486         if(point == "append" && !targetNode.isExpanded()){
11487             targetNode.expand(false, null, function(){
11488                 this.completeDrop(dropEvent);
11489             }.createDelegate(this));
11490         }else{
11491             this.completeDrop(dropEvent);
11492         }
11493         return true;
11494     },
11495     
11496     completeDrop : function(de){
11497         var ns = de.dropNode, p = de.point, t = de.target;
11498         if(!(ns instanceof Array)){
11499             ns = [ns];
11500         }
11501         var n;
11502         for(var i = 0, len = ns.length; i < len; i++){
11503             n = ns[i];
11504             if(p == "above"){
11505                 t.parentNode.insertBefore(n, t);
11506             }else if(p == "below"){
11507                 t.parentNode.insertBefore(n, t.nextSibling);
11508             }else{
11509                 t.appendChild(n);
11510             }
11511         }
11512         n.ui.focus();
11513         if(this.tree.hlDrop){
11514             n.ui.highlight();
11515         }
11516         t.ui.endDrop();
11517         this.tree.fireEvent("nodedrop", de);
11518     },
11519     
11520     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
11521         if(this.tree.hlDrop){
11522             dropNode.ui.focus();
11523             dropNode.ui.highlight();
11524         }
11525         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
11526     },
11527     
11528     getTree : function(){
11529         return this.tree;
11530     },
11531     
11532     removeDropIndicators : function(n){
11533         if(n && n.ddel){
11534             var el = n.ddel;
11535             Roo.fly(el).removeClass([
11536                     "x-tree-drag-insert-above",
11537                     "x-tree-drag-insert-below",
11538                     "x-tree-drag-append"]);
11539             this.lastInsertClass = "_noclass";
11540         }
11541     },
11542     
11543     beforeDragDrop : function(target, e, id){
11544         this.cancelExpand();
11545         return true;
11546     },
11547     
11548     afterRepair : function(data){
11549         if(data && Roo.enableFx){
11550             data.node.ui.highlight();
11551         }
11552         this.hideProxy();
11553     }    
11554 });
11555
11556 }/*
11557  * Based on:
11558  * Ext JS Library 1.1.1
11559  * Copyright(c) 2006-2007, Ext JS, LLC.
11560  *
11561  * Originally Released Under LGPL - original licence link has changed is not relivant.
11562  *
11563  * Fork - LGPL
11564  * <script type="text/javascript">
11565  */
11566  
11567
11568 if(Roo.dd.DragZone){
11569 Roo.tree.TreeDragZone = function(tree, config){
11570     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
11571     this.tree = tree;
11572 };
11573
11574 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
11575     ddGroup : "TreeDD",
11576     
11577     onBeforeDrag : function(data, e){
11578         var n = data.node;
11579         return n && n.draggable && !n.disabled;
11580     },
11581     
11582     onInitDrag : function(e){
11583         var data = this.dragData;
11584         this.tree.getSelectionModel().select(data.node);
11585         this.proxy.update("");
11586         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
11587         this.tree.fireEvent("startdrag", this.tree, data.node, e);
11588     },
11589     
11590     getRepairXY : function(e, data){
11591         return data.node.ui.getDDRepairXY();
11592     },
11593     
11594     onEndDrag : function(data, e){
11595         this.tree.fireEvent("enddrag", this.tree, data.node, e);
11596     },
11597     
11598     onValidDrop : function(dd, e, id){
11599         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
11600         this.hideProxy();
11601     },
11602     
11603     beforeInvalidDrop : function(e, id){
11604         // this scrolls the original position back into view
11605         var sm = this.tree.getSelectionModel();
11606         sm.clearSelections();
11607         sm.select(this.dragData.node);
11608     }
11609 });
11610 }/*
11611  * Based on:
11612  * Ext JS Library 1.1.1
11613  * Copyright(c) 2006-2007, Ext JS, LLC.
11614  *
11615  * Originally Released Under LGPL - original licence link has changed is not relivant.
11616  *
11617  * Fork - LGPL
11618  * <script type="text/javascript">
11619  */
11620 /**
11621  * @class Roo.tree.TreeEditor
11622  * @extends Roo.Editor
11623  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
11624  * as the editor field.
11625  * @constructor
11626  * @param {TreePanel} tree
11627  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
11628  */
11629 Roo.tree.TreeEditor = function(tree, config){
11630     config = config || {};
11631     var field = config.events ? config : new Roo.form.TextField(config);
11632     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
11633
11634     this.tree = tree;
11635
11636     tree.on('beforeclick', this.beforeNodeClick, this);
11637     tree.getTreeEl().on('mousedown', this.hide, this);
11638     this.on('complete', this.updateNode, this);
11639     this.on('beforestartedit', this.fitToTree, this);
11640     this.on('startedit', this.bindScroll, this, {delay:10});
11641     this.on('specialkey', this.onSpecialKey, this);
11642 };
11643
11644 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
11645     /**
11646      * @cfg {String} alignment
11647      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
11648      */
11649     alignment: "l-l",
11650     // inherit
11651     autoSize: false,
11652     /**
11653      * @cfg {Boolean} hideEl
11654      * True to hide the bound element while the editor is displayed (defaults to false)
11655      */
11656     hideEl : false,
11657     /**
11658      * @cfg {String} cls
11659      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
11660      */
11661     cls: "x-small-editor x-tree-editor",
11662     /**
11663      * @cfg {Boolean} shim
11664      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
11665      */
11666     shim:false,
11667     // inherit
11668     shadow:"frame",
11669     /**
11670      * @cfg {Number} maxWidth
11671      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
11672      * the containing tree element's size, it will be automatically limited for you to the container width, taking
11673      * scroll and client offsets into account prior to each edit.
11674      */
11675     maxWidth: 250,
11676
11677     editDelay : 350,
11678
11679     // private
11680     fitToTree : function(ed, el){
11681         var td = this.tree.getTreeEl().dom, nd = el.dom;
11682         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
11683             td.scrollLeft = nd.offsetLeft;
11684         }
11685         var w = Math.min(
11686                 this.maxWidth,
11687                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
11688         this.setSize(w, '');
11689     },
11690
11691     // private
11692     triggerEdit : function(node){
11693         this.completeEdit();
11694         this.editNode = node;
11695         this.startEdit(node.ui.textNode, node.text);
11696     },
11697
11698     // private
11699     bindScroll : function(){
11700         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
11701     },
11702
11703     // private
11704     beforeNodeClick : function(node, e){
11705         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
11706         this.lastClick = new Date();
11707         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
11708             e.stopEvent();
11709             this.triggerEdit(node);
11710             return false;
11711         }
11712     },
11713
11714     // private
11715     updateNode : function(ed, value){
11716         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
11717         this.editNode.setText(value);
11718     },
11719
11720     // private
11721     onHide : function(){
11722         Roo.tree.TreeEditor.superclass.onHide.call(this);
11723         if(this.editNode){
11724             this.editNode.ui.focus();
11725         }
11726     },
11727
11728     // private
11729     onSpecialKey : function(field, e){
11730         var k = e.getKey();
11731         if(k == e.ESC){
11732             e.stopEvent();
11733             this.cancelEdit();
11734         }else if(k == e.ENTER && !e.hasModifier()){
11735             e.stopEvent();
11736             this.completeEdit();
11737         }
11738     }
11739 });//<Script type="text/javascript">
11740 /*
11741  * Based on:
11742  * Ext JS Library 1.1.1
11743  * Copyright(c) 2006-2007, Ext JS, LLC.
11744  *
11745  * Originally Released Under LGPL - original licence link has changed is not relivant.
11746  *
11747  * Fork - LGPL
11748  * <script type="text/javascript">
11749  */
11750  
11751 /**
11752  * Not documented??? - probably should be...
11753  */
11754
11755 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
11756     //focus: Roo.emptyFn, // prevent odd scrolling behavior
11757     
11758     renderElements : function(n, a, targetNode, bulkRender){
11759         //consel.log("renderElements?");
11760         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
11761
11762         var t = n.getOwnerTree();
11763         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
11764         
11765         var cols = t.columns;
11766         var bw = t.borderWidth;
11767         var c = cols[0];
11768         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
11769          var cb = typeof a.checked == "boolean";
11770         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
11771         var colcls = 'x-t-' + tid + '-c0';
11772         var buf = [
11773             '<li class="x-tree-node">',
11774             
11775                 
11776                 '<div class="x-tree-node-el ', a.cls,'">',
11777                     // extran...
11778                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
11779                 
11780                 
11781                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
11782                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
11783                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
11784                            (a.icon ? ' x-tree-node-inline-icon' : ''),
11785                            (a.iconCls ? ' '+a.iconCls : ''),
11786                            '" unselectable="on" />',
11787                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
11788                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
11789                              
11790                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
11791                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
11792                             '<span unselectable="on" qtip="' + tx + '">',
11793                              tx,
11794                              '</span></a>' ,
11795                     '</div>',
11796                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
11797                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
11798                  ];
11799         
11800         for(var i = 1, len = cols.length; i < len; i++){
11801             c = cols[i];
11802             colcls = 'x-t-' + tid + '-c' +i;
11803             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
11804             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
11805                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
11806                       "</div>");
11807          }
11808          
11809          buf.push(
11810             '</a>',
11811             '<div class="x-clear"></div></div>',
11812             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
11813             "</li>");
11814         
11815         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
11816             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
11817                                 n.nextSibling.ui.getEl(), buf.join(""));
11818         }else{
11819             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
11820         }
11821         var el = this.wrap.firstChild;
11822         this.elRow = el;
11823         this.elNode = el.firstChild;
11824         this.ranchor = el.childNodes[1];
11825         this.ctNode = this.wrap.childNodes[1];
11826         var cs = el.firstChild.childNodes;
11827         this.indentNode = cs[0];
11828         this.ecNode = cs[1];
11829         this.iconNode = cs[2];
11830         var index = 3;
11831         if(cb){
11832             this.checkbox = cs[3];
11833             index++;
11834         }
11835         this.anchor = cs[index];
11836         
11837         this.textNode = cs[index].firstChild;
11838         
11839         //el.on("click", this.onClick, this);
11840         //el.on("dblclick", this.onDblClick, this);
11841         
11842         
11843        // console.log(this);
11844     },
11845     initEvents : function(){
11846         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
11847         
11848             
11849         var a = this.ranchor;
11850
11851         var el = Roo.get(a);
11852
11853         if(Roo.isOpera){ // opera render bug ignores the CSS
11854             el.setStyle("text-decoration", "none");
11855         }
11856
11857         el.on("click", this.onClick, this);
11858         el.on("dblclick", this.onDblClick, this);
11859         el.on("contextmenu", this.onContextMenu, this);
11860         
11861     },
11862     
11863     /*onSelectedChange : function(state){
11864         if(state){
11865             this.focus();
11866             this.addClass("x-tree-selected");
11867         }else{
11868             //this.blur();
11869             this.removeClass("x-tree-selected");
11870         }
11871     },*/
11872     addClass : function(cls){
11873         if(this.elRow){
11874             Roo.fly(this.elRow).addClass(cls);
11875         }
11876         
11877     },
11878     
11879     
11880     removeClass : function(cls){
11881         if(this.elRow){
11882             Roo.fly(this.elRow).removeClass(cls);
11883         }
11884     }
11885
11886     
11887     
11888 });//<Script type="text/javascript">
11889
11890 /*
11891  * Based on:
11892  * Ext JS Library 1.1.1
11893  * Copyright(c) 2006-2007, Ext JS, LLC.
11894  *
11895  * Originally Released Under LGPL - original licence link has changed is not relivant.
11896  *
11897  * Fork - LGPL
11898  * <script type="text/javascript">
11899  */
11900  
11901
11902 /**
11903  * @class Roo.tree.ColumnTree
11904  * @extends Roo.data.TreePanel
11905  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
11906  * @cfg {int} borderWidth  compined right/left border allowance
11907  * @constructor
11908  * @param {String/HTMLElement/Element} el The container element
11909  * @param {Object} config
11910  */
11911 Roo.tree.ColumnTree =  function(el, config)
11912 {
11913    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
11914    this.addEvents({
11915         /**
11916         * @event resize
11917         * Fire this event on a container when it resizes
11918         * @param {int} w Width
11919         * @param {int} h Height
11920         */
11921        "resize" : true
11922     });
11923     this.on('resize', this.onResize, this);
11924 };
11925
11926 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
11927     //lines:false,
11928     
11929     
11930     borderWidth: Roo.isBorderBox ? 0 : 2, 
11931     headEls : false,
11932     
11933     render : function(){
11934         // add the header.....
11935        
11936         Roo.tree.ColumnTree.superclass.render.apply(this);
11937         
11938         this.el.addClass('x-column-tree');
11939         
11940         this.headers = this.el.createChild(
11941             {cls:'x-tree-headers'},this.innerCt.dom);
11942    
11943         var cols = this.columns, c;
11944         var totalWidth = 0;
11945         this.headEls = [];
11946         var  len = cols.length;
11947         for(var i = 0; i < len; i++){
11948              c = cols[i];
11949              totalWidth += c.width;
11950             this.headEls.push(this.headers.createChild({
11951                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
11952                  cn: {
11953                      cls:'x-tree-hd-text',
11954                      html: c.header
11955                  },
11956                  style:'width:'+(c.width-this.borderWidth)+'px;'
11957              }));
11958         }
11959         this.headers.createChild({cls:'x-clear'});
11960         // prevent floats from wrapping when clipped
11961         this.headers.setWidth(totalWidth);
11962         //this.innerCt.setWidth(totalWidth);
11963         this.innerCt.setStyle({ overflow: 'auto' });
11964         this.onResize(this.width, this.height);
11965              
11966         
11967     },
11968     onResize : function(w,h)
11969     {
11970         this.height = h;
11971         this.width = w;
11972         // resize cols..
11973         this.innerCt.setWidth(this.width);
11974         this.innerCt.setHeight(this.height-20);
11975         
11976         // headers...
11977         var cols = this.columns, c;
11978         var totalWidth = 0;
11979         var expEl = false;
11980         var len = cols.length;
11981         for(var i = 0; i < len; i++){
11982             c = cols[i];
11983             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
11984                 // it's the expander..
11985                 expEl  = this.headEls[i];
11986                 continue;
11987             }
11988             totalWidth += c.width;
11989             
11990         }
11991         if (expEl) {
11992             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
11993         }
11994         this.headers.setWidth(w-20);
11995
11996         
11997         
11998         
11999     }
12000 });
12001 /*
12002  * Based on:
12003  * Ext JS Library 1.1.1
12004  * Copyright(c) 2006-2007, Ext JS, LLC.
12005  *
12006  * Originally Released Under LGPL - original licence link has changed is not relivant.
12007  *
12008  * Fork - LGPL
12009  * <script type="text/javascript">
12010  */
12011  
12012 /**
12013  * @class Roo.menu.Menu
12014  * @extends Roo.util.Observable
12015  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
12016  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
12017  * @constructor
12018  * Creates a new Menu
12019  * @param {Object} config Configuration options
12020  */
12021 Roo.menu.Menu = function(config){
12022     Roo.apply(this, config);
12023     this.id = this.id || Roo.id();
12024     this.addEvents({
12025         /**
12026          * @event beforeshow
12027          * Fires before this menu is displayed
12028          * @param {Roo.menu.Menu} this
12029          */
12030         beforeshow : true,
12031         /**
12032          * @event beforehide
12033          * Fires before this menu is hidden
12034          * @param {Roo.menu.Menu} this
12035          */
12036         beforehide : true,
12037         /**
12038          * @event show
12039          * Fires after this menu is displayed
12040          * @param {Roo.menu.Menu} this
12041          */
12042         show : true,
12043         /**
12044          * @event hide
12045          * Fires after this menu is hidden
12046          * @param {Roo.menu.Menu} this
12047          */
12048         hide : true,
12049         /**
12050          * @event click
12051          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
12052          * @param {Roo.menu.Menu} this
12053          * @param {Roo.menu.Item} menuItem The menu item that was clicked
12054          * @param {Roo.EventObject} e
12055          */
12056         click : true,
12057         /**
12058          * @event mouseover
12059          * Fires when the mouse is hovering over this menu
12060          * @param {Roo.menu.Menu} this
12061          * @param {Roo.EventObject} e
12062          * @param {Roo.menu.Item} menuItem The menu item that was clicked
12063          */
12064         mouseover : true,
12065         /**
12066          * @event mouseout
12067          * Fires when the mouse exits this menu
12068          * @param {Roo.menu.Menu} this
12069          * @param {Roo.EventObject} e
12070          * @param {Roo.menu.Item} menuItem The menu item that was clicked
12071          */
12072         mouseout : true,
12073         /**
12074          * @event itemclick
12075          * Fires when a menu item contained in this menu is clicked
12076          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
12077          * @param {Roo.EventObject} e
12078          */
12079         itemclick: true
12080     });
12081     if (this.registerMenu) {
12082         Roo.menu.MenuMgr.register(this);
12083     }
12084     
12085     var mis = this.items;
12086     this.items = new Roo.util.MixedCollection();
12087     if(mis){
12088         this.add.apply(this, mis);
12089     }
12090 };
12091
12092 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
12093     /**
12094      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
12095      */
12096     minWidth : 120,
12097     /**
12098      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
12099      * for bottom-right shadow (defaults to "sides")
12100      */
12101     shadow : "sides",
12102     /**
12103      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
12104      * this menu (defaults to "tl-tr?")
12105      */
12106     subMenuAlign : "tl-tr?",
12107     /**
12108      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
12109      * relative to its element of origin (defaults to "tl-bl?")
12110      */
12111     defaultAlign : "tl-bl?",
12112     /**
12113      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
12114      */
12115     allowOtherMenus : false,
12116     /**
12117      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
12118      */
12119     registerMenu : true,
12120
12121     hidden:true,
12122
12123     // private
12124     render : function(){
12125         if(this.el){
12126             return;
12127         }
12128         var el = this.el = new Roo.Layer({
12129             cls: "x-menu",
12130             shadow:this.shadow,
12131             constrain: false,
12132             parentEl: this.parentEl || document.body,
12133             zindex:15000
12134         });
12135
12136         this.keyNav = new Roo.menu.MenuNav(this);
12137
12138         if(this.plain){
12139             el.addClass("x-menu-plain");
12140         }
12141         if(this.cls){
12142             el.addClass(this.cls);
12143         }
12144         // generic focus element
12145         this.focusEl = el.createChild({
12146             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
12147         });
12148         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
12149         ul.on("click", this.onClick, this);
12150         ul.on("mouseover", this.onMouseOver, this);
12151         ul.on("mouseout", this.onMouseOut, this);
12152         this.items.each(function(item){
12153             var li = document.createElement("li");
12154             li.className = "x-menu-list-item";
12155             ul.dom.appendChild(li);
12156             item.render(li, this);
12157         }, this);
12158         this.ul = ul;
12159         this.autoWidth();
12160     },
12161
12162     // private
12163     autoWidth : function(){
12164         var el = this.el, ul = this.ul;
12165         if(!el){
12166             return;
12167         }
12168         var w = this.width;
12169         if(w){
12170             el.setWidth(w);
12171         }else if(Roo.isIE){
12172             el.setWidth(this.minWidth);
12173             var t = el.dom.offsetWidth; // force recalc
12174             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
12175         }
12176     },
12177
12178     // private
12179     delayAutoWidth : function(){
12180         if(this.rendered){
12181             if(!this.awTask){
12182                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
12183             }
12184             this.awTask.delay(20);
12185         }
12186     },
12187
12188     // private
12189     findTargetItem : function(e){
12190         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
12191         if(t && t.menuItemId){
12192             return this.items.get(t.menuItemId);
12193         }
12194     },
12195
12196     // private
12197     onClick : function(e){
12198         var t;
12199         if(t = this.findTargetItem(e)){
12200             t.onClick(e);
12201             this.fireEvent("click", this, t, e);
12202         }
12203     },
12204
12205     // private
12206     setActiveItem : function(item, autoExpand){
12207         if(item != this.activeItem){
12208             if(this.activeItem){
12209                 this.activeItem.deactivate();
12210             }
12211             this.activeItem = item;
12212             item.activate(autoExpand);
12213         }else if(autoExpand){
12214             item.expandMenu();
12215         }
12216     },
12217
12218     // private
12219     tryActivate : function(start, step){
12220         var items = this.items;
12221         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
12222             var item = items.get(i);
12223             if(!item.disabled && item.canActivate){
12224                 this.setActiveItem(item, false);
12225                 return item;
12226             }
12227         }
12228         return false;
12229     },
12230
12231     // private
12232     onMouseOver : function(e){
12233         var t;
12234         if(t = this.findTargetItem(e)){
12235             if(t.canActivate && !t.disabled){
12236                 this.setActiveItem(t, true);
12237             }
12238         }
12239         this.fireEvent("mouseover", this, e, t);
12240     },
12241
12242     // private
12243     onMouseOut : function(e){
12244         var t;
12245         if(t = this.findTargetItem(e)){
12246             if(t == this.activeItem && t.shouldDeactivate(e)){
12247                 this.activeItem.deactivate();
12248                 delete this.activeItem;
12249             }
12250         }
12251         this.fireEvent("mouseout", this, e, t);
12252     },
12253
12254     /**
12255      * Read-only.  Returns true if the menu is currently displayed, else false.
12256      * @type Boolean
12257      */
12258     isVisible : function(){
12259         return this.el && !this.hidden;
12260     },
12261
12262     /**
12263      * Displays this menu relative to another element
12264      * @param {String/HTMLElement/Roo.Element} element The element to align to
12265      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
12266      * the element (defaults to this.defaultAlign)
12267      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
12268      */
12269     show : function(el, pos, parentMenu){
12270         this.parentMenu = parentMenu;
12271         if(!this.el){
12272             this.render();
12273         }
12274         this.fireEvent("beforeshow", this);
12275         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
12276     },
12277
12278     /**
12279      * Displays this menu at a specific xy position
12280      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
12281      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
12282      */
12283     showAt : function(xy, parentMenu, /* private: */_e){
12284         this.parentMenu = parentMenu;
12285         if(!this.el){
12286             this.render();
12287         }
12288         if(_e !== false){
12289             this.fireEvent("beforeshow", this);
12290             xy = this.el.adjustForConstraints(xy);
12291         }
12292         this.el.setXY(xy);
12293         this.el.show();
12294         this.hidden = false;
12295         this.focus();
12296         this.fireEvent("show", this);
12297     },
12298
12299     focus : function(){
12300         if(!this.hidden){
12301             this.doFocus.defer(50, this);
12302         }
12303     },
12304
12305     doFocus : function(){
12306         if(!this.hidden){
12307             this.focusEl.focus();
12308         }
12309     },
12310
12311     /**
12312      * Hides this menu and optionally all parent menus
12313      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
12314      */
12315     hide : function(deep){
12316         if(this.el && this.isVisible()){
12317             this.fireEvent("beforehide", this);
12318             if(this.activeItem){
12319                 this.activeItem.deactivate();
12320                 this.activeItem = null;
12321             }
12322             this.el.hide();
12323             this.hidden = true;
12324             this.fireEvent("hide", this);
12325         }
12326         if(deep === true && this.parentMenu){
12327             this.parentMenu.hide(true);
12328         }
12329     },
12330
12331     /**
12332      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
12333      * Any of the following are valid:
12334      * <ul>
12335      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
12336      * <li>An HTMLElement object which will be converted to a menu item</li>
12337      * <li>A menu item config object that will be created as a new menu item</li>
12338      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
12339      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
12340      * </ul>
12341      * Usage:
12342      * <pre><code>
12343 // Create the menu
12344 var menu = new Roo.menu.Menu();
12345
12346 // Create a menu item to add by reference
12347 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
12348
12349 // Add a bunch of items at once using different methods.
12350 // Only the last item added will be returned.
12351 var item = menu.add(
12352     menuItem,                // add existing item by ref
12353     'Dynamic Item',          // new TextItem
12354     '-',                     // new separator
12355     { text: 'Config Item' }  // new item by config
12356 );
12357 </code></pre>
12358      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
12359      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
12360      */
12361     add : function(){
12362         var a = arguments, l = a.length, item;
12363         for(var i = 0; i < l; i++){
12364             var el = a[i];
12365             if(el.render){ // some kind of Item
12366                 item = this.addItem(el);
12367             }else if(typeof el == "string"){ // string
12368                 if(el == "separator" || el == "-"){
12369                     item = this.addSeparator();
12370                 }else{
12371                     item = this.addText(el);
12372                 }
12373             }else if(el.tagName || el.el){ // element
12374                 item = this.addElement(el);
12375             }else if(typeof el == "object"){ // must be menu item config?
12376                 item = this.addMenuItem(el);
12377             }
12378         }
12379         return item;
12380     },
12381
12382     /**
12383      * Returns this menu's underlying {@link Roo.Element} object
12384      * @return {Roo.Element} The element
12385      */
12386     getEl : function(){
12387         if(!this.el){
12388             this.render();
12389         }
12390         return this.el;
12391     },
12392
12393     /**
12394      * Adds a separator bar to the menu
12395      * @return {Roo.menu.Item} The menu item that was added
12396      */
12397     addSeparator : function(){
12398         return this.addItem(new Roo.menu.Separator());
12399     },
12400
12401     /**
12402      * Adds an {@link Roo.Element} object to the menu
12403      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
12404      * @return {Roo.menu.Item} The menu item that was added
12405      */
12406     addElement : function(el){
12407         return this.addItem(new Roo.menu.BaseItem(el));
12408     },
12409
12410     /**
12411      * Adds an existing object based on {@link Roo.menu.Item} to the menu
12412      * @param {Roo.menu.Item} item The menu item to add
12413      * @return {Roo.menu.Item} The menu item that was added
12414      */
12415     addItem : function(item){
12416         this.items.add(item);
12417         if(this.ul){
12418             var li = document.createElement("li");
12419             li.className = "x-menu-list-item";
12420             this.ul.dom.appendChild(li);
12421             item.render(li, this);
12422             this.delayAutoWidth();
12423         }
12424         return item;
12425     },
12426
12427     /**
12428      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
12429      * @param {Object} config A MenuItem config object
12430      * @return {Roo.menu.Item} The menu item that was added
12431      */
12432     addMenuItem : function(config){
12433         if(!(config instanceof Roo.menu.Item)){
12434             if(typeof config.checked == "boolean"){ // must be check menu item config?
12435                 config = new Roo.menu.CheckItem(config);
12436             }else{
12437                 config = new Roo.menu.Item(config);
12438             }
12439         }
12440         return this.addItem(config);
12441     },
12442
12443     /**
12444      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
12445      * @param {String} text The text to display in the menu item
12446      * @return {Roo.menu.Item} The menu item that was added
12447      */
12448     addText : function(text){
12449         return this.addItem(new Roo.menu.TextItem(text));
12450     },
12451
12452     /**
12453      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
12454      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
12455      * @param {Roo.menu.Item} item The menu item to add
12456      * @return {Roo.menu.Item} The menu item that was added
12457      */
12458     insert : function(index, item){
12459         this.items.insert(index, item);
12460         if(this.ul){
12461             var li = document.createElement("li");
12462             li.className = "x-menu-list-item";
12463             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
12464             item.render(li, this);
12465             this.delayAutoWidth();
12466         }
12467         return item;
12468     },
12469
12470     /**
12471      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
12472      * @param {Roo.menu.Item} item The menu item to remove
12473      */
12474     remove : function(item){
12475         this.items.removeKey(item.id);
12476         item.destroy();
12477     },
12478
12479     /**
12480      * Removes and destroys all items in the menu
12481      */
12482     removeAll : function(){
12483         var f;
12484         while(f = this.items.first()){
12485             this.remove(f);
12486         }
12487     }
12488 });
12489
12490 // MenuNav is a private utility class used internally by the Menu
12491 Roo.menu.MenuNav = function(menu){
12492     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
12493     this.scope = this.menu = menu;
12494 };
12495
12496 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
12497     doRelay : function(e, h){
12498         var k = e.getKey();
12499         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
12500             this.menu.tryActivate(0, 1);
12501             return false;
12502         }
12503         return h.call(this.scope || this, e, this.menu);
12504     },
12505
12506     up : function(e, m){
12507         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
12508             m.tryActivate(m.items.length-1, -1);
12509         }
12510     },
12511
12512     down : function(e, m){
12513         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
12514             m.tryActivate(0, 1);
12515         }
12516     },
12517
12518     right : function(e, m){
12519         if(m.activeItem){
12520             m.activeItem.expandMenu(true);
12521         }
12522     },
12523
12524     left : function(e, m){
12525         m.hide();
12526         if(m.parentMenu && m.parentMenu.activeItem){
12527             m.parentMenu.activeItem.activate();
12528         }
12529     },
12530
12531     enter : function(e, m){
12532         if(m.activeItem){
12533             e.stopPropagation();
12534             m.activeItem.onClick(e);
12535             m.fireEvent("click", this, m.activeItem);
12536             return true;
12537         }
12538     }
12539 });/*
12540  * Based on:
12541  * Ext JS Library 1.1.1
12542  * Copyright(c) 2006-2007, Ext JS, LLC.
12543  *
12544  * Originally Released Under LGPL - original licence link has changed is not relivant.
12545  *
12546  * Fork - LGPL
12547  * <script type="text/javascript">
12548  */
12549  
12550 /**
12551  * @class Roo.menu.MenuMgr
12552  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
12553  * @singleton
12554  */
12555 Roo.menu.MenuMgr = function(){
12556    var menus, active, groups = {}, attached = false, lastShow = new Date();
12557
12558    // private - called when first menu is created
12559    function init(){
12560        menus = {};
12561        active = new Roo.util.MixedCollection();
12562        Roo.get(document).addKeyListener(27, function(){
12563            if(active.length > 0){
12564                hideAll();
12565            }
12566        });
12567    }
12568
12569    // private
12570    function hideAll(){
12571        if(active && active.length > 0){
12572            var c = active.clone();
12573            c.each(function(m){
12574                m.hide();
12575            });
12576        }
12577    }
12578
12579    // private
12580    function onHide(m){
12581        active.remove(m);
12582        if(active.length < 1){
12583            Roo.get(document).un("mousedown", onMouseDown);
12584            attached = false;
12585        }
12586    }
12587
12588    // private
12589    function onShow(m){
12590        var last = active.last();
12591        lastShow = new Date();
12592        active.add(m);
12593        if(!attached){
12594            Roo.get(document).on("mousedown", onMouseDown);
12595            attached = true;
12596        }
12597        if(m.parentMenu){
12598           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
12599           m.parentMenu.activeChild = m;
12600        }else if(last && last.isVisible()){
12601           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
12602        }
12603    }
12604
12605    // private
12606    function onBeforeHide(m){
12607        if(m.activeChild){
12608            m.activeChild.hide();
12609        }
12610        if(m.autoHideTimer){
12611            clearTimeout(m.autoHideTimer);
12612            delete m.autoHideTimer;
12613        }
12614    }
12615
12616    // private
12617    function onBeforeShow(m){
12618        var pm = m.parentMenu;
12619        if(!pm && !m.allowOtherMenus){
12620            hideAll();
12621        }else if(pm && pm.activeChild && active != m){
12622            pm.activeChild.hide();
12623        }
12624    }
12625
12626    // private
12627    function onMouseDown(e){
12628        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
12629            hideAll();
12630        }
12631    }
12632
12633    // private
12634    function onBeforeCheck(mi, state){
12635        if(state){
12636            var g = groups[mi.group];
12637            for(var i = 0, l = g.length; i < l; i++){
12638                if(g[i] != mi){
12639                    g[i].setChecked(false);
12640                }
12641            }
12642        }
12643    }
12644
12645    return {
12646
12647        /**
12648         * Hides all menus that are currently visible
12649         */
12650        hideAll : function(){
12651             hideAll();  
12652        },
12653
12654        // private
12655        register : function(menu){
12656            if(!menus){
12657                init();
12658            }
12659            menus[menu.id] = menu;
12660            menu.on("beforehide", onBeforeHide);
12661            menu.on("hide", onHide);
12662            menu.on("beforeshow", onBeforeShow);
12663            menu.on("show", onShow);
12664            var g = menu.group;
12665            if(g && menu.events["checkchange"]){
12666                if(!groups[g]){
12667                    groups[g] = [];
12668                }
12669                groups[g].push(menu);
12670                menu.on("checkchange", onCheck);
12671            }
12672        },
12673
12674         /**
12675          * Returns a {@link Roo.menu.Menu} object
12676          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
12677          * be used to generate and return a new Menu instance.
12678          */
12679        get : function(menu){
12680            if(typeof menu == "string"){ // menu id
12681                return menus[menu];
12682            }else if(menu.events){  // menu instance
12683                return menu;
12684            }else if(typeof menu.length == 'number'){ // array of menu items?
12685                return new Roo.menu.Menu({items:menu});
12686            }else{ // otherwise, must be a config
12687                return new Roo.menu.Menu(menu);
12688            }
12689        },
12690
12691        // private
12692        unregister : function(menu){
12693            delete menus[menu.id];
12694            menu.un("beforehide", onBeforeHide);
12695            menu.un("hide", onHide);
12696            menu.un("beforeshow", onBeforeShow);
12697            menu.un("show", onShow);
12698            var g = menu.group;
12699            if(g && menu.events["checkchange"]){
12700                groups[g].remove(menu);
12701                menu.un("checkchange", onCheck);
12702            }
12703        },
12704
12705        // private
12706        registerCheckable : function(menuItem){
12707            var g = menuItem.group;
12708            if(g){
12709                if(!groups[g]){
12710                    groups[g] = [];
12711                }
12712                groups[g].push(menuItem);
12713                menuItem.on("beforecheckchange", onBeforeCheck);
12714            }
12715        },
12716
12717        // private
12718        unregisterCheckable : function(menuItem){
12719            var g = menuItem.group;
12720            if(g){
12721                groups[g].remove(menuItem);
12722                menuItem.un("beforecheckchange", onBeforeCheck);
12723            }
12724        }
12725    };
12726 }();/*
12727  * Based on:
12728  * Ext JS Library 1.1.1
12729  * Copyright(c) 2006-2007, Ext JS, LLC.
12730  *
12731  * Originally Released Under LGPL - original licence link has changed is not relivant.
12732  *
12733  * Fork - LGPL
12734  * <script type="text/javascript">
12735  */
12736  
12737
12738 /**
12739  * @class Roo.menu.BaseItem
12740  * @extends Roo.Component
12741  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
12742  * management and base configuration options shared by all menu components.
12743  * @constructor
12744  * Creates a new BaseItem
12745  * @param {Object} config Configuration options
12746  */
12747 Roo.menu.BaseItem = function(config){
12748     Roo.menu.BaseItem.superclass.constructor.call(this, config);
12749
12750     this.addEvents({
12751         /**
12752          * @event click
12753          * Fires when this item is clicked
12754          * @param {Roo.menu.BaseItem} this
12755          * @param {Roo.EventObject} e
12756          */
12757         click: true,
12758         /**
12759          * @event activate
12760          * Fires when this item is activated
12761          * @param {Roo.menu.BaseItem} this
12762          */
12763         activate : true,
12764         /**
12765          * @event deactivate
12766          * Fires when this item is deactivated
12767          * @param {Roo.menu.BaseItem} this
12768          */
12769         deactivate : true
12770     });
12771
12772     if(this.handler){
12773         this.on("click", this.handler, this.scope, true);
12774     }
12775 };
12776
12777 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
12778     /**
12779      * @cfg {Function} handler
12780      * A function that will handle the click event of this menu item (defaults to undefined)
12781      */
12782     /**
12783      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
12784      */
12785     canActivate : false,
12786     /**
12787      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
12788      */
12789     activeClass : "x-menu-item-active",
12790     /**
12791      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
12792      */
12793     hideOnClick : true,
12794     /**
12795      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
12796      */
12797     hideDelay : 100,
12798
12799     // private
12800     ctype: "Roo.menu.BaseItem",
12801
12802     // private
12803     actionMode : "container",
12804
12805     // private
12806     render : function(container, parentMenu){
12807         this.parentMenu = parentMenu;
12808         Roo.menu.BaseItem.superclass.render.call(this, container);
12809         this.container.menuItemId = this.id;
12810     },
12811
12812     // private
12813     onRender : function(container, position){
12814         this.el = Roo.get(this.el);
12815         container.dom.appendChild(this.el.dom);
12816     },
12817
12818     // private
12819     onClick : function(e){
12820         if(!this.disabled && this.fireEvent("click", this, e) !== false
12821                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
12822             this.handleClick(e);
12823         }else{
12824             e.stopEvent();
12825         }
12826     },
12827
12828     // private
12829     activate : function(){
12830         if(this.disabled){
12831             return false;
12832         }
12833         var li = this.container;
12834         li.addClass(this.activeClass);
12835         this.region = li.getRegion().adjust(2, 2, -2, -2);
12836         this.fireEvent("activate", this);
12837         return true;
12838     },
12839
12840     // private
12841     deactivate : function(){
12842         this.container.removeClass(this.activeClass);
12843         this.fireEvent("deactivate", this);
12844     },
12845
12846     // private
12847     shouldDeactivate : function(e){
12848         return !this.region || !this.region.contains(e.getPoint());
12849     },
12850
12851     // private
12852     handleClick : function(e){
12853         if(this.hideOnClick){
12854             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
12855         }
12856     },
12857
12858     // private
12859     expandMenu : function(autoActivate){
12860         // do nothing
12861     },
12862
12863     // private
12864     hideMenu : function(){
12865         // do nothing
12866     }
12867 });/*
12868  * Based on:
12869  * Ext JS Library 1.1.1
12870  * Copyright(c) 2006-2007, Ext JS, LLC.
12871  *
12872  * Originally Released Under LGPL - original licence link has changed is not relivant.
12873  *
12874  * Fork - LGPL
12875  * <script type="text/javascript">
12876  */
12877  
12878 /**
12879  * @class Roo.menu.Adapter
12880  * @extends Roo.menu.BaseItem
12881  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
12882  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
12883  * @constructor
12884  * Creates a new Adapter
12885  * @param {Object} config Configuration options
12886  */
12887 Roo.menu.Adapter = function(component, config){
12888     Roo.menu.Adapter.superclass.constructor.call(this, config);
12889     this.component = component;
12890 };
12891 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
12892     // private
12893     canActivate : true,
12894
12895     // private
12896     onRender : function(container, position){
12897         this.component.render(container);
12898         this.el = this.component.getEl();
12899     },
12900
12901     // private
12902     activate : function(){
12903         if(this.disabled){
12904             return false;
12905         }
12906         this.component.focus();
12907         this.fireEvent("activate", this);
12908         return true;
12909     },
12910
12911     // private
12912     deactivate : function(){
12913         this.fireEvent("deactivate", this);
12914     },
12915
12916     // private
12917     disable : function(){
12918         this.component.disable();
12919         Roo.menu.Adapter.superclass.disable.call(this);
12920     },
12921
12922     // private
12923     enable : function(){
12924         this.component.enable();
12925         Roo.menu.Adapter.superclass.enable.call(this);
12926     }
12927 });/*
12928  * Based on:
12929  * Ext JS Library 1.1.1
12930  * Copyright(c) 2006-2007, Ext JS, LLC.
12931  *
12932  * Originally Released Under LGPL - original licence link has changed is not relivant.
12933  *
12934  * Fork - LGPL
12935  * <script type="text/javascript">
12936  */
12937
12938 /**
12939  * @class Roo.menu.TextItem
12940  * @extends Roo.menu.BaseItem
12941  * Adds a static text string to a menu, usually used as either a heading or group separator.
12942  * @constructor
12943  * Creates a new TextItem
12944  * @param {String} text The text to display
12945  */
12946 Roo.menu.TextItem = function(text){
12947     this.text = text;
12948     Roo.menu.TextItem.superclass.constructor.call(this);
12949 };
12950
12951 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
12952     /**
12953      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
12954      */
12955     hideOnClick : false,
12956     /**
12957      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
12958      */
12959     itemCls : "x-menu-text",
12960
12961     // private
12962     onRender : function(){
12963         var s = document.createElement("span");
12964         s.className = this.itemCls;
12965         s.innerHTML = this.text;
12966         this.el = s;
12967         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
12968     }
12969 });/*
12970  * Based on:
12971  * Ext JS Library 1.1.1
12972  * Copyright(c) 2006-2007, Ext JS, LLC.
12973  *
12974  * Originally Released Under LGPL - original licence link has changed is not relivant.
12975  *
12976  * Fork - LGPL
12977  * <script type="text/javascript">
12978  */
12979
12980 /**
12981  * @class Roo.menu.Separator
12982  * @extends Roo.menu.BaseItem
12983  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
12984  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
12985  * @constructor
12986  * @param {Object} config Configuration options
12987  */
12988 Roo.menu.Separator = function(config){
12989     Roo.menu.Separator.superclass.constructor.call(this, config);
12990 };
12991
12992 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
12993     /**
12994      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
12995      */
12996     itemCls : "x-menu-sep",
12997     /**
12998      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
12999      */
13000     hideOnClick : false,
13001
13002     // private
13003     onRender : function(li){
13004         var s = document.createElement("span");
13005         s.className = this.itemCls;
13006         s.innerHTML = "&#160;";
13007         this.el = s;
13008         li.addClass("x-menu-sep-li");
13009         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
13010     }
13011 });/*
13012  * Based on:
13013  * Ext JS Library 1.1.1
13014  * Copyright(c) 2006-2007, Ext JS, LLC.
13015  *
13016  * Originally Released Under LGPL - original licence link has changed is not relivant.
13017  *
13018  * Fork - LGPL
13019  * <script type="text/javascript">
13020  */
13021 /**
13022  * @class Roo.menu.Item
13023  * @extends Roo.menu.BaseItem
13024  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
13025  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
13026  * activation and click handling.
13027  * @constructor
13028  * Creates a new Item
13029  * @param {Object} config Configuration options
13030  */
13031 Roo.menu.Item = function(config){
13032     Roo.menu.Item.superclass.constructor.call(this, config);
13033     if(this.menu){
13034         this.menu = Roo.menu.MenuMgr.get(this.menu);
13035     }
13036 };
13037 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
13038     /**
13039      * @cfg {String} icon
13040      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
13041      */
13042     /**
13043      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
13044      */
13045     itemCls : "x-menu-item",
13046     /**
13047      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
13048      */
13049     canActivate : true,
13050     /**
13051      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
13052      */
13053     showDelay: 200,
13054     // doc'd in BaseItem
13055     hideDelay: 200,
13056
13057     // private
13058     ctype: "Roo.menu.Item",
13059     
13060     // private
13061     onRender : function(container, position){
13062         var el = document.createElement("a");
13063         el.hideFocus = true;
13064         el.unselectable = "on";
13065         el.href = this.href || "#";
13066         if(this.hrefTarget){
13067             el.target = this.hrefTarget;
13068         }
13069         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
13070         el.innerHTML = String.format(
13071                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
13072                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
13073         this.el = el;
13074         Roo.menu.Item.superclass.onRender.call(this, container, position);
13075     },
13076
13077     /**
13078      * Sets the text to display in this menu item
13079      * @param {String} text The text to display
13080      */
13081     setText : function(text){
13082         this.text = text;
13083         if(this.rendered){
13084             this.el.update(String.format(
13085                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
13086                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
13087             this.parentMenu.autoWidth();
13088         }
13089     },
13090
13091     // private
13092     handleClick : function(e){
13093         if(!this.href){ // if no link defined, stop the event automatically
13094             e.stopEvent();
13095         }
13096         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
13097     },
13098
13099     // private
13100     activate : function(autoExpand){
13101         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
13102             this.focus();
13103             if(autoExpand){
13104                 this.expandMenu();
13105             }
13106         }
13107         return true;
13108     },
13109
13110     // private
13111     shouldDeactivate : function(e){
13112         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
13113             if(this.menu && this.menu.isVisible()){
13114                 return !this.menu.getEl().getRegion().contains(e.getPoint());
13115             }
13116             return true;
13117         }
13118         return false;
13119     },
13120
13121     // private
13122     deactivate : function(){
13123         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
13124         this.hideMenu();
13125     },
13126
13127     // private
13128     expandMenu : function(autoActivate){
13129         if(!this.disabled && this.menu){
13130             clearTimeout(this.hideTimer);
13131             delete this.hideTimer;
13132             if(!this.menu.isVisible() && !this.showTimer){
13133                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
13134             }else if (this.menu.isVisible() && autoActivate){
13135                 this.menu.tryActivate(0, 1);
13136             }
13137         }
13138     },
13139
13140     // private
13141     deferExpand : function(autoActivate){
13142         delete this.showTimer;
13143         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
13144         if(autoActivate){
13145             this.menu.tryActivate(0, 1);
13146         }
13147     },
13148
13149     // private
13150     hideMenu : function(){
13151         clearTimeout(this.showTimer);
13152         delete this.showTimer;
13153         if(!this.hideTimer && this.menu && this.menu.isVisible()){
13154             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
13155         }
13156     },
13157
13158     // private
13159     deferHide : function(){
13160         delete this.hideTimer;
13161         this.menu.hide();
13162     }
13163 });/*
13164  * Based on:
13165  * Ext JS Library 1.1.1
13166  * Copyright(c) 2006-2007, Ext JS, LLC.
13167  *
13168  * Originally Released Under LGPL - original licence link has changed is not relivant.
13169  *
13170  * Fork - LGPL
13171  * <script type="text/javascript">
13172  */
13173  
13174 /**
13175  * @class Roo.menu.CheckItem
13176  * @extends Roo.menu.Item
13177  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
13178  * @constructor
13179  * Creates a new CheckItem
13180  * @param {Object} config Configuration options
13181  */
13182 Roo.menu.CheckItem = function(config){
13183     Roo.menu.CheckItem.superclass.constructor.call(this, config);
13184     this.addEvents({
13185         /**
13186          * @event beforecheckchange
13187          * Fires before the checked value is set, providing an opportunity to cancel if needed
13188          * @param {Roo.menu.CheckItem} this
13189          * @param {Boolean} checked The new checked value that will be set
13190          */
13191         "beforecheckchange" : true,
13192         /**
13193          * @event checkchange
13194          * Fires after the checked value has been set
13195          * @param {Roo.menu.CheckItem} this
13196          * @param {Boolean} checked The checked value that was set
13197          */
13198         "checkchange" : true
13199     });
13200     if(this.checkHandler){
13201         this.on('checkchange', this.checkHandler, this.scope);
13202     }
13203 };
13204 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
13205     /**
13206      * @cfg {String} group
13207      * All check items with the same group name will automatically be grouped into a single-select
13208      * radio button group (defaults to '')
13209      */
13210     /**
13211      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
13212      */
13213     itemCls : "x-menu-item x-menu-check-item",
13214     /**
13215      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
13216      */
13217     groupClass : "x-menu-group-item",
13218
13219     /**
13220      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
13221      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
13222      * initialized with checked = true will be rendered as checked.
13223      */
13224     checked: false,
13225
13226     // private
13227     ctype: "Roo.menu.CheckItem",
13228
13229     // private
13230     onRender : function(c){
13231         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
13232         if(this.group){
13233             this.el.addClass(this.groupClass);
13234         }
13235         Roo.menu.MenuMgr.registerCheckable(this);
13236         if(this.checked){
13237             this.checked = false;
13238             this.setChecked(true, true);
13239         }
13240     },
13241
13242     // private
13243     destroy : function(){
13244         if(this.rendered){
13245             Roo.menu.MenuMgr.unregisterCheckable(this);
13246         }
13247         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
13248     },
13249
13250     /**
13251      * Set the checked state of this item
13252      * @param {Boolean} checked The new checked value
13253      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
13254      */
13255     setChecked : function(state, suppressEvent){
13256         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
13257             if(this.container){
13258                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
13259             }
13260             this.checked = state;
13261             if(suppressEvent !== true){
13262                 this.fireEvent("checkchange", this, state);
13263             }
13264         }
13265     },
13266
13267     // private
13268     handleClick : function(e){
13269        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
13270            this.setChecked(!this.checked);
13271        }
13272        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
13273     }
13274 });/*
13275  * Based on:
13276  * Ext JS Library 1.1.1
13277  * Copyright(c) 2006-2007, Ext JS, LLC.
13278  *
13279  * Originally Released Under LGPL - original licence link has changed is not relivant.
13280  *
13281  * Fork - LGPL
13282  * <script type="text/javascript">
13283  */
13284  
13285 /**
13286  * @class Roo.menu.DateItem
13287  * @extends Roo.menu.Adapter
13288  * A menu item that wraps the {@link Roo.DatPicker} component.
13289  * @constructor
13290  * Creates a new DateItem
13291  * @param {Object} config Configuration options
13292  */
13293 Roo.menu.DateItem = function(config){
13294     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
13295     /** The Roo.DatePicker object @type Roo.DatePicker */
13296     this.picker = this.component;
13297     this.addEvents({select: true});
13298     
13299     this.picker.on("render", function(picker){
13300         picker.getEl().swallowEvent("click");
13301         picker.container.addClass("x-menu-date-item");
13302     });
13303
13304     this.picker.on("select", this.onSelect, this);
13305 };
13306
13307 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
13308     // private
13309     onSelect : function(picker, date){
13310         this.fireEvent("select", this, date, picker);
13311         Roo.menu.DateItem.superclass.handleClick.call(this);
13312     }
13313 });/*
13314  * Based on:
13315  * Ext JS Library 1.1.1
13316  * Copyright(c) 2006-2007, Ext JS, LLC.
13317  *
13318  * Originally Released Under LGPL - original licence link has changed is not relivant.
13319  *
13320  * Fork - LGPL
13321  * <script type="text/javascript">
13322  */
13323  
13324 /**
13325  * @class Roo.menu.ColorItem
13326  * @extends Roo.menu.Adapter
13327  * A menu item that wraps the {@link Roo.ColorPalette} component.
13328  * @constructor
13329  * Creates a new ColorItem
13330  * @param {Object} config Configuration options
13331  */
13332 Roo.menu.ColorItem = function(config){
13333     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
13334     /** The Roo.ColorPalette object @type Roo.ColorPalette */
13335     this.palette = this.component;
13336     this.relayEvents(this.palette, ["select"]);
13337     if(this.selectHandler){
13338         this.on('select', this.selectHandler, this.scope);
13339     }
13340 };
13341 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
13342  * Based on:
13343  * Ext JS Library 1.1.1
13344  * Copyright(c) 2006-2007, Ext JS, LLC.
13345  *
13346  * Originally Released Under LGPL - original licence link has changed is not relivant.
13347  *
13348  * Fork - LGPL
13349  * <script type="text/javascript">
13350  */
13351  
13352
13353 /**
13354  * @class Roo.menu.DateMenu
13355  * @extends Roo.menu.Menu
13356  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
13357  * @constructor
13358  * Creates a new DateMenu
13359  * @param {Object} config Configuration options
13360  */
13361 Roo.menu.DateMenu = function(config){
13362     Roo.menu.DateMenu.superclass.constructor.call(this, config);
13363     this.plain = true;
13364     var di = new Roo.menu.DateItem(config);
13365     this.add(di);
13366     /**
13367      * The {@link Roo.DatePicker} instance for this DateMenu
13368      * @type DatePicker
13369      */
13370     this.picker = di.picker;
13371     /**
13372      * @event select
13373      * @param {DatePicker} picker
13374      * @param {Date} date
13375      */
13376     this.relayEvents(di, ["select"]);
13377
13378     this.on('beforeshow', function(){
13379         if(this.picker){
13380             this.picker.hideMonthPicker(true);
13381         }
13382     }, this);
13383 };
13384 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
13385     cls:'x-date-menu'
13386 });/*
13387  * Based on:
13388  * Ext JS Library 1.1.1
13389  * Copyright(c) 2006-2007, Ext JS, LLC.
13390  *
13391  * Originally Released Under LGPL - original licence link has changed is not relivant.
13392  *
13393  * Fork - LGPL
13394  * <script type="text/javascript">
13395  */
13396  
13397
13398 /**
13399  * @class Roo.menu.ColorMenu
13400  * @extends Roo.menu.Menu
13401  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
13402  * @constructor
13403  * Creates a new ColorMenu
13404  * @param {Object} config Configuration options
13405  */
13406 Roo.menu.ColorMenu = function(config){
13407     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
13408     this.plain = true;
13409     var ci = new Roo.menu.ColorItem(config);
13410     this.add(ci);
13411     /**
13412      * The {@link Roo.ColorPalette} instance for this ColorMenu
13413      * @type ColorPalette
13414      */
13415     this.palette = ci.palette;
13416     /**
13417      * @event select
13418      * @param {ColorPalette} palette
13419      * @param {String} color
13420      */
13421     this.relayEvents(ci, ["select"]);
13422 };
13423 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
13424  * Based on:
13425  * Ext JS Library 1.1.1
13426  * Copyright(c) 2006-2007, Ext JS, LLC.
13427  *
13428  * Originally Released Under LGPL - original licence link has changed is not relivant.
13429  *
13430  * Fork - LGPL
13431  * <script type="text/javascript">
13432  */
13433  
13434 /**
13435  * @class Roo.form.Field
13436  * @extends Roo.BoxComponent
13437  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
13438  * @constructor
13439  * Creates a new Field
13440  * @param {Object} config Configuration options
13441  */
13442 Roo.form.Field = function(config){
13443     Roo.form.Field.superclass.constructor.call(this, config);
13444 };
13445
13446 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
13447     /**
13448      * @cfg {String} fieldLabel Label to use when rendering a form.
13449      */
13450        /**
13451      * @cfg {String} qtip Mouse over tip
13452      */
13453      
13454     /**
13455      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13456      */
13457     invalidClass : "x-form-invalid",
13458     /**
13459      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
13460      */
13461     invalidText : "The value in this field is invalid",
13462     /**
13463      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
13464      */
13465     focusClass : "x-form-focus",
13466     /**
13467      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
13468       automatic validation (defaults to "keyup").
13469      */
13470     validationEvent : "keyup",
13471     /**
13472      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
13473      */
13474     validateOnBlur : true,
13475     /**
13476      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
13477      */
13478     validationDelay : 250,
13479     /**
13480      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
13481      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
13482      */
13483     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
13484     /**
13485      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
13486      */
13487     fieldClass : "x-form-field",
13488     /**
13489      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
13490      *<pre>
13491 Value         Description
13492 -----------   ----------------------------------------------------------------------
13493 qtip          Display a quick tip when the user hovers over the field
13494 title         Display a default browser title attribute popup
13495 under         Add a block div beneath the field containing the error text
13496 side          Add an error icon to the right of the field with a popup on hover
13497 [element id]  Add the error text directly to the innerHTML of the specified element
13498 </pre>
13499      */
13500     msgTarget : 'qtip',
13501     /**
13502      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
13503      */
13504     msgFx : 'normal',
13505
13506     /**
13507      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
13508      */
13509     readOnly : false,
13510
13511     /**
13512      * @cfg {Boolean} disabled True to disable the field (defaults to false).
13513      */
13514     disabled : false,
13515
13516     /**
13517      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
13518      */
13519     inputType : undefined,
13520     
13521     /**
13522      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
13523          */
13524         tabIndex : undefined,
13525         
13526     // private
13527     isFormField : true,
13528
13529     // private
13530     hasFocus : false,
13531     /**
13532      * @property {Roo.Element} fieldEl
13533      * Element Containing the rendered Field (with label etc.)
13534      */
13535     /**
13536      * @cfg {Mixed} value A value to initialize this field with.
13537      */
13538     value : undefined,
13539
13540     /**
13541      * @cfg {String} name The field's HTML name attribute.
13542      */
13543     /**
13544      * @cfg {String} cls A CSS class to apply to the field's underlying element.
13545      */
13546
13547         // private ??
13548         initComponent : function(){
13549         Roo.form.Field.superclass.initComponent.call(this);
13550         this.addEvents({
13551             /**
13552              * @event focus
13553              * Fires when this field receives input focus.
13554              * @param {Roo.form.Field} this
13555              */
13556             focus : true,
13557             /**
13558              * @event blur
13559              * Fires when this field loses input focus.
13560              * @param {Roo.form.Field} this
13561              */
13562             blur : true,
13563             /**
13564              * @event specialkey
13565              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13566              * {@link Roo.EventObject#getKey} to determine which key was pressed.
13567              * @param {Roo.form.Field} this
13568              * @param {Roo.EventObject} e The event object
13569              */
13570             specialkey : true,
13571             /**
13572              * @event change
13573              * Fires just before the field blurs if the field value has changed.
13574              * @param {Roo.form.Field} this
13575              * @param {Mixed} newValue The new value
13576              * @param {Mixed} oldValue The original value
13577              */
13578             change : true,
13579             /**
13580              * @event invalid
13581              * Fires after the field has been marked as invalid.
13582              * @param {Roo.form.Field} this
13583              * @param {String} msg The validation message
13584              */
13585             invalid : true,
13586             /**
13587              * @event valid
13588              * Fires after the field has been validated with no errors.
13589              * @param {Roo.form.Field} this
13590              */
13591             valid : true
13592         });
13593     },
13594
13595     /**
13596      * Returns the name attribute of the field if available
13597      * @return {String} name The field name
13598      */
13599     getName: function(){
13600          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
13601     },
13602
13603     // private
13604     onRender : function(ct, position){
13605         Roo.form.Field.superclass.onRender.call(this, ct, position);
13606         if(!this.el){
13607             var cfg = this.getAutoCreate();
13608             if(!cfg.name){
13609                 cfg.name = this.name || this.id;
13610             }
13611             if(this.inputType){
13612                 cfg.type = this.inputType;
13613             }
13614             this.el = ct.createChild(cfg, position);
13615         }
13616         var type = this.el.dom.type;
13617         if(type){
13618             if(type == 'password'){
13619                 type = 'text';
13620             }
13621             this.el.addClass('x-form-'+type);
13622         }
13623         if(this.readOnly){
13624             this.el.dom.readOnly = true;
13625         }
13626         if(this.tabIndex !== undefined){
13627             this.el.dom.setAttribute('tabIndex', this.tabIndex);
13628         }
13629
13630         this.el.addClass([this.fieldClass, this.cls]);
13631         this.initValue();
13632     },
13633
13634     /**
13635      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
13636      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
13637      * @return {Roo.form.Field} this
13638      */
13639     applyTo : function(target){
13640         this.allowDomMove = false;
13641         this.el = Roo.get(target);
13642         this.render(this.el.dom.parentNode);
13643         return this;
13644     },
13645
13646     // private
13647     initValue : function(){
13648         if(this.value !== undefined){
13649             this.setValue(this.value);
13650         }else if(this.el.dom.value.length > 0){
13651             this.setValue(this.el.dom.value);
13652         }
13653     },
13654
13655     /**
13656      * Returns true if this field has been changed since it was originally loaded and is not disabled.
13657      */
13658     isDirty : function() {
13659         if(this.disabled) {
13660             return false;
13661         }
13662         return String(this.getValue()) !== String(this.originalValue);
13663     },
13664
13665     // private
13666     afterRender : function(){
13667         Roo.form.Field.superclass.afterRender.call(this);
13668         this.initEvents();
13669     },
13670
13671     // private
13672     fireKey : function(e){
13673         if(e.isNavKeyPress()){
13674             this.fireEvent("specialkey", this, e);
13675         }
13676     },
13677
13678     /**
13679      * Resets the current field value to the originally loaded value and clears any validation messages
13680      */
13681     reset : function(){
13682         this.setValue(this.originalValue);
13683         this.clearInvalid();
13684     },
13685
13686     // private
13687     initEvents : function(){
13688         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
13689         this.el.on("focus", this.onFocus,  this);
13690         this.el.on("blur", this.onBlur,  this);
13691
13692         // reference to original value for reset
13693         this.originalValue = this.getValue();
13694     },
13695
13696     // private
13697     onFocus : function(){
13698         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13699             this.el.addClass(this.focusClass);
13700         }
13701         if(!this.hasFocus){
13702             this.hasFocus = true;
13703             this.startValue = this.getValue();
13704             this.fireEvent("focus", this);
13705         }
13706     },
13707
13708     beforeBlur : Roo.emptyFn,
13709
13710     // private
13711     onBlur : function(){
13712         this.beforeBlur();
13713         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13714             this.el.removeClass(this.focusClass);
13715         }
13716         this.hasFocus = false;
13717         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13718             this.validate();
13719         }
13720         var v = this.getValue();
13721         if(String(v) !== String(this.startValue)){
13722             this.fireEvent('change', this, v, this.startValue);
13723         }
13724         this.fireEvent("blur", this);
13725     },
13726
13727     /**
13728      * Returns whether or not the field value is currently valid
13729      * @param {Boolean} preventMark True to disable marking the field invalid
13730      * @return {Boolean} True if the value is valid, else false
13731      */
13732     isValid : function(preventMark){
13733         if(this.disabled){
13734             return true;
13735         }
13736         var restore = this.preventMark;
13737         this.preventMark = preventMark === true;
13738         var v = this.validateValue(this.processValue(this.getRawValue()));
13739         this.preventMark = restore;
13740         return v;
13741     },
13742
13743     /**
13744      * Validates the field value
13745      * @return {Boolean} True if the value is valid, else false
13746      */
13747     validate : function(){
13748         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
13749             this.clearInvalid();
13750             return true;
13751         }
13752         return false;
13753     },
13754
13755     processValue : function(value){
13756         return value;
13757     },
13758
13759     // private
13760     // Subclasses should provide the validation implementation by overriding this
13761     validateValue : function(value){
13762         return true;
13763     },
13764
13765     /**
13766      * Mark this field as invalid
13767      * @param {String} msg The validation message
13768      */
13769     markInvalid : function(msg){
13770         if(!this.rendered || this.preventMark){ // not rendered
13771             return;
13772         }
13773         this.el.addClass(this.invalidClass);
13774         msg = msg || this.invalidText;
13775         switch(this.msgTarget){
13776             case 'qtip':
13777                 this.el.dom.qtip = msg;
13778                 this.el.dom.qclass = 'x-form-invalid-tip';
13779                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
13780                     Roo.QuickTips.enable();
13781                 }
13782                 break;
13783             case 'title':
13784                 this.el.dom.title = msg;
13785                 break;
13786             case 'under':
13787                 if(!this.errorEl){
13788                     var elp = this.el.findParent('.x-form-element', 5, true);
13789                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
13790                     this.errorEl.setWidth(elp.getWidth(true)-20);
13791                 }
13792                 this.errorEl.update(msg);
13793                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
13794                 break;
13795             case 'side':
13796                 if(!this.errorIcon){
13797                     var elp = this.el.findParent('.x-form-element', 5, true);
13798                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
13799                 }
13800                 this.alignErrorIcon();
13801                 this.errorIcon.dom.qtip = msg;
13802                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
13803                 this.errorIcon.show();
13804                 this.on('resize', this.alignErrorIcon, this);
13805                 break;
13806             default:
13807                 var t = Roo.getDom(this.msgTarget);
13808                 t.innerHTML = msg;
13809                 t.style.display = this.msgDisplay;
13810                 break;
13811         }
13812         this.fireEvent('invalid', this, msg);
13813     },
13814
13815     // private
13816     alignErrorIcon : function(){
13817         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
13818     },
13819
13820     /**
13821      * Clear any invalid styles/messages for this field
13822      */
13823     clearInvalid : function(){
13824         if(!this.rendered || this.preventMark){ // not rendered
13825             return;
13826         }
13827         this.el.removeClass(this.invalidClass);
13828         switch(this.msgTarget){
13829             case 'qtip':
13830                 this.el.dom.qtip = '';
13831                 break;
13832             case 'title':
13833                 this.el.dom.title = '';
13834                 break;
13835             case 'under':
13836                 if(this.errorEl){
13837                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
13838                 }
13839                 break;
13840             case 'side':
13841                 if(this.errorIcon){
13842                     this.errorIcon.dom.qtip = '';
13843                     this.errorIcon.hide();
13844                     this.un('resize', this.alignErrorIcon, this);
13845                 }
13846                 break;
13847             default:
13848                 var t = Roo.getDom(this.msgTarget);
13849                 t.innerHTML = '';
13850                 t.style.display = 'none';
13851                 break;
13852         }
13853         this.fireEvent('valid', this);
13854     },
13855
13856     /**
13857      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13858      * @return {Mixed} value The field value
13859      */
13860     getRawValue : function(){
13861         var v = this.el.getValue();
13862         if(v === this.emptyText){
13863             v = '';
13864         }
13865         return v;
13866     },
13867
13868     /**
13869      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13870      * @return {Mixed} value The field value
13871      */
13872     getValue : function(){
13873         var v = this.el.getValue();
13874         if(v === this.emptyText || v === undefined){
13875             v = '';
13876         }
13877         return v;
13878     },
13879
13880     /**
13881      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13882      * @param {Mixed} value The value to set
13883      */
13884     setRawValue : function(v){
13885         return this.el.dom.value = (v === null || v === undefined ? '' : v);
13886     },
13887
13888     /**
13889      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13890      * @param {Mixed} value The value to set
13891      */
13892     setValue : function(v){
13893         this.value = v;
13894         if(this.rendered){
13895             this.el.dom.value = (v === null || v === undefined ? '' : v);
13896             this.validate();
13897         }
13898     },
13899
13900     adjustSize : function(w, h){
13901         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
13902         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
13903         return s;
13904     },
13905
13906     adjustWidth : function(tag, w){
13907         tag = tag.toLowerCase();
13908         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13909             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13910                 if(tag == 'input'){
13911                     return w + 2;
13912                 }
13913                 if(tag = 'textarea'){
13914                     return w-2;
13915                 }
13916             }else if(Roo.isOpera){
13917                 if(tag == 'input'){
13918                     return w + 2;
13919                 }
13920                 if(tag = 'textarea'){
13921                     return w-2;
13922                 }
13923             }
13924         }
13925         return w;
13926     }
13927 });
13928
13929
13930 // anything other than normal should be considered experimental
13931 Roo.form.Field.msgFx = {
13932     normal : {
13933         show: function(msgEl, f){
13934             msgEl.setDisplayed('block');
13935         },
13936
13937         hide : function(msgEl, f){
13938             msgEl.setDisplayed(false).update('');
13939         }
13940     },
13941
13942     slide : {
13943         show: function(msgEl, f){
13944             msgEl.slideIn('t', {stopFx:true});
13945         },
13946
13947         hide : function(msgEl, f){
13948             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
13949         }
13950     },
13951
13952     slideRight : {
13953         show: function(msgEl, f){
13954             msgEl.fixDisplay();
13955             msgEl.alignTo(f.el, 'tl-tr');
13956             msgEl.slideIn('l', {stopFx:true});
13957         },
13958
13959         hide : function(msgEl, f){
13960             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
13961         }
13962     }
13963 };/*
13964  * Based on:
13965  * Ext JS Library 1.1.1
13966  * Copyright(c) 2006-2007, Ext JS, LLC.
13967  *
13968  * Originally Released Under LGPL - original licence link has changed is not relivant.
13969  *
13970  * Fork - LGPL
13971  * <script type="text/javascript">
13972  */
13973  
13974
13975 /**
13976  * @class Roo.form.TextField
13977  * @extends Roo.form.Field
13978  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
13979  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
13980  * @constructor
13981  * Creates a new TextField
13982  * @param {Object} config Configuration options
13983  */
13984 Roo.form.TextField = function(config){
13985     Roo.form.TextField.superclass.constructor.call(this, config);
13986     this.addEvents({
13987         /**
13988          * @event autosize
13989          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
13990          * according to the default logic, but this event provides a hook for the developer to apply additional
13991          * logic at runtime to resize the field if needed.
13992              * @param {Roo.form.Field} this This text field
13993              * @param {Number} width The new field width
13994              */
13995         autosize : true
13996     });
13997 };
13998
13999 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
14000     /**
14001      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
14002      */
14003     grow : false,
14004     /**
14005      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
14006      */
14007     growMin : 30,
14008     /**
14009      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
14010      */
14011     growMax : 800,
14012     /**
14013      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
14014      */
14015     vtype : null,
14016     /**
14017      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
14018      */
14019     maskRe : null,
14020     /**
14021      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
14022      */
14023     disableKeyFilter : false,
14024     /**
14025      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
14026      */
14027     allowBlank : true,
14028     /**
14029      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
14030      */
14031     minLength : 0,
14032     /**
14033      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
14034      */
14035     maxLength : Number.MAX_VALUE,
14036     /**
14037      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
14038      */
14039     minLengthText : "The minimum length for this field is {0}",
14040     /**
14041      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
14042      */
14043     maxLengthText : "The maximum length for this field is {0}",
14044     /**
14045      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
14046      */
14047     selectOnFocus : false,
14048     /**
14049      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
14050      */
14051     blankText : "This field is required",
14052     /**
14053      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
14054      * If available, this function will be called only after the basic validators all return true, and will be passed the
14055      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
14056      */
14057     validator : null,
14058     /**
14059      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
14060      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
14061      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
14062      */
14063     regex : null,
14064     /**
14065      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
14066      */
14067     regexText : "",
14068     /**
14069      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
14070      */
14071     emptyText : null,
14072     /**
14073      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
14074      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
14075      */
14076     emptyClass : 'x-form-empty-field',
14077
14078     // private
14079     initEvents : function(){
14080         Roo.form.TextField.superclass.initEvents.call(this);
14081         if(this.validationEvent == 'keyup'){
14082             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
14083             this.el.on('keyup', this.filterValidation, this);
14084         }
14085         else if(this.validationEvent !== false){
14086             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
14087         }
14088         if(this.selectOnFocus || this.emptyText){
14089             this.on("focus", this.preFocus, this);
14090             if(this.emptyText){
14091                 this.on('blur', this.postBlur, this);
14092                 this.applyEmptyText();
14093             }
14094         }
14095         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
14096             this.el.on("keypress", this.filterKeys, this);
14097         }
14098         if(this.grow){
14099             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
14100             this.el.on("click", this.autoSize,  this);
14101         }
14102     },
14103
14104     processValue : function(value){
14105         if(this.stripCharsRe){
14106             var newValue = value.replace(this.stripCharsRe, '');
14107             if(newValue !== value){
14108                 this.setRawValue(newValue);
14109                 return newValue;
14110             }
14111         }
14112         return value;
14113     },
14114
14115     filterValidation : function(e){
14116         if(!e.isNavKeyPress()){
14117             this.validationTask.delay(this.validationDelay);
14118         }
14119     },
14120
14121     // private
14122     onKeyUp : function(e){
14123         if(!e.isNavKeyPress()){
14124             this.autoSize();
14125         }
14126     },
14127
14128     /**
14129      * Resets the current field value to the originally-loaded value and clears any validation messages.
14130      * Also adds emptyText and emptyClass if the original value was blank.
14131      */
14132     reset : function(){
14133         Roo.form.TextField.superclass.reset.call(this);
14134         this.applyEmptyText();
14135     },
14136
14137     applyEmptyText : function(){
14138         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
14139             this.setRawValue(this.emptyText);
14140             this.el.addClass(this.emptyClass);
14141         }
14142     },
14143
14144     // private
14145     preFocus : function(){
14146         if(this.emptyText){
14147             if(this.el.dom.value == this.emptyText){
14148                 this.setRawValue('');
14149             }
14150             this.el.removeClass(this.emptyClass);
14151         }
14152         if(this.selectOnFocus){
14153             this.el.dom.select();
14154         }
14155     },
14156
14157     // private
14158     postBlur : function(){
14159         this.applyEmptyText();
14160     },
14161
14162     // private
14163     filterKeys : function(e){
14164         var k = e.getKey();
14165         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
14166             return;
14167         }
14168         var c = e.getCharCode(), cc = String.fromCharCode(c);
14169         if(Roo.isIE && (e.isSpecialKey() || !cc)){
14170             return;
14171         }
14172         if(!this.maskRe.test(cc)){
14173             e.stopEvent();
14174         }
14175     },
14176
14177     setValue : function(v){
14178         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
14179             this.el.removeClass(this.emptyClass);
14180         }
14181         Roo.form.TextField.superclass.setValue.apply(this, arguments);
14182         this.applyEmptyText();
14183         this.autoSize();
14184     },
14185
14186     /**
14187      * Validates a value according to the field's validation rules and marks the field as invalid
14188      * if the validation fails
14189      * @param {Mixed} value The value to validate
14190      * @return {Boolean} True if the value is valid, else false
14191      */
14192     validateValue : function(value){
14193         if(value.length < 1 || value === this.emptyText){ // if it's blank
14194              if(this.allowBlank){
14195                 this.clearInvalid();
14196                 return true;
14197              }else{
14198                 this.markInvalid(this.blankText);
14199                 return false;
14200              }
14201         }
14202         if(value.length < this.minLength){
14203             this.markInvalid(String.format(this.minLengthText, this.minLength));
14204             return false;
14205         }
14206         if(value.length > this.maxLength){
14207             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
14208             return false;
14209         }
14210         if(this.vtype){
14211             var vt = Roo.form.VTypes;
14212             if(!vt[this.vtype](value, this)){
14213                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
14214                 return false;
14215             }
14216         }
14217         if(typeof this.validator == "function"){
14218             var msg = this.validator(value);
14219             if(msg !== true){
14220                 this.markInvalid(msg);
14221                 return false;
14222             }
14223         }
14224         if(this.regex && !this.regex.test(value)){
14225             this.markInvalid(this.regexText);
14226             return false;
14227         }
14228         return true;
14229     },
14230
14231     /**
14232      * Selects text in this field
14233      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
14234      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
14235      */
14236     selectText : function(start, end){
14237         var v = this.getRawValue();
14238         if(v.length > 0){
14239             start = start === undefined ? 0 : start;
14240             end = end === undefined ? v.length : end;
14241             var d = this.el.dom;
14242             if(d.setSelectionRange){
14243                 d.setSelectionRange(start, end);
14244             }else if(d.createTextRange){
14245                 var range = d.createTextRange();
14246                 range.moveStart("character", start);
14247                 range.moveEnd("character", v.length-end);
14248                 range.select();
14249             }
14250         }
14251     },
14252
14253     /**
14254      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
14255      * This only takes effect if grow = true, and fires the autosize event.
14256      */
14257     autoSize : function(){
14258         if(!this.grow || !this.rendered){
14259             return;
14260         }
14261         if(!this.metrics){
14262             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
14263         }
14264         var el = this.el;
14265         var v = el.dom.value;
14266         var d = document.createElement('div');
14267         d.appendChild(document.createTextNode(v));
14268         v = d.innerHTML;
14269         d = null;
14270         v += "&#160;";
14271         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
14272         this.el.setWidth(w);
14273         this.fireEvent("autosize", this, w);
14274     }
14275 });/*
14276  * Based on:
14277  * Ext JS Library 1.1.1
14278  * Copyright(c) 2006-2007, Ext JS, LLC.
14279  *
14280  * Originally Released Under LGPL - original licence link has changed is not relivant.
14281  *
14282  * Fork - LGPL
14283  * <script type="text/javascript">
14284  */
14285  
14286 /**
14287  * @class Roo.form.Hidden
14288  * @extends Roo.form.TextField
14289  * Simple Hidden element used on forms 
14290  * 
14291  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
14292  * 
14293  * @constructor
14294  * Creates a new Hidden form element.
14295  * @param {Object} config Configuration options
14296  */
14297
14298
14299
14300 // easy hidden field...
14301 Roo.form.Hidden = function(config){
14302     Roo.form.Hidden.superclass.constructor.call(this, config);
14303 };
14304   
14305 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
14306     fieldLabel:      '',
14307     inputType:      'hidden',
14308     width:          50,
14309     allowBlank:     true,
14310     labelSeparator: '',
14311     hidden:         true,
14312     itemCls :       'x-form-item-display-none'
14313
14314
14315 });
14316
14317
14318 /*
14319  * Based on:
14320  * Ext JS Library 1.1.1
14321  * Copyright(c) 2006-2007, Ext JS, LLC.
14322  *
14323  * Originally Released Under LGPL - original licence link has changed is not relivant.
14324  *
14325  * Fork - LGPL
14326  * <script type="text/javascript">
14327  */
14328  
14329 /**
14330  * @class Roo.form.TriggerField
14331  * @extends Roo.form.TextField
14332  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
14333  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
14334  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
14335  * for which you can provide a custom implementation.  For example:
14336  * <pre><code>
14337 var trigger = new Roo.form.TriggerField();
14338 trigger.onTriggerClick = myTriggerFn;
14339 trigger.applyTo('my-field');
14340 </code></pre>
14341  *
14342  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
14343  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
14344  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
14345  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
14346  * @constructor
14347  * Create a new TriggerField.
14348  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
14349  * to the base TextField)
14350  */
14351 Roo.form.TriggerField = function(config){
14352     this.mimicing = false;
14353     Roo.form.TriggerField.superclass.constructor.call(this, config);
14354 };
14355
14356 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
14357     /**
14358      * @cfg {String} triggerClass A CSS class to apply to the trigger
14359      */
14360     /**
14361      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
14362      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
14363      */
14364     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
14365     /**
14366      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
14367      */
14368     hideTrigger:false,
14369
14370     /** @cfg {Boolean} grow @hide */
14371     /** @cfg {Number} growMin @hide */
14372     /** @cfg {Number} growMax @hide */
14373
14374     /**
14375      * @hide 
14376      * @method
14377      */
14378     autoSize: Roo.emptyFn,
14379     // private
14380     monitorTab : true,
14381     // private
14382     deferHeight : true,
14383
14384     
14385     actionMode : 'wrap',
14386     // private
14387     onResize : function(w, h){
14388         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
14389         if(typeof w == 'number'){
14390             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
14391         }
14392     },
14393
14394     // private
14395     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14396
14397     // private
14398     getResizeEl : function(){
14399         return this.wrap;
14400     },
14401
14402     // private
14403     getPositionEl : function(){
14404         return this.wrap;
14405     },
14406
14407     // private
14408     alignErrorIcon : function(){
14409         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
14410     },
14411
14412     // private
14413     onRender : function(ct, position){
14414         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
14415         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14416         this.trigger = this.wrap.createChild(this.triggerConfig ||
14417                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
14418         if(this.hideTrigger){
14419             this.trigger.setDisplayed(false);
14420         }
14421         this.initTrigger();
14422         if(!this.width){
14423             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14424         }
14425     },
14426
14427     // private
14428     initTrigger : function(){
14429         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14430         this.trigger.addClassOnOver('x-form-trigger-over');
14431         this.trigger.addClassOnClick('x-form-trigger-click');
14432     },
14433
14434     // private
14435     onDestroy : function(){
14436         if(this.trigger){
14437             this.trigger.removeAllListeners();
14438             this.trigger.remove();
14439         }
14440         if(this.wrap){
14441             this.wrap.remove();
14442         }
14443         Roo.form.TriggerField.superclass.onDestroy.call(this);
14444     },
14445
14446     // private
14447     onFocus : function(){
14448         Roo.form.TriggerField.superclass.onFocus.call(this);
14449         if(!this.mimicing){
14450             this.wrap.addClass('x-trigger-wrap-focus');
14451             this.mimicing = true;
14452             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14453             if(this.monitorTab){
14454                 this.el.on("keydown", this.checkTab, this);
14455             }
14456         }
14457     },
14458
14459     // private
14460     checkTab : function(e){
14461         if(e.getKey() == e.TAB){
14462             this.triggerBlur();
14463         }
14464     },
14465
14466     // private
14467     onBlur : function(){
14468         // do nothing
14469     },
14470
14471     // private
14472     mimicBlur : function(e, t){
14473         if(!this.wrap.contains(t) && this.validateBlur()){
14474             this.triggerBlur();
14475         }
14476     },
14477
14478     // private
14479     triggerBlur : function(){
14480         this.mimicing = false;
14481         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14482         if(this.monitorTab){
14483             this.el.un("keydown", this.checkTab, this);
14484         }
14485         this.wrap.removeClass('x-trigger-wrap-focus');
14486         Roo.form.TriggerField.superclass.onBlur.call(this);
14487     },
14488
14489     // private
14490     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14491     validateBlur : function(e, t){
14492         return true;
14493     },
14494
14495     // private
14496     onDisable : function(){
14497         Roo.form.TriggerField.superclass.onDisable.call(this);
14498         if(this.wrap){
14499             this.wrap.addClass('x-item-disabled');
14500         }
14501     },
14502
14503     // private
14504     onEnable : function(){
14505         Roo.form.TriggerField.superclass.onEnable.call(this);
14506         if(this.wrap){
14507             this.wrap.removeClass('x-item-disabled');
14508         }
14509     },
14510
14511     // private
14512     onShow : function(){
14513         var ae = this.getActionEl();
14514         
14515         if(ae){
14516             ae.dom.style.display = '';
14517             ae.dom.style.visibility = 'visible';
14518         }
14519     },
14520
14521     // private
14522     
14523     onHide : function(){
14524         var ae = this.getActionEl();
14525         ae.dom.style.display = 'none';
14526     },
14527
14528     /**
14529      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14530      * by an implementing function.
14531      * @method
14532      * @param {EventObject} e
14533      */
14534     onTriggerClick : Roo.emptyFn
14535 });
14536
14537 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
14538 // to be extended by an implementing class.  For an example of implementing this class, see the custom
14539 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
14540 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
14541     initComponent : function(){
14542         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
14543
14544         this.triggerConfig = {
14545             tag:'span', cls:'x-form-twin-triggers', cn:[
14546             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
14547             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
14548         ]};
14549     },
14550
14551     getTrigger : function(index){
14552         return this.triggers[index];
14553     },
14554
14555     initTrigger : function(){
14556         var ts = this.trigger.select('.x-form-trigger', true);
14557         this.wrap.setStyle('overflow', 'hidden');
14558         var triggerField = this;
14559         ts.each(function(t, all, index){
14560             t.hide = function(){
14561                 var w = triggerField.wrap.getWidth();
14562                 this.dom.style.display = 'none';
14563                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
14564             };
14565             t.show = function(){
14566                 var w = triggerField.wrap.getWidth();
14567                 this.dom.style.display = '';
14568                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
14569             };
14570             var triggerIndex = 'Trigger'+(index+1);
14571
14572             if(this['hide'+triggerIndex]){
14573                 t.dom.style.display = 'none';
14574             }
14575             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
14576             t.addClassOnOver('x-form-trigger-over');
14577             t.addClassOnClick('x-form-trigger-click');
14578         }, this);
14579         this.triggers = ts.elements;
14580     },
14581
14582     onTrigger1Click : Roo.emptyFn,
14583     onTrigger2Click : Roo.emptyFn
14584 });/*
14585  * Based on:
14586  * Ext JS Library 1.1.1
14587  * Copyright(c) 2006-2007, Ext JS, LLC.
14588  *
14589  * Originally Released Under LGPL - original licence link has changed is not relivant.
14590  *
14591  * Fork - LGPL
14592  * <script type="text/javascript">
14593  */
14594  
14595 /**
14596  * @class Roo.form.TextArea
14597  * @extends Roo.form.TextField
14598  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
14599  * support for auto-sizing.
14600  * @constructor
14601  * Creates a new TextArea
14602  * @param {Object} config Configuration options
14603  */
14604 Roo.form.TextArea = function(config){
14605     Roo.form.TextArea.superclass.constructor.call(this, config);
14606     // these are provided exchanges for backwards compat
14607     // minHeight/maxHeight were replaced by growMin/growMax to be
14608     // compatible with TextField growing config values
14609     if(this.minHeight !== undefined){
14610         this.growMin = this.minHeight;
14611     }
14612     if(this.maxHeight !== undefined){
14613         this.growMax = this.maxHeight;
14614     }
14615 };
14616
14617 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
14618     /**
14619      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
14620      */
14621     growMin : 60,
14622     /**
14623      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
14624      */
14625     growMax: 1000,
14626     /**
14627      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
14628      * in the field (equivalent to setting overflow: hidden, defaults to false)
14629      */
14630     preventScrollbars: false,
14631     /**
14632      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
14633      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
14634      */
14635
14636     // private
14637     onRender : function(ct, position){
14638         if(!this.el){
14639             this.defaultAutoCreate = {
14640                 tag: "textarea",
14641                 style:"width:300px;height:60px;",
14642                 autocomplete: "off"
14643             };
14644         }
14645         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
14646         if(this.grow){
14647             this.textSizeEl = Roo.DomHelper.append(document.body, {
14648                 tag: "pre", cls: "x-form-grow-sizer"
14649             });
14650             if(this.preventScrollbars){
14651                 this.el.setStyle("overflow", "hidden");
14652             }
14653             this.el.setHeight(this.growMin);
14654         }
14655     },
14656
14657     onDestroy : function(){
14658         if(this.textSizeEl){
14659             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
14660         }
14661         Roo.form.TextArea.superclass.onDestroy.call(this);
14662     },
14663
14664     // private
14665     onKeyUp : function(e){
14666         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
14667             this.autoSize();
14668         }
14669     },
14670
14671     /**
14672      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
14673      * This only takes effect if grow = true, and fires the autosize event if the height changes.
14674      */
14675     autoSize : function(){
14676         if(!this.grow || !this.textSizeEl){
14677             return;
14678         }
14679         var el = this.el;
14680         var v = el.dom.value;
14681         var ts = this.textSizeEl;
14682
14683         ts.innerHTML = '';
14684         ts.appendChild(document.createTextNode(v));
14685         v = ts.innerHTML;
14686
14687         Roo.fly(ts).setWidth(this.el.getWidth());
14688         if(v.length < 1){
14689             v = "&#160;&#160;";
14690         }else{
14691             if(Roo.isIE){
14692                 v = v.replace(/\n/g, '<p>&#160;</p>');
14693             }
14694             v += "&#160;\n&#160;";
14695         }
14696         ts.innerHTML = v;
14697         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
14698         if(h != this.lastHeight){
14699             this.lastHeight = h;
14700             this.el.setHeight(h);
14701             this.fireEvent("autosize", this, h);
14702         }
14703     }
14704 });/*
14705  * Based on:
14706  * Ext JS Library 1.1.1
14707  * Copyright(c) 2006-2007, Ext JS, LLC.
14708  *
14709  * Originally Released Under LGPL - original licence link has changed is not relivant.
14710  *
14711  * Fork - LGPL
14712  * <script type="text/javascript">
14713  */
14714  
14715
14716 /**
14717  * @class Roo.form.NumberField
14718  * @extends Roo.form.TextField
14719  * Numeric text field that provides automatic keystroke filtering and numeric validation.
14720  * @constructor
14721  * Creates a new NumberField
14722  * @param {Object} config Configuration options
14723  */
14724 Roo.form.NumberField = function(config){
14725     Roo.form.NumberField.superclass.constructor.call(this, config);
14726 };
14727
14728 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
14729     /**
14730      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
14731      */
14732     fieldClass: "x-form-field x-form-num-field",
14733     /**
14734      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
14735      */
14736     allowDecimals : true,
14737     /**
14738      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
14739      */
14740     decimalSeparator : ".",
14741     /**
14742      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
14743      */
14744     decimalPrecision : 2,
14745     /**
14746      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
14747      */
14748     allowNegative : true,
14749     /**
14750      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
14751      */
14752     minValue : Number.NEGATIVE_INFINITY,
14753     /**
14754      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
14755      */
14756     maxValue : Number.MAX_VALUE,
14757     /**
14758      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
14759      */
14760     minText : "The minimum value for this field is {0}",
14761     /**
14762      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
14763      */
14764     maxText : "The maximum value for this field is {0}",
14765     /**
14766      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
14767      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
14768      */
14769     nanText : "{0} is not a valid number",
14770
14771     // private
14772     initEvents : function(){
14773         Roo.form.NumberField.superclass.initEvents.call(this);
14774         var allowed = "0123456789";
14775         if(this.allowDecimals){
14776             allowed += this.decimalSeparator;
14777         }
14778         if(this.allowNegative){
14779             allowed += "-";
14780         }
14781         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
14782         var keyPress = function(e){
14783             var k = e.getKey();
14784             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
14785                 return;
14786             }
14787             var c = e.getCharCode();
14788             if(allowed.indexOf(String.fromCharCode(c)) === -1){
14789                 e.stopEvent();
14790             }
14791         };
14792         this.el.on("keypress", keyPress, this);
14793     },
14794
14795     // private
14796     validateValue : function(value){
14797         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
14798             return false;
14799         }
14800         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
14801              return true;
14802         }
14803         var num = this.parseValue(value);
14804         if(isNaN(num)){
14805             this.markInvalid(String.format(this.nanText, value));
14806             return false;
14807         }
14808         if(num < this.minValue){
14809             this.markInvalid(String.format(this.minText, this.minValue));
14810             return false;
14811         }
14812         if(num > this.maxValue){
14813             this.markInvalid(String.format(this.maxText, this.maxValue));
14814             return false;
14815         }
14816         return true;
14817     },
14818
14819     getValue : function(){
14820         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
14821     },
14822
14823     // private
14824     parseValue : function(value){
14825         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
14826         return isNaN(value) ? '' : value;
14827     },
14828
14829     // private
14830     fixPrecision : function(value){
14831         var nan = isNaN(value);
14832         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
14833             return nan ? '' : value;
14834         }
14835         return parseFloat(value).toFixed(this.decimalPrecision);
14836     },
14837
14838     setValue : function(v){
14839         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
14840     },
14841
14842     // private
14843     decimalPrecisionFcn : function(v){
14844         return Math.floor(v);
14845     },
14846
14847     beforeBlur : function(){
14848         var v = this.parseValue(this.getRawValue());
14849         if(v){
14850             this.setValue(this.fixPrecision(v));
14851         }
14852     }
14853 });/*
14854  * Based on:
14855  * Ext JS Library 1.1.1
14856  * Copyright(c) 2006-2007, Ext JS, LLC.
14857  *
14858  * Originally Released Under LGPL - original licence link has changed is not relivant.
14859  *
14860  * Fork - LGPL
14861  * <script type="text/javascript">
14862  */
14863  
14864 /**
14865  * @class Roo.form.DateField
14866  * @extends Roo.form.TriggerField
14867  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
14868 * @constructor
14869 * Create a new DateField
14870 * @param {Object} config
14871  */
14872 Roo.form.DateField = function(config){
14873     Roo.form.DateField.superclass.constructor.call(this, config);
14874     
14875       this.addEvents({
14876          
14877         /**
14878          * @event select
14879          * Fires when a date is selected
14880              * @param {Roo.form.DateField} combo This combo box
14881              * @param {Date} date The date selected
14882              */
14883         'select' : true
14884          
14885     });
14886     
14887     
14888     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
14889     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
14890     this.ddMatch = null;
14891     if(this.disabledDates){
14892         var dd = this.disabledDates;
14893         var re = "(?:";
14894         for(var i = 0; i < dd.length; i++){
14895             re += dd[i];
14896             if(i != dd.length-1) re += "|";
14897         }
14898         this.ddMatch = new RegExp(re + ")");
14899     }
14900 };
14901
14902 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
14903     /**
14904      * @cfg {String} format
14905      * The default date format string which can be overriden for localization support.  The format must be
14906      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14907      */
14908     format : "m/d/y",
14909     /**
14910      * @cfg {String} altFormats
14911      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14912      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14913      */
14914     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14915     /**
14916      * @cfg {Array} disabledDays
14917      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
14918      */
14919     disabledDays : null,
14920     /**
14921      * @cfg {String} disabledDaysText
14922      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
14923      */
14924     disabledDaysText : "Disabled",
14925     /**
14926      * @cfg {Array} disabledDates
14927      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
14928      * expression so they are very powerful. Some examples:
14929      * <ul>
14930      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
14931      * <li>["03/08", "09/16"] would disable those days for every year</li>
14932      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
14933      * <li>["03/../2006"] would disable every day in March 2006</li>
14934      * <li>["^03"] would disable every day in every March</li>
14935      * </ul>
14936      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
14937      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
14938      */
14939     disabledDates : null,
14940     /**
14941      * @cfg {String} disabledDatesText
14942      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
14943      */
14944     disabledDatesText : "Disabled",
14945     /**
14946      * @cfg {Date/String} minValue
14947      * The minimum allowed date. Can be either a Javascript date object or a string date in a
14948      * valid format (defaults to null).
14949      */
14950     minValue : null,
14951     /**
14952      * @cfg {Date/String} maxValue
14953      * The maximum allowed date. Can be either a Javascript date object or a string date in a
14954      * valid format (defaults to null).
14955      */
14956     maxValue : null,
14957     /**
14958      * @cfg {String} minText
14959      * The error text to display when the date in the cell is before minValue (defaults to
14960      * 'The date in this field must be after {minValue}').
14961      */
14962     minText : "The date in this field must be equal to or after {0}",
14963     /**
14964      * @cfg {String} maxText
14965      * The error text to display when the date in the cell is after maxValue (defaults to
14966      * 'The date in this field must be before {maxValue}').
14967      */
14968     maxText : "The date in this field must be equal to or before {0}",
14969     /**
14970      * @cfg {String} invalidText
14971      * The error text to display when the date in the field is invalid (defaults to
14972      * '{value} is not a valid date - it must be in the format {format}').
14973      */
14974     invalidText : "{0} is not a valid date - it must be in the format {1}",
14975     /**
14976      * @cfg {String} triggerClass
14977      * An additional CSS class used to style the trigger button.  The trigger will always get the
14978      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
14979      * which displays a calendar icon).
14980      */
14981     triggerClass : 'x-form-date-trigger',
14982     
14983
14984     /**
14985      * @cfg {bool} useIso
14986      * if enabled, then the date field will use a hidden field to store the 
14987      * real value as iso formated date. default (false)
14988      */ 
14989     useIso : false,
14990     /**
14991      * @cfg {String/Object} autoCreate
14992      * A DomHelper element spec, or true for a default element spec (defaults to
14993      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
14994      */ 
14995     // private
14996     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
14997     
14998     // private
14999     hiddenField: false,
15000     
15001     onRender : function(ct, position)
15002     {
15003         Roo.form.DateField.superclass.onRender.call(this, ct, position);
15004         if (this.useIso) {
15005             this.el.dom.removeAttribute('name'); 
15006             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
15007                     'before', true);
15008             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
15009             // prevent input submission
15010             this.hiddenName = this.name;
15011         }
15012             
15013             
15014     },
15015     
15016     // private
15017     validateValue : function(value)
15018     {
15019         value = this.formatDate(value);
15020         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
15021             return false;
15022         }
15023         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
15024              return true;
15025         }
15026         var svalue = value;
15027         value = this.parseDate(value);
15028         if(!value){
15029             this.markInvalid(String.format(this.invalidText, svalue, this.format));
15030             return false;
15031         }
15032         var time = value.getTime();
15033         if(this.minValue && time < this.minValue.getTime()){
15034             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
15035             return false;
15036         }
15037         if(this.maxValue && time > this.maxValue.getTime()){
15038             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
15039             return false;
15040         }
15041         if(this.disabledDays){
15042             var day = value.getDay();
15043             for(var i = 0; i < this.disabledDays.length; i++) {
15044                 if(day === this.disabledDays[i]){
15045                     this.markInvalid(this.disabledDaysText);
15046                     return false;
15047                 }
15048             }
15049         }
15050         var fvalue = this.formatDate(value);
15051         if(this.ddMatch && this.ddMatch.test(fvalue)){
15052             this.markInvalid(String.format(this.disabledDatesText, fvalue));
15053             return false;
15054         }
15055         return true;
15056     },
15057
15058     // private
15059     // Provides logic to override the default TriggerField.validateBlur which just returns true
15060     validateBlur : function(){
15061         return !this.menu || !this.menu.isVisible();
15062     },
15063
15064     /**
15065      * Returns the current date value of the date field.
15066      * @return {Date} The date value
15067      */
15068     getValue : function(){
15069         
15070         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
15071     },
15072
15073     /**
15074      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
15075      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
15076      * (the default format used is "m/d/y").
15077      * <br />Usage:
15078      * <pre><code>
15079 //All of these calls set the same date value (May 4, 2006)
15080
15081 //Pass a date object:
15082 var dt = new Date('5/4/06');
15083 dateField.setValue(dt);
15084
15085 //Pass a date string (default format):
15086 dateField.setValue('5/4/06');
15087
15088 //Pass a date string (custom format):
15089 dateField.format = 'Y-m-d';
15090 dateField.setValue('2006-5-4');
15091 </code></pre>
15092      * @param {String/Date} date The date or valid date string
15093      */
15094     setValue : function(date){
15095         if (this.hiddenField) {
15096             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
15097         }
15098         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
15099     },
15100
15101     // private
15102     parseDate : function(value){
15103         if(!value || value instanceof Date){
15104             return value;
15105         }
15106         var v = Date.parseDate(value, this.format);
15107         if(!v && this.altFormats){
15108             if(!this.altFormatsArray){
15109                 this.altFormatsArray = this.altFormats.split("|");
15110             }
15111             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15112                 v = Date.parseDate(value, this.altFormatsArray[i]);
15113             }
15114         }
15115         return v;
15116     },
15117
15118     // private
15119     formatDate : function(date, fmt){
15120         return (!date || !(date instanceof Date)) ?
15121                date : date.dateFormat(fmt || this.format);
15122     },
15123
15124     // private
15125     menuListeners : {
15126         select: function(m, d){
15127             this.setValue(d);
15128             this.fireEvent('select', this, d);
15129         },
15130         show : function(){ // retain focus styling
15131             this.onFocus();
15132         },
15133         hide : function(){
15134             this.focus.defer(10, this);
15135             var ml = this.menuListeners;
15136             this.menu.un("select", ml.select,  this);
15137             this.menu.un("show", ml.show,  this);
15138             this.menu.un("hide", ml.hide,  this);
15139         }
15140     },
15141
15142     // private
15143     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
15144     onTriggerClick : function(){
15145         if(this.disabled){
15146             return;
15147         }
15148         if(this.menu == null){
15149             this.menu = new Roo.menu.DateMenu();
15150         }
15151         Roo.apply(this.menu.picker,  {
15152             showClear: this.allowBlank,
15153             minDate : this.minValue,
15154             maxDate : this.maxValue,
15155             disabledDatesRE : this.ddMatch,
15156             disabledDatesText : this.disabledDatesText,
15157             disabledDays : this.disabledDays,
15158             disabledDaysText : this.disabledDaysText,
15159             format : this.format,
15160             minText : String.format(this.minText, this.formatDate(this.minValue)),
15161             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
15162         });
15163         this.menu.on(Roo.apply({}, this.menuListeners, {
15164             scope:this
15165         }));
15166         this.menu.picker.setValue(this.getValue() || new Date());
15167         this.menu.show(this.el, "tl-bl?");
15168     },
15169
15170     beforeBlur : function(){
15171         var v = this.parseDate(this.getRawValue());
15172         if(v){
15173             this.setValue(v);
15174         }
15175     }
15176
15177     /** @cfg {Boolean} grow @hide */
15178     /** @cfg {Number} growMin @hide */
15179     /** @cfg {Number} growMax @hide */
15180     /**
15181      * @hide
15182      * @method autoSize
15183      */
15184 });/*
15185  * Based on:
15186  * Ext JS Library 1.1.1
15187  * Copyright(c) 2006-2007, Ext JS, LLC.
15188  *
15189  * Originally Released Under LGPL - original licence link has changed is not relivant.
15190  *
15191  * Fork - LGPL
15192  * <script type="text/javascript">
15193  */
15194  
15195
15196 /**
15197  * @class Roo.form.ComboBox
15198  * @extends Roo.form.TriggerField
15199  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15200  * @constructor
15201  * Create a new ComboBox.
15202  * @param {Object} config Configuration options
15203  */
15204 Roo.form.ComboBox = function(config){
15205     Roo.form.ComboBox.superclass.constructor.call(this, config);
15206     this.addEvents({
15207         /**
15208          * @event expand
15209          * Fires when the dropdown list is expanded
15210              * @param {Roo.form.ComboBox} combo This combo box
15211              */
15212         'expand' : true,
15213         /**
15214          * @event collapse
15215          * Fires when the dropdown list is collapsed
15216              * @param {Roo.form.ComboBox} combo This combo box
15217              */
15218         'collapse' : true,
15219         /**
15220          * @event beforeselect
15221          * Fires before a list item is selected. Return false to cancel the selection.
15222              * @param {Roo.form.ComboBox} combo This combo box
15223              * @param {Roo.data.Record} record The data record returned from the underlying store
15224              * @param {Number} index The index of the selected item in the dropdown list
15225              */
15226         'beforeselect' : true,
15227         /**
15228          * @event select
15229          * Fires when a list item is selected
15230              * @param {Roo.form.ComboBox} combo This combo box
15231              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15232              * @param {Number} index The index of the selected item in the dropdown list
15233              */
15234         'select' : true,
15235         /**
15236          * @event beforequery
15237          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15238          * The event object passed has these properties:
15239              * @param {Roo.form.ComboBox} combo This combo box
15240              * @param {String} query The query
15241              * @param {Boolean} forceAll true to force "all" query
15242              * @param {Boolean} cancel true to cancel the query
15243              * @param {Object} e The query event object
15244              */
15245         'beforequery': true
15246     });
15247     if(this.transform){
15248         this.allowDomMove = false;
15249         var s = Roo.getDom(this.transform);
15250         if(!this.hiddenName){
15251             this.hiddenName = s.name;
15252         }
15253         if(!this.store){
15254             this.mode = 'local';
15255             var d = [], opts = s.options;
15256             for(var i = 0, len = opts.length;i < len; i++){
15257                 var o = opts[i];
15258                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
15259                 if(o.selected) {
15260                     this.value = value;
15261                 }
15262                 d.push([value, o.text]);
15263             }
15264             this.store = new Roo.data.SimpleStore({
15265                 'id': 0,
15266                 fields: ['value', 'text'],
15267                 data : d
15268             });
15269             this.valueField = 'value';
15270             this.displayField = 'text';
15271         }
15272         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
15273         if(!this.lazyRender){
15274             this.target = true;
15275             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
15276             s.parentNode.removeChild(s); // remove it
15277             this.render(this.el.parentNode);
15278         }else{
15279             s.parentNode.removeChild(s); // remove it
15280         }
15281
15282     }
15283     if (this.store) {
15284         this.store = Roo.factory(this.store, Roo.data);
15285     }
15286     
15287     this.selectedIndex = -1;
15288     if(this.mode == 'local'){
15289         if(config.queryDelay === undefined){
15290             this.queryDelay = 10;
15291         }
15292         if(config.minChars === undefined){
15293             this.minChars = 0;
15294         }
15295     }
15296 };
15297
15298 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
15299     /**
15300      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
15301      */
15302     /**
15303      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15304      * rendering into an Roo.Editor, defaults to false)
15305      */
15306     /**
15307      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15308      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15309      */
15310     /**
15311      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15312      */
15313     /**
15314      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15315      * the dropdown list (defaults to undefined, with no header element)
15316      */
15317
15318      /**
15319      * @cfg {String/Roo.Template} tpl The template to use to render the output
15320      */
15321      
15322     // private
15323     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
15324     /**
15325      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15326      */
15327     listWidth: undefined,
15328     /**
15329      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15330      * mode = 'remote' or 'text' if mode = 'local')
15331      */
15332     displayField: undefined,
15333     /**
15334      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15335      * mode = 'remote' or 'value' if mode = 'local'). 
15336      * Note: use of a valueField requires the user make a selection
15337      * in order for a value to be mapped.
15338      */
15339     valueField: undefined,
15340     /**
15341      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15342      * field's data value (defaults to the underlying DOM element's name)
15343      */
15344     hiddenName: undefined,
15345     /**
15346      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15347      */
15348     listClass: '',
15349     /**
15350      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15351      */
15352     selectedClass: 'x-combo-selected',
15353     /**
15354      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
15355      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
15356      * which displays a downward arrow icon).
15357      */
15358     triggerClass : 'x-form-arrow-trigger',
15359     /**
15360      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15361      */
15362     shadow:'sides',
15363     /**
15364      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15365      * anchor positions (defaults to 'tl-bl')
15366      */
15367     listAlign: 'tl-bl?',
15368     /**
15369      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15370      */
15371     maxHeight: 300,
15372     /**
15373      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15374      * query specified by the allQuery config option (defaults to 'query')
15375      */
15376     triggerAction: 'query',
15377     /**
15378      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15379      * (defaults to 4, does not apply if editable = false)
15380      */
15381     minChars : 4,
15382     /**
15383      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15384      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15385      */
15386     typeAhead: false,
15387     /**
15388      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15389      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15390      */
15391     queryDelay: 500,
15392     /**
15393      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15394      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15395      */
15396     pageSize: 0,
15397     /**
15398      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15399      * when editable = true (defaults to false)
15400      */
15401     selectOnFocus:false,
15402     /**
15403      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15404      */
15405     queryParam: 'query',
15406     /**
15407      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15408      * when mode = 'remote' (defaults to 'Loading...')
15409      */
15410     loadingText: 'Loading...',
15411     /**
15412      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15413      */
15414     resizable: false,
15415     /**
15416      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15417      */
15418     handleHeight : 8,
15419     /**
15420      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15421      * traditional select (defaults to true)
15422      */
15423     editable: true,
15424     /**
15425      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15426      */
15427     allQuery: '',
15428     /**
15429      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15430      */
15431     mode: 'remote',
15432     /**
15433      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15434      * listWidth has a higher value)
15435      */
15436     minListWidth : 70,
15437     /**
15438      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15439      * allow the user to set arbitrary text into the field (defaults to false)
15440      */
15441     forceSelection:false,
15442     /**
15443      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15444      * if typeAhead = true (defaults to 250)
15445      */
15446     typeAheadDelay : 250,
15447     /**
15448      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15449      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15450      */
15451     valueNotFoundText : undefined,
15452     /**
15453      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15454      */
15455     blockFocus : false,
15456     
15457     /**
15458      * @cfg {bool} disableClear Disable showing of clear button.
15459      */
15460     disableClear : false,
15461     
15462     // private
15463     onRender : function(ct, position){
15464         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
15465         if(this.hiddenName){
15466             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
15467                     'before', true);
15468             this.hiddenField.value =
15469                 this.hiddenValue !== undefined ? this.hiddenValue :
15470                 this.value !== undefined ? this.value : '';
15471
15472             // prevent input submission
15473             this.el.dom.removeAttribute('name');
15474         }
15475         if(Roo.isGecko){
15476             this.el.dom.setAttribute('autocomplete', 'off');
15477         }
15478
15479         var cls = 'x-combo-list';
15480
15481         this.list = new Roo.Layer({
15482             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15483         });
15484
15485         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
15486         this.list.setWidth(lw);
15487         this.list.swallowEvent('mousewheel');
15488         this.assetHeight = 0;
15489
15490         if(this.title){
15491             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15492             this.assetHeight += this.header.getHeight();
15493         }
15494
15495         this.innerList = this.list.createChild({cls:cls+'-inner'});
15496         this.innerList.on('mouseover', this.onViewOver, this);
15497         this.innerList.on('mousemove', this.onViewMove, this);
15498         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15499         
15500         if(this.allowBlank && !this.pageSize && !this.disableClear){
15501             this.footer = this.list.createChild({cls:cls+'-ft'});
15502             this.pageTb = new Roo.Toolbar(this.footer);
15503            
15504         }
15505         if(this.pageSize){
15506             this.footer = this.list.createChild({cls:cls+'-ft'});
15507             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15508                     {pageSize: this.pageSize});
15509             
15510         }
15511         
15512         if (this.pageTb && this.allowBlank && !this.disableClear) {
15513             var _this = this;
15514             this.pageTb.add(new Roo.Toolbar.Fill(), {
15515                 cls: 'x-btn-icon x-btn-clear',
15516                 text: '&#160;',
15517                 handler: function()
15518                 {
15519                     _this.collapse();
15520                     _this.clearValue();
15521                     _this.onSelect(false, -1);
15522                 }
15523             });
15524         }
15525         if (this.footer) {
15526             this.assetHeight += this.footer.getHeight();
15527         }
15528         
15529
15530         if(!this.tpl){
15531             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
15532         }
15533
15534         this.view = new Roo.View(this.innerList, this.tpl, {
15535             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15536         });
15537
15538         this.view.on('click', this.onViewClick, this);
15539
15540         this.store.on('beforeload', this.onBeforeLoad, this);
15541         this.store.on('load', this.onLoad, this);
15542         this.store.on('loadexception', this.collapse, this);
15543
15544         if(this.resizable){
15545             this.resizer = new Roo.Resizable(this.list,  {
15546                pinned:true, handles:'se'
15547             });
15548             this.resizer.on('resize', function(r, w, h){
15549                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15550                 this.listWidth = w;
15551                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15552                 this.restrictHeight();
15553             }, this);
15554             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15555         }
15556         if(!this.editable){
15557             this.editable = true;
15558             this.setEditable(false);
15559         }
15560     },
15561
15562     // private
15563     initEvents : function(){
15564         Roo.form.ComboBox.superclass.initEvents.call(this);
15565
15566         this.keyNav = new Roo.KeyNav(this.el, {
15567             "up" : function(e){
15568                 this.inKeyMode = true;
15569                 this.selectPrev();
15570             },
15571
15572             "down" : function(e){
15573                 if(!this.isExpanded()){
15574                     this.onTriggerClick();
15575                 }else{
15576                     this.inKeyMode = true;
15577                     this.selectNext();
15578                 }
15579             },
15580
15581             "enter" : function(e){
15582                 this.onViewClick();
15583                 //return true;
15584             },
15585
15586             "esc" : function(e){
15587                 this.collapse();
15588             },
15589
15590             "tab" : function(e){
15591                 this.onViewClick(false);
15592                 return true;
15593             },
15594
15595             scope : this,
15596
15597             doRelay : function(foo, bar, hname){
15598                 if(hname == 'down' || this.scope.isExpanded()){
15599                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15600                 }
15601                 return true;
15602             },
15603
15604             forceKeyDown: true
15605         });
15606         this.queryDelay = Math.max(this.queryDelay || 10,
15607                 this.mode == 'local' ? 10 : 250);
15608         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15609         if(this.typeAhead){
15610             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15611         }
15612         if(this.editable !== false){
15613             this.el.on("keyup", this.onKeyUp, this);
15614         }
15615         if(this.forceSelection){
15616             this.on('blur', this.doForce, this);
15617         }
15618     },
15619
15620     onDestroy : function(){
15621         if(this.view){
15622             this.view.setStore(null);
15623             this.view.el.removeAllListeners();
15624             this.view.el.remove();
15625             this.view.purgeListeners();
15626         }
15627         if(this.list){
15628             this.list.destroy();
15629         }
15630         if(this.store){
15631             this.store.un('beforeload', this.onBeforeLoad, this);
15632             this.store.un('load', this.onLoad, this);
15633             this.store.un('loadexception', this.collapse, this);
15634         }
15635         Roo.form.ComboBox.superclass.onDestroy.call(this);
15636     },
15637
15638     // private
15639     fireKey : function(e){
15640         if(e.isNavKeyPress() && !this.list.isVisible()){
15641             this.fireEvent("specialkey", this, e);
15642         }
15643     },
15644
15645     // private
15646     onResize: function(w, h){
15647         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
15648         if(this.list && this.listWidth === undefined){
15649             var lw = Math.max(w, this.minListWidth);
15650             this.list.setWidth(lw);
15651             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15652         }
15653     },
15654
15655     /**
15656      * Allow or prevent the user from directly editing the field text.  If false is passed,
15657      * the user will only be able to select from the items defined in the dropdown list.  This method
15658      * is the runtime equivalent of setting the 'editable' config option at config time.
15659      * @param {Boolean} value True to allow the user to directly edit the field text
15660      */
15661     setEditable : function(value){
15662         if(value == this.editable){
15663             return;
15664         }
15665         this.editable = value;
15666         if(!value){
15667             this.el.dom.setAttribute('readOnly', true);
15668             this.el.on('mousedown', this.onTriggerClick,  this);
15669             this.el.addClass('x-combo-noedit');
15670         }else{
15671             this.el.dom.setAttribute('readOnly', false);
15672             this.el.un('mousedown', this.onTriggerClick,  this);
15673             this.el.removeClass('x-combo-noedit');
15674         }
15675     },
15676
15677     // private
15678     onBeforeLoad : function(){
15679         if(!this.hasFocus){
15680             return;
15681         }
15682         this.innerList.update(this.loadingText ?
15683                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
15684         this.restrictHeight();
15685         this.selectedIndex = -1;
15686     },
15687
15688     // private
15689     onLoad : function(){
15690         if(!this.hasFocus){
15691             return;
15692         }
15693         if(this.store.getCount() > 0){
15694             this.expand();
15695             this.restrictHeight();
15696             if(this.lastQuery == this.allQuery){
15697                 if(this.editable){
15698                     this.el.dom.select();
15699                 }
15700                 if(!this.selectByValue(this.value, true)){
15701                     this.select(0, true);
15702                 }
15703             }else{
15704                 this.selectNext();
15705                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15706                     this.taTask.delay(this.typeAheadDelay);
15707                 }
15708             }
15709         }else{
15710             this.onEmptyResults();
15711         }
15712         //this.el.focus();
15713     },
15714
15715     // private
15716     onTypeAhead : function(){
15717         if(this.store.getCount() > 0){
15718             var r = this.store.getAt(0);
15719             var newValue = r.data[this.displayField];
15720             var len = newValue.length;
15721             var selStart = this.getRawValue().length;
15722             if(selStart != len){
15723                 this.setRawValue(newValue);
15724                 this.selectText(selStart, newValue.length);
15725             }
15726         }
15727     },
15728
15729     // private
15730     onSelect : function(record, index){
15731         if(this.fireEvent('beforeselect', this, record, index) !== false){
15732             this.setFromData(index > -1 ? record.data : false);
15733             this.collapse();
15734             this.fireEvent('select', this, record, index);
15735         }
15736     },
15737
15738     /**
15739      * Returns the currently selected field value or empty string if no value is set.
15740      * @return {String} value The selected value
15741      */
15742     getValue : function(){
15743         if(this.valueField){
15744             return typeof this.value != 'undefined' ? this.value : '';
15745         }else{
15746             return Roo.form.ComboBox.superclass.getValue.call(this);
15747         }
15748     },
15749
15750     /**
15751      * Clears any text/value currently set in the field
15752      */
15753     clearValue : function(){
15754         if(this.hiddenField){
15755             this.hiddenField.value = '';
15756         }
15757         this.value = '';
15758         this.setRawValue('');
15759         this.lastSelectionText = '';
15760         this.applyEmptyText();
15761     },
15762
15763     /**
15764      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15765      * will be displayed in the field.  If the value does not match the data value of an existing item,
15766      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15767      * Otherwise the field will be blank (although the value will still be set).
15768      * @param {String} value The value to match
15769      */
15770     setValue : function(v){
15771         var text = v;
15772         if(this.valueField){
15773             var r = this.findRecord(this.valueField, v);
15774             if(r){
15775                 text = r.data[this.displayField];
15776             }else if(this.valueNotFoundText !== undefined){
15777                 text = this.valueNotFoundText;
15778             }
15779         }
15780         this.lastSelectionText = text;
15781         if(this.hiddenField){
15782             this.hiddenField.value = v;
15783         }
15784         Roo.form.ComboBox.superclass.setValue.call(this, text);
15785         this.value = v;
15786     },
15787     /**
15788      * @property {Object} the last set data for the element
15789      */
15790     
15791     lastData : false,
15792     /**
15793      * Sets the value of the field based on a object which is related to the record format for the store.
15794      * @param {Object} value the value to set as. or false on reset?
15795      */
15796     setFromData : function(o){
15797         var dv = ''; // display value
15798         var vv = ''; // value value..
15799         this.lastData = o;
15800         if (this.displayField) {
15801             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15802         } else {
15803             // this is an error condition!!!
15804             console.log('no value field set for '+ this.name);
15805         }
15806         
15807         if(this.valueField){
15808             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15809         }
15810         if(this.hiddenField){
15811             this.hiddenField.value = vv;
15812             
15813             this.lastSelectionText = dv;
15814             Roo.form.ComboBox.superclass.setValue.call(this, dv);
15815             this.value = vv;
15816             return;
15817         }
15818         // no hidden field.. - we store the value in 'value', but still display
15819         // display field!!!!
15820         this.lastSelectionText = dv;
15821         Roo.form.ComboBox.superclass.setValue.call(this, dv);
15822         this.value = vv;
15823         
15824         
15825     },
15826     // private
15827     reset : function(){
15828         // overridden so that last data is reset..
15829         this.setValue(this.originalValue);
15830         this.clearInvalid();
15831         this.lastData = false;
15832     },
15833     // private
15834     findRecord : function(prop, value){
15835         var record;
15836         if(this.store.getCount() > 0){
15837             this.store.each(function(r){
15838                 if(r.data[prop] == value){
15839                     record = r;
15840                     return false;
15841                 }
15842             });
15843         }
15844         return record;
15845     },
15846
15847     // private
15848     onViewMove : function(e, t){
15849         this.inKeyMode = false;
15850     },
15851
15852     // private
15853     onViewOver : function(e, t){
15854         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15855             return;
15856         }
15857         var item = this.view.findItemFromChild(t);
15858         if(item){
15859             var index = this.view.indexOf(item);
15860             this.select(index, false);
15861         }
15862     },
15863
15864     // private
15865     onViewClick : function(doFocus){
15866         var index = this.view.getSelectedIndexes()[0];
15867         var r = this.store.getAt(index);
15868         if(r){
15869             this.onSelect(r, index);
15870         }
15871         if(doFocus !== false && !this.blockFocus){
15872             this.el.focus();
15873         }
15874     },
15875
15876     // private
15877     restrictHeight : function(){
15878         this.innerList.dom.style.height = '';
15879         var inner = this.innerList.dom;
15880         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15881         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15882         this.list.beginUpdate();
15883         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15884         this.list.alignTo(this.el, this.listAlign);
15885         this.list.endUpdate();
15886     },
15887
15888     // private
15889     onEmptyResults : function(){
15890         this.collapse();
15891     },
15892
15893     /**
15894      * Returns true if the dropdown list is expanded, else false.
15895      */
15896     isExpanded : function(){
15897         return this.list.isVisible();
15898     },
15899
15900     /**
15901      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15902      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15903      * @param {String} value The data value of the item to select
15904      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15905      * selected item if it is not currently in view (defaults to true)
15906      * @return {Boolean} True if the value matched an item in the list, else false
15907      */
15908     selectByValue : function(v, scrollIntoView){
15909         if(v !== undefined && v !== null){
15910             var r = this.findRecord(this.valueField || this.displayField, v);
15911             if(r){
15912                 this.select(this.store.indexOf(r), scrollIntoView);
15913                 return true;
15914             }
15915         }
15916         return false;
15917     },
15918
15919     /**
15920      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15921      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15922      * @param {Number} index The zero-based index of the list item to select
15923      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15924      * selected item if it is not currently in view (defaults to true)
15925      */
15926     select : function(index, scrollIntoView){
15927         this.selectedIndex = index;
15928         this.view.select(index);
15929         if(scrollIntoView !== false){
15930             var el = this.view.getNode(index);
15931             if(el){
15932                 this.innerList.scrollChildIntoView(el, false);
15933             }
15934         }
15935     },
15936
15937     // private
15938     selectNext : function(){
15939         var ct = this.store.getCount();
15940         if(ct > 0){
15941             if(this.selectedIndex == -1){
15942                 this.select(0);
15943             }else if(this.selectedIndex < ct-1){
15944                 this.select(this.selectedIndex+1);
15945             }
15946         }
15947     },
15948
15949     // private
15950     selectPrev : function(){
15951         var ct = this.store.getCount();
15952         if(ct > 0){
15953             if(this.selectedIndex == -1){
15954                 this.select(0);
15955             }else if(this.selectedIndex != 0){
15956                 this.select(this.selectedIndex-1);
15957             }
15958         }
15959     },
15960
15961     // private
15962     onKeyUp : function(e){
15963         if(this.editable !== false && !e.isSpecialKey()){
15964             this.lastKey = e.getKey();
15965             this.dqTask.delay(this.queryDelay);
15966         }
15967     },
15968
15969     // private
15970     validateBlur : function(){
15971         return !this.list || !this.list.isVisible();   
15972     },
15973
15974     // private
15975     initQuery : function(){
15976         this.doQuery(this.getRawValue());
15977     },
15978
15979     // private
15980     doForce : function(){
15981         if(this.el.dom.value.length > 0){
15982             this.el.dom.value =
15983                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15984             this.applyEmptyText();
15985         }
15986     },
15987
15988     /**
15989      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15990      * query allowing the query action to be canceled if needed.
15991      * @param {String} query The SQL query to execute
15992      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15993      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15994      * saved in the current store (defaults to false)
15995      */
15996     doQuery : function(q, forceAll){
15997         if(q === undefined || q === null){
15998             q = '';
15999         }
16000         var qe = {
16001             query: q,
16002             forceAll: forceAll,
16003             combo: this,
16004             cancel:false
16005         };
16006         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16007             return false;
16008         }
16009         q = qe.query;
16010         forceAll = qe.forceAll;
16011         if(forceAll === true || (q.length >= this.minChars)){
16012             if(this.lastQuery != q){
16013                 this.lastQuery = q;
16014                 if(this.mode == 'local'){
16015                     this.selectedIndex = -1;
16016                     if(forceAll){
16017                         this.store.clearFilter();
16018                     }else{
16019                         this.store.filter(this.displayField, q);
16020                     }
16021                     this.onLoad();
16022                 }else{
16023                     this.store.baseParams[this.queryParam] = q;
16024                     this.store.load({
16025                         params: this.getParams(q)
16026                     });
16027                     this.expand();
16028                 }
16029             }else{
16030                 this.selectedIndex = -1;
16031                 this.onLoad();   
16032             }
16033         }
16034     },
16035
16036     // private
16037     getParams : function(q){
16038         var p = {};
16039         //p[this.queryParam] = q;
16040         if(this.pageSize){
16041             p.start = 0;
16042             p.limit = this.pageSize;
16043         }
16044         return p;
16045     },
16046
16047     /**
16048      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16049      */
16050     collapse : function(){
16051         if(!this.isExpanded()){
16052             return;
16053         }
16054         this.list.hide();
16055         Roo.get(document).un('mousedown', this.collapseIf, this);
16056         Roo.get(document).un('mousewheel', this.collapseIf, this);
16057         this.fireEvent('collapse', this);
16058     },
16059
16060     // private
16061     collapseIf : function(e){
16062         if(!e.within(this.wrap) && !e.within(this.list)){
16063             this.collapse();
16064         }
16065     },
16066
16067     /**
16068      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16069      */
16070     expand : function(){
16071         if(this.isExpanded() || !this.hasFocus){
16072             return;
16073         }
16074         this.list.alignTo(this.el, this.listAlign);
16075         this.list.show();
16076         Roo.get(document).on('mousedown', this.collapseIf, this);
16077         Roo.get(document).on('mousewheel', this.collapseIf, this);
16078         this.fireEvent('expand', this);
16079     },
16080
16081     // private
16082     // Implements the default empty TriggerField.onTriggerClick function
16083     onTriggerClick : function(){
16084         if(this.disabled){
16085             return;
16086         }
16087         if(this.isExpanded()){
16088             this.collapse();
16089             if (!this.blockFocus) {
16090                 this.el.focus();
16091             }
16092             
16093         }else {
16094             this.hasFocus = true;
16095             if(this.triggerAction == 'all') {
16096                 this.doQuery(this.allQuery, true);
16097             } else {
16098                 this.doQuery(this.getRawValue());
16099             }
16100             if (!this.blockFocus) {
16101                 this.el.focus();
16102             }
16103         }
16104     }
16105
16106     /** 
16107     * @cfg {Boolean} grow 
16108     * @hide 
16109     */
16110     /** 
16111     * @cfg {Number} growMin 
16112     * @hide 
16113     */
16114     /** 
16115     * @cfg {Number} growMax 
16116     * @hide 
16117     */
16118     /**
16119      * @hide
16120      * @method autoSize
16121      */
16122 });/*
16123  * Based on:
16124  * Ext JS Library 1.1.1
16125  * Copyright(c) 2006-2007, Ext JS, LLC.
16126  *
16127  * Originally Released Under LGPL - original licence link has changed is not relivant.
16128  *
16129  * Fork - LGPL
16130  * <script type="text/javascript">
16131  */
16132 /**
16133  * @class Roo.form.Checkbox
16134  * @extends Roo.form.Field
16135  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
16136  * @constructor
16137  * Creates a new Checkbox
16138  * @param {Object} config Configuration options
16139  */
16140 Roo.form.Checkbox = function(config){
16141     Roo.form.Checkbox.superclass.constructor.call(this, config);
16142     this.addEvents({
16143         /**
16144          * @event check
16145          * Fires when the checkbox is checked or unchecked.
16146              * @param {Roo.form.Checkbox} this This checkbox
16147              * @param {Boolean} checked The new checked value
16148              */
16149         check : true
16150     });
16151 };
16152
16153 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
16154     /**
16155      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
16156      */
16157     focusClass : undefined,
16158     /**
16159      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
16160      */
16161     fieldClass: "x-form-field",
16162     /**
16163      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
16164      */
16165     checked: false,
16166     /**
16167      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16168      * {tag: "input", type: "checkbox", autocomplete: "off"})
16169      */
16170     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
16171     /**
16172      * @cfg {String} boxLabel The text that appears beside the checkbox
16173      */
16174     boxLabel : "",
16175     /**
16176      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
16177      */  
16178     inputValue : '1',
16179     /**
16180      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16181      */
16182      valueOff: '0', // value when not checked..
16183
16184     actionMode : 'viewEl', 
16185     //
16186     // private
16187     itemCls : 'x-menu-check-item x-form-item',
16188     groupClass : 'x-menu-group-item',
16189     inputType : 'hidden',
16190     
16191     
16192     inSetChecked: false, // check that we are not calling self...
16193     
16194     inputElement: false, // real input element?
16195     basedOn: false, // ????
16196     
16197     isFormField: true, // not sure where this is needed!!!!
16198
16199     onResize : function(){
16200         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
16201         if(!this.boxLabel){
16202             this.el.alignTo(this.wrap, 'c-c');
16203         }
16204     },
16205
16206     initEvents : function(){
16207         Roo.form.Checkbox.superclass.initEvents.call(this);
16208         this.el.on("click", this.onClick,  this);
16209         this.el.on("change", this.onClick,  this);
16210     },
16211
16212
16213     getResizeEl : function(){
16214         return this.wrap;
16215     },
16216
16217     getPositionEl : function(){
16218         return this.wrap;
16219     },
16220
16221     // private
16222     onRender : function(ct, position){
16223         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
16224         /*
16225         if(this.inputValue !== undefined){
16226             this.el.dom.value = this.inputValue;
16227         }
16228         */
16229         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
16230         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
16231         var viewEl = this.wrap.createChild({ 
16232             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
16233         this.viewEl = viewEl;   
16234         this.wrap.on('click', this.onClick,  this); 
16235         
16236         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
16237         this.el.on('propertychange', this.setFromHidden,  this);  //ie
16238         
16239         
16240         
16241         if(this.boxLabel){
16242             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
16243         //    viewEl.on('click', this.onClick,  this); 
16244         }
16245         //if(this.checked){
16246             this.setChecked(this.checked);
16247         //}else{
16248             //this.checked = this.el.dom;
16249         //}
16250
16251     },
16252
16253     // private
16254     initValue : Roo.emptyFn,
16255
16256     /**
16257      * Returns the checked state of the checkbox.
16258      * @return {Boolean} True if checked, else false
16259      */
16260     getValue : function(){
16261         if(this.el){
16262             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
16263         }
16264         return this.valueOff;
16265         
16266     },
16267
16268         // private
16269     onClick : function(){ 
16270         this.setChecked(!this.checked);
16271
16272         //if(this.el.dom.checked != this.checked){
16273         //    this.setValue(this.el.dom.checked);
16274        // }
16275     },
16276
16277     /**
16278      * Sets the checked state of the checkbox.
16279      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
16280      */
16281     setValue : function(v,suppressEvent){
16282         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
16283         //if(this.el && this.el.dom){
16284         //    this.el.dom.checked = this.checked;
16285         //    this.el.dom.defaultChecked = this.checked;
16286         //}
16287         this.setChecked(v === this.inputValue);
16288         //this.fireEvent("check", this, this.checked);
16289     },
16290     // private..
16291     setChecked : function(state,suppressEvent)
16292     {
16293         if (this.inSetChecked) {
16294             this.checked = state;
16295             return;
16296         }
16297         
16298     
16299         if(this.wrap){
16300             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
16301         }
16302         this.checked = state;
16303         if(suppressEvent !== true){
16304             this.fireEvent('checkchange', this, state);
16305         }
16306         this.inSetChecked = true;
16307         this.el.dom.value = state ? this.inputValue : this.valueOff;
16308         this.inSetChecked = false;
16309         
16310     },
16311     // handle setting of hidden value by some other method!!?!?
16312     setFromHidden: function()
16313     {
16314         if(!this.el){
16315             return;
16316         }
16317         //console.log("SET FROM HIDDEN");
16318         //alert('setFrom hidden');
16319         this.setValue(this.el.dom.value);
16320     },
16321     
16322     onDestroy : function()
16323     {
16324         if(this.viewEl){
16325             Roo.get(this.viewEl).remove();
16326         }
16327          
16328         Roo.form.Checkbox.superclass.onDestroy.call(this);
16329     }
16330
16331 });/*
16332  * Based on:
16333  * Ext JS Library 1.1.1
16334  * Copyright(c) 2006-2007, Ext JS, LLC.
16335  *
16336  * Originally Released Under LGPL - original licence link has changed is not relivant.
16337  *
16338  * Fork - LGPL
16339  * <script type="text/javascript">
16340  */
16341  
16342 /**
16343  * @class Roo.form.Radio
16344  * @extends Roo.form.Checkbox
16345  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
16346  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
16347  * @constructor
16348  * Creates a new Radio
16349  * @param {Object} config Configuration options
16350  */
16351 Roo.form.Radio = function(){
16352     Roo.form.Radio.superclass.constructor.apply(this, arguments);
16353 };
16354 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
16355     inputType: 'radio',
16356
16357     /**
16358      * If this radio is part of a group, it will return the selected value
16359      * @return {String}
16360      */
16361     getGroupValue : function(){
16362         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
16363     }
16364 });//<script type="text/javascript">
16365
16366 /*
16367  * Ext JS Library 1.1.1
16368  * Copyright(c) 2006-2007, Ext JS, LLC.
16369  * licensing@extjs.com
16370  * 
16371  * http://www.extjs.com/license
16372  */
16373  
16374  /*
16375   * 
16376   * Known bugs:
16377   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
16378   * - IE ? - no idea how much works there.
16379   * 
16380   * 
16381   * 
16382   */
16383  
16384
16385 /**
16386  * @class Ext.form.HtmlEditor
16387  * @extends Ext.form.Field
16388  * Provides a lightweight HTML Editor component.
16389  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
16390  * 
16391  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
16392  * supported by this editor.</b><br/><br/>
16393  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
16394  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16395  */
16396 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
16397       /**
16398      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
16399      */
16400     toolbars : false,
16401     /**
16402      * @cfg {String} createLinkText The default text for the create link prompt
16403      */
16404     createLinkText : 'Please enter the URL for the link:',
16405     /**
16406      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
16407      */
16408     defaultLinkValue : 'http:/'+'/',
16409    
16410     
16411     // id of frame..
16412     frameId: false,
16413     
16414     // private properties
16415     validationEvent : false,
16416     deferHeight: true,
16417     initialized : false,
16418     activated : false,
16419     sourceEditMode : false,
16420     onFocus : Roo.emptyFn,
16421     iframePad:3,
16422     hideMode:'offsets',
16423     defaultAutoCreate : {
16424         tag: "textarea",
16425         style:"width:500px;height:300px;",
16426         autocomplete: "off"
16427     },
16428
16429     // private
16430     initComponent : function(){
16431         this.addEvents({
16432             /**
16433              * @event initialize
16434              * Fires when the editor is fully initialized (including the iframe)
16435              * @param {HtmlEditor} this
16436              */
16437             initialize: true,
16438             /**
16439              * @event activate
16440              * Fires when the editor is first receives the focus. Any insertion must wait
16441              * until after this event.
16442              * @param {HtmlEditor} this
16443              */
16444             activate: true,
16445              /**
16446              * @event beforesync
16447              * Fires before the textarea is updated with content from the editor iframe. Return false
16448              * to cancel the sync.
16449              * @param {HtmlEditor} this
16450              * @param {String} html
16451              */
16452             beforesync: true,
16453              /**
16454              * @event beforepush
16455              * Fires before the iframe editor is updated with content from the textarea. Return false
16456              * to cancel the push.
16457              * @param {HtmlEditor} this
16458              * @param {String} html
16459              */
16460             beforepush: true,
16461              /**
16462              * @event sync
16463              * Fires when the textarea is updated with content from the editor iframe.
16464              * @param {HtmlEditor} this
16465              * @param {String} html
16466              */
16467             sync: true,
16468              /**
16469              * @event push
16470              * Fires when the iframe editor is updated with content from the textarea.
16471              * @param {HtmlEditor} this
16472              * @param {String} html
16473              */
16474             push: true,
16475              /**
16476              * @event editmodechange
16477              * Fires when the editor switches edit modes
16478              * @param {HtmlEditor} this
16479              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
16480              */
16481             editmodechange: true,
16482             /**
16483              * @event editorevent
16484              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16485              * @param {HtmlEditor} this
16486              */
16487             editorevent: true
16488         })
16489     },
16490
16491     /**
16492      * Protected method that will not generally be called directly. It
16493      * is called when the editor creates its toolbar. Override this method if you need to
16494      * add custom toolbar buttons.
16495      * @param {HtmlEditor} editor
16496      */
16497     createToolbar : function(editor){
16498         if (!editor.toolbars || !editor.toolbars.length) {
16499             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
16500         }
16501         
16502         for (var i =0 ; i < editor.toolbars.length;i++) {
16503             editor.toolbars[i].init(editor);
16504         }
16505          
16506         
16507     },
16508
16509     /**
16510      * Protected method that will not generally be called directly. It
16511      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16512      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16513      */
16514     getDocMarkup : function(){
16515         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
16516     },
16517
16518     // private
16519     onRender : function(ct, position){
16520         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
16521         this.el.dom.style.border = '0 none';
16522         this.el.dom.setAttribute('tabIndex', -1);
16523         this.el.addClass('x-hidden');
16524         if(Roo.isIE){ // fix IE 1px bogus margin
16525             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16526         }
16527         this.wrap = this.el.wrap({
16528             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
16529         });
16530
16531         this.frameId = Roo.id();
16532         this.createToolbar(this);
16533         
16534         
16535         
16536         
16537       
16538         
16539         var iframe = this.wrap.createChild({
16540             tag: 'iframe',
16541             id: this.frameId,
16542             name: this.frameId,
16543             frameBorder : 'no',
16544             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16545         });
16546         
16547        // console.log(iframe);
16548         //this.wrap.dom.appendChild(iframe);
16549
16550         this.iframe = iframe.dom;
16551
16552          this.assignDocWin();
16553         
16554         this.doc.designMode = 'on';
16555        
16556         this.doc.open();
16557         this.doc.write(this.getDocMarkup());
16558         this.doc.close();
16559
16560         
16561         var task = { // must defer to wait for browser to be ready
16562             run : function(){
16563                 //console.log("run task?" + this.doc.readyState);
16564                 this.assignDocWin();
16565                 if(this.doc.body || this.doc.readyState == 'complete'){
16566                     try {
16567                         
16568                        
16569                         this.doc.designMode="on";
16570                     } catch (e) {
16571                         return;
16572                     }
16573                     Roo.TaskMgr.stop(task);
16574                     this.initEditor.defer(10, this);
16575                 }
16576             },
16577             interval : 10,
16578             duration:10000,
16579             scope: this
16580         };
16581         Roo.TaskMgr.start(task);
16582
16583         if(!this.width){
16584             this.setSize(this.el.getSize());
16585         }
16586     },
16587
16588     // private
16589     onResize : function(w, h){
16590         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
16591         if(this.el && this.iframe){
16592             if(typeof w == 'number'){
16593                 var aw = w - this.wrap.getFrameWidth('lr');
16594                 this.el.setWidth(this.adjustWidth('textarea', aw));
16595                 this.iframe.style.width = aw + 'px';
16596             }
16597             if(typeof h == 'number'){
16598                 var tbh = 0;
16599                 for (var i =0; i < this.toolbars.length;i++) {
16600                     // fixme - ask toolbars for heights?
16601                     tbh += this.toolbars[i].tb.el.getHeight();
16602                 }
16603                 
16604                 
16605                 
16606                 
16607                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16608                 this.el.setHeight(this.adjustWidth('textarea', ah));
16609                 this.iframe.style.height = ah + 'px';
16610                 if(this.doc){
16611                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
16612                 }
16613             }
16614         }
16615     },
16616
16617     /**
16618      * Toggles the editor between standard and source edit mode.
16619      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16620      */
16621     toggleSourceEdit : function(sourceEditMode){
16622         
16623         this.sourceEditMode = sourceEditMode === true;
16624         
16625         if(this.sourceEditMode){
16626           
16627             this.syncValue();
16628             this.iframe.className = 'x-hidden';
16629             this.el.removeClass('x-hidden');
16630             this.el.dom.removeAttribute('tabIndex');
16631             this.el.focus();
16632         }else{
16633              
16634             this.pushValue();
16635             this.iframe.className = '';
16636             this.el.addClass('x-hidden');
16637             this.el.dom.setAttribute('tabIndex', -1);
16638             this.deferFocus();
16639         }
16640         this.setSize(this.wrap.getSize());
16641         this.fireEvent('editmodechange', this, this.sourceEditMode);
16642     },
16643
16644     // private used internally
16645     createLink : function(){
16646         var url = prompt(this.createLinkText, this.defaultLinkValue);
16647         if(url && url != 'http:/'+'/'){
16648             this.relayCmd('createlink', url);
16649         }
16650     },
16651
16652     // private (for BoxComponent)
16653     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16654
16655     // private (for BoxComponent)
16656     getResizeEl : function(){
16657         return this.wrap;
16658     },
16659
16660     // private (for BoxComponent)
16661     getPositionEl : function(){
16662         return this.wrap;
16663     },
16664
16665     // private
16666     initEvents : function(){
16667         this.originalValue = this.getValue();
16668     },
16669
16670     /**
16671      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16672      * @method
16673      */
16674     markInvalid : Roo.emptyFn,
16675     /**
16676      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16677      * @method
16678      */
16679     clearInvalid : Roo.emptyFn,
16680
16681     setValue : function(v){
16682         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
16683         this.pushValue();
16684     },
16685
16686     /**
16687      * Protected method that will not generally be called directly. If you need/want
16688      * custom HTML cleanup, this is the method you should override.
16689      * @param {String} html The HTML to be cleaned
16690      * return {String} The cleaned HTML
16691      */
16692     cleanHtml : function(html){
16693         html = String(html);
16694         if(html.length > 5){
16695             if(Roo.isSafari){ // strip safari nonsense
16696                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16697             }
16698         }
16699         if(html == '&nbsp;'){
16700             html = '';
16701         }
16702         return html;
16703     },
16704
16705     /**
16706      * Protected method that will not generally be called directly. Syncs the contents
16707      * of the editor iframe with the textarea.
16708      */
16709     syncValue : function(){
16710         if(this.initialized){
16711             var bd = (this.doc.body || this.doc.documentElement);
16712             var html = bd.innerHTML;
16713             if(Roo.isSafari){
16714                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16715                 var m = bs.match(/text-align:(.*?);/i);
16716                 if(m && m[1]){
16717                     html = '<div style="'+m[0]+'">' + html + '</div>';
16718                 }
16719             }
16720             html = this.cleanHtml(html);
16721             if(this.fireEvent('beforesync', this, html) !== false){
16722                 this.el.dom.value = html;
16723                 this.fireEvent('sync', this, html);
16724             }
16725         }
16726     },
16727
16728     /**
16729      * Protected method that will not generally be called directly. Pushes the value of the textarea
16730      * into the iframe editor.
16731      */
16732     pushValue : function(){
16733         if(this.initialized){
16734             var v = this.el.dom.value;
16735             if(v.length < 1){
16736                 v = '&#160;';
16737             }
16738             if(this.fireEvent('beforepush', this, v) !== false){
16739                 (this.doc.body || this.doc.documentElement).innerHTML = v;
16740                 this.fireEvent('push', this, v);
16741             }
16742         }
16743     },
16744
16745     // private
16746     deferFocus : function(){
16747         this.focus.defer(10, this);
16748     },
16749
16750     // doc'ed in Field
16751     focus : function(){
16752         if(this.win && !this.sourceEditMode){
16753             this.win.focus();
16754         }else{
16755             this.el.focus();
16756         }
16757     },
16758     
16759     assignDocWin: function()
16760     {
16761         var iframe = this.iframe;
16762         
16763          if(Roo.isIE){
16764             this.doc = iframe.contentWindow.document;
16765             this.win = iframe.contentWindow;
16766         } else {
16767             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16768             this.win = Roo.get(this.frameId).dom.contentWindow;
16769         }
16770     },
16771     
16772     // private
16773     initEditor : function(){
16774         //console.log("INIT EDITOR");
16775         this.assignDocWin();
16776         
16777         
16778         
16779         this.doc.designMode="on";
16780         this.doc.open();
16781         this.doc.write(this.getDocMarkup());
16782         this.doc.close();
16783         
16784         var dbody = (this.doc.body || this.doc.documentElement);
16785         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16786         // this copies styles from the containing element into thsi one..
16787         // not sure why we need all of this..
16788         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16789         ss['background-attachment'] = 'fixed'; // w3c
16790         dbody.bgProperties = 'fixed'; // ie
16791         Roo.DomHelper.applyStyles(dbody, ss);
16792         Roo.EventManager.on(this.doc, {
16793             'mousedown': this.onEditorEvent,
16794             'dblclick': this.onEditorEvent,
16795             'click': this.onEditorEvent,
16796             'keyup': this.onEditorEvent,
16797             buffer:100,
16798             scope: this
16799         });
16800         if(Roo.isGecko){
16801             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
16802         }
16803         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16804             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16805         }
16806         this.initialized = true;
16807
16808         this.fireEvent('initialize', this);
16809         this.pushValue();
16810     },
16811
16812     // private
16813     onDestroy : function(){
16814         
16815         
16816         
16817         if(this.rendered){
16818             
16819             for (var i =0; i < this.toolbars.length;i++) {
16820                 // fixme - ask toolbars for heights?
16821                 this.toolbars[i].onDestroy();
16822             }
16823             
16824             this.wrap.dom.innerHTML = '';
16825             this.wrap.remove();
16826         }
16827     },
16828
16829     // private
16830     onFirstFocus : function(){
16831         
16832         this.assignDocWin();
16833         
16834         
16835         this.activated = true;
16836         for (var i =0; i < this.toolbars.length;i++) {
16837             this.toolbars[i].onFirstFocus();
16838         }
16839        
16840         if(Roo.isGecko){ // prevent silly gecko errors
16841             this.win.focus();
16842             var s = this.win.getSelection();
16843             if(!s.focusNode || s.focusNode.nodeType != 3){
16844                 var r = s.getRangeAt(0);
16845                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16846                 r.collapse(true);
16847                 this.deferFocus();
16848             }
16849             try{
16850                 this.execCmd('useCSS', true);
16851                 this.execCmd('styleWithCSS', false);
16852             }catch(e){}
16853         }
16854         this.fireEvent('activate', this);
16855     },
16856
16857     // private
16858     adjustFont: function(btn){
16859         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16860         //if(Roo.isSafari){ // safari
16861         //    adjust *= 2;
16862        // }
16863         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16864         if(Roo.isSafari){ // safari
16865             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16866             v =  (v < 10) ? 10 : v;
16867             v =  (v > 48) ? 48 : v;
16868             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16869             
16870         }
16871         
16872         
16873         v = Math.max(1, v+adjust);
16874         
16875         this.execCmd('FontSize', v  );
16876     },
16877
16878     onEditorEvent : function(e){
16879         this.fireEvent('editorevent', this, e);
16880       //  this.updateToolbar();
16881         this.syncValue();
16882     },
16883
16884     insertTag : function(tg)
16885     {
16886         // could be a bit smarter... -> wrap the current selected tRoo..
16887         
16888         this.execCmd("formatblock",   tg);
16889         
16890     },
16891     
16892     insertText : function(txt)
16893     {
16894         
16895         
16896         range = this.createRange();
16897         range.deleteContents();
16898                //alert(Sender.getAttribute('label'));
16899                
16900         range.insertNode(this.doc.createTextNode(txt));
16901     } ,
16902     
16903     // private
16904     relayBtnCmd : function(btn){
16905         this.relayCmd(btn.cmd);
16906     },
16907
16908     /**
16909      * Executes a Midas editor command on the editor document and performs necessary focus and
16910      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16911      * @param {String} cmd The Midas command
16912      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16913      */
16914     relayCmd : function(cmd, value){
16915         this.win.focus();
16916         this.execCmd(cmd, value);
16917         this.fireEvent('editorevent', this);
16918         //this.updateToolbar();
16919         this.deferFocus();
16920     },
16921
16922     /**
16923      * Executes a Midas editor command directly on the editor document.
16924      * For visual commands, you should use {@link #relayCmd} instead.
16925      * <b>This should only be called after the editor is initialized.</b>
16926      * @param {String} cmd The Midas command
16927      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16928      */
16929     execCmd : function(cmd, value){
16930         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16931         this.syncValue();
16932     },
16933
16934     // private
16935     applyCommand : function(e){
16936         if(e.ctrlKey){
16937             var c = e.getCharCode(), cmd;
16938             if(c > 0){
16939                 c = String.fromCharCode(c);
16940                 switch(c){
16941                     case 'b':
16942                         cmd = 'bold';
16943                     break;
16944                     case 'i':
16945                         cmd = 'italic';
16946                     break;
16947                     case 'u':
16948                         cmd = 'underline';
16949                     break;
16950                 }
16951                 if(cmd){
16952                     this.win.focus();
16953                     this.execCmd(cmd);
16954                     this.deferFocus();
16955                     e.preventDefault();
16956                 }
16957             }
16958         }
16959     },
16960
16961     /**
16962      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16963      * to insert tRoo.
16964      * @param {String} text
16965      */
16966     insertAtCursor : function(text){
16967         if(!this.activated){
16968             return;
16969         }
16970         if(Roo.isIE){
16971             this.win.focus();
16972             var r = this.doc.selection.createRange();
16973             if(r){
16974                 r.collapse(true);
16975                 r.pasteHTML(text);
16976                 this.syncValue();
16977                 this.deferFocus();
16978             }
16979         }else if(Roo.isGecko || Roo.isOpera){
16980             this.win.focus();
16981             this.execCmd('InsertHTML', text);
16982             this.deferFocus();
16983         }else if(Roo.isSafari){
16984             this.execCmd('InsertText', text);
16985             this.deferFocus();
16986         }
16987     },
16988
16989     // private
16990     fixKeys : function(){ // load time branching for fastest keydown performance
16991         if(Roo.isIE){
16992             return function(e){
16993                 var k = e.getKey(), r;
16994                 if(k == e.TAB){
16995                     e.stopEvent();
16996                     r = this.doc.selection.createRange();
16997                     if(r){
16998                         r.collapse(true);
16999                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17000                         this.deferFocus();
17001                     }
17002                 }else if(k == e.ENTER){
17003                     r = this.doc.selection.createRange();
17004                     if(r){
17005                         var target = r.parentElement();
17006                         if(!target || target.tagName.toLowerCase() != 'li'){
17007                             e.stopEvent();
17008                             r.pasteHTML('<br />');
17009                             r.collapse(false);
17010                             r.select();
17011                         }
17012                     }
17013                 }
17014             };
17015         }else if(Roo.isOpera){
17016             return function(e){
17017                 var k = e.getKey();
17018                 if(k == e.TAB){
17019                     e.stopEvent();
17020                     this.win.focus();
17021                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17022                     this.deferFocus();
17023                 }
17024             };
17025         }else if(Roo.isSafari){
17026             return function(e){
17027                 var k = e.getKey();
17028                 if(k == e.TAB){
17029                     e.stopEvent();
17030                     this.execCmd('InsertText','\t');
17031                     this.deferFocus();
17032                 }
17033              };
17034         }
17035     }(),
17036     
17037     getAllAncestors: function()
17038     {
17039         var p = this.getSelectedNode();
17040         var a = [];
17041         if (!p) {
17042             a.push(p); // push blank onto stack..
17043             p = this.getParentElement();
17044         }
17045         
17046         
17047         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17048             a.push(p);
17049             p = p.parentNode;
17050         }
17051         a.push(this.doc.body);
17052         return a;
17053     },
17054     lastSel : false,
17055     lastSelNode : false,
17056     
17057     
17058     getSelection : function() 
17059     {
17060         this.assignDocWin();
17061         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17062     },
17063     
17064     getSelectedNode: function() 
17065     {
17066         // this may only work on Gecko!!!
17067         
17068         // should we cache this!!!!
17069         
17070         
17071         
17072          
17073         var range = this.createRange(this.getSelection());
17074         
17075         if (Roo.isIE) {
17076             var parent = range.parentElement();
17077             while (true) {
17078                 var testRange = range.duplicate();
17079                 testRange.moveToElementText(parent);
17080                 if (testRange.inRange(range)) {
17081                     break;
17082                 }
17083                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17084                     break;
17085                 }
17086                 parent = parent.parentElement;
17087             }
17088             return parent;
17089         }
17090         
17091         
17092         var ar = range.endContainer.childNodes;
17093         if (!ar.length) {
17094             ar = range.commonAncestorContainer.childNodes;
17095             //alert(ar.length);
17096         }
17097         var nodes = [];
17098         var other_nodes = [];
17099         var has_other_nodes = false;
17100         for (var i=0;i<ar.length;i++) {
17101             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17102                 continue;
17103             }
17104             // fullly contained node.
17105             
17106             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17107                 nodes.push(ar[i]);
17108                 continue;
17109             }
17110             
17111             // probably selected..
17112             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17113                 other_nodes.push(ar[i]);
17114                 continue;
17115             }
17116             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17117                 continue;
17118             }
17119             
17120             
17121             has_other_nodes = true;
17122         }
17123         if (!nodes.length && other_nodes.length) {
17124             nodes= other_nodes;
17125         }
17126         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17127             return false;
17128         }
17129         
17130         return nodes[0];
17131     },
17132     createRange: function(sel)
17133     {
17134         // this has strange effects when using with 
17135         // top toolbar - not sure if it's a great idea.
17136         //this.editor.contentWindow.focus();
17137         if (typeof sel != "undefined") {
17138             try {
17139                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17140             } catch(e) {
17141                 return this.doc.createRange();
17142             }
17143         } else {
17144             return this.doc.createRange();
17145         }
17146     },
17147     getParentElement: function()
17148     {
17149         
17150         this.assignDocWin();
17151         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17152         
17153         var range = this.createRange(sel);
17154          
17155         try {
17156             var p = range.commonAncestorContainer;
17157             while (p.nodeType == 3) { // text node
17158                 p = p.parentNode;
17159             }
17160             return p;
17161         } catch (e) {
17162             return null;
17163         }
17164     
17165     },
17166     
17167     
17168     
17169     // BC Hacks - cause I cant work out what i was trying to do..
17170     rangeIntersectsNode : function(range, node)
17171     {
17172         var nodeRange = node.ownerDocument.createRange();
17173         try {
17174             nodeRange.selectNode(node);
17175         }
17176         catch (e) {
17177             nodeRange.selectNodeContents(node);
17178         }
17179
17180         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
17181                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
17182     },
17183     rangeCompareNode : function(range, node) {
17184         var nodeRange = node.ownerDocument.createRange();
17185         try {
17186             nodeRange.selectNode(node);
17187         } catch (e) {
17188             nodeRange.selectNodeContents(node);
17189         }
17190         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
17191         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
17192
17193         if (nodeIsBefore && !nodeIsAfter)
17194             return 0;
17195         if (!nodeIsBefore && nodeIsAfter)
17196             return 1;
17197         if (nodeIsBefore && nodeIsAfter)
17198             return 2;
17199
17200         return 3;
17201     }
17202
17203     
17204     
17205     // hide stuff that is not compatible
17206     /**
17207      * @event blur
17208      * @hide
17209      */
17210     /**
17211      * @event change
17212      * @hide
17213      */
17214     /**
17215      * @event focus
17216      * @hide
17217      */
17218     /**
17219      * @event specialkey
17220      * @hide
17221      */
17222     /**
17223      * @cfg {String} fieldClass @hide
17224      */
17225     /**
17226      * @cfg {String} focusClass @hide
17227      */
17228     /**
17229      * @cfg {String} autoCreate @hide
17230      */
17231     /**
17232      * @cfg {String} inputType @hide
17233      */
17234     /**
17235      * @cfg {String} invalidClass @hide
17236      */
17237     /**
17238      * @cfg {String} invalidText @hide
17239      */
17240     /**
17241      * @cfg {String} msgFx @hide
17242      */
17243     /**
17244      * @cfg {String} validateOnBlur @hide
17245      */
17246 });// <script type="text/javascript">
17247 /*
17248  * Based on
17249  * Ext JS Library 1.1.1
17250  * Copyright(c) 2006-2007, Ext JS, LLC.
17251  *  
17252  
17253  */
17254
17255 /**
17256  * @class Roo.form.HtmlEditorToolbar1
17257  * Basic Toolbar
17258  * 
17259  * Usage:
17260  *
17261  new Roo.form.HtmlEditor({
17262     ....
17263     toolbars : [
17264         new Roo.form.HtmlEditorToolbar1({
17265             disable : { fonts: 1 , format: 1, ..., ... , ...],
17266             btns : [ .... ]
17267         })
17268     }
17269      
17270  * 
17271  * @cfg {Object} disable List of elements to disable..
17272  * @cfg {Array} btns List of additional buttons.
17273  * 
17274  * 
17275  * NEEDS Extra CSS? 
17276  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17277  */
17278  
17279 Roo.form.HtmlEditor.ToolbarStandard = function(config)
17280 {
17281     
17282     Roo.apply(this, config);
17283     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17284     // dont call parent... till later.
17285 }
17286
17287 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
17288     
17289     tb: false,
17290     
17291     rendered: false,
17292     
17293     editor : false,
17294     /**
17295      * @cfg {Object} disable  List of toolbar elements to disable
17296          
17297      */
17298     disable : false,
17299       /**
17300      * @cfg {Array} fontFamilies An array of available font families
17301      */
17302     fontFamilies : [
17303         'Arial',
17304         'Courier New',
17305         'Tahoma',
17306         'Times New Roman',
17307         'Verdana'
17308     ],
17309     
17310     specialChars : [
17311            "&#169;",
17312           "&#174;",     
17313           "&#8482;",    
17314           "&#163;" ,    
17315          // "&#8212;",    
17316           "&#8230;",    
17317           "&#247;" ,    
17318         //  "&#225;" ,     ?? a acute?
17319            "&#8364;"    , //Euro
17320        //   "&#8220;"    ,
17321         //  "&#8221;"    ,
17322         //  "&#8226;"    ,
17323           "&#176;"  //   , // degrees
17324
17325          // "&#233;"     , // e ecute
17326          // "&#250;"     , // u ecute?
17327     ],
17328     inputElements : [ 
17329             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
17330             "input:submit", "input:button", "select", "textarea", "label" ],
17331     formats : [
17332         ["p"] ,  
17333         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
17334         ["pre"],[ "code"], 
17335         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
17336     ],
17337      /**
17338      * @cfg {String} defaultFont default font to use.
17339      */
17340     defaultFont: 'tahoma',
17341    
17342     fontSelect : false,
17343     
17344     
17345     formatCombo : false,
17346     
17347     init : function(editor)
17348     {
17349         this.editor = editor;
17350         
17351         
17352         var fid = editor.frameId;
17353         var etb = this;
17354         function btn(id, toggle, handler){
17355             var xid = fid + '-'+ id ;
17356             return {
17357                 id : xid,
17358                 cmd : id,
17359                 cls : 'x-btn-icon x-edit-'+id,
17360                 enableToggle:toggle !== false,
17361                 scope: editor, // was editor...
17362                 handler:handler||editor.relayBtnCmd,
17363                 clickEvent:'mousedown',
17364                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
17365                 tabIndex:-1
17366             };
17367         }
17368         
17369         
17370         
17371         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
17372         this.tb = tb;
17373          // stop form submits
17374         tb.el.on('click', function(e){
17375             e.preventDefault(); // what does this do?
17376         });
17377
17378         if(!this.disable.font && !Roo.isSafari){
17379             /* why no safari for fonts
17380             editor.fontSelect = tb.el.createChild({
17381                 tag:'select',
17382                 tabIndex: -1,
17383                 cls:'x-font-select',
17384                 html: editor.createFontOptions()
17385             });
17386             editor.fontSelect.on('change', function(){
17387                 var font = editor.fontSelect.dom.value;
17388                 editor.relayCmd('fontname', font);
17389                 editor.deferFocus();
17390             }, editor);
17391             tb.add(
17392                 editor.fontSelect.dom,
17393                 '-'
17394             );
17395             */
17396         };
17397         if(!this.disable.formats){
17398             this.formatCombo = new Roo.form.ComboBox({
17399                 store: new Roo.data.SimpleStore({
17400                     id : 'tag',
17401                     fields: ['tag'],
17402                     data : this.formats // from states.js
17403                 }),
17404                 blockFocus : true,
17405                 //autoCreate : {tag: "div",  size: "20"},
17406                 displayField:'tag',
17407                 typeAhead: false,
17408                 mode: 'local',
17409                 editable : false,
17410                 triggerAction: 'all',
17411                 emptyText:'Add tag',
17412                 selectOnFocus:true,
17413                 width:135,
17414                 listeners : {
17415                     'select': function(c, r, i) {
17416                         editor.insertTag(r.get('tag'));
17417                         editor.focus();
17418                     }
17419                 }
17420
17421             });
17422             tb.addField(this.formatCombo);
17423             
17424         }
17425         
17426         if(!this.disable.format){
17427             tb.add(
17428                 btn('bold'),
17429                 btn('italic'),
17430                 btn('underline')
17431             );
17432         };
17433         if(!this.disable.fontSize){
17434             tb.add(
17435                 '-',
17436                 
17437                 
17438                 btn('increasefontsize', false, editor.adjustFont),
17439                 btn('decreasefontsize', false, editor.adjustFont)
17440             );
17441         };
17442         
17443         
17444         if(this.disable.colors){
17445             tb.add(
17446                 '-', {
17447                     id:editor.frameId +'-forecolor',
17448                     cls:'x-btn-icon x-edit-forecolor',
17449                     clickEvent:'mousedown',
17450                     tooltip: this.buttonTips['forecolor'] || undefined,
17451                     tabIndex:-1,
17452                     menu : new Roo.menu.ColorMenu({
17453                         allowReselect: true,
17454                         focus: Roo.emptyFn,
17455                         value:'000000',
17456                         plain:true,
17457                         selectHandler: function(cp, color){
17458                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
17459                             editor.deferFocus();
17460                         },
17461                         scope: editor,
17462                         clickEvent:'mousedown'
17463                     })
17464                 }, {
17465                     id:editor.frameId +'backcolor',
17466                     cls:'x-btn-icon x-edit-backcolor',
17467                     clickEvent:'mousedown',
17468                     tooltip: this.buttonTips['backcolor'] || undefined,
17469                     tabIndex:-1,
17470                     menu : new Roo.menu.ColorMenu({
17471                         focus: Roo.emptyFn,
17472                         value:'FFFFFF',
17473                         plain:true,
17474                         allowReselect: true,
17475                         selectHandler: function(cp, color){
17476                             if(Roo.isGecko){
17477                                 editor.execCmd('useCSS', false);
17478                                 editor.execCmd('hilitecolor', color);
17479                                 editor.execCmd('useCSS', true);
17480                                 editor.deferFocus();
17481                             }else{
17482                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
17483                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
17484                                 editor.deferFocus();
17485                             }
17486                         },
17487                         scope:editor,
17488                         clickEvent:'mousedown'
17489                     })
17490                 }
17491             );
17492         };
17493         // now add all the items...
17494         
17495
17496         if(!this.disable.alignments){
17497             tb.add(
17498                 '-',
17499                 btn('justifyleft'),
17500                 btn('justifycenter'),
17501                 btn('justifyright')
17502             );
17503         };
17504
17505         //if(!Roo.isSafari){
17506             if(!this.disable.links){
17507                 tb.add(
17508                     '-',
17509                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
17510                 );
17511             };
17512
17513             if(!this.disable.lists){
17514                 tb.add(
17515                     '-',
17516                     btn('insertorderedlist'),
17517                     btn('insertunorderedlist')
17518                 );
17519             }
17520             if(!this.disable.sourceEdit){
17521                 tb.add(
17522                     '-',
17523                     btn('sourceedit', true, function(btn){
17524                         this.toggleSourceEdit(btn.pressed);
17525                     })
17526                 );
17527             }
17528         //}
17529         
17530         var smenu = { };
17531         // special menu.. - needs to be tidied up..
17532         if (!this.disable.special) {
17533             smenu = {
17534                 text: "&#169;",
17535                 cls: 'x-edit-none',
17536                 menu : {
17537                     items : []
17538                    }
17539             };
17540             for (var i =0; i < this.specialChars.length; i++) {
17541                 smenu.menu.items.push({
17542                     
17543                     text: this.specialChars[i],
17544                     handler: function(a,b) {
17545                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
17546                     },
17547                     tabIndex:-1
17548                 });
17549             }
17550             
17551             
17552             tb.add(smenu);
17553             
17554             
17555         }
17556         if (this.btns) {
17557             for(var i =0; i< this.btns.length;i++) {
17558                 var b = this.btns[i];
17559                 b.cls =  'x-edit-none';
17560                 b.scope = editor;
17561                 tb.add(b);
17562             }
17563         
17564         }
17565         
17566         
17567         
17568         // disable everything...
17569         
17570         this.tb.items.each(function(item){
17571            if(item.id != editor.frameId+ '-sourceedit'){
17572                 item.disable();
17573             }
17574         });
17575         this.rendered = true;
17576         
17577         // the all the btns;
17578         editor.on('editorevent', this.updateToolbar, this);
17579         // other toolbars need to implement this..
17580         //editor.on('editmodechange', this.updateToolbar, this);
17581     },
17582     
17583     
17584     
17585     /**
17586      * Protected method that will not generally be called directly. It triggers
17587      * a toolbar update by reading the markup state of the current selection in the editor.
17588      */
17589     updateToolbar: function(){
17590
17591         if(!this.editor.activated){
17592             this.editor.onFirstFocus();
17593             return;
17594         }
17595
17596         var btns = this.tb.items.map, 
17597             doc = this.editor.doc,
17598             frameId = this.editor.frameId;
17599
17600         if(!this.disable.font && !Roo.isSafari){
17601             /*
17602             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
17603             if(name != this.fontSelect.dom.value){
17604                 this.fontSelect.dom.value = name;
17605             }
17606             */
17607         }
17608         if(!this.disable.format){
17609             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
17610             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
17611             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
17612         }
17613         if(!this.disable.alignments){
17614             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
17615             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
17616             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
17617         }
17618         if(!Roo.isSafari && !this.disable.lists){
17619             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
17620             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
17621         }
17622         
17623         var ans = this.editor.getAllAncestors();
17624         if (this.formatCombo) {
17625             
17626             
17627             var store = this.formatCombo.store;
17628             this.formatCombo.setValue("");
17629             for (var i =0; i < ans.length;i++) {
17630                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
17631                     // select it..
17632                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
17633                     break;
17634                 }
17635             }
17636         }
17637         
17638         
17639         
17640         // hides menus... - so this cant be on a menu...
17641         Roo.menu.MenuMgr.hideAll();
17642
17643         //this.editorsyncValue();
17644     },
17645    
17646     
17647     createFontOptions : function(){
17648         var buf = [], fs = this.fontFamilies, ff, lc;
17649         for(var i = 0, len = fs.length; i< len; i++){
17650             ff = fs[i];
17651             lc = ff.toLowerCase();
17652             buf.push(
17653                 '<option value="',lc,'" style="font-family:',ff,';"',
17654                     (this.defaultFont == lc ? ' selected="true">' : '>'),
17655                     ff,
17656                 '</option>'
17657             );
17658         }
17659         return buf.join('');
17660     },
17661     
17662     toggleSourceEdit : function(sourceEditMode){
17663         if(sourceEditMode === undefined){
17664             sourceEditMode = !this.sourceEditMode;
17665         }
17666         this.sourceEditMode = sourceEditMode === true;
17667         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
17668         // just toggle the button?
17669         if(btn.pressed !== this.editor.sourceEditMode){
17670             btn.toggle(this.editor.sourceEditMode);
17671             return;
17672         }
17673         
17674         if(this.sourceEditMode){
17675             this.tb.items.each(function(item){
17676                 if(item.cmd != 'sourceedit'){
17677                     item.disable();
17678                 }
17679             });
17680           
17681         }else{
17682             if(this.initialized){
17683                 this.tb.items.each(function(item){
17684                     item.enable();
17685                 });
17686             }
17687             
17688         }
17689         // tell the editor that it's been pressed..
17690         this.editor.toggleSourceEdit(sourceEditMode);
17691        
17692     },
17693      /**
17694      * Object collection of toolbar tooltips for the buttons in the editor. The key
17695      * is the command id associated with that button and the value is a valid QuickTips object.
17696      * For example:
17697 <pre><code>
17698 {
17699     bold : {
17700         title: 'Bold (Ctrl+B)',
17701         text: 'Make the selected text bold.',
17702         cls: 'x-html-editor-tip'
17703     },
17704     italic : {
17705         title: 'Italic (Ctrl+I)',
17706         text: 'Make the selected text italic.',
17707         cls: 'x-html-editor-tip'
17708     },
17709     ...
17710 </code></pre>
17711     * @type Object
17712      */
17713     buttonTips : {
17714         bold : {
17715             title: 'Bold (Ctrl+B)',
17716             text: 'Make the selected text bold.',
17717             cls: 'x-html-editor-tip'
17718         },
17719         italic : {
17720             title: 'Italic (Ctrl+I)',
17721             text: 'Make the selected text italic.',
17722             cls: 'x-html-editor-tip'
17723         },
17724         underline : {
17725             title: 'Underline (Ctrl+U)',
17726             text: 'Underline the selected text.',
17727             cls: 'x-html-editor-tip'
17728         },
17729         increasefontsize : {
17730             title: 'Grow Text',
17731             text: 'Increase the font size.',
17732             cls: 'x-html-editor-tip'
17733         },
17734         decreasefontsize : {
17735             title: 'Shrink Text',
17736             text: 'Decrease the font size.',
17737             cls: 'x-html-editor-tip'
17738         },
17739         backcolor : {
17740             title: 'Text Highlight Color',
17741             text: 'Change the background color of the selected text.',
17742             cls: 'x-html-editor-tip'
17743         },
17744         forecolor : {
17745             title: 'Font Color',
17746             text: 'Change the color of the selected text.',
17747             cls: 'x-html-editor-tip'
17748         },
17749         justifyleft : {
17750             title: 'Align Text Left',
17751             text: 'Align text to the left.',
17752             cls: 'x-html-editor-tip'
17753         },
17754         justifycenter : {
17755             title: 'Center Text',
17756             text: 'Center text in the editor.',
17757             cls: 'x-html-editor-tip'
17758         },
17759         justifyright : {
17760             title: 'Align Text Right',
17761             text: 'Align text to the right.',
17762             cls: 'x-html-editor-tip'
17763         },
17764         insertunorderedlist : {
17765             title: 'Bullet List',
17766             text: 'Start a bulleted list.',
17767             cls: 'x-html-editor-tip'
17768         },
17769         insertorderedlist : {
17770             title: 'Numbered List',
17771             text: 'Start a numbered list.',
17772             cls: 'x-html-editor-tip'
17773         },
17774         createlink : {
17775             title: 'Hyperlink',
17776             text: 'Make the selected text a hyperlink.',
17777             cls: 'x-html-editor-tip'
17778         },
17779         sourceedit : {
17780             title: 'Source Edit',
17781             text: 'Switch to source editing mode.',
17782             cls: 'x-html-editor-tip'
17783         }
17784     },
17785     // private
17786     onDestroy : function(){
17787         if(this.rendered){
17788             
17789             this.tb.items.each(function(item){
17790                 if(item.menu){
17791                     item.menu.removeAll();
17792                     if(item.menu.el){
17793                         item.menu.el.destroy();
17794                     }
17795                 }
17796                 item.destroy();
17797             });
17798              
17799         }
17800     },
17801     onFirstFocus: function() {
17802         this.tb.items.each(function(item){
17803            item.enable();
17804         });
17805     }
17806 });
17807
17808
17809
17810
17811 // <script type="text/javascript">
17812 /*
17813  * Based on
17814  * Ext JS Library 1.1.1
17815  * Copyright(c) 2006-2007, Ext JS, LLC.
17816  *  
17817  
17818  */
17819
17820  
17821 /**
17822  * @class Roo.form.HtmlEditor.ToolbarContext
17823  * Context Toolbar
17824  * 
17825  * Usage:
17826  *
17827  new Roo.form.HtmlEditor({
17828     ....
17829     toolbars : [
17830         new Roo.form.HtmlEditor.ToolbarStandard(),
17831         new Roo.form.HtmlEditor.ToolbarContext()
17832         })
17833     }
17834      
17835  * 
17836  * @config : {Object} disable List of elements to disable.. (not done yet.)
17837  * 
17838  * 
17839  */
17840
17841 Roo.form.HtmlEditor.ToolbarContext = function(config)
17842 {
17843     
17844     Roo.apply(this, config);
17845     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17846     // dont call parent... till later.
17847 }
17848 Roo.form.HtmlEditor.ToolbarContext.types = {
17849     'IMG' : {
17850         width : {
17851             title: "Width",
17852             width: 40
17853         },
17854         height:  {
17855             title: "Height",
17856             width: 40
17857         },
17858         align: {
17859             title: "Align",
17860             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
17861             width : 80
17862             
17863         },
17864         border: {
17865             title: "Border",
17866             width: 40
17867         },
17868         alt: {
17869             title: "Alt",
17870             width: 120
17871         },
17872         src : {
17873             title: "Src",
17874             width: 220
17875         }
17876         
17877     },
17878     'A' : {
17879         name : {
17880             title: "Name",
17881             width: 50
17882         },
17883         href:  {
17884             title: "Href",
17885             width: 220
17886         } // border?
17887         
17888     },
17889     'TABLE' : {
17890         rows : {
17891             title: "Rows",
17892             width: 20
17893         },
17894         cols : {
17895             title: "Cols",
17896             width: 20
17897         },
17898         width : {
17899             title: "Width",
17900             width: 40
17901         },
17902         height : {
17903             title: "Height",
17904             width: 40
17905         },
17906         border : {
17907             title: "Border",
17908             width: 20
17909         }
17910     },
17911     'TD' : {
17912         width : {
17913             title: "Width",
17914             width: 40
17915         },
17916         height : {
17917             title: "Height",
17918             width: 40
17919         },   
17920         align: {
17921             title: "Align",
17922             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
17923             width: 40
17924         },
17925         valign: {
17926             title: "Valign",
17927             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
17928             width: 40
17929         },
17930         colspan: {
17931             title: "Colspan",
17932             width: 20
17933             
17934         }
17935     },
17936     'INPUT' : {
17937         name : {
17938             title: "name",
17939             width: 120
17940         },
17941         value : {
17942             title: "Value",
17943             width: 120
17944         },
17945         width : {
17946             title: "Width",
17947             width: 40
17948         }
17949     },
17950     'LABEL' : {
17951         'for' : {
17952             title: "For",
17953             width: 120
17954         }
17955     },
17956     'TEXTAREA' : {
17957           name : {
17958             title: "name",
17959             width: 120
17960         },
17961         rows : {
17962             title: "Rows",
17963             width: 20
17964         },
17965         cols : {
17966             title: "Cols",
17967             width: 20
17968         }
17969     },
17970     'SELECT' : {
17971         name : {
17972             title: "name",
17973             width: 120
17974         },
17975         selectoptions : {
17976             title: "Options",
17977             width: 200
17978         }
17979     },
17980     'BODY' : {
17981         title : {
17982             title: "title",
17983             width: 120,
17984             disabled : true
17985         }
17986     }
17987 };
17988
17989
17990
17991 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
17992     
17993     tb: false,
17994     
17995     rendered: false,
17996     
17997     editor : false,
17998     /**
17999      * @cfg {Object} disable  List of toolbar elements to disable
18000          
18001      */
18002     disable : false,
18003     
18004     
18005     
18006     toolbars : false,
18007     
18008     init : function(editor)
18009     {
18010         this.editor = editor;
18011         
18012         
18013         var fid = editor.frameId;
18014         var etb = this;
18015         function btn(id, toggle, handler){
18016             var xid = fid + '-'+ id ;
18017             return {
18018                 id : xid,
18019                 cmd : id,
18020                 cls : 'x-btn-icon x-edit-'+id,
18021                 enableToggle:toggle !== false,
18022                 scope: editor, // was editor...
18023                 handler:handler||editor.relayBtnCmd,
18024                 clickEvent:'mousedown',
18025                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
18026                 tabIndex:-1
18027             };
18028         }
18029         // create a new element.
18030         var wdiv = editor.wrap.createChild({
18031                 tag: 'div'
18032             }, editor.wrap.dom.firstChild.nextSibling, true);
18033         
18034         // can we do this more than once??
18035         
18036          // stop form submits
18037       
18038  
18039         // disable everything...
18040         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
18041         this.toolbars = {};
18042            
18043         for (var i in  ty) {
18044             this.toolbars[i] = this.buildToolbar(ty[i],i);
18045         }
18046         this.tb = this.toolbars.BODY;
18047         this.tb.el.show();
18048         
18049          
18050         this.rendered = true;
18051         
18052         // the all the btns;
18053         editor.on('editorevent', this.updateToolbar, this);
18054         // other toolbars need to implement this..
18055         //editor.on('editmodechange', this.updateToolbar, this);
18056     },
18057     
18058     
18059     
18060     /**
18061      * Protected method that will not generally be called directly. It triggers
18062      * a toolbar update by reading the markup state of the current selection in the editor.
18063      */
18064     updateToolbar: function(){
18065
18066         if(!this.editor.activated){
18067             this.editor.onFirstFocus();
18068             return;
18069         }
18070
18071         
18072         var ans = this.editor.getAllAncestors();
18073         
18074         // pick
18075         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
18076         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
18077         sel = sel ? sel : this.editor.doc.body;
18078         sel = sel.tagName.length ? sel : this.editor.doc.body;
18079         var tn = sel.tagName.toUpperCase();
18080         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
18081         tn = sel.tagName.toUpperCase();
18082         if (this.tb.name  == tn) {
18083             return; // no change
18084         }
18085         this.tb.el.hide();
18086         ///console.log("show: " + tn);
18087         this.tb =  this.toolbars[tn];
18088         this.tb.el.show();
18089         this.tb.fields.each(function(e) {
18090             e.setValue(sel.getAttribute(e.name));
18091         });
18092         this.tb.selectedNode = sel;
18093         
18094         
18095         Roo.menu.MenuMgr.hideAll();
18096
18097         //this.editorsyncValue();
18098     },
18099    
18100        
18101     // private
18102     onDestroy : function(){
18103         if(this.rendered){
18104             
18105             this.tb.items.each(function(item){
18106                 if(item.menu){
18107                     item.menu.removeAll();
18108                     if(item.menu.el){
18109                         item.menu.el.destroy();
18110                     }
18111                 }
18112                 item.destroy();
18113             });
18114              
18115         }
18116     },
18117     onFirstFocus: function() {
18118         // need to do this for all the toolbars..
18119         this.tb.items.each(function(item){
18120            item.enable();
18121         });
18122     },
18123     buildToolbar: function(tlist, nm)
18124     {
18125         var editor = this.editor;
18126          // create a new element.
18127         var wdiv = editor.wrap.createChild({
18128                 tag: 'div'
18129             }, editor.wrap.dom.firstChild.nextSibling, true);
18130         
18131        
18132         var tb = new Roo.Toolbar(wdiv);
18133         tb.add(nm+ ":&nbsp;");
18134         for (var i in tlist) {
18135             var item = tlist[i];
18136             tb.add(item.title + ":&nbsp;");
18137             if (item.opts) {
18138                 // fixme
18139                 
18140               
18141                 tb.addField( new Roo.form.ComboBox({
18142                     store: new Roo.data.SimpleStore({
18143                         id : 'val',
18144                         fields: ['val'],
18145                         data : item.opts // from states.js
18146                     }),
18147                     name : i,
18148                     displayField:'val',
18149                     typeAhead: false,
18150                     mode: 'local',
18151                     editable : false,
18152                     triggerAction: 'all',
18153                     emptyText:'Select',
18154                     selectOnFocus:true,
18155                     width: item.width ? item.width  : 130,
18156                     listeners : {
18157                         'select': function(c, r, i) {
18158                             tb.selectedNode.setAttribute(c.name, r.get('val'));
18159                         }
18160                     }
18161
18162                 }));
18163                 continue;
18164                     
18165                 
18166                 
18167                 
18168                 
18169                 tb.addField( new Roo.form.TextField({
18170                     name: i,
18171                     width: 100,
18172                     //allowBlank:false,
18173                     value: ''
18174                 }));
18175                 continue;
18176             }
18177             tb.addField( new Roo.form.TextField({
18178                 name: i,
18179                 width: item.width,
18180                 //allowBlank:true,
18181                 value: '',
18182                 listeners: {
18183                     'change' : function(f, nv, ov) {
18184                         tb.selectedNode.setAttribute(f.name, nv);
18185                     }
18186                 }
18187             }));
18188              
18189         }
18190         tb.el.on('click', function(e){
18191             e.preventDefault(); // what does this do?
18192         });
18193         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
18194         tb.el.hide();
18195         tb.name = nm;
18196         // dont need to disable them... as they will get hidden
18197         return tb;
18198          
18199         
18200     }
18201     
18202     
18203     
18204     
18205 });
18206
18207
18208
18209
18210
18211 /*
18212  * Based on:
18213  * Ext JS Library 1.1.1
18214  * Copyright(c) 2006-2007, Ext JS, LLC.
18215  *
18216  * Originally Released Under LGPL - original licence link has changed is not relivant.
18217  *
18218  * Fork - LGPL
18219  * <script type="text/javascript">
18220  */
18221  
18222 /**
18223  * @class Roo.form.BasicForm
18224  * @extends Roo.util.Observable
18225  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
18226  * @constructor
18227  * @param {String/HTMLElement/Roo.Element} el The form element or its id
18228  * @param {Object} config Configuration options
18229  */
18230 Roo.form.BasicForm = function(el, config){
18231     Roo.apply(this, config);
18232     /*
18233      * The Roo.form.Field items in this form.
18234      * @type MixedCollection
18235      */
18236     this.items = new Roo.util.MixedCollection(false, function(o){
18237         return o.id || (o.id = Roo.id());
18238     });
18239     this.addEvents({
18240         /**
18241          * @event beforeaction
18242          * Fires before any action is performed. Return false to cancel the action.
18243          * @param {Form} this
18244          * @param {Action} action The action to be performed
18245          */
18246         beforeaction: true,
18247         /**
18248          * @event actionfailed
18249          * Fires when an action fails.
18250          * @param {Form} this
18251          * @param {Action} action The action that failed
18252          */
18253         actionfailed : true,
18254         /**
18255          * @event actioncomplete
18256          * Fires when an action is completed.
18257          * @param {Form} this
18258          * @param {Action} action The action that completed
18259          */
18260         actioncomplete : true
18261     });
18262     if(el){
18263         this.initEl(el);
18264     }
18265     Roo.form.BasicForm.superclass.constructor.call(this);
18266 };
18267
18268 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
18269     /**
18270      * @cfg {String} method
18271      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
18272      */
18273     /**
18274      * @cfg {DataReader} reader
18275      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
18276      * This is optional as there is built-in support for processing JSON.
18277      */
18278     /**
18279      * @cfg {DataReader} errorReader
18280      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
18281      * This is completely optional as there is built-in support for processing JSON.
18282      */
18283     /**
18284      * @cfg {String} url
18285      * The URL to use for form actions if one isn't supplied in the action options.
18286      */
18287     /**
18288      * @cfg {Boolean} fileUpload
18289      * Set to true if this form is a file upload.
18290      */
18291     /**
18292      * @cfg {Object} baseParams
18293      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
18294      */
18295     /**
18296      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
18297      */
18298     timeout: 30,
18299
18300     // private
18301     activeAction : null,
18302
18303     /**
18304      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
18305      * or setValues() data instead of when the form was first created.
18306      */
18307     trackResetOnLoad : false,
18308
18309     /**
18310      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
18311      * element by passing it or its id or mask the form itself by passing in true.
18312      * @type Mixed
18313      */
18314     waitMsgTarget : undefined,
18315
18316     // private
18317     initEl : function(el){
18318         this.el = Roo.get(el);
18319         this.id = this.el.id || Roo.id();
18320         this.el.on('submit', this.onSubmit, this);
18321         this.el.addClass('x-form');
18322     },
18323
18324     // private
18325     onSubmit : function(e){
18326         e.stopEvent();
18327     },
18328
18329     /**
18330      * Returns true if client-side validation on the form is successful.
18331      * @return Boolean
18332      */
18333     isValid : function(){
18334         var valid = true;
18335         this.items.each(function(f){
18336            if(!f.validate()){
18337                valid = false;
18338            }
18339         });
18340         return valid;
18341     },
18342
18343     /**
18344      * Returns true if any fields in this form have changed since their original load.
18345      * @return Boolean
18346      */
18347     isDirty : function(){
18348         var dirty = false;
18349         this.items.each(function(f){
18350            if(f.isDirty()){
18351                dirty = true;
18352                return false;
18353            }
18354         });
18355         return dirty;
18356     },
18357
18358     /**
18359      * Performs a predefined action (submit or load) or custom actions you define on this form.
18360      * @param {String} actionName The name of the action type
18361      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
18362      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
18363      * accept other config options):
18364      * <pre>
18365 Property          Type             Description
18366 ----------------  ---------------  ----------------------------------------------------------------------------------
18367 url               String           The url for the action (defaults to the form's url)
18368 method            String           The form method to use (defaults to the form's method, or POST if not defined)
18369 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
18370 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
18371                                    validate the form on the client (defaults to false)
18372      * </pre>
18373      * @return {BasicForm} this
18374      */
18375     doAction : function(action, options){
18376         if(typeof action == 'string'){
18377             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
18378         }
18379         if(this.fireEvent('beforeaction', this, action) !== false){
18380             this.beforeAction(action);
18381             action.run.defer(100, action);
18382         }
18383         return this;
18384     },
18385
18386     /**
18387      * Shortcut to do a submit action.
18388      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
18389      * @return {BasicForm} this
18390      */
18391     submit : function(options){
18392         this.doAction('submit', options);
18393         return this;
18394     },
18395
18396     /**
18397      * Shortcut to do a load action.
18398      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
18399      * @return {BasicForm} this
18400      */
18401     load : function(options){
18402         this.doAction('load', options);
18403         return this;
18404     },
18405
18406     /**
18407      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
18408      * @param {Record} record The record to edit
18409      * @return {BasicForm} this
18410      */
18411     updateRecord : function(record){
18412         record.beginEdit();
18413         var fs = record.fields;
18414         fs.each(function(f){
18415             var field = this.findField(f.name);
18416             if(field){
18417                 record.set(f.name, field.getValue());
18418             }
18419         }, this);
18420         record.endEdit();
18421         return this;
18422     },
18423
18424     /**
18425      * Loads an Roo.data.Record into this form.
18426      * @param {Record} record The record to load
18427      * @return {BasicForm} this
18428      */
18429     loadRecord : function(record){
18430         this.setValues(record.data);
18431         return this;
18432     },
18433
18434     // private
18435     beforeAction : function(action){
18436         var o = action.options;
18437         if(o.waitMsg){
18438             if(this.waitMsgTarget === true){
18439                 this.el.mask(o.waitMsg, 'x-mask-loading');
18440             }else if(this.waitMsgTarget){
18441                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
18442                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
18443             }else{
18444                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
18445             }
18446         }
18447     },
18448
18449     // private
18450     afterAction : function(action, success){
18451         this.activeAction = null;
18452         var o = action.options;
18453         if(o.waitMsg){
18454             if(this.waitMsgTarget === true){
18455                 this.el.unmask();
18456             }else if(this.waitMsgTarget){
18457                 this.waitMsgTarget.unmask();
18458             }else{
18459                 Roo.MessageBox.updateProgress(1);
18460                 Roo.MessageBox.hide();
18461             }
18462         }
18463         if(success){
18464             if(o.reset){
18465                 this.reset();
18466             }
18467             Roo.callback(o.success, o.scope, [this, action]);
18468             this.fireEvent('actioncomplete', this, action);
18469         }else{
18470             Roo.callback(o.failure, o.scope, [this, action]);
18471             this.fireEvent('actionfailed', this, action);
18472         }
18473     },
18474
18475     /**
18476      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
18477      * @param {String} id The value to search for
18478      * @return Field
18479      */
18480     findField : function(id){
18481         var field = this.items.get(id);
18482         if(!field){
18483             this.items.each(function(f){
18484                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
18485                     field = f;
18486                     return false;
18487                 }
18488             });
18489         }
18490         return field || null;
18491     },
18492
18493
18494     /**
18495      * Mark fields in this form invalid in bulk.
18496      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
18497      * @return {BasicForm} this
18498      */
18499     markInvalid : function(errors){
18500         if(errors instanceof Array){
18501             for(var i = 0, len = errors.length; i < len; i++){
18502                 var fieldError = errors[i];
18503                 var f = this.findField(fieldError.id);
18504                 if(f){
18505                     f.markInvalid(fieldError.msg);
18506                 }
18507             }
18508         }else{
18509             var field, id;
18510             for(id in errors){
18511                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
18512                     field.markInvalid(errors[id]);
18513                 }
18514             }
18515         }
18516         return this;
18517     },
18518
18519     /**
18520      * Set values for fields in this form in bulk.
18521      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
18522      * @return {BasicForm} this
18523      */
18524     setValues : function(values){
18525         if(values instanceof Array){ // array of objects
18526             for(var i = 0, len = values.length; i < len; i++){
18527                 var v = values[i];
18528                 var f = this.findField(v.id);
18529                 if(f){
18530                     f.setValue(v.value);
18531                     if(this.trackResetOnLoad){
18532                         f.originalValue = f.getValue();
18533                     }
18534                 }
18535             }
18536         }else{ // object hash
18537             var field, id;
18538             for(id in values){
18539                 if(typeof values[id] != 'function' && (field = this.findField(id))){
18540                     
18541                     if (field.setFromData && 
18542                         field.valueField && 
18543                         field.displayField &&
18544                         // combos' with local stores can 
18545                         // be queried via setValue()
18546                         // to set their value..
18547                         (field.store && !field.store.isLocal)
18548                         ) {
18549                         // it's a combo
18550                         var sd = { };
18551                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
18552                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
18553                         field.setFromData(sd);
18554                         
18555                     } else {
18556                         field.setValue(values[id]);
18557                     }
18558                     
18559                     
18560                     if(this.trackResetOnLoad){
18561                         field.originalValue = field.getValue();
18562                     }
18563                 }
18564             }
18565         }
18566         return this;
18567     },
18568
18569     /**
18570      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
18571      * they are returned as an array.
18572      * @param {Boolean} asString
18573      * @return {Object}
18574      */
18575     getValues : function(asString){
18576         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
18577         if(asString === true){
18578             return fs;
18579         }
18580         return Roo.urlDecode(fs);
18581     },
18582
18583     /**
18584      * Clears all invalid messages in this form.
18585      * @return {BasicForm} this
18586      */
18587     clearInvalid : function(){
18588         this.items.each(function(f){
18589            f.clearInvalid();
18590         });
18591         return this;
18592     },
18593
18594     /**
18595      * Resets this form.
18596      * @return {BasicForm} this
18597      */
18598     reset : function(){
18599         this.items.each(function(f){
18600             f.reset();
18601         });
18602         return this;
18603     },
18604
18605     /**
18606      * Add Roo.form components to this form.
18607      * @param {Field} field1
18608      * @param {Field} field2 (optional)
18609      * @param {Field} etc (optional)
18610      * @return {BasicForm} this
18611      */
18612     add : function(){
18613         this.items.addAll(Array.prototype.slice.call(arguments, 0));
18614         return this;
18615     },
18616
18617
18618     /**
18619      * Removes a field from the items collection (does NOT remove its markup).
18620      * @param {Field} field
18621      * @return {BasicForm} this
18622      */
18623     remove : function(field){
18624         this.items.remove(field);
18625         return this;
18626     },
18627
18628     /**
18629      * Looks at the fields in this form, checks them for an id attribute,
18630      * and calls applyTo on the existing dom element with that id.
18631      * @return {BasicForm} this
18632      */
18633     render : function(){
18634         this.items.each(function(f){
18635             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
18636                 f.applyTo(f.id);
18637             }
18638         });
18639         return this;
18640     },
18641
18642     /**
18643      * Calls {@link Ext#apply} for all fields in this form with the passed object.
18644      * @param {Object} values
18645      * @return {BasicForm} this
18646      */
18647     applyToFields : function(o){
18648         this.items.each(function(f){
18649            Roo.apply(f, o);
18650         });
18651         return this;
18652     },
18653
18654     /**
18655      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
18656      * @param {Object} values
18657      * @return {BasicForm} this
18658      */
18659     applyIfToFields : function(o){
18660         this.items.each(function(f){
18661            Roo.applyIf(f, o);
18662         });
18663         return this;
18664     }
18665 });
18666
18667 // back compat
18668 Roo.BasicForm = Roo.form.BasicForm;/*
18669  * Based on:
18670  * Ext JS Library 1.1.1
18671  * Copyright(c) 2006-2007, Ext JS, LLC.
18672  *
18673  * Originally Released Under LGPL - original licence link has changed is not relivant.
18674  *
18675  * Fork - LGPL
18676  * <script type="text/javascript">
18677  */
18678
18679 /**
18680  * @class Roo.form.Form
18681  * @extends Roo.form.BasicForm
18682  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
18683  * @constructor
18684  * @param {Object} config Configuration options
18685  */
18686 Roo.form.Form = function(config){
18687     var xitems =  [];
18688     if (config.items) {
18689         xitems = config.items;
18690         delete config.items;
18691     }
18692     
18693     
18694     Roo.form.Form.superclass.constructor.call(this, null, config);
18695     this.url = this.url || this.action;
18696     if(!this.root){
18697         this.root = new Roo.form.Layout(Roo.applyIf({
18698             id: Roo.id()
18699         }, config));
18700     }
18701     this.active = this.root;
18702     /**
18703      * Array of all the buttons that have been added to this form via {@link addButton}
18704      * @type Array
18705      */
18706     this.buttons = [];
18707     this.allItems = [];
18708     this.addEvents({
18709         /**
18710          * @event clientvalidation
18711          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
18712          * @param {Form} this
18713          * @param {Boolean} valid true if the form has passed client-side validation
18714          */
18715         clientvalidation: true,
18716         /**
18717          * @event rendered
18718          * Fires when the form is rendered
18719          * @param {Roo.form.Form} form
18720          */
18721         rendered : true
18722     });
18723     
18724     Roo.each(xitems, this.addxtype, this);
18725     
18726     
18727     
18728 };
18729
18730 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
18731     /**
18732      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
18733      */
18734     /**
18735      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
18736      */
18737     /**
18738      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
18739      */
18740     buttonAlign:'center',
18741
18742     /**
18743      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
18744      */
18745     minButtonWidth:75,
18746
18747     /**
18748      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
18749      * This property cascades to child containers if not set.
18750      */
18751     labelAlign:'left',
18752
18753     /**
18754      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
18755      * fires a looping event with that state. This is required to bind buttons to the valid
18756      * state using the config value formBind:true on the button.
18757      */
18758     monitorValid : false,
18759
18760     /**
18761      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
18762      */
18763     monitorPoll : 200,
18764
18765     /**
18766      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
18767      * fields are added and the column is closed. If no fields are passed the column remains open
18768      * until end() is called.
18769      * @param {Object} config The config to pass to the column
18770      * @param {Field} field1 (optional)
18771      * @param {Field} field2 (optional)
18772      * @param {Field} etc (optional)
18773      * @return Column The column container object
18774      */
18775     column : function(c){
18776         var col = new Roo.form.Column(c);
18777         this.start(col);
18778         if(arguments.length > 1){ // duplicate code required because of Opera
18779             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
18780             this.end();
18781         }
18782         return col;
18783     },
18784
18785     /**
18786      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
18787      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
18788      * until end() is called.
18789      * @param {Object} config The config to pass to the fieldset
18790      * @param {Field} field1 (optional)
18791      * @param {Field} field2 (optional)
18792      * @param {Field} etc (optional)
18793      * @return FieldSet The fieldset container object
18794      */
18795     fieldset : function(c){
18796         var fs = new Roo.form.FieldSet(c);
18797         this.start(fs);
18798         if(arguments.length > 1){ // duplicate code required because of Opera
18799             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
18800             this.end();
18801         }
18802         return fs;
18803     },
18804
18805     /**
18806      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
18807      * fields are added and the container is closed. If no fields are passed the container remains open
18808      * until end() is called.
18809      * @param {Object} config The config to pass to the Layout
18810      * @param {Field} field1 (optional)
18811      * @param {Field} field2 (optional)
18812      * @param {Field} etc (optional)
18813      * @return Layout The container object
18814      */
18815     container : function(c){
18816         var l = new Roo.form.Layout(c);
18817         this.start(l);
18818         if(arguments.length > 1){ // duplicate code required because of Opera
18819             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
18820             this.end();
18821         }
18822         return l;
18823     },
18824
18825     /**
18826      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
18827      * @param {Object} container A Roo.form.Layout or subclass of Layout
18828      * @return {Form} this
18829      */
18830     start : function(c){
18831         // cascade label info
18832         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
18833         this.active.stack.push(c);
18834         c.ownerCt = this.active;
18835         this.active = c;
18836         return this;
18837     },
18838
18839     /**
18840      * Closes the current open container
18841      * @return {Form} this
18842      */
18843     end : function(){
18844         if(this.active == this.root){
18845             return this;
18846         }
18847         this.active = this.active.ownerCt;
18848         return this;
18849     },
18850
18851     /**
18852      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
18853      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
18854      * as the label of the field.
18855      * @param {Field} field1
18856      * @param {Field} field2 (optional)
18857      * @param {Field} etc. (optional)
18858      * @return {Form} this
18859      */
18860     add : function(){
18861         this.active.stack.push.apply(this.active.stack, arguments);
18862         this.allItems.push.apply(this.allItems,arguments);
18863         var r = [];
18864         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
18865             if(a[i].isFormField){
18866                 r.push(a[i]);
18867             }
18868         }
18869         if(r.length > 0){
18870             Roo.form.Form.superclass.add.apply(this, r);
18871         }
18872         return this;
18873     },
18874      /**
18875      * Find any element that has been added to a form, using it's ID or name
18876      * This can include framesets, columns etc. along with regular fields..
18877      * @param {String} id - id or name to find.
18878      
18879      * @return {Element} e - or false if nothing found.
18880      */
18881     findbyId : function(id)
18882     {
18883         var ret = false;
18884         if (!id) {
18885             return ret;
18886         }
18887         Ext.each(this.allItems, function(f){
18888             if (f.id == id || f.name == id ){
18889                 ret = f;
18890                 return false;
18891             }
18892         });
18893         return ret;
18894     },
18895
18896     
18897     
18898     /**
18899      * Render this form into the passed container. This should only be called once!
18900      * @param {String/HTMLElement/Element} container The element this component should be rendered into
18901      * @return {Form} this
18902      */
18903     render : function(ct){
18904         ct = Roo.get(ct);
18905         var o = this.autoCreate || {
18906             tag: 'form',
18907             method : this.method || 'POST',
18908             id : this.id || Roo.id()
18909         };
18910         this.initEl(ct.createChild(o));
18911
18912         this.root.render(this.el);
18913
18914         this.items.each(function(f){
18915             f.render('x-form-el-'+f.id);
18916         });
18917
18918         if(this.buttons.length > 0){
18919             // tables are required to maintain order and for correct IE layout
18920             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
18921                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
18922                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
18923             }}, null, true);
18924             var tr = tb.getElementsByTagName('tr')[0];
18925             for(var i = 0, len = this.buttons.length; i < len; i++) {
18926                 var b = this.buttons[i];
18927                 var td = document.createElement('td');
18928                 td.className = 'x-form-btn-td';
18929                 b.render(tr.appendChild(td));
18930             }
18931         }
18932         if(this.monitorValid){ // initialize after render
18933             this.startMonitoring();
18934         }
18935         this.fireEvent('rendered', this);
18936         return this;
18937     },
18938
18939     /**
18940      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
18941      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
18942      * object or a valid Roo.DomHelper element config
18943      * @param {Function} handler The function called when the button is clicked
18944      * @param {Object} scope (optional) The scope of the handler function
18945      * @return {Roo.Button}
18946      */
18947     addButton : function(config, handler, scope){
18948         var bc = {
18949             handler: handler,
18950             scope: scope,
18951             minWidth: this.minButtonWidth,
18952             hideParent:true
18953         };
18954         if(typeof config == "string"){
18955             bc.text = config;
18956         }else{
18957             Roo.apply(bc, config);
18958         }
18959         var btn = new Roo.Button(null, bc);
18960         this.buttons.push(btn);
18961         return btn;
18962     },
18963
18964      /**
18965      * Adds a series of form elements (using the xtype property as the factory method.
18966      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
18967      * @param {Object} config 
18968      */
18969     
18970     addxtype : function()
18971     {
18972         var ar = Array.prototype.slice.call(arguments, 0);
18973         var ret = false;
18974         for(var i = 0; i < ar.length; i++) {
18975             if (!ar[i]) {
18976                 continue; // skip -- if this happends something invalid got sent, we 
18977                 // should ignore it, as basically that interface element will not show up
18978                 // and that should be pretty obvious!!
18979             }
18980             
18981             if (Roo.form[ar[i].xtype]) {
18982                 ar[i].form = this;
18983                 var fe = Roo.factory(ar[i], Roo.form);
18984                 if (!ret) {
18985                     ret = fe;
18986                 }
18987                 fe.form = this;
18988                 if (fe.store) {
18989                     fe.store.form = this;
18990                 }
18991                 if (fe.isLayout) {  
18992                          
18993                     this.start(fe);
18994                     this.allItems.push(fe);
18995                     if (fe.items && fe.addxtype) {
18996                         fe.addxtype.apply(fe, fe.items);
18997                         delete fe.items;
18998                     }
18999                      this.end();
19000                     continue;
19001                 }
19002                 
19003                 
19004                  
19005                 this.add(fe);
19006               //  console.log('adding ' + ar[i].xtype);
19007             }
19008             if (ar[i].xtype == 'Button') {  
19009                 //console.log('adding button');
19010                 //console.log(ar[i]);
19011                 this.addButton(ar[i]);
19012                 this.allItems.push(fe);
19013                 continue;
19014             }
19015             
19016             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
19017                 alert('end is not supported on xtype any more, use items');
19018             //    this.end();
19019             //    //console.log('adding end');
19020             }
19021             
19022         }
19023         return ret;
19024     },
19025     
19026     /**
19027      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
19028      * option "monitorValid"
19029      */
19030     startMonitoring : function(){
19031         if(!this.bound){
19032             this.bound = true;
19033             Roo.TaskMgr.start({
19034                 run : this.bindHandler,
19035                 interval : this.monitorPoll || 200,
19036                 scope: this
19037             });
19038         }
19039     },
19040
19041     /**
19042      * Stops monitoring of the valid state of this form
19043      */
19044     stopMonitoring : function(){
19045         this.bound = false;
19046     },
19047
19048     // private
19049     bindHandler : function(){
19050         if(!this.bound){
19051             return false; // stops binding
19052         }
19053         var valid = true;
19054         this.items.each(function(f){
19055             if(!f.isValid(true)){
19056                 valid = false;
19057                 return false;
19058             }
19059         });
19060         for(var i = 0, len = this.buttons.length; i < len; i++){
19061             var btn = this.buttons[i];
19062             if(btn.formBind === true && btn.disabled === valid){
19063                 btn.setDisabled(!valid);
19064             }
19065         }
19066         this.fireEvent('clientvalidation', this, valid);
19067     }
19068     
19069     
19070     
19071     
19072     
19073     
19074     
19075     
19076 });
19077
19078
19079 // back compat
19080 Roo.Form = Roo.form.Form;
19081 /*
19082  * Based on:
19083  * Ext JS Library 1.1.1
19084  * Copyright(c) 2006-2007, Ext JS, LLC.
19085  *
19086  * Originally Released Under LGPL - original licence link has changed is not relivant.
19087  *
19088  * Fork - LGPL
19089  * <script type="text/javascript">
19090  */
19091  
19092  /**
19093  * @class Roo.form.Action
19094  * Internal Class used to handle form actions
19095  * @constructor
19096  * @param {Roo.form.BasicForm} el The form element or its id
19097  * @param {Object} config Configuration options
19098  */
19099  
19100  
19101 // define the action interface
19102 Roo.form.Action = function(form, options){
19103     this.form = form;
19104     this.options = options || {};
19105 };
19106 /**
19107  * Client Validation Failed
19108  * @const 
19109  */
19110 Roo.form.Action.CLIENT_INVALID = 'client';
19111 /**
19112  * Server Validation Failed
19113  * @const 
19114  */
19115  Roo.form.Action.SERVER_INVALID = 'server';
19116  /**
19117  * Connect to Server Failed
19118  * @const 
19119  */
19120 Roo.form.Action.CONNECT_FAILURE = 'connect';
19121 /**
19122  * Reading Data from Server Failed
19123  * @const 
19124  */
19125 Roo.form.Action.LOAD_FAILURE = 'load';
19126
19127 Roo.form.Action.prototype = {
19128     type : 'default',
19129     failureType : undefined,
19130     response : undefined,
19131     result : undefined,
19132
19133     // interface method
19134     run : function(options){
19135
19136     },
19137
19138     // interface method
19139     success : function(response){
19140
19141     },
19142
19143     // interface method
19144     handleResponse : function(response){
19145
19146     },
19147
19148     // default connection failure
19149     failure : function(response){
19150         this.response = response;
19151         this.failureType = Roo.form.Action.CONNECT_FAILURE;
19152         this.form.afterAction(this, false);
19153     },
19154
19155     processResponse : function(response){
19156         this.response = response;
19157         if(!response.responseText){
19158             return true;
19159         }
19160         this.result = this.handleResponse(response);
19161         return this.result;
19162     },
19163
19164     // utility functions used internally
19165     getUrl : function(appendParams){
19166         var url = this.options.url || this.form.url || this.form.el.dom.action;
19167         if(appendParams){
19168             var p = this.getParams();
19169             if(p){
19170                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
19171             }
19172         }
19173         return url;
19174     },
19175
19176     getMethod : function(){
19177         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
19178     },
19179
19180     getParams : function(){
19181         var bp = this.form.baseParams;
19182         var p = this.options.params;
19183         if(p){
19184             if(typeof p == "object"){
19185                 p = Roo.urlEncode(Roo.applyIf(p, bp));
19186             }else if(typeof p == 'string' && bp){
19187                 p += '&' + Roo.urlEncode(bp);
19188             }
19189         }else if(bp){
19190             p = Roo.urlEncode(bp);
19191         }
19192         return p;
19193     },
19194
19195     createCallback : function(){
19196         return {
19197             success: this.success,
19198             failure: this.failure,
19199             scope: this,
19200             timeout: (this.form.timeout*1000),
19201             upload: this.form.fileUpload ? this.success : undefined
19202         };
19203     }
19204 };
19205
19206 Roo.form.Action.Submit = function(form, options){
19207     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
19208 };
19209
19210 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
19211     type : 'submit',
19212
19213     run : function(){
19214         var o = this.options;
19215         var method = this.getMethod();
19216         var isPost = method == 'POST';
19217         if(o.clientValidation === false || this.form.isValid()){
19218             Roo.Ajax.request(Roo.apply(this.createCallback(), {
19219                 form:this.form.el.dom,
19220                 url:this.getUrl(!isPost),
19221                 method: method,
19222                 params:isPost ? this.getParams() : null,
19223                 isUpload: this.form.fileUpload
19224             }));
19225
19226         }else if (o.clientValidation !== false){ // client validation failed
19227             this.failureType = Roo.form.Action.CLIENT_INVALID;
19228             this.form.afterAction(this, false);
19229         }
19230     },
19231
19232     success : function(response){
19233         var result = this.processResponse(response);
19234         if(result === true || result.success){
19235             this.form.afterAction(this, true);
19236             return;
19237         }
19238         if(result.errors){
19239             this.form.markInvalid(result.errors);
19240             this.failureType = Roo.form.Action.SERVER_INVALID;
19241         }
19242         this.form.afterAction(this, false);
19243     },
19244
19245     handleResponse : function(response){
19246         if(this.form.errorReader){
19247             var rs = this.form.errorReader.read(response);
19248             var errors = [];
19249             if(rs.records){
19250                 for(var i = 0, len = rs.records.length; i < len; i++) {
19251                     var r = rs.records[i];
19252                     errors[i] = r.data;
19253                 }
19254             }
19255             if(errors.length < 1){
19256                 errors = null;
19257             }
19258             return {
19259                 success : rs.success,
19260                 errors : errors
19261             };
19262         }
19263         var ret = false;
19264         try {
19265             ret = Roo.decode(response.responseText);
19266         } catch (e) {
19267             ret = {
19268                 success: false,
19269                 errorMsg: "Failed to read server message: " + response.responseText,
19270                 errors : []
19271             };
19272         }
19273         return ret;
19274         
19275     }
19276 });
19277
19278
19279 Roo.form.Action.Load = function(form, options){
19280     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
19281     this.reader = this.form.reader;
19282 };
19283
19284 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
19285     type : 'load',
19286
19287     run : function(){
19288         Roo.Ajax.request(Roo.apply(
19289                 this.createCallback(), {
19290                     method:this.getMethod(),
19291                     url:this.getUrl(false),
19292                     params:this.getParams()
19293         }));
19294     },
19295
19296     success : function(response){
19297         var result = this.processResponse(response);
19298         if(result === true || !result.success || !result.data){
19299             this.failureType = Roo.form.Action.LOAD_FAILURE;
19300             this.form.afterAction(this, false);
19301             return;
19302         }
19303         this.form.clearInvalid();
19304         this.form.setValues(result.data);
19305         this.form.afterAction(this, true);
19306     },
19307
19308     handleResponse : function(response){
19309         if(this.form.reader){
19310             var rs = this.form.reader.read(response);
19311             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
19312             return {
19313                 success : rs.success,
19314                 data : data
19315             };
19316         }
19317         return Roo.decode(response.responseText);
19318     }
19319 });
19320
19321 Roo.form.Action.ACTION_TYPES = {
19322     'load' : Roo.form.Action.Load,
19323     'submit' : Roo.form.Action.Submit
19324 };/*
19325  * Based on:
19326  * Ext JS Library 1.1.1
19327  * Copyright(c) 2006-2007, Ext JS, LLC.
19328  *
19329  * Originally Released Under LGPL - original licence link has changed is not relivant.
19330  *
19331  * Fork - LGPL
19332  * <script type="text/javascript">
19333  */
19334  
19335 /**
19336  * @class Roo.form.Layout
19337  * @extends Roo.Component
19338  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
19339  * @constructor
19340  * @param {Object} config Configuration options
19341  */
19342 Roo.form.Layout = function(config){
19343     var xitems = [];
19344     if (config.items) {
19345         xitems = config.items;
19346         delete config.items;
19347     }
19348     Roo.form.Layout.superclass.constructor.call(this, config);
19349     this.stack = [];
19350     Roo.each(xitems, this.addxtype, this);
19351      
19352 };
19353
19354 Roo.extend(Roo.form.Layout, Roo.Component, {
19355     /**
19356      * @cfg {String/Object} autoCreate
19357      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
19358      */
19359     /**
19360      * @cfg {String/Object/Function} style
19361      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
19362      * a function which returns such a specification.
19363      */
19364     /**
19365      * @cfg {String} labelAlign
19366      * Valid values are "left," "top" and "right" (defaults to "left")
19367      */
19368     /**
19369      * @cfg {Number} labelWidth
19370      * Fixed width in pixels of all field labels (defaults to undefined)
19371      */
19372     /**
19373      * @cfg {Boolean} clear
19374      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
19375      */
19376     clear : true,
19377     /**
19378      * @cfg {String} labelSeparator
19379      * The separator to use after field labels (defaults to ':')
19380      */
19381     labelSeparator : ':',
19382     /**
19383      * @cfg {Boolean} hideLabels
19384      * True to suppress the display of field labels in this layout (defaults to false)
19385      */
19386     hideLabels : false,
19387
19388     // private
19389     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
19390     
19391     isLayout : true,
19392     
19393     // private
19394     onRender : function(ct, position){
19395         if(this.el){ // from markup
19396             this.el = Roo.get(this.el);
19397         }else {  // generate
19398             var cfg = this.getAutoCreate();
19399             this.el = ct.createChild(cfg, position);
19400         }
19401         if(this.style){
19402             this.el.applyStyles(this.style);
19403         }
19404         if(this.labelAlign){
19405             this.el.addClass('x-form-label-'+this.labelAlign);
19406         }
19407         if(this.hideLabels){
19408             this.labelStyle = "display:none";
19409             this.elementStyle = "padding-left:0;";
19410         }else{
19411             if(typeof this.labelWidth == 'number'){
19412                 this.labelStyle = "width:"+this.labelWidth+"px;";
19413                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
19414             }
19415             if(this.labelAlign == 'top'){
19416                 this.labelStyle = "width:auto;";
19417                 this.elementStyle = "padding-left:0;";
19418             }
19419         }
19420         var stack = this.stack;
19421         var slen = stack.length;
19422         if(slen > 0){
19423             if(!this.fieldTpl){
19424                 var t = new Roo.Template(
19425                     '<div class="x-form-item {5}">',
19426                         '<label for="{0}" style="{2}">{1}{4}</label>',
19427                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
19428                         '</div>',
19429                     '</div><div class="x-form-clear-left"></div>'
19430                 );
19431                 t.disableFormats = true;
19432                 t.compile();
19433                 Roo.form.Layout.prototype.fieldTpl = t;
19434             }
19435             for(var i = 0; i < slen; i++) {
19436                 if(stack[i].isFormField){
19437                     this.renderField(stack[i]);
19438                 }else{
19439                     this.renderComponent(stack[i]);
19440                 }
19441             }
19442         }
19443         if(this.clear){
19444             this.el.createChild({cls:'x-form-clear'});
19445         }
19446     },
19447
19448     // private
19449     renderField : function(f){
19450         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
19451                f.id, //0
19452                f.fieldLabel, //1
19453                f.labelStyle||this.labelStyle||'', //2
19454                this.elementStyle||'', //3
19455                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
19456                f.itemCls||this.itemCls||''  //5
19457        ], true).getPrevSibling());
19458     },
19459
19460     // private
19461     renderComponent : function(c){
19462         c.render(c.isLayout ? this.el : this.el.createChild());    
19463     },
19464     /**
19465      * Adds a object form elements (using the xtype property as the factory method.)
19466      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
19467      * @param {Object} config 
19468      */
19469     addxtype : function(o)
19470     {
19471         // create the lement.
19472         o.form = this.form;
19473         var fe = Roo.factory(o, Roo.form);
19474         this.form.allItems.push(fe);
19475         this.stack.push(fe);
19476         
19477         if (fe.isFormField) {
19478             this.form.items.add(fe);
19479         }
19480          
19481         return fe;
19482     }
19483 });
19484
19485 /**
19486  * @class Roo.form.Column
19487  * @extends Roo.form.Layout
19488  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
19489  * @constructor
19490  * @param {Object} config Configuration options
19491  */
19492 Roo.form.Column = function(config){
19493     Roo.form.Column.superclass.constructor.call(this, config);
19494 };
19495
19496 Roo.extend(Roo.form.Column, Roo.form.Layout, {
19497     /**
19498      * @cfg {Number/String} width
19499      * The fixed width of the column in pixels or CSS value (defaults to "auto")
19500      */
19501     /**
19502      * @cfg {String/Object} autoCreate
19503      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
19504      */
19505
19506     // private
19507     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
19508
19509     // private
19510     onRender : function(ct, position){
19511         Roo.form.Column.superclass.onRender.call(this, ct, position);
19512         if(this.width){
19513             this.el.setWidth(this.width);
19514         }
19515     }
19516 });
19517
19518
19519 /**
19520  * @class Roo.form.Row
19521  * @extends Roo.form.Layout
19522  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
19523  * @constructor
19524  * @param {Object} config Configuration options
19525  */
19526
19527  
19528 Roo.form.Row = function(config){
19529     Roo.form.Row.superclass.constructor.call(this, config);
19530 };
19531  
19532 Roo.extend(Roo.form.Row, Roo.form.Layout, {
19533       /**
19534      * @cfg {Number/String} width
19535      * The fixed width of the column in pixels or CSS value (defaults to "auto")
19536      */
19537     /**
19538      * @cfg {Number/String} height
19539      * The fixed height of the column in pixels or CSS value (defaults to "auto")
19540      */
19541     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
19542     
19543     padWidth : 20,
19544     // private
19545     onRender : function(ct, position){
19546         //console.log('row render');
19547         if(!this.rowTpl){
19548             var t = new Roo.Template(
19549                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
19550                     '<label for="{0}" style="{2}">{1}{4}</label>',
19551                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
19552                     '</div>',
19553                 '</div>'
19554             );
19555             t.disableFormats = true;
19556             t.compile();
19557             Roo.form.Layout.prototype.rowTpl = t;
19558         }
19559         this.fieldTpl = this.rowTpl;
19560         
19561         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
19562         var labelWidth = 100;
19563         
19564         if ((this.labelAlign != 'top')) {
19565             if (typeof this.labelWidth == 'number') {
19566                 labelWidth = this.labelWidth
19567             }
19568             this.padWidth =  20 + labelWidth;
19569             
19570         }
19571         
19572         Roo.form.Column.superclass.onRender.call(this, ct, position);
19573         if(this.width){
19574             this.el.setWidth(this.width);
19575         }
19576         if(this.height){
19577             this.el.setHeight(this.height);
19578         }
19579     },
19580     
19581     // private
19582     renderField : function(f){
19583         f.fieldEl = this.fieldTpl.append(this.el, [
19584                f.id, f.fieldLabel,
19585                f.labelStyle||this.labelStyle||'',
19586                this.elementStyle||'',
19587                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
19588                f.itemCls||this.itemCls||'',
19589                f.width ? f.width + this.padWidth : 160 + this.padWidth
19590        ],true);
19591     }
19592 });
19593  
19594
19595 /**
19596  * @class Roo.form.FieldSet
19597  * @extends Roo.form.Layout
19598  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
19599  * @constructor
19600  * @param {Object} config Configuration options
19601  */
19602 Roo.form.FieldSet = function(config){
19603     Roo.form.FieldSet.superclass.constructor.call(this, config);
19604 };
19605
19606 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
19607     /**
19608      * @cfg {String} legend
19609      * The text to display as the legend for the FieldSet (defaults to '')
19610      */
19611     /**
19612      * @cfg {String/Object} autoCreate
19613      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
19614      */
19615
19616     // private
19617     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
19618
19619     // private
19620     onRender : function(ct, position){
19621         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
19622         if(this.legend){
19623             this.setLegend(this.legend);
19624         }
19625     },
19626
19627     // private
19628     setLegend : function(text){
19629         if(this.rendered){
19630             this.el.child('legend').update(text);
19631         }
19632     }
19633 });/*
19634  * Based on:
19635  * Ext JS Library 1.1.1
19636  * Copyright(c) 2006-2007, Ext JS, LLC.
19637  *
19638  * Originally Released Under LGPL - original licence link has changed is not relivant.
19639  *
19640  * Fork - LGPL
19641  * <script type="text/javascript">
19642  */
19643 /**
19644  * @class Roo.form.VTypes
19645  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
19646  * @singleton
19647  */
19648 Roo.form.VTypes = function(){
19649     // closure these in so they are only created once.
19650     var alpha = /^[a-zA-Z_]+$/;
19651     var alphanum = /^[a-zA-Z0-9_]+$/;
19652     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
19653     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
19654
19655     // All these messages and functions are configurable
19656     return {
19657         /**
19658          * The function used to validate email addresses
19659          * @param {String} value The email address
19660          */
19661         'email' : function(v){
19662             return email.test(v);
19663         },
19664         /**
19665          * The error text to display when the email validation function returns false
19666          * @type String
19667          */
19668         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
19669         /**
19670          * The keystroke filter mask to be applied on email input
19671          * @type RegExp
19672          */
19673         'emailMask' : /[a-z0-9_\.\-@]/i,
19674
19675         /**
19676          * The function used to validate URLs
19677          * @param {String} value The URL
19678          */
19679         'url' : function(v){
19680             return url.test(v);
19681         },
19682         /**
19683          * The error text to display when the url validation function returns false
19684          * @type String
19685          */
19686         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
19687         
19688         /**
19689          * The function used to validate alpha values
19690          * @param {String} value The value
19691          */
19692         'alpha' : function(v){
19693             return alpha.test(v);
19694         },
19695         /**
19696          * The error text to display when the alpha validation function returns false
19697          * @type String
19698          */
19699         'alphaText' : 'This field should only contain letters and _',
19700         /**
19701          * The keystroke filter mask to be applied on alpha input
19702          * @type RegExp
19703          */
19704         'alphaMask' : /[a-z_]/i,
19705
19706         /**
19707          * The function used to validate alphanumeric values
19708          * @param {String} value The value
19709          */
19710         'alphanum' : function(v){
19711             return alphanum.test(v);
19712         },
19713         /**
19714          * The error text to display when the alphanumeric validation function returns false
19715          * @type String
19716          */
19717         'alphanumText' : 'This field should only contain letters, numbers and _',
19718         /**
19719          * The keystroke filter mask to be applied on alphanumeric input
19720          * @type RegExp
19721          */
19722         'alphanumMask' : /[a-z0-9_]/i
19723     };
19724 }();//<script type="text/javascript">
19725
19726 /**
19727  * @class Roo.form.FCKeditor
19728  * @extends Roo.form.TextArea
19729  * Wrapper around the FCKEditor http://www.fckeditor.net
19730  * @constructor
19731  * Creates a new FCKeditor
19732  * @param {Object} config Configuration options
19733  */
19734 Roo.form.FCKeditor = function(config){
19735     Roo.form.FCKeditor.superclass.constructor.call(this, config);
19736     this.addEvents({
19737          /**
19738          * @event editorinit
19739          * Fired when the editor is initialized - you can add extra handlers here..
19740          * @param {FCKeditor} this
19741          * @param {Object} the FCK object.
19742          */
19743         editorinit : true
19744     });
19745     
19746     
19747 };
19748 Roo.form.FCKeditor.editors = { };
19749 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
19750 {
19751     //defaultAutoCreate : {
19752     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
19753     //},
19754     // private
19755     /**
19756      * @cfg {Object} fck options - see fck manual for details.
19757      */
19758     fckconfig : false,
19759     
19760     /**
19761      * @cfg {Object} fck toolbar set (Basic or Default)
19762      */
19763     toolbarSet : 'Basic',
19764     /**
19765      * @cfg {Object} fck BasePath
19766      */ 
19767     basePath : '/fckeditor/',
19768     
19769     
19770     frame : false,
19771     
19772     value : '',
19773     
19774    
19775     onRender : function(ct, position)
19776     {
19777         if(!this.el){
19778             this.defaultAutoCreate = {
19779                 tag: "textarea",
19780                 style:"width:300px;height:60px;",
19781                 autocomplete: "off"
19782             };
19783         }
19784         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
19785         /*
19786         if(this.grow){
19787             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
19788             if(this.preventScrollbars){
19789                 this.el.setStyle("overflow", "hidden");
19790             }
19791             this.el.setHeight(this.growMin);
19792         }
19793         */
19794         //console.log('onrender' + this.getId() );
19795         Roo.form.FCKeditor.editors[this.getId()] = this;
19796          
19797
19798         this.replaceTextarea() ;
19799         
19800     },
19801     
19802     getEditor : function() {
19803         return this.fckEditor;
19804     },
19805     /**
19806      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
19807      * @param {Mixed} value The value to set
19808      */
19809     
19810     
19811     setValue : function(value)
19812     {
19813         //console.log('setValue: ' + value);
19814         
19815         if(typeof(value) == 'undefined') { // not sure why this is happending...
19816             return;
19817         }
19818         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
19819         
19820         //if(!this.el || !this.getEditor()) {
19821         //    this.value = value;
19822             //this.setValue.defer(100,this,[value]);    
19823         //    return;
19824         //} 
19825         
19826         if(!this.getEditor()) {
19827             return;
19828         }
19829         
19830         this.getEditor().SetData(value);
19831         
19832         //
19833
19834     },
19835
19836     /**
19837      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19838      * @return {Mixed} value The field value
19839      */
19840     getValue : function()
19841     {
19842         
19843         if (this.frame && this.frame.dom.style.display == 'none') {
19844             return Roo.form.FCKeditor.superclass.getValue.call(this);
19845         }
19846         
19847         if(!this.el || !this.getEditor()) {
19848            
19849            // this.getValue.defer(100,this); 
19850             return this.value;
19851         }
19852        
19853         
19854         var value=this.getEditor().GetData();
19855         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
19856         return Roo.form.FCKeditor.superclass.getValue.call(this);
19857         
19858
19859     },
19860
19861     /**
19862      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
19863      * @return {Mixed} value The field value
19864      */
19865     getRawValue : function()
19866     {
19867         if (this.frame && this.frame.dom.style.display == 'none') {
19868             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
19869         }
19870         
19871         if(!this.el || !this.getEditor()) {
19872             //this.getRawValue.defer(100,this); 
19873             return this.value;
19874             return;
19875         }
19876         
19877         
19878         
19879         var value=this.getEditor().GetData();
19880         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
19881         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
19882          
19883     },
19884     
19885     setSize : function(w,h) {
19886         
19887         
19888         
19889         //if (this.frame && this.frame.dom.style.display == 'none') {
19890         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
19891         //    return;
19892         //}
19893         //if(!this.el || !this.getEditor()) {
19894         //    this.setSize.defer(100,this, [w,h]); 
19895         //    return;
19896         //}
19897         
19898         
19899         
19900         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
19901         
19902         this.frame.dom.setAttribute('width', w);
19903         this.frame.dom.setAttribute('height', h);
19904         this.frame.setSize(w,h);
19905         
19906     },
19907     
19908     toggleSourceEdit : function(value) {
19909         
19910       
19911          
19912         this.el.dom.style.display = value ? '' : 'none';
19913         this.frame.dom.style.display = value ?  'none' : '';
19914         
19915     },
19916     
19917     
19918     focus: function(tag)
19919     {
19920         if (this.frame.dom.style.display == 'none') {
19921             return Roo.form.FCKeditor.superclass.focus.call(this);
19922         }
19923         if(!this.el || !this.getEditor()) {
19924             this.focus.defer(100,this, [tag]); 
19925             return;
19926         }
19927         
19928         
19929         
19930         
19931         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
19932         this.getEditor().Focus();
19933         if (tgs.length) {
19934             if (!this.getEditor().Selection.GetSelection()) {
19935                 this.focus.defer(100,this, [tag]); 
19936                 return;
19937             }
19938             
19939             
19940             var r = this.getEditor().EditorDocument.createRange();
19941             r.setStart(tgs[0],0);
19942             r.setEnd(tgs[0],0);
19943             this.getEditor().Selection.GetSelection().removeAllRanges();
19944             this.getEditor().Selection.GetSelection().addRange(r);
19945             this.getEditor().Focus();
19946         }
19947         
19948     },
19949     
19950     
19951     
19952     replaceTextarea : function()
19953     {
19954         if ( document.getElementById( this.getId() + '___Frame' ) )
19955             return ;
19956         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
19957         //{
19958             // We must check the elements firstly using the Id and then the name.
19959         var oTextarea = document.getElementById( this.getId() );
19960         
19961         var colElementsByName = document.getElementsByName( this.getId() ) ;
19962          
19963         oTextarea.style.display = 'none' ;
19964
19965         if ( oTextarea.tabIndex ) {            
19966             this.TabIndex = oTextarea.tabIndex ;
19967         }
19968         
19969         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
19970         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
19971         this.frame = Roo.get(this.getId() + '___Frame')
19972     },
19973     
19974     _getConfigHtml : function()
19975     {
19976         var sConfig = '' ;
19977
19978         for ( var o in this.fckconfig ) {
19979             sConfig += sConfig.length > 0  ? '&amp;' : '';
19980             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
19981         }
19982
19983         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
19984     },
19985     
19986     
19987     _getIFrameHtml : function()
19988     {
19989         var sFile = 'fckeditor.html' ;
19990         /* no idea what this is about..
19991         try
19992         {
19993             if ( (/fcksource=true/i).test( window.top.location.search ) )
19994                 sFile = 'fckeditor.original.html' ;
19995         }
19996         catch (e) { 
19997         */
19998
19999         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
20000         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
20001         
20002         
20003         var html = '<iframe id="' + this.getId() +
20004             '___Frame" src="' + sLink +
20005             '" width="' + this.width +
20006             '" height="' + this.height + '"' +
20007             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
20008             ' frameborder="0" scrolling="no"></iframe>' ;
20009
20010         return html ;
20011     },
20012     
20013     _insertHtmlBefore : function( html, element )
20014     {
20015         if ( element.insertAdjacentHTML )       {
20016             // IE
20017             element.insertAdjacentHTML( 'beforeBegin', html ) ;
20018         } else { // Gecko
20019             var oRange = document.createRange() ;
20020             oRange.setStartBefore( element ) ;
20021             var oFragment = oRange.createContextualFragment( html );
20022             element.parentNode.insertBefore( oFragment, element ) ;
20023         }
20024     }
20025     
20026     
20027   
20028     
20029     
20030     
20031     
20032
20033 });
20034
20035 //Roo.reg('fckeditor', Roo.form.FCKeditor);
20036
20037 function FCKeditor_OnComplete(editorInstance){
20038     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
20039     f.fckEditor = editorInstance;
20040     //console.log("loaded");
20041     f.fireEvent('editorinit', f, editorInstance);
20042
20043   
20044
20045  
20046
20047
20048
20049
20050
20051
20052
20053
20054
20055
20056
20057
20058
20059
20060
20061 //<script type="text/javascript">
20062 /**
20063  * @class Roo.form.GridField
20064  * @extends Roo.form.Field
20065  * Embed a grid (or editable grid into a form)
20066  * STATUS ALPHA
20067  * @constructor
20068  * Creates a new GridField
20069  * @param {Object} config Configuration options
20070  */
20071 Roo.form.GridField = function(config){
20072     Roo.form.GridField.superclass.constructor.call(this, config);
20073      
20074 };
20075
20076 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
20077     /**
20078      * @cfg {Number} width  - used to restrict width of grid..
20079      */
20080     width : 100,
20081     /**
20082      * @cfg {Number} height - used to restrict height of grid..
20083      */
20084     height : 50,
20085      /**
20086      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
20087      */
20088     xgrid : false, 
20089     /**
20090      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20091      * {tag: "input", type: "checkbox", autocomplete: "off"})
20092      */
20093    // defaultAutoCreate : { tag: 'div' },
20094     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
20095     /**
20096      * @cfg {String} addTitle Text to include for adding a title.
20097      */
20098     addTitle : false,
20099     //
20100     onResize : function(){
20101         Roo.form.Field.superclass.onResize.apply(this, arguments);
20102     },
20103
20104     initEvents : function(){
20105         // Roo.form.Checkbox.superclass.initEvents.call(this);
20106         // has no events...
20107        
20108     },
20109
20110
20111     getResizeEl : function(){
20112         return this.wrap;
20113     },
20114
20115     getPositionEl : function(){
20116         return this.wrap;
20117     },
20118
20119     // private
20120     onRender : function(ct, position){
20121         
20122         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
20123         var style = this.style;
20124         delete this.style;
20125         
20126         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
20127         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
20128         this.viewEl = this.wrap.createChild({ tag: 'div' });
20129         if (style) {
20130             this.viewEl.applyStyles(style);
20131         }
20132         if (this.width) {
20133             this.viewEl.setWidth(this.width);
20134         }
20135         if (this.height) {
20136             this.viewEl.setHeight(this.height);
20137         }
20138         //if(this.inputValue !== undefined){
20139         //this.setValue(this.value);
20140         
20141         
20142         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
20143         
20144         
20145         this.grid.render();
20146         this.grid.getDataSource().on('remove', this.refreshValue, this);
20147         this.grid.getDataSource().on('update', this.refreshValue, this);
20148         this.grid.on('afteredit', this.refreshValue, this);
20149  
20150     },
20151      
20152     
20153     /**
20154      * Sets the value of the item. 
20155      * @param {String} either an object  or a string..
20156      */
20157     setValue : function(v){
20158         //this.value = v;
20159         v = v || []; // empty set..
20160         // this does not seem smart - it really only affects memoryproxy grids..
20161         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
20162             var ds = this.grid.getDataSource();
20163             // assumes a json reader..
20164             var data = {}
20165             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
20166             ds.loadData( data);
20167         }
20168         Roo.form.GridField.superclass.setValue.call(this, v);
20169         this.refreshValue();
20170         // should load data in the grid really....
20171     },
20172     
20173     // private
20174     refreshValue: function() {
20175          var val = [];
20176         this.grid.getDataSource().each(function(r) {
20177             val.push(r.data);
20178         });
20179         this.el.dom.value = Roo.encode(val);
20180     }
20181     
20182      
20183     
20184     
20185 });//<script type="text/javasscript">
20186  
20187
20188 /**
20189  * @class Roo.DDView
20190  * A DnD enabled version of Roo.View.
20191  * @param {Element/String} container The Element in which to create the View.
20192  * @param {String} tpl The template string used to create the markup for each element of the View
20193  * @param {Object} config The configuration properties. These include all the config options of
20194  * {@link Roo.View} plus some specific to this class.<br>
20195  * <p>
20196  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
20197  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
20198  * <p>
20199  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
20200 .x-view-drag-insert-above {
20201         border-top:1px dotted #3366cc;
20202 }
20203 .x-view-drag-insert-below {
20204         border-bottom:1px dotted #3366cc;
20205 }
20206 </code></pre>
20207  * 
20208  */
20209  
20210 Roo.DDView = function(container, tpl, config) {
20211     Roo.DDView.superclass.constructor.apply(this, arguments);
20212     this.getEl().setStyle("outline", "0px none");
20213     this.getEl().unselectable();
20214     if (this.dragGroup) {
20215                 this.setDraggable(this.dragGroup.split(","));
20216     }
20217     if (this.dropGroup) {
20218                 this.setDroppable(this.dropGroup.split(","));
20219     }
20220     if (this.deletable) {
20221         this.setDeletable();
20222     }
20223     this.isDirtyFlag = false;
20224         this.addEvents({
20225                 "drop" : true
20226         });
20227 };
20228
20229 Roo.extend(Roo.DDView, Roo.View, {
20230 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
20231 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
20232 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
20233 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
20234
20235         isFormField: true,
20236
20237         reset: Roo.emptyFn,
20238         
20239         clearInvalid: Roo.form.Field.prototype.clearInvalid,
20240
20241         validate: function() {
20242                 return true;
20243         },
20244         
20245         destroy: function() {
20246                 this.purgeListeners();
20247                 this.getEl.removeAllListeners();
20248                 this.getEl().remove();
20249                 if (this.dragZone) {
20250                         if (this.dragZone.destroy) {
20251                                 this.dragZone.destroy();
20252                         }
20253                 }
20254                 if (this.dropZone) {
20255                         if (this.dropZone.destroy) {
20256                                 this.dropZone.destroy();
20257                         }
20258                 }
20259         },
20260
20261 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
20262         getName: function() {
20263                 return this.name;
20264         },
20265
20266 /**     Loads the View from a JSON string representing the Records to put into the Store. */
20267         setValue: function(v) {
20268                 if (!this.store) {
20269                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
20270                 }
20271                 var data = {};
20272                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
20273                 this.store.proxy = new Roo.data.MemoryProxy(data);
20274                 this.store.load();
20275         },
20276
20277 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
20278         getValue: function() {
20279                 var result = '(';
20280                 this.store.each(function(rec) {
20281                         result += rec.id + ',';
20282                 });
20283                 return result.substr(0, result.length - 1) + ')';
20284         },
20285         
20286         getIds: function() {
20287                 var i = 0, result = new Array(this.store.getCount());
20288                 this.store.each(function(rec) {
20289                         result[i++] = rec.id;
20290                 });
20291                 return result;
20292         },
20293         
20294         isDirty: function() {
20295                 return this.isDirtyFlag;
20296         },
20297
20298 /**
20299  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
20300  *      whole Element becomes the target, and this causes the drop gesture to append.
20301  */
20302     getTargetFromEvent : function(e) {
20303                 var target = e.getTarget();
20304                 while ((target !== null) && (target.parentNode != this.el.dom)) {
20305                 target = target.parentNode;
20306                 }
20307                 if (!target) {
20308                         target = this.el.dom.lastChild || this.el.dom;
20309                 }
20310                 return target;
20311     },
20312
20313 /**
20314  *      Create the drag data which consists of an object which has the property "ddel" as
20315  *      the drag proxy element. 
20316  */
20317     getDragData : function(e) {
20318         var target = this.findItemFromChild(e.getTarget());
20319                 if(target) {
20320                         this.handleSelection(e);
20321                         var selNodes = this.getSelectedNodes();
20322             var dragData = {
20323                 source: this,
20324                 copy: this.copy || (this.allowCopy && e.ctrlKey),
20325                 nodes: selNodes,
20326                 records: []
20327                         };
20328                         var selectedIndices = this.getSelectedIndexes();
20329                         for (var i = 0; i < selectedIndices.length; i++) {
20330                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
20331                         }
20332                         if (selNodes.length == 1) {
20333                                 dragData.ddel = target.cloneNode(true); // the div element
20334                         } else {
20335                                 var div = document.createElement('div'); // create the multi element drag "ghost"
20336                                 div.className = 'multi-proxy';
20337                                 for (var i = 0, len = selNodes.length; i < len; i++) {
20338                                         div.appendChild(selNodes[i].cloneNode(true));
20339                                 }
20340                                 dragData.ddel = div;
20341                         }
20342             //console.log(dragData)
20343             //console.log(dragData.ddel.innerHTML)
20344                         return dragData;
20345                 }
20346         //console.log('nodragData')
20347                 return false;
20348     },
20349     
20350 /**     Specify to which ddGroup items in this DDView may be dragged. */
20351     setDraggable: function(ddGroup) {
20352         if (ddGroup instanceof Array) {
20353                 Roo.each(ddGroup, this.setDraggable, this);
20354                 return;
20355         }
20356         if (this.dragZone) {
20357                 this.dragZone.addToGroup(ddGroup);
20358         } else {
20359                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
20360                                 containerScroll: true,
20361                                 ddGroup: ddGroup 
20362
20363                         });
20364 //                      Draggability implies selection. DragZone's mousedown selects the element.
20365                         if (!this.multiSelect) { this.singleSelect = true; }
20366
20367 //                      Wire the DragZone's handlers up to methods in *this*
20368                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
20369                 }
20370     },
20371
20372 /**     Specify from which ddGroup this DDView accepts drops. */
20373     setDroppable: function(ddGroup) {
20374         if (ddGroup instanceof Array) {
20375                 Roo.each(ddGroup, this.setDroppable, this);
20376                 return;
20377         }
20378         if (this.dropZone) {
20379                 this.dropZone.addToGroup(ddGroup);
20380         } else {
20381                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
20382                                 containerScroll: true,
20383                                 ddGroup: ddGroup
20384                         });
20385
20386 //                      Wire the DropZone's handlers up to methods in *this*
20387                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
20388                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
20389                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
20390                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
20391                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
20392                 }
20393     },
20394
20395 /**     Decide whether to drop above or below a View node. */
20396     getDropPoint : function(e, n, dd){
20397         if (n == this.el.dom) { return "above"; }
20398                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
20399                 var c = t + (b - t) / 2;
20400                 var y = Roo.lib.Event.getPageY(e);
20401                 if(y <= c) {
20402                         return "above";
20403                 }else{
20404                         return "below";
20405                 }
20406     },
20407
20408     onNodeEnter : function(n, dd, e, data){
20409                 return false;
20410     },
20411     
20412     onNodeOver : function(n, dd, e, data){
20413                 var pt = this.getDropPoint(e, n, dd);
20414                 // set the insert point style on the target node
20415                 var dragElClass = this.dropNotAllowed;
20416                 if (pt) {
20417                         var targetElClass;
20418                         if (pt == "above"){
20419                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
20420                                 targetElClass = "x-view-drag-insert-above";
20421                         } else {
20422                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
20423                                 targetElClass = "x-view-drag-insert-below";
20424                         }
20425                         if (this.lastInsertClass != targetElClass){
20426                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
20427                                 this.lastInsertClass = targetElClass;
20428                         }
20429                 }
20430                 return dragElClass;
20431         },
20432
20433     onNodeOut : function(n, dd, e, data){
20434                 this.removeDropIndicators(n);
20435     },
20436
20437     onNodeDrop : function(n, dd, e, data){
20438         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
20439                 return false;
20440         }
20441         var pt = this.getDropPoint(e, n, dd);
20442                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
20443                 if (pt == "below") { insertAt++; }
20444                 for (var i = 0; i < data.records.length; i++) {
20445                         var r = data.records[i];
20446                         var dup = this.store.getById(r.id);
20447                         if (dup && (dd != this.dragZone)) {
20448                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
20449                         } else {
20450                                 if (data.copy) {
20451                                         this.store.insert(insertAt++, r.copy());
20452                                 } else {
20453                                         data.source.isDirtyFlag = true;
20454                                         r.store.remove(r);
20455                                         this.store.insert(insertAt++, r);
20456                                 }
20457                                 this.isDirtyFlag = true;
20458                         }
20459                 }
20460                 this.dragZone.cachedTarget = null;
20461                 return true;
20462     },
20463
20464     removeDropIndicators : function(n){
20465                 if(n){
20466                         Roo.fly(n).removeClass([
20467                                 "x-view-drag-insert-above",
20468                                 "x-view-drag-insert-below"]);
20469                         this.lastInsertClass = "_noclass";
20470                 }
20471     },
20472
20473 /**
20474  *      Utility method. Add a delete option to the DDView's context menu.
20475  *      @param {String} imageUrl The URL of the "delete" icon image.
20476  */
20477         setDeletable: function(imageUrl) {
20478                 if (!this.singleSelect && !this.multiSelect) {
20479                         this.singleSelect = true;
20480                 }
20481                 var c = this.getContextMenu();
20482                 this.contextMenu.on("itemclick", function(item) {
20483                         switch (item.id) {
20484                                 case "delete":
20485                                         this.remove(this.getSelectedIndexes());
20486                                         break;
20487                         }
20488                 }, this);
20489                 this.contextMenu.add({
20490                         icon: imageUrl,
20491                         id: "delete",
20492                         text: 'Delete'
20493                 });
20494         },
20495         
20496 /**     Return the context menu for this DDView. */
20497         getContextMenu: function() {
20498                 if (!this.contextMenu) {
20499 //                      Create the View's context menu
20500                         this.contextMenu = new Roo.menu.Menu({
20501                                 id: this.id + "-contextmenu"
20502                         });
20503                         this.el.on("contextmenu", this.showContextMenu, this);
20504                 }
20505                 return this.contextMenu;
20506         },
20507         
20508         disableContextMenu: function() {
20509                 if (this.contextMenu) {
20510                         this.el.un("contextmenu", this.showContextMenu, this);
20511                 }
20512         },
20513
20514         showContextMenu: function(e, item) {
20515         item = this.findItemFromChild(e.getTarget());
20516                 if (item) {
20517                         e.stopEvent();
20518                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
20519                         this.contextMenu.showAt(e.getXY());
20520             }
20521     },
20522
20523 /**
20524  *      Remove {@link Roo.data.Record}s at the specified indices.
20525  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
20526  */
20527     remove: function(selectedIndices) {
20528                 selectedIndices = [].concat(selectedIndices);
20529                 for (var i = 0; i < selectedIndices.length; i++) {
20530                         var rec = this.store.getAt(selectedIndices[i]);
20531                         this.store.remove(rec);
20532                 }
20533     },
20534
20535 /**
20536  *      Double click fires the event, but also, if this is draggable, and there is only one other
20537  *      related DropZone, it transfers the selected node.
20538  */
20539     onDblClick : function(e){
20540         var item = this.findItemFromChild(e.getTarget());
20541         if(item){
20542             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
20543                 return false;
20544             }
20545             if (this.dragGroup) {
20546                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
20547                     while (targets.indexOf(this.dropZone) > -1) {
20548                             targets.remove(this.dropZone);
20549                                 }
20550                     if (targets.length == 1) {
20551                                         this.dragZone.cachedTarget = null;
20552                         var el = Roo.get(targets[0].getEl());
20553                         var box = el.getBox(true);
20554                         targets[0].onNodeDrop(el.dom, {
20555                                 target: el.dom,
20556                                 xy: [box.x, box.y + box.height - 1]
20557                         }, null, this.getDragData(e));
20558                     }
20559                 }
20560         }
20561     },
20562     
20563     handleSelection: function(e) {
20564                 this.dragZone.cachedTarget = null;
20565         var item = this.findItemFromChild(e.getTarget());
20566         if (!item) {
20567                 this.clearSelections(true);
20568                 return;
20569         }
20570                 if (item && (this.multiSelect || this.singleSelect)){
20571                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
20572                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
20573                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
20574                                 this.unselect(item);
20575                         } else {
20576                                 this.select(item, this.multiSelect && e.ctrlKey);
20577                                 this.lastSelection = item;
20578                         }
20579                 }
20580     },
20581
20582     onItemClick : function(item, index, e){
20583                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20584                         return false;
20585                 }
20586                 return true;
20587     },
20588
20589     unselect : function(nodeInfo, suppressEvent){
20590                 var node = this.getNode(nodeInfo);
20591                 if(node && this.isSelected(node)){
20592                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20593                                 Roo.fly(node).removeClass(this.selectedClass);
20594                                 this.selections.remove(node);
20595                                 if(!suppressEvent){
20596                                         this.fireEvent("selectionchange", this, this.selections);
20597                                 }
20598                         }
20599                 }
20600     }
20601 });
20602 /*
20603  * Based on:
20604  * Ext JS Library 1.1.1
20605  * Copyright(c) 2006-2007, Ext JS, LLC.
20606  *
20607  * Originally Released Under LGPL - original licence link has changed is not relivant.
20608  *
20609  * Fork - LGPL
20610  * <script type="text/javascript">
20611  */
20612  
20613 /**
20614  * @class Roo.LayoutManager
20615  * @extends Roo.util.Observable
20616  * Base class for layout managers.
20617  */
20618 Roo.LayoutManager = function(container, config){
20619     Roo.LayoutManager.superclass.constructor.call(this);
20620     this.el = Roo.get(container);
20621     // ie scrollbar fix
20622     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
20623         document.body.scroll = "no";
20624     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
20625         this.el.position('relative');
20626     }
20627     this.id = this.el.id;
20628     this.el.addClass("x-layout-container");
20629     /** false to disable window resize monitoring @type Boolean */
20630     this.monitorWindowResize = true;
20631     this.regions = {};
20632     this.addEvents({
20633         /**
20634          * @event layout
20635          * Fires when a layout is performed. 
20636          * @param {Roo.LayoutManager} this
20637          */
20638         "layout" : true,
20639         /**
20640          * @event regionresized
20641          * Fires when the user resizes a region. 
20642          * @param {Roo.LayoutRegion} region The resized region
20643          * @param {Number} newSize The new size (width for east/west, height for north/south)
20644          */
20645         "regionresized" : true,
20646         /**
20647          * @event regioncollapsed
20648          * Fires when a region is collapsed. 
20649          * @param {Roo.LayoutRegion} region The collapsed region
20650          */
20651         "regioncollapsed" : true,
20652         /**
20653          * @event regionexpanded
20654          * Fires when a region is expanded.  
20655          * @param {Roo.LayoutRegion} region The expanded region
20656          */
20657         "regionexpanded" : true
20658     });
20659     this.updating = false;
20660     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
20661 };
20662
20663 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
20664     /**
20665      * Returns true if this layout is currently being updated
20666      * @return {Boolean}
20667      */
20668     isUpdating : function(){
20669         return this.updating; 
20670     },
20671     
20672     /**
20673      * Suspend the LayoutManager from doing auto-layouts while
20674      * making multiple add or remove calls
20675      */
20676     beginUpdate : function(){
20677         this.updating = true;    
20678     },
20679     
20680     /**
20681      * Restore auto-layouts and optionally disable the manager from performing a layout
20682      * @param {Boolean} noLayout true to disable a layout update 
20683      */
20684     endUpdate : function(noLayout){
20685         this.updating = false;
20686         if(!noLayout){
20687             this.layout();
20688         }    
20689     },
20690     
20691     layout: function(){
20692         
20693     },
20694     
20695     onRegionResized : function(region, newSize){
20696         this.fireEvent("regionresized", region, newSize);
20697         this.layout();
20698     },
20699     
20700     onRegionCollapsed : function(region){
20701         this.fireEvent("regioncollapsed", region);
20702     },
20703     
20704     onRegionExpanded : function(region){
20705         this.fireEvent("regionexpanded", region);
20706     },
20707         
20708     /**
20709      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
20710      * performs box-model adjustments.
20711      * @return {Object} The size as an object {width: (the width), height: (the height)}
20712      */
20713     getViewSize : function(){
20714         var size;
20715         if(this.el.dom != document.body){
20716             size = this.el.getSize();
20717         }else{
20718             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20719         }
20720         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
20721         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
20722         return size;
20723     },
20724     
20725     /**
20726      * Returns the Element this layout is bound to.
20727      * @return {Roo.Element}
20728      */
20729     getEl : function(){
20730         return this.el;
20731     },
20732     
20733     /**
20734      * Returns the specified region.
20735      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
20736      * @return {Roo.LayoutRegion}
20737      */
20738     getRegion : function(target){
20739         return this.regions[target.toLowerCase()];
20740     },
20741     
20742     onWindowResize : function(){
20743         if(this.monitorWindowResize){
20744             this.layout();
20745         }
20746     }
20747 });/*
20748  * Based on:
20749  * Ext JS Library 1.1.1
20750  * Copyright(c) 2006-2007, Ext JS, LLC.
20751  *
20752  * Originally Released Under LGPL - original licence link has changed is not relivant.
20753  *
20754  * Fork - LGPL
20755  * <script type="text/javascript">
20756  */
20757 /**
20758  * @class Roo.BorderLayout
20759  * @extends Roo.LayoutManager
20760  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
20761  * please see: <br><br>
20762  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
20763  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
20764  * Example:
20765  <pre><code>
20766  var layout = new Roo.BorderLayout(document.body, {
20767     north: {
20768         initialSize: 25,
20769         titlebar: false
20770     },
20771     west: {
20772         split:true,
20773         initialSize: 200,
20774         minSize: 175,
20775         maxSize: 400,
20776         titlebar: true,
20777         collapsible: true
20778     },
20779     east: {
20780         split:true,
20781         initialSize: 202,
20782         minSize: 175,
20783         maxSize: 400,
20784         titlebar: true,
20785         collapsible: true
20786     },
20787     south: {
20788         split:true,
20789         initialSize: 100,
20790         minSize: 100,
20791         maxSize: 200,
20792         titlebar: true,
20793         collapsible: true
20794     },
20795     center: {
20796         titlebar: true,
20797         autoScroll:true,
20798         resizeTabs: true,
20799         minTabWidth: 50,
20800         preferredTabWidth: 150
20801     }
20802 });
20803
20804 // shorthand
20805 var CP = Roo.ContentPanel;
20806
20807 layout.beginUpdate();
20808 layout.add("north", new CP("north", "North"));
20809 layout.add("south", new CP("south", {title: "South", closable: true}));
20810 layout.add("west", new CP("west", {title: "West"}));
20811 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
20812 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
20813 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
20814 layout.getRegion("center").showPanel("center1");
20815 layout.endUpdate();
20816 </code></pre>
20817
20818 <b>The container the layout is rendered into can be either the body element or any other element.
20819 If it is not the body element, the container needs to either be an absolute positioned element,
20820 or you will need to add "position:relative" to the css of the container.  You will also need to specify
20821 the container size if it is not the body element.</b>
20822
20823 * @constructor
20824 * Create a new BorderLayout
20825 * @param {String/HTMLElement/Element} container The container this layout is bound to
20826 * @param {Object} config Configuration options
20827  */
20828 Roo.BorderLayout = function(container, config){
20829     config = config || {};
20830     Roo.BorderLayout.superclass.constructor.call(this, container, config);
20831     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
20832     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
20833         var target = this.factory.validRegions[i];
20834         if(config[target]){
20835             this.addRegion(target, config[target]);
20836         }
20837     }
20838 };
20839
20840 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
20841     /**
20842      * Creates and adds a new region if it doesn't already exist.
20843      * @param {String} target The target region key (north, south, east, west or center).
20844      * @param {Object} config The regions config object
20845      * @return {BorderLayoutRegion} The new region
20846      */
20847     addRegion : function(target, config){
20848         if(!this.regions[target]){
20849             var r = this.factory.create(target, this, config);
20850             this.bindRegion(target, r);
20851         }
20852         return this.regions[target];
20853     },
20854
20855     // private (kinda)
20856     bindRegion : function(name, r){
20857         this.regions[name] = r;
20858         r.on("visibilitychange", this.layout, this);
20859         r.on("paneladded", this.layout, this);
20860         r.on("panelremoved", this.layout, this);
20861         r.on("invalidated", this.layout, this);
20862         r.on("resized", this.onRegionResized, this);
20863         r.on("collapsed", this.onRegionCollapsed, this);
20864         r.on("expanded", this.onRegionExpanded, this);
20865     },
20866
20867     /**
20868      * Performs a layout update.
20869      */
20870     layout : function(){
20871         if(this.updating) return;
20872         var size = this.getViewSize();
20873         var w = size.width;
20874         var h = size.height;
20875         var centerW = w;
20876         var centerH = h;
20877         var centerY = 0;
20878         var centerX = 0;
20879         //var x = 0, y = 0;
20880
20881         var rs = this.regions;
20882         var north = rs["north"];
20883         var south = rs["south"]; 
20884         var west = rs["west"];
20885         var east = rs["east"];
20886         var center = rs["center"];
20887         //if(this.hideOnLayout){ // not supported anymore
20888             //c.el.setStyle("display", "none");
20889         //}
20890         if(north && north.isVisible()){
20891             var b = north.getBox();
20892             var m = north.getMargins();
20893             b.width = w - (m.left+m.right);
20894             b.x = m.left;
20895             b.y = m.top;
20896             centerY = b.height + b.y + m.bottom;
20897             centerH -= centerY;
20898             north.updateBox(this.safeBox(b));
20899         }
20900         if(south && south.isVisible()){
20901             var b = south.getBox();
20902             var m = south.getMargins();
20903             b.width = w - (m.left+m.right);
20904             b.x = m.left;
20905             var totalHeight = (b.height + m.top + m.bottom);
20906             b.y = h - totalHeight + m.top;
20907             centerH -= totalHeight;
20908             south.updateBox(this.safeBox(b));
20909         }
20910         if(west && west.isVisible()){
20911             var b = west.getBox();
20912             var m = west.getMargins();
20913             b.height = centerH - (m.top+m.bottom);
20914             b.x = m.left;
20915             b.y = centerY + m.top;
20916             var totalWidth = (b.width + m.left + m.right);
20917             centerX += totalWidth;
20918             centerW -= totalWidth;
20919             west.updateBox(this.safeBox(b));
20920         }
20921         if(east && east.isVisible()){
20922             var b = east.getBox();
20923             var m = east.getMargins();
20924             b.height = centerH - (m.top+m.bottom);
20925             var totalWidth = (b.width + m.left + m.right);
20926             b.x = w - totalWidth + m.left;
20927             b.y = centerY + m.top;
20928             centerW -= totalWidth;
20929             east.updateBox(this.safeBox(b));
20930         }
20931         if(center){
20932             var m = center.getMargins();
20933             var centerBox = {
20934                 x: centerX + m.left,
20935                 y: centerY + m.top,
20936                 width: centerW - (m.left+m.right),
20937                 height: centerH - (m.top+m.bottom)
20938             };
20939             //if(this.hideOnLayout){
20940                 //center.el.setStyle("display", "block");
20941             //}
20942             center.updateBox(this.safeBox(centerBox));
20943         }
20944         this.el.repaint();
20945         this.fireEvent("layout", this);
20946     },
20947
20948     // private
20949     safeBox : function(box){
20950         box.width = Math.max(0, box.width);
20951         box.height = Math.max(0, box.height);
20952         return box;
20953     },
20954
20955     /**
20956      * Adds a ContentPanel (or subclass) to this layout.
20957      * @param {String} target The target region key (north, south, east, west or center).
20958      * @param {Roo.ContentPanel} panel The panel to add
20959      * @return {Roo.ContentPanel} The added panel
20960      */
20961     add : function(target, panel){
20962          
20963         target = target.toLowerCase();
20964         return this.regions[target].add(panel);
20965     },
20966
20967     /**
20968      * Remove a ContentPanel (or subclass) to this layout.
20969      * @param {String} target The target region key (north, south, east, west or center).
20970      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
20971      * @return {Roo.ContentPanel} The removed panel
20972      */
20973     remove : function(target, panel){
20974         target = target.toLowerCase();
20975         return this.regions[target].remove(panel);
20976     },
20977
20978     /**
20979      * Searches all regions for a panel with the specified id
20980      * @param {String} panelId
20981      * @return {Roo.ContentPanel} The panel or null if it wasn't found
20982      */
20983     findPanel : function(panelId){
20984         var rs = this.regions;
20985         for(var target in rs){
20986             if(typeof rs[target] != "function"){
20987                 var p = rs[target].getPanel(panelId);
20988                 if(p){
20989                     return p;
20990                 }
20991             }
20992         }
20993         return null;
20994     },
20995
20996     /**
20997      * Searches all regions for a panel with the specified id and activates (shows) it.
20998      * @param {String/ContentPanel} panelId The panels id or the panel itself
20999      * @return {Roo.ContentPanel} The shown panel or null
21000      */
21001     showPanel : function(panelId) {
21002       var rs = this.regions;
21003       for(var target in rs){
21004          var r = rs[target];
21005          if(typeof r != "function"){
21006             if(r.hasPanel(panelId)){
21007                return r.showPanel(panelId);
21008             }
21009          }
21010       }
21011       return null;
21012    },
21013
21014    /**
21015      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
21016      * @param {Roo.state.Provider} provider (optional) An alternate state provider
21017      */
21018     restoreState : function(provider){
21019         if(!provider){
21020             provider = Roo.state.Manager;
21021         }
21022         var sm = new Roo.LayoutStateManager();
21023         sm.init(this, provider);
21024     },
21025
21026     /**
21027      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
21028      * object should contain properties for each region to add ContentPanels to, and each property's value should be
21029      * a valid ContentPanel config object.  Example:
21030      * <pre><code>
21031 // Create the main layout
21032 var layout = new Roo.BorderLayout('main-ct', {
21033     west: {
21034         split:true,
21035         minSize: 175,
21036         titlebar: true
21037     },
21038     center: {
21039         title:'Components'
21040     }
21041 }, 'main-ct');
21042
21043 // Create and add multiple ContentPanels at once via configs
21044 layout.batchAdd({
21045    west: {
21046        id: 'source-files',
21047        autoCreate:true,
21048        title:'Ext Source Files',
21049        autoScroll:true,
21050        fitToFrame:true
21051    },
21052    center : {
21053        el: cview,
21054        autoScroll:true,
21055        fitToFrame:true,
21056        toolbar: tb,
21057        resizeEl:'cbody'
21058    }
21059 });
21060 </code></pre>
21061      * @param {Object} regions An object containing ContentPanel configs by region name
21062      */
21063     batchAdd : function(regions){
21064         this.beginUpdate();
21065         for(var rname in regions){
21066             var lr = this.regions[rname];
21067             if(lr){
21068                 this.addTypedPanels(lr, regions[rname]);
21069             }
21070         }
21071         this.endUpdate();
21072     },
21073
21074     // private
21075     addTypedPanels : function(lr, ps){
21076         if(typeof ps == 'string'){
21077             lr.add(new Roo.ContentPanel(ps));
21078         }
21079         else if(ps instanceof Array){
21080             for(var i =0, len = ps.length; i < len; i++){
21081                 this.addTypedPanels(lr, ps[i]);
21082             }
21083         }
21084         else if(!ps.events){ // raw config?
21085             var el = ps.el;
21086             delete ps.el; // prevent conflict
21087             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
21088         }
21089         else {  // panel object assumed!
21090             lr.add(ps);
21091         }
21092     },
21093     /**
21094      * Adds a xtype elements to the layout.
21095      * <pre><code>
21096
21097 layout.addxtype({
21098        xtype : 'ContentPanel',
21099        region: 'west',
21100        items: [ .... ]
21101    }
21102 );
21103
21104 layout.addxtype({
21105         xtype : 'NestedLayoutPanel',
21106         region: 'west',
21107         layout: {
21108            center: { },
21109            west: { }   
21110         },
21111         items : [ ... list of content panels or nested layout panels.. ]
21112    }
21113 );
21114 </code></pre>
21115      * @param {Object} cfg Xtype definition of item to add.
21116      */
21117     addxtype : function(cfg)
21118     {
21119         // basically accepts a pannel...
21120         // can accept a layout region..!?!?
21121        // console.log('BorderLayout add ' + cfg.xtype)
21122         
21123         if (!cfg.xtype.match(/Panel$/)) {
21124             return false;
21125         }
21126         var ret = false;
21127         var region = cfg.region;
21128         delete cfg.region;
21129         
21130           
21131         var xitems = [];
21132         if (cfg.items) {
21133             xitems = cfg.items;
21134             delete cfg.items;
21135         }
21136         
21137         
21138         switch(cfg.xtype) 
21139         {
21140             case 'ContentPanel':  // ContentPanel (el, cfg)
21141                 if(cfg.autoCreate) {
21142                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
21143                 } else {
21144                     var el = this.el.createChild();
21145                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
21146                 }
21147                 
21148                 this.add(region, ret);
21149                 break;
21150             
21151             
21152             case 'TreePanel': // our new panel!
21153                 cfg.el = this.el.createChild();
21154                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
21155                 this.add(region, ret);
21156                 break;
21157             
21158             case 'NestedLayoutPanel': 
21159                 // create a new Layout (which is  a Border Layout...
21160                 var el = this.el.createChild();
21161                 var clayout = cfg.layout;
21162                 delete cfg.layout;
21163                 clayout.items   = clayout.items  || [];
21164                 // replace this exitems with the clayout ones..
21165                 xitems = clayout.items;
21166                  
21167                 
21168                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
21169                     cfg.background = false;
21170                 }
21171                 var layout = new Roo.BorderLayout(el, clayout);
21172                 
21173                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
21174                 //console.log('adding nested layout panel '  + cfg.toSource());
21175                 this.add(region, ret);
21176                 
21177                 break;
21178                 
21179             case 'GridPanel': 
21180             
21181                 // needs grid and region
21182                 
21183                 //var el = this.getRegion(region).el.createChild();
21184                 var el = this.el.createChild();
21185                 // create the grid first...
21186                 
21187                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
21188                 delete cfg.grid;
21189                 if (region == 'center' && this.active ) {
21190                     cfg.background = false;
21191                 }
21192                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
21193                 
21194                 this.add(region, ret);
21195                 if (cfg.background) {
21196                     ret.on('activate', function(gp) {
21197                         if (!gp.grid.rendered) {
21198                             gp.grid.render();
21199                         }
21200                     });
21201                 } else {
21202                     grid.render();
21203                 }
21204                 break;
21205            
21206                
21207                 
21208                 
21209             default: 
21210                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
21211                 return;
21212              // GridPanel (grid, cfg)
21213             
21214         }
21215         this.beginUpdate();
21216         // add children..
21217         Roo.each(xitems, function(i)  {
21218             ret.addxtype(i);
21219         });
21220         this.endUpdate();
21221         return ret;
21222         
21223     }
21224 });
21225
21226 /**
21227  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
21228  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
21229  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
21230  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
21231  * <pre><code>
21232 // shorthand
21233 var CP = Roo.ContentPanel;
21234
21235 var layout = Roo.BorderLayout.create({
21236     north: {
21237         initialSize: 25,
21238         titlebar: false,
21239         panels: [new CP("north", "North")]
21240     },
21241     west: {
21242         split:true,
21243         initialSize: 200,
21244         minSize: 175,
21245         maxSize: 400,
21246         titlebar: true,
21247         collapsible: true,
21248         panels: [new CP("west", {title: "West"})]
21249     },
21250     east: {
21251         split:true,
21252         initialSize: 202,
21253         minSize: 175,
21254         maxSize: 400,
21255         titlebar: true,
21256         collapsible: true,
21257         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
21258     },
21259     south: {
21260         split:true,
21261         initialSize: 100,
21262         minSize: 100,
21263         maxSize: 200,
21264         titlebar: true,
21265         collapsible: true,
21266         panels: [new CP("south", {title: "South", closable: true})]
21267     },
21268     center: {
21269         titlebar: true,
21270         autoScroll:true,
21271         resizeTabs: true,
21272         minTabWidth: 50,
21273         preferredTabWidth: 150,
21274         panels: [
21275             new CP("center1", {title: "Close Me", closable: true}),
21276             new CP("center2", {title: "Center Panel", closable: false})
21277         ]
21278     }
21279 }, document.body);
21280
21281 layout.getRegion("center").showPanel("center1");
21282 </code></pre>
21283  * @param config
21284  * @param targetEl
21285  */
21286 Roo.BorderLayout.create = function(config, targetEl){
21287     var layout = new Roo.BorderLayout(targetEl || document.body, config);
21288     layout.beginUpdate();
21289     var regions = Roo.BorderLayout.RegionFactory.validRegions;
21290     for(var j = 0, jlen = regions.length; j < jlen; j++){
21291         var lr = regions[j];
21292         if(layout.regions[lr] && config[lr].panels){
21293             var r = layout.regions[lr];
21294             var ps = config[lr].panels;
21295             layout.addTypedPanels(r, ps);
21296         }
21297     }
21298     layout.endUpdate();
21299     return layout;
21300 };
21301
21302 // private
21303 Roo.BorderLayout.RegionFactory = {
21304     // private
21305     validRegions : ["north","south","east","west","center"],
21306
21307     // private
21308     create : function(target, mgr, config){
21309         target = target.toLowerCase();
21310         if(config.lightweight || config.basic){
21311             return new Roo.BasicLayoutRegion(mgr, config, target);
21312         }
21313         switch(target){
21314             case "north":
21315                 return new Roo.NorthLayoutRegion(mgr, config);
21316             case "south":
21317                 return new Roo.SouthLayoutRegion(mgr, config);
21318             case "east":
21319                 return new Roo.EastLayoutRegion(mgr, config);
21320             case "west":
21321                 return new Roo.WestLayoutRegion(mgr, config);
21322             case "center":
21323                 return new Roo.CenterLayoutRegion(mgr, config);
21324         }
21325         throw 'Layout region "'+target+'" not supported.';
21326     }
21327 };/*
21328  * Based on:
21329  * Ext JS Library 1.1.1
21330  * Copyright(c) 2006-2007, Ext JS, LLC.
21331  *
21332  * Originally Released Under LGPL - original licence link has changed is not relivant.
21333  *
21334  * Fork - LGPL
21335  * <script type="text/javascript">
21336  */
21337  
21338 /**
21339  * @class Roo.BasicLayoutRegion
21340  * @extends Roo.util.Observable
21341  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
21342  * and does not have a titlebar, tabs or any other features. All it does is size and position 
21343  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
21344  */
21345 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
21346     this.mgr = mgr;
21347     this.position  = pos;
21348     this.events = {
21349         /**
21350          * @scope Roo.BasicLayoutRegion
21351          */
21352         
21353         /**
21354          * @event beforeremove
21355          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
21356          * @param {Roo.LayoutRegion} this
21357          * @param {Roo.ContentPanel} panel The panel
21358          * @param {Object} e The cancel event object
21359          */
21360         "beforeremove" : true,
21361         /**
21362          * @event invalidated
21363          * Fires when the layout for this region is changed.
21364          * @param {Roo.LayoutRegion} this
21365          */
21366         "invalidated" : true,
21367         /**
21368          * @event visibilitychange
21369          * Fires when this region is shown or hidden 
21370          * @param {Roo.LayoutRegion} this
21371          * @param {Boolean} visibility true or false
21372          */
21373         "visibilitychange" : true,
21374         /**
21375          * @event paneladded
21376          * Fires when a panel is added. 
21377          * @param {Roo.LayoutRegion} this
21378          * @param {Roo.ContentPanel} panel The panel
21379          */
21380         "paneladded" : true,
21381         /**
21382          * @event panelremoved
21383          * Fires when a panel is removed. 
21384          * @param {Roo.LayoutRegion} this
21385          * @param {Roo.ContentPanel} panel The panel
21386          */
21387         "panelremoved" : true,
21388         /**
21389          * @event collapsed
21390          * Fires when this region is collapsed.
21391          * @param {Roo.LayoutRegion} this
21392          */
21393         "collapsed" : true,
21394         /**
21395          * @event expanded
21396          * Fires when this region is expanded.
21397          * @param {Roo.LayoutRegion} this
21398          */
21399         "expanded" : true,
21400         /**
21401          * @event slideshow
21402          * Fires when this region is slid into view.
21403          * @param {Roo.LayoutRegion} this
21404          */
21405         "slideshow" : true,
21406         /**
21407          * @event slidehide
21408          * Fires when this region slides out of view. 
21409          * @param {Roo.LayoutRegion} this
21410          */
21411         "slidehide" : true,
21412         /**
21413          * @event panelactivated
21414          * Fires when a panel is activated. 
21415          * @param {Roo.LayoutRegion} this
21416          * @param {Roo.ContentPanel} panel The activated panel
21417          */
21418         "panelactivated" : true,
21419         /**
21420          * @event resized
21421          * Fires when the user resizes this region. 
21422          * @param {Roo.LayoutRegion} this
21423          * @param {Number} newSize The new size (width for east/west, height for north/south)
21424          */
21425         "resized" : true
21426     };
21427     /** A collection of panels in this region. @type Roo.util.MixedCollection */
21428     this.panels = new Roo.util.MixedCollection();
21429     this.panels.getKey = this.getPanelId.createDelegate(this);
21430     this.box = null;
21431     this.activePanel = null;
21432     // ensure listeners are added...
21433     
21434     if (config.listeners || config.events) {
21435         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
21436             listeners : config.listeners || {},
21437             events : config.events || {}
21438         });
21439     }
21440     
21441     if(skipConfig !== true){
21442         this.applyConfig(config);
21443     }
21444 };
21445
21446 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
21447     getPanelId : function(p){
21448         return p.getId();
21449     },
21450     
21451     applyConfig : function(config){
21452         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
21453         this.config = config;
21454         
21455     },
21456     
21457     /**
21458      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
21459      * the width, for horizontal (north, south) the height.
21460      * @param {Number} newSize The new width or height
21461      */
21462     resizeTo : function(newSize){
21463         var el = this.el ? this.el :
21464                  (this.activePanel ? this.activePanel.getEl() : null);
21465         if(el){
21466             switch(this.position){
21467                 case "east":
21468                 case "west":
21469                     el.setWidth(newSize);
21470                     this.fireEvent("resized", this, newSize);
21471                 break;
21472                 case "north":
21473                 case "south":
21474                     el.setHeight(newSize);
21475                     this.fireEvent("resized", this, newSize);
21476                 break;                
21477             }
21478         }
21479     },
21480     
21481     getBox : function(){
21482         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
21483     },
21484     
21485     getMargins : function(){
21486         return this.margins;
21487     },
21488     
21489     updateBox : function(box){
21490         this.box = box;
21491         var el = this.activePanel.getEl();
21492         el.dom.style.left = box.x + "px";
21493         el.dom.style.top = box.y + "px";
21494         this.activePanel.setSize(box.width, box.height);
21495     },
21496     
21497     /**
21498      * Returns the container element for this region.
21499      * @return {Roo.Element}
21500      */
21501     getEl : function(){
21502         return this.activePanel;
21503     },
21504     
21505     /**
21506      * Returns true if this region is currently visible.
21507      * @return {Boolean}
21508      */
21509     isVisible : function(){
21510         return this.activePanel ? true : false;
21511     },
21512     
21513     setActivePanel : function(panel){
21514         panel = this.getPanel(panel);
21515         if(this.activePanel && this.activePanel != panel){
21516             this.activePanel.setActiveState(false);
21517             this.activePanel.getEl().setLeftTop(-10000,-10000);
21518         }
21519         this.activePanel = panel;
21520         panel.setActiveState(true);
21521         if(this.box){
21522             panel.setSize(this.box.width, this.box.height);
21523         }
21524         this.fireEvent("panelactivated", this, panel);
21525         this.fireEvent("invalidated");
21526     },
21527     
21528     /**
21529      * Show the specified panel.
21530      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
21531      * @return {Roo.ContentPanel} The shown panel or null
21532      */
21533     showPanel : function(panel){
21534         if(panel = this.getPanel(panel)){
21535             this.setActivePanel(panel);
21536         }
21537         return panel;
21538     },
21539     
21540     /**
21541      * Get the active panel for this region.
21542      * @return {Roo.ContentPanel} The active panel or null
21543      */
21544     getActivePanel : function(){
21545         return this.activePanel;
21546     },
21547     
21548     /**
21549      * Add the passed ContentPanel(s)
21550      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
21551      * @return {Roo.ContentPanel} The panel added (if only one was added)
21552      */
21553     add : function(panel){
21554         if(arguments.length > 1){
21555             for(var i = 0, len = arguments.length; i < len; i++) {
21556                 this.add(arguments[i]);
21557             }
21558             return null;
21559         }
21560         if(this.hasPanel(panel)){
21561             this.showPanel(panel);
21562             return panel;
21563         }
21564         var el = panel.getEl();
21565         if(el.dom.parentNode != this.mgr.el.dom){
21566             this.mgr.el.dom.appendChild(el.dom);
21567         }
21568         if(panel.setRegion){
21569             panel.setRegion(this);
21570         }
21571         this.panels.add(panel);
21572         el.setStyle("position", "absolute");
21573         if(!panel.background){
21574             this.setActivePanel(panel);
21575             if(this.config.initialSize && this.panels.getCount()==1){
21576                 this.resizeTo(this.config.initialSize);
21577             }
21578         }
21579         this.fireEvent("paneladded", this, panel);
21580         return panel;
21581     },
21582     
21583     /**
21584      * Returns true if the panel is in this region.
21585      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
21586      * @return {Boolean}
21587      */
21588     hasPanel : function(panel){
21589         if(typeof panel == "object"){ // must be panel obj
21590             panel = panel.getId();
21591         }
21592         return this.getPanel(panel) ? true : false;
21593     },
21594     
21595     /**
21596      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
21597      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
21598      * @param {Boolean} preservePanel Overrides the config preservePanel option
21599      * @return {Roo.ContentPanel} The panel that was removed
21600      */
21601     remove : function(panel, preservePanel){
21602         panel = this.getPanel(panel);
21603         if(!panel){
21604             return null;
21605         }
21606         var e = {};
21607         this.fireEvent("beforeremove", this, panel, e);
21608         if(e.cancel === true){
21609             return null;
21610         }
21611         var panelId = panel.getId();
21612         this.panels.removeKey(panelId);
21613         return panel;
21614     },
21615     
21616     /**
21617      * Returns the panel specified or null if it's not in this region.
21618      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
21619      * @return {Roo.ContentPanel}
21620      */
21621     getPanel : function(id){
21622         if(typeof id == "object"){ // must be panel obj
21623             return id;
21624         }
21625         return this.panels.get(id);
21626     },
21627     
21628     /**
21629      * Returns this regions position (north/south/east/west/center).
21630      * @return {String} 
21631      */
21632     getPosition: function(){
21633         return this.position;    
21634     }
21635 });/*
21636  * Based on:
21637  * Ext JS Library 1.1.1
21638  * Copyright(c) 2006-2007, Ext JS, LLC.
21639  *
21640  * Originally Released Under LGPL - original licence link has changed is not relivant.
21641  *
21642  * Fork - LGPL
21643  * <script type="text/javascript">
21644  */
21645  
21646 /**
21647  * @class Roo.LayoutRegion
21648  * @extends Roo.BasicLayoutRegion
21649  * This class represents a region in a layout manager.
21650  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
21651  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
21652  * @cfg {Boolean} floatable False to disable floating (defaults to true)
21653  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
21654  * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
21655  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
21656  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
21657  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
21658  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
21659  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
21660  * @cfg {String} title The title for the region (overrides panel titles)
21661  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
21662  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
21663  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
21664  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
21665  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
21666  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
21667  * the space available, similar to FireFox 1.5 tabs (defaults to false)
21668  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
21669  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
21670  * @cfg {Boolean} showPin True to show a pin button
21671 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
21672 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
21673 * @cfg {Boolean} disableTabTips True to disable tab tooltips
21674 * @cfg {Number} width  For East/West panels
21675 * @cfg {Number} height For North/South panels
21676 * @cfg {Boolean} split To show the splitter
21677  */
21678 Roo.LayoutRegion = function(mgr, config, pos){
21679     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
21680     var dh = Roo.DomHelper;
21681     /** This region's container element 
21682     * @type Roo.Element */
21683     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
21684     /** This region's title element 
21685     * @type Roo.Element */
21686
21687     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
21688         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
21689         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
21690     ]}, true);
21691     this.titleEl.enableDisplayMode();
21692     /** This region's title text element 
21693     * @type HTMLElement */
21694     this.titleTextEl = this.titleEl.dom.firstChild;
21695     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
21696     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
21697     this.closeBtn.enableDisplayMode();
21698     this.closeBtn.on("click", this.closeClicked, this);
21699     this.closeBtn.hide();
21700
21701     this.createBody(config);
21702     this.visible = true;
21703     this.collapsed = false;
21704
21705     if(config.hideWhenEmpty){
21706         this.hide();
21707         this.on("paneladded", this.validateVisibility, this);
21708         this.on("panelremoved", this.validateVisibility, this);
21709     }
21710     this.applyConfig(config);
21711 };
21712
21713 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
21714
21715     createBody : function(){
21716         /** This region's body element 
21717         * @type Roo.Element */
21718         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
21719     },
21720
21721     applyConfig : function(c){
21722         if(c.collapsible && this.position != "center" && !this.collapsedEl){
21723             var dh = Roo.DomHelper;
21724             if(c.titlebar !== false){
21725                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
21726                 this.collapseBtn.on("click", this.collapse, this);
21727                 this.collapseBtn.enableDisplayMode();
21728
21729                 if(c.showPin === true || this.showPin){
21730                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
21731                     this.stickBtn.enableDisplayMode();
21732                     this.stickBtn.on("click", this.expand, this);
21733                     this.stickBtn.hide();
21734                 }
21735             }
21736             /** This region's collapsed element
21737             * @type Roo.Element */
21738             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
21739                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
21740             ]}, true);
21741             if(c.floatable !== false){
21742                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
21743                this.collapsedEl.on("click", this.collapseClick, this);
21744             }
21745
21746             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
21747                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
21748                    id: "message", unselectable: "on", style:{"float":"left"}});
21749                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
21750              }
21751             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
21752             this.expandBtn.on("click", this.expand, this);
21753         }
21754         if(this.collapseBtn){
21755             this.collapseBtn.setVisible(c.collapsible == true);
21756         }
21757         this.cmargins = c.cmargins || this.cmargins ||
21758                          (this.position == "west" || this.position == "east" ?
21759                              {top: 0, left: 2, right:2, bottom: 0} :
21760                              {top: 2, left: 0, right:0, bottom: 2});
21761         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
21762         this.bottomTabs = c.tabPosition != "top";
21763         this.autoScroll = c.autoScroll || false;
21764         if(this.autoScroll){
21765             this.bodyEl.setStyle("overflow", "auto");
21766         }else{
21767             this.bodyEl.setStyle("overflow", "hidden");
21768         }
21769         //if(c.titlebar !== false){
21770             if((!c.titlebar && !c.title) || c.titlebar === false){
21771                 this.titleEl.hide();
21772             }else{
21773                 this.titleEl.show();
21774                 if(c.title){
21775                     this.titleTextEl.innerHTML = c.title;
21776                 }
21777             }
21778         //}
21779         this.duration = c.duration || .30;
21780         this.slideDuration = c.slideDuration || .45;
21781         this.config = c;
21782         if(c.collapsed){
21783             this.collapse(true);
21784         }
21785         if(c.hidden){
21786             this.hide();
21787         }
21788     },
21789     /**
21790      * Returns true if this region is currently visible.
21791      * @return {Boolean}
21792      */
21793     isVisible : function(){
21794         return this.visible;
21795     },
21796
21797     /**
21798      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
21799      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
21800      */
21801     setCollapsedTitle : function(title){
21802         title = title || "&#160;";
21803         if(this.collapsedTitleTextEl){
21804             this.collapsedTitleTextEl.innerHTML = title;
21805         }
21806     },
21807
21808     getBox : function(){
21809         var b;
21810         if(!this.collapsed){
21811             b = this.el.getBox(false, true);
21812         }else{
21813             b = this.collapsedEl.getBox(false, true);
21814         }
21815         return b;
21816     },
21817
21818     getMargins : function(){
21819         return this.collapsed ? this.cmargins : this.margins;
21820     },
21821
21822     highlight : function(){
21823         this.el.addClass("x-layout-panel-dragover");
21824     },
21825
21826     unhighlight : function(){
21827         this.el.removeClass("x-layout-panel-dragover");
21828     },
21829
21830     updateBox : function(box){
21831         this.box = box;
21832         if(!this.collapsed){
21833             this.el.dom.style.left = box.x + "px";
21834             this.el.dom.style.top = box.y + "px";
21835             this.updateBody(box.width, box.height);
21836         }else{
21837             this.collapsedEl.dom.style.left = box.x + "px";
21838             this.collapsedEl.dom.style.top = box.y + "px";
21839             this.collapsedEl.setSize(box.width, box.height);
21840         }
21841         if(this.tabs){
21842             this.tabs.autoSizeTabs();
21843         }
21844     },
21845
21846     updateBody : function(w, h){
21847         if(w !== null){
21848             this.el.setWidth(w);
21849             w -= this.el.getBorderWidth("rl");
21850             if(this.config.adjustments){
21851                 w += this.config.adjustments[0];
21852             }
21853         }
21854         if(h !== null){
21855             this.el.setHeight(h);
21856             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
21857             h -= this.el.getBorderWidth("tb");
21858             if(this.config.adjustments){
21859                 h += this.config.adjustments[1];
21860             }
21861             this.bodyEl.setHeight(h);
21862             if(this.tabs){
21863                 h = this.tabs.syncHeight(h);
21864             }
21865         }
21866         if(this.panelSize){
21867             w = w !== null ? w : this.panelSize.width;
21868             h = h !== null ? h : this.panelSize.height;
21869         }
21870         if(this.activePanel){
21871             var el = this.activePanel.getEl();
21872             w = w !== null ? w : el.getWidth();
21873             h = h !== null ? h : el.getHeight();
21874             this.panelSize = {width: w, height: h};
21875             this.activePanel.setSize(w, h);
21876         }
21877         if(Roo.isIE && this.tabs){
21878             this.tabs.el.repaint();
21879         }
21880     },
21881
21882     /**
21883      * Returns the container element for this region.
21884      * @return {Roo.Element}
21885      */
21886     getEl : function(){
21887         return this.el;
21888     },
21889
21890     /**
21891      * Hides this region.
21892      */
21893     hide : function(){
21894         if(!this.collapsed){
21895             this.el.dom.style.left = "-2000px";
21896             this.el.hide();
21897         }else{
21898             this.collapsedEl.dom.style.left = "-2000px";
21899             this.collapsedEl.hide();
21900         }
21901         this.visible = false;
21902         this.fireEvent("visibilitychange", this, false);
21903     },
21904
21905     /**
21906      * Shows this region if it was previously hidden.
21907      */
21908     show : function(){
21909         if(!this.collapsed){
21910             this.el.show();
21911         }else{
21912             this.collapsedEl.show();
21913         }
21914         this.visible = true;
21915         this.fireEvent("visibilitychange", this, true);
21916     },
21917
21918     closeClicked : function(){
21919         if(this.activePanel){
21920             this.remove(this.activePanel);
21921         }
21922     },
21923
21924     collapseClick : function(e){
21925         if(this.isSlid){
21926            e.stopPropagation();
21927            this.slideIn();
21928         }else{
21929            e.stopPropagation();
21930            this.slideOut();
21931         }
21932     },
21933
21934     /**
21935      * Collapses this region.
21936      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
21937      */
21938     collapse : function(skipAnim){
21939         if(this.collapsed) return;
21940         this.collapsed = true;
21941         if(this.split){
21942             this.split.el.hide();
21943         }
21944         if(this.config.animate && skipAnim !== true){
21945             this.fireEvent("invalidated", this);
21946             this.animateCollapse();
21947         }else{
21948             this.el.setLocation(-20000,-20000);
21949             this.el.hide();
21950             this.collapsedEl.show();
21951             this.fireEvent("collapsed", this);
21952             this.fireEvent("invalidated", this);
21953         }
21954     },
21955
21956     animateCollapse : function(){
21957         // overridden
21958     },
21959
21960     /**
21961      * Expands this region if it was previously collapsed.
21962      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
21963      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
21964      */
21965     expand : function(e, skipAnim){
21966         if(e) e.stopPropagation();
21967         if(!this.collapsed || this.el.hasActiveFx()) return;
21968         if(this.isSlid){
21969             this.afterSlideIn();
21970             skipAnim = true;
21971         }
21972         this.collapsed = false;
21973         if(this.config.animate && skipAnim !== true){
21974             this.animateExpand();
21975         }else{
21976             this.el.show();
21977             if(this.split){
21978                 this.split.el.show();
21979             }
21980             this.collapsedEl.setLocation(-2000,-2000);
21981             this.collapsedEl.hide();
21982             this.fireEvent("invalidated", this);
21983             this.fireEvent("expanded", this);
21984         }
21985     },
21986
21987     animateExpand : function(){
21988         // overridden
21989     },
21990
21991     initTabs : function(){
21992         this.bodyEl.setStyle("overflow", "hidden");
21993         var ts = new Roo.TabPanel(this.bodyEl.dom, {
21994             tabPosition: this.bottomTabs ? 'bottom' : 'top',
21995             disableTooltips: this.config.disableTabTips
21996         });
21997         if(this.config.hideTabs){
21998             ts.stripWrap.setDisplayed(false);
21999         }
22000         this.tabs = ts;
22001         ts.resizeTabs = this.config.resizeTabs === true;
22002         ts.minTabWidth = this.config.minTabWidth || 40;
22003         ts.maxTabWidth = this.config.maxTabWidth || 250;
22004         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
22005         ts.monitorResize = false;
22006         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
22007         ts.bodyEl.addClass('x-layout-tabs-body');
22008         this.panels.each(this.initPanelAsTab, this);
22009     },
22010
22011     initPanelAsTab : function(panel){
22012         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
22013                     this.config.closeOnTab && panel.isClosable());
22014         if(panel.tabTip !== undefined){
22015             ti.setTooltip(panel.tabTip);
22016         }
22017         ti.on("activate", function(){
22018               this.setActivePanel(panel);
22019         }, this);
22020         if(this.config.closeOnTab){
22021             ti.on("beforeclose", function(t, e){
22022                 e.cancel = true;
22023                 this.remove(panel);
22024             }, this);
22025         }
22026         return ti;
22027     },
22028
22029     updatePanelTitle : function(panel, title){
22030         if(this.activePanel == panel){
22031             this.updateTitle(title);
22032         }
22033         if(this.tabs){
22034             var ti = this.tabs.getTab(panel.getEl().id);
22035             ti.setText(title);
22036             if(panel.tabTip !== undefined){
22037                 ti.setTooltip(panel.tabTip);
22038             }
22039         }
22040     },
22041
22042     updateTitle : function(title){
22043         if(this.titleTextEl && !this.config.title){
22044             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
22045         }
22046     },
22047
22048     setActivePanel : function(panel){
22049         panel = this.getPanel(panel);
22050         if(this.activePanel && this.activePanel != panel){
22051             this.activePanel.setActiveState(false);
22052         }
22053         this.activePanel = panel;
22054         panel.setActiveState(true);
22055         if(this.panelSize){
22056             panel.setSize(this.panelSize.width, this.panelSize.height);
22057         }
22058         if(this.closeBtn){
22059             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
22060         }
22061         this.updateTitle(panel.getTitle());
22062         if(this.tabs){
22063             this.fireEvent("invalidated", this);
22064         }
22065         this.fireEvent("panelactivated", this, panel);
22066     },
22067
22068     /**
22069      * Shows the specified panel.
22070      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
22071      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
22072      */
22073     showPanel : function(panel){
22074         if(panel = this.getPanel(panel)){
22075             if(this.tabs){
22076                 var tab = this.tabs.getTab(panel.getEl().id);
22077                 if(tab.isHidden()){
22078                     this.tabs.unhideTab(tab.id);
22079                 }
22080                 tab.activate();
22081             }else{
22082                 this.setActivePanel(panel);
22083             }
22084         }
22085         return panel;
22086     },
22087
22088     /**
22089      * Get the active panel for this region.
22090      * @return {Roo.ContentPanel} The active panel or null
22091      */
22092     getActivePanel : function(){
22093         return this.activePanel;
22094     },
22095
22096     validateVisibility : function(){
22097         if(this.panels.getCount() < 1){
22098             this.updateTitle("&#160;");
22099             this.closeBtn.hide();
22100             this.hide();
22101         }else{
22102             if(!this.isVisible()){
22103                 this.show();
22104             }
22105         }
22106     },
22107
22108     /**
22109      * Adds the passed ContentPanel(s) to this region.
22110      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
22111      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
22112      */
22113     add : function(panel){
22114         if(arguments.length > 1){
22115             for(var i = 0, len = arguments.length; i < len; i++) {
22116                 this.add(arguments[i]);
22117             }
22118             return null;
22119         }
22120         if(this.hasPanel(panel)){
22121             this.showPanel(panel);
22122             return panel;
22123         }
22124         panel.setRegion(this);
22125         this.panels.add(panel);
22126         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
22127             this.bodyEl.dom.appendChild(panel.getEl().dom);
22128             if(panel.background !== true){
22129                 this.setActivePanel(panel);
22130             }
22131             this.fireEvent("paneladded", this, panel);
22132             return panel;
22133         }
22134         if(!this.tabs){
22135             this.initTabs();
22136         }else{
22137             this.initPanelAsTab(panel);
22138         }
22139         if(panel.background !== true){
22140             this.tabs.activate(panel.getEl().id);
22141         }
22142         this.fireEvent("paneladded", this, panel);
22143         return panel;
22144     },
22145
22146     /**
22147      * Hides the tab for the specified panel.
22148      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
22149      */
22150     hidePanel : function(panel){
22151         if(this.tabs && (panel = this.getPanel(panel))){
22152             this.tabs.hideTab(panel.getEl().id);
22153         }
22154     },
22155
22156     /**
22157      * Unhides the tab for a previously hidden panel.
22158      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
22159      */
22160     unhidePanel : function(panel){
22161         if(this.tabs && (panel = this.getPanel(panel))){
22162             this.tabs.unhideTab(panel.getEl().id);
22163         }
22164     },
22165
22166     clearPanels : function(){
22167         while(this.panels.getCount() > 0){
22168              this.remove(this.panels.first());
22169         }
22170     },
22171
22172     /**
22173      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
22174      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
22175      * @param {Boolean} preservePanel Overrides the config preservePanel option
22176      * @return {Roo.ContentPanel} The panel that was removed
22177      */
22178     remove : function(panel, preservePanel){
22179         panel = this.getPanel(panel);
22180         if(!panel){
22181             return null;
22182         }
22183         var e = {};
22184         this.fireEvent("beforeremove", this, panel, e);
22185         if(e.cancel === true){
22186             return null;
22187         }
22188         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
22189         var panelId = panel.getId();
22190         this.panels.removeKey(panelId);
22191         if(preservePanel){
22192             document.body.appendChild(panel.getEl().dom);
22193         }
22194         if(this.tabs){
22195             this.tabs.removeTab(panel.getEl().id);
22196         }else if (!preservePanel){
22197             this.bodyEl.dom.removeChild(panel.getEl().dom);
22198         }
22199         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
22200             var p = this.panels.first();
22201             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
22202             tempEl.appendChild(p.getEl().dom);
22203             this.bodyEl.update("");
22204             this.bodyEl.dom.appendChild(p.getEl().dom);
22205             tempEl = null;
22206             this.updateTitle(p.getTitle());
22207             this.tabs = null;
22208             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
22209             this.setActivePanel(p);
22210         }
22211         panel.setRegion(null);
22212         if(this.activePanel == panel){
22213             this.activePanel = null;
22214         }
22215         if(this.config.autoDestroy !== false && preservePanel !== true){
22216             try{panel.destroy();}catch(e){}
22217         }
22218         this.fireEvent("panelremoved", this, panel);
22219         return panel;
22220     },
22221
22222     /**
22223      * Returns the TabPanel component used by this region
22224      * @return {Roo.TabPanel}
22225      */
22226     getTabs : function(){
22227         return this.tabs;
22228     },
22229
22230     createTool : function(parentEl, className){
22231         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
22232             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
22233         btn.addClassOnOver("x-layout-tools-button-over");
22234         return btn;
22235     }
22236 });/*
22237  * Based on:
22238  * Ext JS Library 1.1.1
22239  * Copyright(c) 2006-2007, Ext JS, LLC.
22240  *
22241  * Originally Released Under LGPL - original licence link has changed is not relivant.
22242  *
22243  * Fork - LGPL
22244  * <script type="text/javascript">
22245  */
22246  
22247
22248
22249 /**
22250  * @class Roo.SplitLayoutRegion
22251  * @extends Roo.LayoutRegion
22252  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
22253  */
22254 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
22255     this.cursor = cursor;
22256     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
22257 };
22258
22259 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
22260     splitTip : "Drag to resize.",
22261     collapsibleSplitTip : "Drag to resize. Double click to hide.",
22262     useSplitTips : false,
22263
22264     applyConfig : function(config){
22265         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
22266         if(config.split){
22267             if(!this.split){
22268                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
22269                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
22270                 /** The SplitBar for this region 
22271                 * @type Roo.SplitBar */
22272                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
22273                 this.split.on("moved", this.onSplitMove, this);
22274                 this.split.useShim = config.useShim === true;
22275                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
22276                 if(this.useSplitTips){
22277                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
22278                 }
22279                 if(config.collapsible){
22280                     this.split.el.on("dblclick", this.collapse,  this);
22281                 }
22282             }
22283             if(typeof config.minSize != "undefined"){
22284                 this.split.minSize = config.minSize;
22285             }
22286             if(typeof config.maxSize != "undefined"){
22287                 this.split.maxSize = config.maxSize;
22288             }
22289             if(config.hideWhenEmpty || config.hidden || config.collapsed){
22290                 this.hideSplitter();
22291             }
22292         }
22293     },
22294
22295     getHMaxSize : function(){
22296          var cmax = this.config.maxSize || 10000;
22297          var center = this.mgr.getRegion("center");
22298          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
22299     },
22300
22301     getVMaxSize : function(){
22302          var cmax = this.config.maxSize || 10000;
22303          var center = this.mgr.getRegion("center");
22304          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
22305     },
22306
22307     onSplitMove : function(split, newSize){
22308         this.fireEvent("resized", this, newSize);
22309     },
22310     
22311     /** 
22312      * Returns the {@link Roo.SplitBar} for this region.
22313      * @return {Roo.SplitBar}
22314      */
22315     getSplitBar : function(){
22316         return this.split;
22317     },
22318     
22319     hide : function(){
22320         this.hideSplitter();
22321         Roo.SplitLayoutRegion.superclass.hide.call(this);
22322     },
22323
22324     hideSplitter : function(){
22325         if(this.split){
22326             this.split.el.setLocation(-2000,-2000);
22327             this.split.el.hide();
22328         }
22329     },
22330
22331     show : function(){
22332         if(this.split){
22333             this.split.el.show();
22334         }
22335         Roo.SplitLayoutRegion.superclass.show.call(this);
22336     },
22337     
22338     beforeSlide: function(){
22339         if(Roo.isGecko){// firefox overflow auto bug workaround
22340             this.bodyEl.clip();
22341             if(this.tabs) this.tabs.bodyEl.clip();
22342             if(this.activePanel){
22343                 this.activePanel.getEl().clip();
22344                 
22345                 if(this.activePanel.beforeSlide){
22346                     this.activePanel.beforeSlide();
22347                 }
22348             }
22349         }
22350     },
22351     
22352     afterSlide : function(){
22353         if(Roo.isGecko){// firefox overflow auto bug workaround
22354             this.bodyEl.unclip();
22355             if(this.tabs) this.tabs.bodyEl.unclip();
22356             if(this.activePanel){
22357                 this.activePanel.getEl().unclip();
22358                 if(this.activePanel.afterSlide){
22359                     this.activePanel.afterSlide();
22360                 }
22361             }
22362         }
22363     },
22364
22365     initAutoHide : function(){
22366         if(this.autoHide !== false){
22367             if(!this.autoHideHd){
22368                 var st = new Roo.util.DelayedTask(this.slideIn, this);
22369                 this.autoHideHd = {
22370                     "mouseout": function(e){
22371                         if(!e.within(this.el, true)){
22372                             st.delay(500);
22373                         }
22374                     },
22375                     "mouseover" : function(e){
22376                         st.cancel();
22377                     },
22378                     scope : this
22379                 };
22380             }
22381             this.el.on(this.autoHideHd);
22382         }
22383     },
22384
22385     clearAutoHide : function(){
22386         if(this.autoHide !== false){
22387             this.el.un("mouseout", this.autoHideHd.mouseout);
22388             this.el.un("mouseover", this.autoHideHd.mouseover);
22389         }
22390     },
22391
22392     clearMonitor : function(){
22393         Roo.get(document).un("click", this.slideInIf, this);
22394     },
22395
22396     // these names are backwards but not changed for compat
22397     slideOut : function(){
22398         if(this.isSlid || this.el.hasActiveFx()){
22399             return;
22400         }
22401         this.isSlid = true;
22402         if(this.collapseBtn){
22403             this.collapseBtn.hide();
22404         }
22405         this.closeBtnState = this.closeBtn.getStyle('display');
22406         this.closeBtn.hide();
22407         if(this.stickBtn){
22408             this.stickBtn.show();
22409         }
22410         this.el.show();
22411         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
22412         this.beforeSlide();
22413         this.el.setStyle("z-index", 10001);
22414         this.el.slideIn(this.getSlideAnchor(), {
22415             callback: function(){
22416                 this.afterSlide();
22417                 this.initAutoHide();
22418                 Roo.get(document).on("click", this.slideInIf, this);
22419                 this.fireEvent("slideshow", this);
22420             },
22421             scope: this,
22422             block: true
22423         });
22424     },
22425
22426     afterSlideIn : function(){
22427         this.clearAutoHide();
22428         this.isSlid = false;
22429         this.clearMonitor();
22430         this.el.setStyle("z-index", "");
22431         if(this.collapseBtn){
22432             this.collapseBtn.show();
22433         }
22434         this.closeBtn.setStyle('display', this.closeBtnState);
22435         if(this.stickBtn){
22436             this.stickBtn.hide();
22437         }
22438         this.fireEvent("slidehide", this);
22439     },
22440
22441     slideIn : function(cb){
22442         if(!this.isSlid || this.el.hasActiveFx()){
22443             Roo.callback(cb);
22444             return;
22445         }
22446         this.isSlid = false;
22447         this.beforeSlide();
22448         this.el.slideOut(this.getSlideAnchor(), {
22449             callback: function(){
22450                 this.el.setLeftTop(-10000, -10000);
22451                 this.afterSlide();
22452                 this.afterSlideIn();
22453                 Roo.callback(cb);
22454             },
22455             scope: this,
22456             block: true
22457         });
22458     },
22459     
22460     slideInIf : function(e){
22461         if(!e.within(this.el)){
22462             this.slideIn();
22463         }
22464     },
22465
22466     animateCollapse : function(){
22467         this.beforeSlide();
22468         this.el.setStyle("z-index", 20000);
22469         var anchor = this.getSlideAnchor();
22470         this.el.slideOut(anchor, {
22471             callback : function(){
22472                 this.el.setStyle("z-index", "");
22473                 this.collapsedEl.slideIn(anchor, {duration:.3});
22474                 this.afterSlide();
22475                 this.el.setLocation(-10000,-10000);
22476                 this.el.hide();
22477                 this.fireEvent("collapsed", this);
22478             },
22479             scope: this,
22480             block: true
22481         });
22482     },
22483
22484     animateExpand : function(){
22485         this.beforeSlide();
22486         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
22487         this.el.setStyle("z-index", 20000);
22488         this.collapsedEl.hide({
22489             duration:.1
22490         });
22491         this.el.slideIn(this.getSlideAnchor(), {
22492             callback : function(){
22493                 this.el.setStyle("z-index", "");
22494                 this.afterSlide();
22495                 if(this.split){
22496                     this.split.el.show();
22497                 }
22498                 this.fireEvent("invalidated", this);
22499                 this.fireEvent("expanded", this);
22500             },
22501             scope: this,
22502             block: true
22503         });
22504     },
22505
22506     anchors : {
22507         "west" : "left",
22508         "east" : "right",
22509         "north" : "top",
22510         "south" : "bottom"
22511     },
22512
22513     sanchors : {
22514         "west" : "l",
22515         "east" : "r",
22516         "north" : "t",
22517         "south" : "b"
22518     },
22519
22520     canchors : {
22521         "west" : "tl-tr",
22522         "east" : "tr-tl",
22523         "north" : "tl-bl",
22524         "south" : "bl-tl"
22525     },
22526
22527     getAnchor : function(){
22528         return this.anchors[this.position];
22529     },
22530
22531     getCollapseAnchor : function(){
22532         return this.canchors[this.position];
22533     },
22534
22535     getSlideAnchor : function(){
22536         return this.sanchors[this.position];
22537     },
22538
22539     getAlignAdj : function(){
22540         var cm = this.cmargins;
22541         switch(this.position){
22542             case "west":
22543                 return [0, 0];
22544             break;
22545             case "east":
22546                 return [0, 0];
22547             break;
22548             case "north":
22549                 return [0, 0];
22550             break;
22551             case "south":
22552                 return [0, 0];
22553             break;
22554         }
22555     },
22556
22557     getExpandAdj : function(){
22558         var c = this.collapsedEl, cm = this.cmargins;
22559         switch(this.position){
22560             case "west":
22561                 return [-(cm.right+c.getWidth()+cm.left), 0];
22562             break;
22563             case "east":
22564                 return [cm.right+c.getWidth()+cm.left, 0];
22565             break;
22566             case "north":
22567                 return [0, -(cm.top+cm.bottom+c.getHeight())];
22568             break;
22569             case "south":
22570                 return [0, cm.top+cm.bottom+c.getHeight()];
22571             break;
22572         }
22573     }
22574 });/*
22575  * Based on:
22576  * Ext JS Library 1.1.1
22577  * Copyright(c) 2006-2007, Ext JS, LLC.
22578  *
22579  * Originally Released Under LGPL - original licence link has changed is not relivant.
22580  *
22581  * Fork - LGPL
22582  * <script type="text/javascript">
22583  */
22584 /*
22585  * These classes are private internal classes
22586  */
22587 Roo.CenterLayoutRegion = function(mgr, config){
22588     Roo.LayoutRegion.call(this, mgr, config, "center");
22589     this.visible = true;
22590     this.minWidth = config.minWidth || 20;
22591     this.minHeight = config.minHeight || 20;
22592 };
22593
22594 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
22595     hide : function(){
22596         // center panel can't be hidden
22597     },
22598     
22599     show : function(){
22600         // center panel can't be hidden
22601     },
22602     
22603     getMinWidth: function(){
22604         return this.minWidth;
22605     },
22606     
22607     getMinHeight: function(){
22608         return this.minHeight;
22609     }
22610 });
22611
22612
22613 Roo.NorthLayoutRegion = function(mgr, config){
22614     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
22615     if(this.split){
22616         this.split.placement = Roo.SplitBar.TOP;
22617         this.split.orientation = Roo.SplitBar.VERTICAL;
22618         this.split.el.addClass("x-layout-split-v");
22619     }
22620     var size = config.initialSize || config.height;
22621     if(typeof size != "undefined"){
22622         this.el.setHeight(size);
22623     }
22624 };
22625 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
22626     orientation: Roo.SplitBar.VERTICAL,
22627     getBox : function(){
22628         if(this.collapsed){
22629             return this.collapsedEl.getBox();
22630         }
22631         var box = this.el.getBox();
22632         if(this.split){
22633             box.height += this.split.el.getHeight();
22634         }
22635         return box;
22636     },
22637     
22638     updateBox : function(box){
22639         if(this.split && !this.collapsed){
22640             box.height -= this.split.el.getHeight();
22641             this.split.el.setLeft(box.x);
22642             this.split.el.setTop(box.y+box.height);
22643             this.split.el.setWidth(box.width);
22644         }
22645         if(this.collapsed){
22646             this.updateBody(box.width, null);
22647         }
22648         Roo.LayoutRegion.prototype.updateBox.call(this, box);
22649     }
22650 });
22651
22652 Roo.SouthLayoutRegion = function(mgr, config){
22653     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
22654     if(this.split){
22655         this.split.placement = Roo.SplitBar.BOTTOM;
22656         this.split.orientation = Roo.SplitBar.VERTICAL;
22657         this.split.el.addClass("x-layout-split-v");
22658     }
22659     var size = config.initialSize || config.height;
22660     if(typeof size != "undefined"){
22661         this.el.setHeight(size);
22662     }
22663 };
22664 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
22665     orientation: Roo.SplitBar.VERTICAL,
22666     getBox : function(){
22667         if(this.collapsed){
22668             return this.collapsedEl.getBox();
22669         }
22670         var box = this.el.getBox();
22671         if(this.split){
22672             var sh = this.split.el.getHeight();
22673             box.height += sh;
22674             box.y -= sh;
22675         }
22676         return box;
22677     },
22678     
22679     updateBox : function(box){
22680         if(this.split && !this.collapsed){
22681             var sh = this.split.el.getHeight();
22682             box.height -= sh;
22683             box.y += sh;
22684             this.split.el.setLeft(box.x);
22685             this.split.el.setTop(box.y-sh);
22686             this.split.el.setWidth(box.width);
22687         }
22688         if(this.collapsed){
22689             this.updateBody(box.width, null);
22690         }
22691         Roo.LayoutRegion.prototype.updateBox.call(this, box);
22692     }
22693 });
22694
22695 Roo.EastLayoutRegion = function(mgr, config){
22696     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
22697     if(this.split){
22698         this.split.placement = Roo.SplitBar.RIGHT;
22699         this.split.orientation = Roo.SplitBar.HORIZONTAL;
22700         this.split.el.addClass("x-layout-split-h");
22701     }
22702     var size = config.initialSize || config.width;
22703     if(typeof size != "undefined"){
22704         this.el.setWidth(size);
22705     }
22706 };
22707 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
22708     orientation: Roo.SplitBar.HORIZONTAL,
22709     getBox : function(){
22710         if(this.collapsed){
22711             return this.collapsedEl.getBox();
22712         }
22713         var box = this.el.getBox();
22714         if(this.split){
22715             var sw = this.split.el.getWidth();
22716             box.width += sw;
22717             box.x -= sw;
22718         }
22719         return box;
22720     },
22721
22722     updateBox : function(box){
22723         if(this.split && !this.collapsed){
22724             var sw = this.split.el.getWidth();
22725             box.width -= sw;
22726             this.split.el.setLeft(box.x);
22727             this.split.el.setTop(box.y);
22728             this.split.el.setHeight(box.height);
22729             box.x += sw;
22730         }
22731         if(this.collapsed){
22732             this.updateBody(null, box.height);
22733         }
22734         Roo.LayoutRegion.prototype.updateBox.call(this, box);
22735     }
22736 });
22737
22738 Roo.WestLayoutRegion = function(mgr, config){
22739     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
22740     if(this.split){
22741         this.split.placement = Roo.SplitBar.LEFT;
22742         this.split.orientation = Roo.SplitBar.HORIZONTAL;
22743         this.split.el.addClass("x-layout-split-h");
22744     }
22745     var size = config.initialSize || config.width;
22746     if(typeof size != "undefined"){
22747         this.el.setWidth(size);
22748     }
22749 };
22750 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
22751     orientation: Roo.SplitBar.HORIZONTAL,
22752     getBox : function(){
22753         if(this.collapsed){
22754             return this.collapsedEl.getBox();
22755         }
22756         var box = this.el.getBox();
22757         if(this.split){
22758             box.width += this.split.el.getWidth();
22759         }
22760         return box;
22761     },
22762     
22763     updateBox : function(box){
22764         if(this.split && !this.collapsed){
22765             var sw = this.split.el.getWidth();
22766             box.width -= sw;
22767             this.split.el.setLeft(box.x+box.width);
22768             this.split.el.setTop(box.y);
22769             this.split.el.setHeight(box.height);
22770         }
22771         if(this.collapsed){
22772             this.updateBody(null, box.height);
22773         }
22774         Roo.LayoutRegion.prototype.updateBox.call(this, box);
22775     }
22776 });
22777 /*
22778  * Based on:
22779  * Ext JS Library 1.1.1
22780  * Copyright(c) 2006-2007, Ext JS, LLC.
22781  *
22782  * Originally Released Under LGPL - original licence link has changed is not relivant.
22783  *
22784  * Fork - LGPL
22785  * <script type="text/javascript">
22786  */
22787  
22788  
22789 /*
22790  * Private internal class for reading and applying state
22791  */
22792 Roo.LayoutStateManager = function(layout){
22793      // default empty state
22794      this.state = {
22795         north: {},
22796         south: {},
22797         east: {},
22798         west: {}       
22799     };
22800 };
22801
22802 Roo.LayoutStateManager.prototype = {
22803     init : function(layout, provider){
22804         this.provider = provider;
22805         var state = provider.get(layout.id+"-layout-state");
22806         if(state){
22807             var wasUpdating = layout.isUpdating();
22808             if(!wasUpdating){
22809                 layout.beginUpdate();
22810             }
22811             for(var key in state){
22812                 if(typeof state[key] != "function"){
22813                     var rstate = state[key];
22814                     var r = layout.getRegion(key);
22815                     if(r && rstate){
22816                         if(rstate.size){
22817                             r.resizeTo(rstate.size);
22818                         }
22819                         if(rstate.collapsed == true){
22820                             r.collapse(true);
22821                         }else{
22822                             r.expand(null, true);
22823                         }
22824                     }
22825                 }
22826             }
22827             if(!wasUpdating){
22828                 layout.endUpdate();
22829             }
22830             this.state = state; 
22831         }
22832         this.layout = layout;
22833         layout.on("regionresized", this.onRegionResized, this);
22834         layout.on("regioncollapsed", this.onRegionCollapsed, this);
22835         layout.on("regionexpanded", this.onRegionExpanded, this);
22836     },
22837     
22838     storeState : function(){
22839         this.provider.set(this.layout.id+"-layout-state", this.state);
22840     },
22841     
22842     onRegionResized : function(region, newSize){
22843         this.state[region.getPosition()].size = newSize;
22844         this.storeState();
22845     },
22846     
22847     onRegionCollapsed : function(region){
22848         this.state[region.getPosition()].collapsed = true;
22849         this.storeState();
22850     },
22851     
22852     onRegionExpanded : function(region){
22853         this.state[region.getPosition()].collapsed = false;
22854         this.storeState();
22855     }
22856 };/*
22857  * Based on:
22858  * Ext JS Library 1.1.1
22859  * Copyright(c) 2006-2007, Ext JS, LLC.
22860  *
22861  * Originally Released Under LGPL - original licence link has changed is not relivant.
22862  *
22863  * Fork - LGPL
22864  * <script type="text/javascript">
22865  */
22866 /**
22867  * @class Roo.ContentPanel
22868  * @extends Roo.util.Observable
22869  * A basic ContentPanel element.
22870  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
22871  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
22872  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
22873  * @cfg {Boolean} closable True if the panel can be closed/removed
22874  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
22875  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
22876  * @cfg {Toolbar} toolbar A toolbar for this panel
22877  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
22878  * @cfg {String} title The title for this panel
22879  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
22880  * @cfg {String} url Calls {@link #setUrl} with this value
22881  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
22882  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
22883  * @constructor
22884  * Create a new ContentPanel.
22885  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
22886  * @param {String/Object} config A string to set only the title or a config object
22887  * @param {String} content (optional) Set the HTML content for this panel
22888  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
22889  */
22890 Roo.ContentPanel = function(el, config, content){
22891     
22892      
22893     /*
22894     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
22895         config = el;
22896         el = Roo.id();
22897     }
22898     if (config && config.parentLayout) { 
22899         el = config.parentLayout.el.createChild(); 
22900     }
22901     */
22902     if(el.autoCreate){ // xtype is available if this is called from factory
22903         config = el;
22904         el = Roo.id();
22905     }
22906     this.el = Roo.get(el);
22907     if(!this.el && config && config.autoCreate){
22908         if(typeof config.autoCreate == "object"){
22909             if(!config.autoCreate.id){
22910                 config.autoCreate.id = config.id||el;
22911             }
22912             this.el = Roo.DomHelper.append(document.body,
22913                         config.autoCreate, true);
22914         }else{
22915             this.el = Roo.DomHelper.append(document.body,
22916                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
22917         }
22918     }
22919     this.closable = false;
22920     this.loaded = false;
22921     this.active = false;
22922     if(typeof config == "string"){
22923         this.title = config;
22924     }else{
22925         Roo.apply(this, config);
22926     }
22927     
22928     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
22929         this.wrapEl = this.el.wrap();    
22930         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
22931         
22932     }
22933     
22934     
22935     
22936     if(this.resizeEl){
22937         this.resizeEl = Roo.get(this.resizeEl, true);
22938     }else{
22939         this.resizeEl = this.el;
22940     }
22941     this.addEvents({
22942         /**
22943          * @event activate
22944          * Fires when this panel is activated. 
22945          * @param {Roo.ContentPanel} this
22946          */
22947         "activate" : true,
22948         /**
22949          * @event deactivate
22950          * Fires when this panel is activated. 
22951          * @param {Roo.ContentPanel} this
22952          */
22953         "deactivate" : true,
22954
22955         /**
22956          * @event resize
22957          * Fires when this panel is resized if fitToFrame is true.
22958          * @param {Roo.ContentPanel} this
22959          * @param {Number} width The width after any component adjustments
22960          * @param {Number} height The height after any component adjustments
22961          */
22962         "resize" : true
22963     });
22964     if(this.autoScroll){
22965         this.resizeEl.setStyle("overflow", "auto");
22966     }
22967     content = content || this.content;
22968     if(content){
22969         this.setContent(content);
22970     }
22971     if(config && config.url){
22972         this.setUrl(this.url, this.params, this.loadOnce);
22973     }
22974     
22975     
22976     
22977     Roo.ContentPanel.superclass.constructor.call(this);
22978 };
22979
22980 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
22981     tabTip:'',
22982     setRegion : function(region){
22983         this.region = region;
22984         if(region){
22985            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
22986         }else{
22987            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
22988         } 
22989     },
22990     
22991     /**
22992      * Returns the toolbar for this Panel if one was configured. 
22993      * @return {Roo.Toolbar} 
22994      */
22995     getToolbar : function(){
22996         return this.toolbar;
22997     },
22998     
22999     setActiveState : function(active){
23000         this.active = active;
23001         if(!active){
23002             this.fireEvent("deactivate", this);
23003         }else{
23004             this.fireEvent("activate", this);
23005         }
23006     },
23007     /**
23008      * Updates this panel's element
23009      * @param {String} content The new content
23010      * @param {Boolean} loadScripts (optional) true to look for and process scripts
23011     */
23012     setContent : function(content, loadScripts){
23013         this.el.update(content, loadScripts);
23014     },
23015
23016     ignoreResize : function(w, h){
23017         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23018             return true;
23019         }else{
23020             this.lastSize = {width: w, height: h};
23021             return false;
23022         }
23023     },
23024     /**
23025      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
23026      * @return {Roo.UpdateManager} The UpdateManager
23027      */
23028     getUpdateManager : function(){
23029         return this.el.getUpdateManager();
23030     },
23031      /**
23032      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
23033      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
23034 <pre><code>
23035 panel.load({
23036     url: "your-url.php",
23037     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23038     callback: yourFunction,
23039     scope: yourObject, //(optional scope)
23040     discardUrl: false,
23041     nocache: false,
23042     text: "Loading...",
23043     timeout: 30,
23044     scripts: false
23045 });
23046 </code></pre>
23047      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23048      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
23049      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
23050      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
23051      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
23052      * @return {Roo.ContentPanel} this
23053      */
23054     load : function(){
23055         var um = this.el.getUpdateManager();
23056         um.update.apply(um, arguments);
23057         return this;
23058     },
23059
23060
23061     /**
23062      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
23063      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
23064      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
23065      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
23066      * @return {Roo.UpdateManager} The UpdateManager
23067      */
23068     setUrl : function(url, params, loadOnce){
23069         if(this.refreshDelegate){
23070             this.removeListener("activate", this.refreshDelegate);
23071         }
23072         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
23073         this.on("activate", this.refreshDelegate);
23074         return this.el.getUpdateManager();
23075     },
23076     
23077     _handleRefresh : function(url, params, loadOnce){
23078         if(!loadOnce || !this.loaded){
23079             var updater = this.el.getUpdateManager();
23080             updater.update(url, params, this._setLoaded.createDelegate(this));
23081         }
23082     },
23083     
23084     _setLoaded : function(){
23085         this.loaded = true;
23086     }, 
23087     
23088     /**
23089      * Returns this panel's id
23090      * @return {String} 
23091      */
23092     getId : function(){
23093         return this.el.id;
23094     },
23095     
23096     /** 
23097      * Returns this panel's element - used by regiosn to add.
23098      * @return {Roo.Element} 
23099      */
23100     getEl : function(){
23101         return this.wrapEl || this.el;
23102     },
23103     
23104     adjustForComponents : function(width, height){
23105         if(this.resizeEl != this.el){
23106             width -= this.el.getFrameWidth('lr');
23107             height -= this.el.getFrameWidth('tb');
23108         }
23109         if(this.toolbar){
23110             var te = this.toolbar.getEl();
23111             height -= te.getHeight();
23112             te.setWidth(width);
23113         }
23114         if(this.adjustments){
23115             width += this.adjustments[0];
23116             height += this.adjustments[1];
23117         }
23118         return {"width": width, "height": height};
23119     },
23120     
23121     setSize : function(width, height){
23122         if(this.fitToFrame && !this.ignoreResize(width, height)){
23123             if(this.fitContainer && this.resizeEl != this.el){
23124                 this.el.setSize(width, height);
23125             }
23126             var size = this.adjustForComponents(width, height);
23127             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
23128             this.fireEvent('resize', this, size.width, size.height);
23129         }
23130     },
23131     
23132     /**
23133      * Returns this panel's title
23134      * @return {String} 
23135      */
23136     getTitle : function(){
23137         return this.title;
23138     },
23139     
23140     /**
23141      * Set this panel's title
23142      * @param {String} title
23143      */
23144     setTitle : function(title){
23145         this.title = title;
23146         if(this.region){
23147             this.region.updatePanelTitle(this, title);
23148         }
23149     },
23150     
23151     /**
23152      * Returns true is this panel was configured to be closable
23153      * @return {Boolean} 
23154      */
23155     isClosable : function(){
23156         return this.closable;
23157     },
23158     
23159     beforeSlide : function(){
23160         this.el.clip();
23161         this.resizeEl.clip();
23162     },
23163     
23164     afterSlide : function(){
23165         this.el.unclip();
23166         this.resizeEl.unclip();
23167     },
23168     
23169     /**
23170      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
23171      *   Will fail silently if the {@link #setUrl} method has not been called.
23172      *   This does not activate the panel, just updates its content.
23173      */
23174     refresh : function(){
23175         if(this.refreshDelegate){
23176            this.loaded = false;
23177            this.refreshDelegate();
23178         }
23179     },
23180     
23181     /**
23182      * Destroys this panel
23183      */
23184     destroy : function(){
23185         this.el.removeAllListeners();
23186         var tempEl = document.createElement("span");
23187         tempEl.appendChild(this.el.dom);
23188         tempEl.innerHTML = "";
23189         this.el.remove();
23190         this.el = null;
23191     },
23192     
23193       /**
23194      * Adds a xtype elements to the panel - currently only supports Forms.
23195      * <pre><code>
23196
23197 layout.addxtype({
23198        xtype : 'Form',
23199        items: [ .... ]
23200    }
23201 );
23202
23203 </code></pre>
23204      * @param {Object} cfg Xtype definition of item to add.
23205      */
23206     
23207     addxtype : function(cfg) {
23208         // add form..
23209         if (!cfg.xtype.match(/^Form$/)) {
23210             return false;
23211         }
23212         var el = this.el.createChild();
23213
23214         this.form = new  Roo.form.Form(cfg);
23215         
23216         
23217         if ( this.form.allItems.length) this.form.render(el.dom);
23218         return this.form;
23219         
23220     }
23221 });
23222
23223 /**
23224  * @class Roo.GridPanel
23225  * @extends Roo.ContentPanel
23226  * @constructor
23227  * Create a new GridPanel.
23228  * @param {Roo.grid.Grid} grid The grid for this panel
23229  * @param {String/Object} config A string to set only the panel's title, or a config object
23230  */
23231 Roo.GridPanel = function(grid, config){
23232     
23233   
23234     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
23235         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
23236         
23237     this.wrapper.dom.appendChild(grid.getGridEl().dom);
23238     
23239     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
23240     
23241     if(this.toolbar){
23242         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
23243     }
23244     // xtype created footer. - not sure if will work as we normally have to render first..
23245     if (this.footer && !this.footer.el && this.footer.xtype) {
23246         
23247         this.footer.container = this.grid.getView().getFooterPanel(true);
23248         this.footer.dataSource = this.grid.dataSource;
23249         this.footer = Roo.factory(this.footer, Roo);
23250         
23251     }
23252     
23253     grid.monitorWindowResize = false; // turn off autosizing
23254     grid.autoHeight = false;
23255     grid.autoWidth = false;
23256     this.grid = grid;
23257     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
23258 };
23259
23260 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
23261     getId : function(){
23262         return this.grid.id;
23263     },
23264     
23265     /**
23266      * Returns the grid for this panel
23267      * @return {Roo.grid.Grid} 
23268      */
23269     getGrid : function(){
23270         return this.grid;    
23271     },
23272     
23273     setSize : function(width, height){
23274         if(!this.ignoreResize(width, height)){
23275             var grid = this.grid;
23276             var size = this.adjustForComponents(width, height);
23277             grid.getGridEl().setSize(size.width, size.height);
23278             grid.autoSize();
23279         }
23280     },
23281     
23282     beforeSlide : function(){
23283         this.grid.getView().scroller.clip();
23284     },
23285     
23286     afterSlide : function(){
23287         this.grid.getView().scroller.unclip();
23288     },
23289     
23290     destroy : function(){
23291         this.grid.destroy();
23292         delete this.grid;
23293         Roo.GridPanel.superclass.destroy.call(this); 
23294     }
23295 });
23296
23297
23298 /**
23299  * @class Roo.NestedLayoutPanel
23300  * @extends Roo.ContentPanel
23301  * @constructor
23302  * Create a new NestedLayoutPanel.
23303  * 
23304  * 
23305  * @param {Roo.BorderLayout} layout The layout for this panel
23306  * @param {String/Object} config A string to set only the title or a config object
23307  */
23308 Roo.NestedLayoutPanel = function(layout, config)
23309 {
23310     // construct with only one argument..
23311     /* FIXME - implement nicer consturctors
23312     if (layout.layout) {
23313         config = layout;
23314         layout = config.layout;
23315         delete config.layout;
23316     }
23317     if (layout.xtype && !layout.getEl) {
23318         // then layout needs constructing..
23319         layout = Roo.factory(layout, Roo);
23320     }
23321     */
23322     
23323     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
23324     
23325     layout.monitorWindowResize = false; // turn off autosizing
23326     this.layout = layout;
23327     this.layout.getEl().addClass("x-layout-nested-layout");
23328     
23329     
23330     
23331 };
23332
23333 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
23334
23335     setSize : function(width, height){
23336         if(!this.ignoreResize(width, height)){
23337             var size = this.adjustForComponents(width, height);
23338             var el = this.layout.getEl();
23339             el.setSize(size.width, size.height);
23340             var touch = el.dom.offsetWidth;
23341             this.layout.layout();
23342             // ie requires a double layout on the first pass
23343             if(Roo.isIE && !this.initialized){
23344                 this.initialized = true;
23345                 this.layout.layout();
23346             }
23347         }
23348     },
23349     
23350     // activate all subpanels if not currently active..
23351     
23352     setActiveState : function(active){
23353         this.active = active;
23354         if(!active){
23355             this.fireEvent("deactivate", this);
23356             return;
23357         }
23358         
23359         this.fireEvent("activate", this);
23360         // not sure if this should happen before or after..
23361         if (!this.layout) {
23362             return; // should not happen..
23363         }
23364         var reg = false;
23365         for (var r in this.layout.regions) {
23366             reg = this.layout.getRegion(r);
23367             if (reg.getActivePanel()) {
23368                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
23369                 reg.setActivePanel(reg.getActivePanel());
23370                 continue;
23371             }
23372             if (!reg.panels.length) {
23373                 continue;
23374             }
23375             reg.showPanel(reg.getPanel(0));
23376         }
23377         
23378         
23379         
23380         
23381     },
23382     
23383     /**
23384      * Returns the nested BorderLayout for this panel
23385      * @return {Roo.BorderLayout} 
23386      */
23387     getLayout : function(){
23388         return this.layout;
23389     },
23390     
23391      /**
23392      * Adds a xtype elements to the layout of the nested panel
23393      * <pre><code>
23394
23395 panel.addxtype({
23396        xtype : 'ContentPanel',
23397        region: 'west',
23398        items: [ .... ]
23399    }
23400 );
23401
23402 panel.addxtype({
23403         xtype : 'NestedLayoutPanel',
23404         region: 'west',
23405         layout: {
23406            center: { },
23407            west: { }   
23408         },
23409         items : [ ... list of content panels or nested layout panels.. ]
23410    }
23411 );
23412 </code></pre>
23413      * @param {Object} cfg Xtype definition of item to add.
23414      */
23415     addxtype : function(cfg) {
23416         return this.layout.addxtype(cfg);
23417     
23418     }
23419 });
23420
23421 Roo.ScrollPanel = function(el, config, content){
23422     config = config || {};
23423     config.fitToFrame = true;
23424     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
23425     
23426     this.el.dom.style.overflow = "hidden";
23427     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
23428     this.el.removeClass("x-layout-inactive-content");
23429     this.el.on("mousewheel", this.onWheel, this);
23430
23431     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
23432     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
23433     up.unselectable(); down.unselectable();
23434     up.on("click", this.scrollUp, this);
23435     down.on("click", this.scrollDown, this);
23436     up.addClassOnOver("x-scroller-btn-over");
23437     down.addClassOnOver("x-scroller-btn-over");
23438     up.addClassOnClick("x-scroller-btn-click");
23439     down.addClassOnClick("x-scroller-btn-click");
23440     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
23441
23442     this.resizeEl = this.el;
23443     this.el = wrap; this.up = up; this.down = down;
23444 };
23445
23446 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
23447     increment : 100,
23448     wheelIncrement : 5,
23449     scrollUp : function(){
23450         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
23451     },
23452
23453     scrollDown : function(){
23454         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
23455     },
23456
23457     afterScroll : function(){
23458         var el = this.resizeEl;
23459         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
23460         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
23461         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
23462     },
23463
23464     setSize : function(){
23465         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
23466         this.afterScroll();
23467     },
23468
23469     onWheel : function(e){
23470         var d = e.getWheelDelta();
23471         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
23472         this.afterScroll();
23473         e.stopEvent();
23474     },
23475
23476     setContent : function(content, loadScripts){
23477         this.resizeEl.update(content, loadScripts);
23478     }
23479
23480 });
23481
23482
23483
23484
23485
23486
23487
23488
23489
23490 /**
23491  * @class Roo.TreePanel
23492  * @extends Roo.ContentPanel
23493  * @constructor
23494  * Create a new TreePanel.
23495  * @param {String/Object} config A string to set only the panel's title, or a config object
23496  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
23497  */
23498 Roo.TreePanel = function(config){
23499     var el = config.el;
23500     var tree = config.tree;
23501     delete config.tree; 
23502     delete config.el; // hopefull!
23503     Roo.TreePanel.superclass.constructor.call(this, el, config);
23504     var treeEl = el.createChild();
23505     this.tree = new Roo.tree.TreePanel(treeEl , tree);
23506     //console.log(tree);
23507     this.on('activate', function()
23508     {
23509         if (this.tree.rendered) {
23510             return;
23511         }
23512         //console.log('render tree');
23513         this.tree.render();
23514     });
23515     
23516     this.on('resize',  function (cp, w, h) {
23517             this.tree.innerCt.setWidth(w);
23518             this.tree.innerCt.setHeight(h);
23519             this.tree.innerCt.setStyle('overflow-y', 'auto');
23520     });
23521
23522         
23523     
23524 };
23525
23526 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
23527
23528
23529
23530
23531
23532
23533
23534
23535
23536
23537
23538 /*
23539  * Based on:
23540  * Ext JS Library 1.1.1
23541  * Copyright(c) 2006-2007, Ext JS, LLC.
23542  *
23543  * Originally Released Under LGPL - original licence link has changed is not relivant.
23544  *
23545  * Fork - LGPL
23546  * <script type="text/javascript">
23547  */
23548  
23549
23550 /**
23551  * @class Roo.ReaderLayout
23552  * @extends Roo.BorderLayout
23553  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
23554  * center region containing two nested regions (a top one for a list view and one for item preview below),
23555  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
23556  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
23557  * expedites the setup of the overall layout and regions for this common application style.
23558  * Example:
23559  <pre><code>
23560 var reader = new Roo.ReaderLayout();
23561 var CP = Roo.ContentPanel;  // shortcut for adding
23562
23563 reader.beginUpdate();
23564 reader.add("north", new CP("north", "North"));
23565 reader.add("west", new CP("west", {title: "West"}));
23566 reader.add("east", new CP("east", {title: "East"}));
23567
23568 reader.regions.listView.add(new CP("listView", "List"));
23569 reader.regions.preview.add(new CP("preview", "Preview"));
23570 reader.endUpdate();
23571 </code></pre>
23572 * @constructor
23573 * Create a new ReaderLayout
23574 * @param {Object} config Configuration options
23575 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
23576 * document.body if omitted)
23577 */
23578 Roo.ReaderLayout = function(config, renderTo){
23579     var c = config || {size:{}};
23580     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
23581         north: c.north !== false ? Roo.apply({
23582             split:false,
23583             initialSize: 32,
23584             titlebar: false
23585         }, c.north) : false,
23586         west: c.west !== false ? Roo.apply({
23587             split:true,
23588             initialSize: 200,
23589             minSize: 175,
23590             maxSize: 400,
23591             titlebar: true,
23592             collapsible: true,
23593             animate: true,
23594             margins:{left:5,right:0,bottom:5,top:5},
23595             cmargins:{left:5,right:5,bottom:5,top:5}
23596         }, c.west) : false,
23597         east: c.east !== false ? Roo.apply({
23598             split:true,
23599             initialSize: 200,
23600             minSize: 175,
23601             maxSize: 400,
23602             titlebar: true,
23603             collapsible: true,
23604             animate: true,
23605             margins:{left:0,right:5,bottom:5,top:5},
23606             cmargins:{left:5,right:5,bottom:5,top:5}
23607         }, c.east) : false,
23608         center: Roo.apply({
23609             tabPosition: 'top',
23610             autoScroll:false,
23611             closeOnTab: true,
23612             titlebar:false,
23613             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
23614         }, c.center)
23615     });
23616
23617     this.el.addClass('x-reader');
23618
23619     this.beginUpdate();
23620
23621     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
23622         south: c.preview !== false ? Roo.apply({
23623             split:true,
23624             initialSize: 200,
23625             minSize: 100,
23626             autoScroll:true,
23627             collapsible:true,
23628             titlebar: true,
23629             cmargins:{top:5,left:0, right:0, bottom:0}
23630         }, c.preview) : false,
23631         center: Roo.apply({
23632             autoScroll:false,
23633             titlebar:false,
23634             minHeight:200
23635         }, c.listView)
23636     });
23637     this.add('center', new Roo.NestedLayoutPanel(inner,
23638             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
23639
23640     this.endUpdate();
23641
23642     this.regions.preview = inner.getRegion('south');
23643     this.regions.listView = inner.getRegion('center');
23644 };
23645
23646 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
23647  * Based on:
23648  * Ext JS Library 1.1.1
23649  * Copyright(c) 2006-2007, Ext JS, LLC.
23650  *
23651  * Originally Released Under LGPL - original licence link has changed is not relivant.
23652  *
23653  * Fork - LGPL
23654  * <script type="text/javascript">
23655  */
23656  
23657 /**
23658  * @class Roo.grid.Grid
23659  * @extends Roo.util.Observable
23660  * This class represents the primary interface of a component based grid control.
23661  * <br><br>Usage:<pre><code>
23662  var grid = new Roo.grid.Grid("my-container-id", {
23663      ds: myDataStore,
23664      cm: myColModel,
23665      selModel: mySelectionModel,
23666      autoSizeColumns: true,
23667      monitorWindowResize: false,
23668      trackMouseOver: true
23669  });
23670  // set any options
23671  grid.render();
23672  * </code></pre>
23673  * <b>Common Problems:</b><br/>
23674  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
23675  * element will correct this<br/>
23676  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
23677  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
23678  * are unpredictable.<br/>
23679  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
23680  * grid to calculate dimensions/offsets.<br/>
23681   * @constructor
23682  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
23683  * The container MUST have some type of size defined for the grid to fill. The container will be
23684  * automatically set to position relative if it isn't already.
23685  * @param {Object} config A config object that sets properties on this grid.
23686  */
23687 Roo.grid.Grid = function(container, config){
23688         // initialize the container
23689         this.container = Roo.get(container);
23690         this.container.update("");
23691         this.container.setStyle("overflow", "hidden");
23692     this.container.addClass('x-grid-container');
23693
23694     this.id = this.container.id;
23695
23696     Roo.apply(this, config);
23697     // check and correct shorthanded configs
23698     if(this.ds){
23699         this.dataSource = this.ds;
23700         delete this.ds;
23701     }
23702     if(this.cm){
23703         this.colModel = this.cm;
23704         delete this.cm;
23705     }
23706     if(this.sm){
23707         this.selModel = this.sm;
23708         delete this.sm;
23709     }
23710
23711     if (this.selModel) {
23712         this.selModel = Roo.factory(this.selModel, Roo.grid);
23713         this.sm = this.selModel;
23714         this.sm.xmodule = this.xmodule || false;
23715     }
23716     if (typeof(this.colModel.config) == 'undefined') {
23717         this.colModel = new Roo.grid.ColumnModel(this.colModel);
23718         this.cm = this.colModel;
23719         this.cm.xmodule = this.xmodule || false;
23720     }
23721     if (this.dataSource) {
23722         this.dataSource= Roo.factory(this.dataSource, Roo.data);
23723         this.ds = this.dataSource;
23724         this.ds.xmodule = this.xmodule || false;
23725         
23726     }
23727     
23728     
23729     
23730     if(this.width){
23731         this.container.setWidth(this.width);
23732     }
23733
23734     if(this.height){
23735         this.container.setHeight(this.height);
23736     }
23737     /** @private */
23738         this.addEvents({
23739             // raw events
23740             /**
23741              * @event click
23742              * The raw click event for the entire grid.
23743              * @param {Roo.EventObject} e
23744              */
23745             "click" : true,
23746             /**
23747              * @event dblclick
23748              * The raw dblclick event for the entire grid.
23749              * @param {Roo.EventObject} e
23750              */
23751             "dblclick" : true,
23752             /**
23753              * @event contextmenu
23754              * The raw contextmenu event for the entire grid.
23755              * @param {Roo.EventObject} e
23756              */
23757             "contextmenu" : true,
23758             /**
23759              * @event mousedown
23760              * The raw mousedown event for the entire grid.
23761              * @param {Roo.EventObject} e
23762              */
23763             "mousedown" : true,
23764             /**
23765              * @event mouseup
23766              * The raw mouseup event for the entire grid.
23767              * @param {Roo.EventObject} e
23768              */
23769             "mouseup" : true,
23770             /**
23771              * @event mouseover
23772              * The raw mouseover event for the entire grid.
23773              * @param {Roo.EventObject} e
23774              */
23775             "mouseover" : true,
23776             /**
23777              * @event mouseout
23778              * The raw mouseout event for the entire grid.
23779              * @param {Roo.EventObject} e
23780              */
23781             "mouseout" : true,
23782             /**
23783              * @event keypress
23784              * The raw keypress event for the entire grid.
23785              * @param {Roo.EventObject} e
23786              */
23787             "keypress" : true,
23788             /**
23789              * @event keydown
23790              * The raw keydown event for the entire grid.
23791              * @param {Roo.EventObject} e
23792              */
23793             "keydown" : true,
23794
23795             // custom events
23796
23797             /**
23798              * @event cellclick
23799              * Fires when a cell is clicked
23800              * @param {Grid} this
23801              * @param {Number} rowIndex
23802              * @param {Number} columnIndex
23803              * @param {Roo.EventObject} e
23804              */
23805             "cellclick" : true,
23806             /**
23807              * @event celldblclick
23808              * Fires when a cell is double clicked
23809              * @param {Grid} this
23810              * @param {Number} rowIndex
23811              * @param {Number} columnIndex
23812              * @param {Roo.EventObject} e
23813              */
23814             "celldblclick" : true,
23815             /**
23816              * @event rowclick
23817              * Fires when a row is clicked
23818              * @param {Grid} this
23819              * @param {Number} rowIndex
23820              * @param {Roo.EventObject} e
23821              */
23822             "rowclick" : true,
23823             /**
23824              * @event rowdblclick
23825              * Fires when a row is double clicked
23826              * @param {Grid} this
23827              * @param {Number} rowIndex
23828              * @param {Roo.EventObject} e
23829              */
23830             "rowdblclick" : true,
23831             /**
23832              * @event headerclick
23833              * Fires when a header is clicked
23834              * @param {Grid} this
23835              * @param {Number} columnIndex
23836              * @param {Roo.EventObject} e
23837              */
23838             "headerclick" : true,
23839             /**
23840              * @event headerdblclick
23841              * Fires when a header cell is double clicked
23842              * @param {Grid} this
23843              * @param {Number} columnIndex
23844              * @param {Roo.EventObject} e
23845              */
23846             "headerdblclick" : true,
23847             /**
23848              * @event rowcontextmenu
23849              * Fires when a row is right clicked
23850              * @param {Grid} this
23851              * @param {Number} rowIndex
23852              * @param {Roo.EventObject} e
23853              */
23854             "rowcontextmenu" : true,
23855             /**
23856          * @event cellcontextmenu
23857          * Fires when a cell is right clicked
23858          * @param {Grid} this
23859          * @param {Number} rowIndex
23860          * @param {Number} cellIndex
23861          * @param {Roo.EventObject} e
23862          */
23863          "cellcontextmenu" : true,
23864             /**
23865              * @event headercontextmenu
23866              * Fires when a header is right clicked
23867              * @param {Grid} this
23868              * @param {Number} columnIndex
23869              * @param {Roo.EventObject} e
23870              */
23871             "headercontextmenu" : true,
23872             /**
23873              * @event bodyscroll
23874              * Fires when the body element is scrolled
23875              * @param {Number} scrollLeft
23876              * @param {Number} scrollTop
23877              */
23878             "bodyscroll" : true,
23879             /**
23880              * @event columnresize
23881              * Fires when the user resizes a column
23882              * @param {Number} columnIndex
23883              * @param {Number} newSize
23884              */
23885             "columnresize" : true,
23886             /**
23887              * @event columnmove
23888              * Fires when the user moves a column
23889              * @param {Number} oldIndex
23890              * @param {Number} newIndex
23891              */
23892             "columnmove" : true,
23893             /**
23894              * @event startdrag
23895              * Fires when row(s) start being dragged
23896              * @param {Grid} this
23897              * @param {Roo.GridDD} dd The drag drop object
23898              * @param {event} e The raw browser event
23899              */
23900             "startdrag" : true,
23901             /**
23902              * @event enddrag
23903              * Fires when a drag operation is complete
23904              * @param {Grid} this
23905              * @param {Roo.GridDD} dd The drag drop object
23906              * @param {event} e The raw browser event
23907              */
23908             "enddrag" : true,
23909             /**
23910              * @event dragdrop
23911              * Fires when dragged row(s) are dropped on a valid DD target
23912              * @param {Grid} this
23913              * @param {Roo.GridDD} dd The drag drop object
23914              * @param {String} targetId The target drag drop object
23915              * @param {event} e The raw browser event
23916              */
23917             "dragdrop" : true,
23918             /**
23919              * @event dragover
23920              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
23921              * @param {Grid} this
23922              * @param {Roo.GridDD} dd The drag drop object
23923              * @param {String} targetId The target drag drop object
23924              * @param {event} e The raw browser event
23925              */
23926             "dragover" : true,
23927             /**
23928              * @event dragenter
23929              *  Fires when the dragged row(s) first cross another DD target while being dragged
23930              * @param {Grid} this
23931              * @param {Roo.GridDD} dd The drag drop object
23932              * @param {String} targetId The target drag drop object
23933              * @param {event} e The raw browser event
23934              */
23935             "dragenter" : true,
23936             /**
23937              * @event dragout
23938              * Fires when the dragged row(s) leave another DD target while being dragged
23939              * @param {Grid} this
23940              * @param {Roo.GridDD} dd The drag drop object
23941              * @param {String} targetId The target drag drop object
23942              * @param {event} e The raw browser event
23943              */
23944             "dragout" : true,
23945         /**
23946          * @event render
23947          * Fires when the grid is rendered
23948          * @param {Grid} grid
23949          */
23950         render : true
23951     });
23952
23953     Roo.grid.Grid.superclass.constructor.call(this);
23954 };
23955 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
23956     /**
23957      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
23958          */
23959         minColumnWidth : 25,
23960
23961     /**
23962          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
23963          * <b>on initial render.</b> It is more efficient to explicitly size the columns
23964          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
23965          */
23966         autoSizeColumns : false,
23967
23968         /**
23969          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
23970          */
23971         autoSizeHeaders : true,
23972
23973         /**
23974          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
23975          */
23976         monitorWindowResize : true,
23977
23978         /**
23979          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
23980          * rows measured to get a columns size. Default is 0 (all rows).
23981          */
23982         maxRowsToMeasure : 0,
23983
23984         /**
23985          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
23986          */
23987         trackMouseOver : true,
23988
23989         /**
23990          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
23991          */
23992         enableDragDrop : false,
23993
23994         /**
23995          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
23996          */
23997         enableColumnMove : true,
23998
23999         /**
24000          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
24001          */
24002         enableColumnHide : true,
24003
24004         /**
24005          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
24006          */
24007         enableRowHeightSync : false,
24008
24009         /**
24010          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
24011          */
24012         stripeRows : true,
24013
24014         /**
24015          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
24016          */
24017         autoHeight : false,
24018
24019     /**
24020      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
24021      */
24022     autoExpandColumn : false,
24023
24024     /**
24025     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
24026     * Default is 50.
24027     */
24028     autoExpandMin : 50,
24029
24030     /**
24031     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
24032     */
24033     autoExpandMax : 1000,
24034
24035     /**
24036          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
24037          */
24038         view : null,
24039
24040         /**
24041      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
24042          */
24043         loadMask : false,
24044
24045     // private
24046     rendered : false,
24047
24048     /**
24049     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
24050     * of a fixed width. Default is false.
24051     */
24052     /**
24053     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
24054     */
24055     /**
24056      * Called once after all setup has been completed and the grid is ready to be rendered.
24057      * @return {Roo.grid.Grid} this
24058      */
24059     render : function(){
24060         var c = this.container;
24061         // try to detect autoHeight/width mode
24062         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
24063             this.autoHeight = true;
24064         }
24065         var view = this.getView();
24066         view.init(this);
24067
24068         c.on("click", this.onClick, this);
24069         c.on("dblclick", this.onDblClick, this);
24070         c.on("contextmenu", this.onContextMenu, this);
24071         c.on("keydown", this.onKeyDown, this);
24072
24073         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
24074
24075         this.getSelectionModel().init(this);
24076
24077         view.render();
24078
24079         if(this.loadMask){
24080             this.loadMask = new Roo.LoadMask(this.container,
24081                     Roo.apply({store:this.dataSource}, this.loadMask));
24082         }
24083         
24084         
24085         if (this.toolbar && this.toolbar.xtype) {
24086             this.toolbar.container = this.getView().getHeaderPanel(true);
24087             this.toolbar = new Ext.Toolbar(this.toolbar);
24088         }
24089         if (this.footer && this.footer.xtype) {
24090             this.footer.dataSource = this.getDataSource();
24091             this.footer.container = this.getView().getFooterPanel(true);
24092             this.footer = Roo.factory(this.footer, Roo);
24093         }
24094         this.rendered = true;
24095         this.fireEvent('render', this);
24096         return this;
24097     },
24098
24099         /**
24100          * Reconfigures the grid to use a different Store and Column Model.
24101          * The View will be bound to the new objects and refreshed.
24102          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
24103          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
24104          */
24105     reconfigure : function(dataSource, colModel){
24106         if(this.loadMask){
24107             this.loadMask.destroy();
24108             this.loadMask = new Roo.LoadMask(this.container,
24109                     Roo.apply({store:dataSource}, this.loadMask));
24110         }
24111         this.view.bind(dataSource, colModel);
24112         this.dataSource = dataSource;
24113         this.colModel = colModel;
24114         this.view.refresh(true);
24115     },
24116
24117     // private
24118     onKeyDown : function(e){
24119         this.fireEvent("keydown", e);
24120     },
24121
24122     /**
24123      * Destroy this grid.
24124      * @param {Boolean} removeEl True to remove the element
24125      */
24126     destroy : function(removeEl, keepListeners){
24127         if(this.loadMask){
24128             this.loadMask.destroy();
24129         }
24130         var c = this.container;
24131         c.removeAllListeners();
24132         this.view.destroy();
24133         this.colModel.purgeListeners();
24134         if(!keepListeners){
24135             this.purgeListeners();
24136         }
24137         c.update("");
24138         if(removeEl === true){
24139             c.remove();
24140         }
24141     },
24142
24143     // private
24144     processEvent : function(name, e){
24145         this.fireEvent(name, e);
24146         var t = e.getTarget();
24147         var v = this.view;
24148         var header = v.findHeaderIndex(t);
24149         if(header !== false){
24150             this.fireEvent("header" + name, this, header, e);
24151         }else{
24152             var row = v.findRowIndex(t);
24153             var cell = v.findCellIndex(t);
24154             if(row !== false){
24155                 this.fireEvent("row" + name, this, row, e);
24156                 if(cell !== false){
24157                     this.fireEvent("cell" + name, this, row, cell, e);
24158                 }
24159             }
24160         }
24161     },
24162
24163     // private
24164     onClick : function(e){
24165         this.processEvent("click", e);
24166     },
24167
24168     // private
24169     onContextMenu : function(e, t){
24170         this.processEvent("contextmenu", e);
24171     },
24172
24173     // private
24174     onDblClick : function(e){
24175         this.processEvent("dblclick", e);
24176     },
24177
24178     // private
24179     walkCells : function(row, col, step, fn, scope){
24180         var cm = this.colModel, clen = cm.getColumnCount();
24181         var ds = this.dataSource, rlen = ds.getCount(), first = true;
24182         if(step < 0){
24183             if(col < 0){
24184                 row--;
24185                 first = false;
24186             }
24187             while(row >= 0){
24188                 if(!first){
24189                     col = clen-1;
24190                 }
24191                 first = false;
24192                 while(col >= 0){
24193                     if(fn.call(scope || this, row, col, cm) === true){
24194                         return [row, col];
24195                     }
24196                     col--;
24197                 }
24198                 row--;
24199             }
24200         } else {
24201             if(col >= clen){
24202                 row++;
24203                 first = false;
24204             }
24205             while(row < rlen){
24206                 if(!first){
24207                     col = 0;
24208                 }
24209                 first = false;
24210                 while(col < clen){
24211                     if(fn.call(scope || this, row, col, cm) === true){
24212                         return [row, col];
24213                     }
24214                     col++;
24215                 }
24216                 row++;
24217             }
24218         }
24219         return null;
24220     },
24221
24222     // private
24223     getSelections : function(){
24224         return this.selModel.getSelections();
24225     },
24226
24227     /**
24228      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
24229      * but if manual update is required this method will initiate it.
24230      */
24231     autoSize : function(){
24232         if(this.rendered){
24233             this.view.layout();
24234             if(this.view.adjustForScroll){
24235                 this.view.adjustForScroll();
24236             }
24237         }
24238     },
24239
24240     /**
24241      * Returns the grid's underlying element.
24242      * @return {Element} The element
24243      */
24244     getGridEl : function(){
24245         return this.container;
24246     },
24247
24248     // private for compatibility, overridden by editor grid
24249     stopEditing : function(){},
24250
24251     /**
24252      * Returns the grid's SelectionModel.
24253      * @return {SelectionModel}
24254      */
24255     getSelectionModel : function(){
24256         if(!this.selModel){
24257             this.selModel = new Roo.grid.RowSelectionModel();
24258         }
24259         return this.selModel;
24260     },
24261
24262     /**
24263      * Returns the grid's DataSource.
24264      * @return {DataSource}
24265      */
24266     getDataSource : function(){
24267         return this.dataSource;
24268     },
24269
24270     /**
24271      * Returns the grid's ColumnModel.
24272      * @return {ColumnModel}
24273      */
24274     getColumnModel : function(){
24275         return this.colModel;
24276     },
24277
24278     /**
24279      * Returns the grid's GridView object.
24280      * @return {GridView}
24281      */
24282     getView : function(){
24283         if(!this.view){
24284             this.view = new Roo.grid.GridView(this.viewConfig);
24285         }
24286         return this.view;
24287     },
24288     /**
24289      * Called to get grid's drag proxy text, by default returns this.ddText.
24290      * @return {String}
24291      */
24292     getDragDropText : function(){
24293         var count = this.selModel.getCount();
24294         return String.format(this.ddText, count, count == 1 ? '' : 's');
24295     }
24296 });
24297 /**
24298  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
24299  * %0 is replaced with the number of selected rows.
24300  * @type String
24301  */
24302 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
24303  * Based on:
24304  * Ext JS Library 1.1.1
24305  * Copyright(c) 2006-2007, Ext JS, LLC.
24306  *
24307  * Originally Released Under LGPL - original licence link has changed is not relivant.
24308  *
24309  * Fork - LGPL
24310  * <script type="text/javascript">
24311  */
24312  
24313 Roo.grid.AbstractGridView = function(){
24314         this.grid = null;
24315         
24316         this.events = {
24317             "beforerowremoved" : true,
24318             "beforerowsinserted" : true,
24319             "beforerefresh" : true,
24320             "rowremoved" : true,
24321             "rowsinserted" : true,
24322             "rowupdated" : true,
24323             "refresh" : true
24324         };
24325     Roo.grid.AbstractGridView.superclass.constructor.call(this);
24326 };
24327
24328 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
24329     rowClass : "x-grid-row",
24330     cellClass : "x-grid-cell",
24331     tdClass : "x-grid-td",
24332     hdClass : "x-grid-hd",
24333     splitClass : "x-grid-hd-split",
24334     
24335         init: function(grid){
24336         this.grid = grid;
24337                 var cid = this.grid.getGridEl().id;
24338         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
24339         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
24340         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
24341         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
24342         },
24343         
24344         getColumnRenderers : function(){
24345         var renderers = [];
24346         var cm = this.grid.colModel;
24347         var colCount = cm.getColumnCount();
24348         for(var i = 0; i < colCount; i++){
24349             renderers[i] = cm.getRenderer(i);
24350         }
24351         return renderers;
24352     },
24353     
24354     getColumnIds : function(){
24355         var ids = [];
24356         var cm = this.grid.colModel;
24357         var colCount = cm.getColumnCount();
24358         for(var i = 0; i < colCount; i++){
24359             ids[i] = cm.getColumnId(i);
24360         }
24361         return ids;
24362     },
24363     
24364     getDataIndexes : function(){
24365         if(!this.indexMap){
24366             this.indexMap = this.buildIndexMap();
24367         }
24368         return this.indexMap.colToData;
24369     },
24370     
24371     getColumnIndexByDataIndex : function(dataIndex){
24372         if(!this.indexMap){
24373             this.indexMap = this.buildIndexMap();
24374         }
24375         return this.indexMap.dataToCol[dataIndex];
24376     },
24377     
24378     /**
24379      * Set a css style for a column dynamically. 
24380      * @param {Number} colIndex The index of the column
24381      * @param {String} name The css property name
24382      * @param {String} value The css value
24383      */
24384     setCSSStyle : function(colIndex, name, value){
24385         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
24386         Roo.util.CSS.updateRule(selector, name, value);
24387     },
24388     
24389     generateRules : function(cm){
24390         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
24391         Roo.util.CSS.removeStyleSheet(rulesId);
24392         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
24393             var cid = cm.getColumnId(i);
24394             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
24395                          this.tdSelector, cid, " {\n}\n",
24396                          this.hdSelector, cid, " {\n}\n",
24397                          this.splitSelector, cid, " {\n}\n");
24398         }
24399         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
24400     }
24401 });/*
24402  * Based on:
24403  * Ext JS Library 1.1.1
24404  * Copyright(c) 2006-2007, Ext JS, LLC.
24405  *
24406  * Originally Released Under LGPL - original licence link has changed is not relivant.
24407  *
24408  * Fork - LGPL
24409  * <script type="text/javascript">
24410  */
24411
24412 // private
24413 // This is a support class used internally by the Grid components
24414 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
24415     this.grid = grid;
24416     this.view = grid.getView();
24417     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
24418     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
24419     if(hd2){
24420         this.setHandleElId(Roo.id(hd));
24421         this.setOuterHandleElId(Roo.id(hd2));
24422     }
24423     this.scroll = false;
24424 };
24425 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
24426     maxDragWidth: 120,
24427     getDragData : function(e){
24428         var t = Roo.lib.Event.getTarget(e);
24429         var h = this.view.findHeaderCell(t);
24430         if(h){
24431             return {ddel: h.firstChild, header:h};
24432         }
24433         return false;
24434     },
24435
24436     onInitDrag : function(e){
24437         this.view.headersDisabled = true;
24438         var clone = this.dragData.ddel.cloneNode(true);
24439         clone.id = Roo.id();
24440         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
24441         this.proxy.update(clone);
24442         return true;
24443     },
24444
24445     afterValidDrop : function(){
24446         var v = this.view;
24447         setTimeout(function(){
24448             v.headersDisabled = false;
24449         }, 50);
24450     },
24451
24452     afterInvalidDrop : function(){
24453         var v = this.view;
24454         setTimeout(function(){
24455             v.headersDisabled = false;
24456         }, 50);
24457     }
24458 });
24459 /*
24460  * Based on:
24461  * Ext JS Library 1.1.1
24462  * Copyright(c) 2006-2007, Ext JS, LLC.
24463  *
24464  * Originally Released Under LGPL - original licence link has changed is not relivant.
24465  *
24466  * Fork - LGPL
24467  * <script type="text/javascript">
24468  */
24469 // private
24470 // This is a support class used internally by the Grid components
24471 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
24472     this.grid = grid;
24473     this.view = grid.getView();
24474     // split the proxies so they don't interfere with mouse events
24475     this.proxyTop = Roo.DomHelper.append(document.body, {
24476         cls:"col-move-top", html:"&#160;"
24477     }, true);
24478     this.proxyBottom = Roo.DomHelper.append(document.body, {
24479         cls:"col-move-bottom", html:"&#160;"
24480     }, true);
24481     this.proxyTop.hide = this.proxyBottom.hide = function(){
24482         this.setLeftTop(-100,-100);
24483         this.setStyle("visibility", "hidden");
24484     };
24485     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
24486     // temporarily disabled
24487     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
24488     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
24489 };
24490 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
24491     proxyOffsets : [-4, -9],
24492     fly: Roo.Element.fly,
24493
24494     getTargetFromEvent : function(e){
24495         var t = Roo.lib.Event.getTarget(e);
24496         var cindex = this.view.findCellIndex(t);
24497         if(cindex !== false){
24498             return this.view.getHeaderCell(cindex);
24499         }
24500     },
24501
24502     nextVisible : function(h){
24503         var v = this.view, cm = this.grid.colModel;
24504         h = h.nextSibling;
24505         while(h){
24506             if(!cm.isHidden(v.getCellIndex(h))){
24507                 return h;
24508             }
24509             h = h.nextSibling;
24510         }
24511         return null;
24512     },
24513
24514     prevVisible : function(h){
24515         var v = this.view, cm = this.grid.colModel;
24516         h = h.prevSibling;
24517         while(h){
24518             if(!cm.isHidden(v.getCellIndex(h))){
24519                 return h;
24520             }
24521             h = h.prevSibling;
24522         }
24523         return null;
24524     },
24525
24526     positionIndicator : function(h, n, e){
24527         var x = Roo.lib.Event.getPageX(e);
24528         var r = Roo.lib.Dom.getRegion(n.firstChild);
24529         var px, pt, py = r.top + this.proxyOffsets[1];
24530         if((r.right - x) <= (r.right-r.left)/2){
24531             px = r.right+this.view.borderWidth;
24532             pt = "after";
24533         }else{
24534             px = r.left;
24535             pt = "before";
24536         }
24537         var oldIndex = this.view.getCellIndex(h);
24538         var newIndex = this.view.getCellIndex(n);
24539
24540         if(this.grid.colModel.isFixed(newIndex)){
24541             return false;
24542         }
24543
24544         var locked = this.grid.colModel.isLocked(newIndex);
24545
24546         if(pt == "after"){
24547             newIndex++;
24548         }
24549         if(oldIndex < newIndex){
24550             newIndex--;
24551         }
24552         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
24553             return false;
24554         }
24555         px +=  this.proxyOffsets[0];
24556         this.proxyTop.setLeftTop(px, py);
24557         this.proxyTop.show();
24558         if(!this.bottomOffset){
24559             this.bottomOffset = this.view.mainHd.getHeight();
24560         }
24561         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
24562         this.proxyBottom.show();
24563         return pt;
24564     },
24565
24566     onNodeEnter : function(n, dd, e, data){
24567         if(data.header != n){
24568             this.positionIndicator(data.header, n, e);
24569         }
24570     },
24571
24572     onNodeOver : function(n, dd, e, data){
24573         var result = false;
24574         if(data.header != n){
24575             result = this.positionIndicator(data.header, n, e);
24576         }
24577         if(!result){
24578             this.proxyTop.hide();
24579             this.proxyBottom.hide();
24580         }
24581         return result ? this.dropAllowed : this.dropNotAllowed;
24582     },
24583
24584     onNodeOut : function(n, dd, e, data){
24585         this.proxyTop.hide();
24586         this.proxyBottom.hide();
24587     },
24588
24589     onNodeDrop : function(n, dd, e, data){
24590         var h = data.header;
24591         if(h != n){
24592             var cm = this.grid.colModel;
24593             var x = Roo.lib.Event.getPageX(e);
24594             var r = Roo.lib.Dom.getRegion(n.firstChild);
24595             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
24596             var oldIndex = this.view.getCellIndex(h);
24597             var newIndex = this.view.getCellIndex(n);
24598             var locked = cm.isLocked(newIndex);
24599             if(pt == "after"){
24600                 newIndex++;
24601             }
24602             if(oldIndex < newIndex){
24603                 newIndex--;
24604             }
24605             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
24606                 return false;
24607             }
24608             cm.setLocked(oldIndex, locked, true);
24609             cm.moveColumn(oldIndex, newIndex);
24610             this.grid.fireEvent("columnmove", oldIndex, newIndex);
24611             return true;
24612         }
24613         return false;
24614     }
24615 });
24616 /*
24617  * Based on:
24618  * Ext JS Library 1.1.1
24619  * Copyright(c) 2006-2007, Ext JS, LLC.
24620  *
24621  * Originally Released Under LGPL - original licence link has changed is not relivant.
24622  *
24623  * Fork - LGPL
24624  * <script type="text/javascript">
24625  */
24626   
24627 /**
24628  * @class Roo.grid.GridView
24629  * @extends Roo.util.Observable
24630  *
24631  * @constructor
24632  * @param {Object} config
24633  */
24634 Roo.grid.GridView = function(config){
24635     Roo.grid.GridView.superclass.constructor.call(this);
24636     this.el = null;
24637
24638     Roo.apply(this, config);
24639 };
24640
24641 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
24642
24643     /**
24644      * Override this function to apply custom css classes to rows during rendering
24645      * @param {Record} record The record
24646      * @param {Number} index
24647      * @method getRowClass
24648      */
24649     rowClass : "x-grid-row",
24650
24651     cellClass : "x-grid-col",
24652
24653     tdClass : "x-grid-td",
24654
24655     hdClass : "x-grid-hd",
24656
24657     splitClass : "x-grid-split",
24658
24659     sortClasses : ["sort-asc", "sort-desc"],
24660
24661     enableMoveAnim : false,
24662
24663     hlColor: "C3DAF9",
24664
24665     dh : Roo.DomHelper,
24666
24667     fly : Roo.Element.fly,
24668
24669     css : Roo.util.CSS,
24670
24671     borderWidth: 1,
24672
24673     splitOffset: 3,
24674
24675     scrollIncrement : 22,
24676
24677     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
24678
24679     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
24680
24681     bind : function(ds, cm){
24682         if(this.ds){
24683             this.ds.un("load", this.onLoad, this);
24684             this.ds.un("datachanged", this.onDataChange, this);
24685             this.ds.un("add", this.onAdd, this);
24686             this.ds.un("remove", this.onRemove, this);
24687             this.ds.un("update", this.onUpdate, this);
24688             this.ds.un("clear", this.onClear, this);
24689         }
24690         if(ds){
24691             ds.on("load", this.onLoad, this);
24692             ds.on("datachanged", this.onDataChange, this);
24693             ds.on("add", this.onAdd, this);
24694             ds.on("remove", this.onRemove, this);
24695             ds.on("update", this.onUpdate, this);
24696             ds.on("clear", this.onClear, this);
24697         }
24698         this.ds = ds;
24699
24700         if(this.cm){
24701             this.cm.un("widthchange", this.onColWidthChange, this);
24702             this.cm.un("headerchange", this.onHeaderChange, this);
24703             this.cm.un("hiddenchange", this.onHiddenChange, this);
24704             this.cm.un("columnmoved", this.onColumnMove, this);
24705             this.cm.un("columnlockchange", this.onColumnLock, this);
24706         }
24707         if(cm){
24708             this.generateRules(cm);
24709             cm.on("widthchange", this.onColWidthChange, this);
24710             cm.on("headerchange", this.onHeaderChange, this);
24711             cm.on("hiddenchange", this.onHiddenChange, this);
24712             cm.on("columnmoved", this.onColumnMove, this);
24713             cm.on("columnlockchange", this.onColumnLock, this);
24714         }
24715         this.cm = cm;
24716     },
24717
24718     init: function(grid){
24719                 Roo.grid.GridView.superclass.init.call(this, grid);
24720
24721                 this.bind(grid.dataSource, grid.colModel);
24722
24723             grid.on("headerclick", this.handleHeaderClick, this);
24724
24725         if(grid.trackMouseOver){
24726             grid.on("mouseover", this.onRowOver, this);
24727                 grid.on("mouseout", this.onRowOut, this);
24728             }
24729             grid.cancelTextSelection = function(){};
24730                 this.gridId = grid.id;
24731
24732                 var tpls = this.templates || {};
24733
24734                 if(!tpls.master){
24735                     tpls.master = new Roo.Template(
24736                        '<div class="x-grid" hidefocus="true">',
24737                           '<div class="x-grid-topbar"></div>',
24738                           '<div class="x-grid-scroller"><div></div></div>',
24739                           '<div class="x-grid-locked">',
24740                               '<div class="x-grid-header">{lockedHeader}</div>',
24741                               '<div class="x-grid-body">{lockedBody}</div>',
24742                           "</div>",
24743                           '<div class="x-grid-viewport">',
24744                               '<div class="x-grid-header">{header}</div>',
24745                               '<div class="x-grid-body">{body}</div>',
24746                           "</div>",
24747                           '<div class="x-grid-bottombar"></div>',
24748                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
24749                           '<div class="x-grid-resize-proxy">&#160;</div>',
24750                        "</div>"
24751                     );
24752                     tpls.master.disableformats = true;
24753                 }
24754
24755                 if(!tpls.header){
24756                     tpls.header = new Roo.Template(
24757                        '<table border="0" cellspacing="0" cellpadding="0">',
24758                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
24759                        "</table>{splits}"
24760                     );
24761                     tpls.header.disableformats = true;
24762                 }
24763                 tpls.header.compile();
24764
24765                 if(!tpls.hcell){
24766                     tpls.hcell = new Roo.Template(
24767                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
24768                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
24769                         "</div></td>"
24770                      );
24771                      tpls.hcell.disableFormats = true;
24772                 }
24773                 tpls.hcell.compile();
24774
24775                 if(!tpls.hsplit){
24776                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
24777                     tpls.hsplit.disableFormats = true;
24778                 }
24779                 tpls.hsplit.compile();
24780
24781                 if(!tpls.body){
24782                     tpls.body = new Roo.Template(
24783                        '<table border="0" cellspacing="0" cellpadding="0">',
24784                        "<tbody>{rows}</tbody>",
24785                        "</table>"
24786                     );
24787                     tpls.body.disableFormats = true;
24788                 }
24789                 tpls.body.compile();
24790
24791                 if(!tpls.row){
24792                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
24793                     tpls.row.disableFormats = true;
24794                 }
24795                 tpls.row.compile();
24796
24797                 if(!tpls.cell){
24798                     tpls.cell = new Roo.Template(
24799                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
24800                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
24801                         "</td>"
24802                     );
24803             tpls.cell.disableFormats = true;
24804         }
24805                 tpls.cell.compile();
24806
24807                 this.templates = tpls;
24808         },
24809
24810         // remap these for backwards compat
24811     onColWidthChange : function(){
24812         this.updateColumns.apply(this, arguments);
24813     },
24814     onHeaderChange : function(){
24815         this.updateHeaders.apply(this, arguments);
24816     }, 
24817     onHiddenChange : function(){
24818         this.handleHiddenChange.apply(this, arguments);
24819     },
24820     onColumnMove : function(){
24821         this.handleColumnMove.apply(this, arguments);
24822     },
24823     onColumnLock : function(){
24824         this.handleLockChange.apply(this, arguments);
24825     },
24826
24827     onDataChange : function(){
24828         this.refresh();
24829         this.updateHeaderSortState();
24830     },
24831
24832         onClear : function(){
24833         this.refresh();
24834     },
24835
24836         onUpdate : function(ds, record){
24837         this.refreshRow(record);
24838     },
24839
24840     refreshRow : function(record){
24841         var ds = this.ds, index;
24842         if(typeof record == 'number'){
24843             index = record;
24844             record = ds.getAt(index);
24845         }else{
24846             index = ds.indexOf(record);
24847         }
24848         this.insertRows(ds, index, index, true);
24849         this.onRemove(ds, record, index+1, true);
24850         this.syncRowHeights(index, index);
24851         this.layout();
24852         this.fireEvent("rowupdated", this, index, record);
24853     },
24854
24855     onAdd : function(ds, records, index){
24856         this.insertRows(ds, index, index + (records.length-1));
24857     },
24858
24859     onRemove : function(ds, record, index, isUpdate){
24860         if(isUpdate !== true){
24861             this.fireEvent("beforerowremoved", this, index, record);
24862         }
24863         var bt = this.getBodyTable(), lt = this.getLockedTable();
24864         if(bt.rows[index]){
24865             bt.firstChild.removeChild(bt.rows[index]);
24866         }
24867         if(lt.rows[index]){
24868             lt.firstChild.removeChild(lt.rows[index]);
24869         }
24870         if(isUpdate !== true){
24871             this.stripeRows(index);
24872             this.syncRowHeights(index, index);
24873             this.layout();
24874             this.fireEvent("rowremoved", this, index, record);
24875         }
24876     },
24877
24878     onLoad : function(){
24879         this.scrollToTop();
24880     },
24881
24882     /**
24883      * Scrolls the grid to the top
24884      */
24885     scrollToTop : function(){
24886         if(this.scroller){
24887             this.scroller.dom.scrollTop = 0;
24888             this.syncScroll();
24889         }
24890     },
24891
24892     /**
24893      * Gets a panel in the header of the grid that can be used for toolbars etc.
24894      * After modifying the contents of this panel a call to grid.autoSize() may be
24895      * required to register any changes in size.
24896      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
24897      * @return Roo.Element
24898      */
24899     getHeaderPanel : function(doShow){
24900         if(doShow){
24901             this.headerPanel.show();
24902         }
24903         return this.headerPanel;
24904         },
24905
24906         /**
24907      * Gets a panel in the footer of the grid that can be used for toolbars etc.
24908      * After modifying the contents of this panel a call to grid.autoSize() may be
24909      * required to register any changes in size.
24910      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
24911      * @return Roo.Element
24912      */
24913     getFooterPanel : function(doShow){
24914         if(doShow){
24915             this.footerPanel.show();
24916         }
24917         return this.footerPanel;
24918         },
24919
24920         initElements : function(){
24921             var E = Roo.Element;
24922             var el = this.grid.getGridEl().dom.firstChild;
24923             var cs = el.childNodes;
24924
24925             this.el = new E(el);
24926             this.headerPanel = new E(el.firstChild);
24927             this.headerPanel.enableDisplayMode("block");
24928
24929         this.scroller = new E(cs[1]);
24930             this.scrollSizer = new E(this.scroller.dom.firstChild);
24931
24932             this.lockedWrap = new E(cs[2]);
24933             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
24934             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
24935
24936             this.mainWrap = new E(cs[3]);
24937             this.mainHd = new E(this.mainWrap.dom.firstChild);
24938             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
24939
24940             this.footerPanel = new E(cs[4]);
24941             this.footerPanel.enableDisplayMode("block");
24942
24943         this.focusEl = new E(cs[5]);
24944         this.focusEl.swallowEvent("click", true);
24945         this.resizeProxy = new E(cs[6]);
24946
24947             this.headerSelector = String.format(
24948                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
24949                this.lockedHd.id, this.mainHd.id
24950             );
24951
24952             this.splitterSelector = String.format(
24953                '#{0} div.x-grid-split, #{1} div.x-grid-split',
24954                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
24955             );
24956     },
24957     idToCssName : function(s)
24958     {
24959         return s.replace(/[^a-z0-9]+/ig, '-');
24960     },
24961
24962         getHeaderCell : function(index){
24963             return Roo.DomQuery.select(this.headerSelector)[index];
24964         },
24965
24966         getHeaderCellMeasure : function(index){
24967             return this.getHeaderCell(index).firstChild;
24968         },
24969
24970         getHeaderCellText : function(index){
24971             return this.getHeaderCell(index).firstChild.firstChild;
24972         },
24973
24974         getLockedTable : function(){
24975             return this.lockedBody.dom.firstChild;
24976         },
24977
24978         getBodyTable : function(){
24979             return this.mainBody.dom.firstChild;
24980         },
24981
24982         getLockedRow : function(index){
24983             return this.getLockedTable().rows[index];
24984         },
24985
24986         getRow : function(index){
24987             return this.getBodyTable().rows[index];
24988         },
24989
24990         getRowComposite : function(index){
24991             if(!this.rowEl){
24992                 this.rowEl = new Roo.CompositeElementLite();
24993             }
24994         var els = [], lrow, mrow;
24995         if(lrow = this.getLockedRow(index)){
24996             els.push(lrow);
24997         }
24998         if(mrow = this.getRow(index)){
24999             els.push(mrow);
25000         }
25001         this.rowEl.elements = els;
25002             return this.rowEl;
25003         },
25004
25005         getCell : function(rowIndex, colIndex){
25006             var locked = this.cm.getLockedCount();
25007             var source;
25008             if(colIndex < locked){
25009                 source = this.lockedBody.dom.firstChild;
25010             }else{
25011                 source = this.mainBody.dom.firstChild;
25012                 colIndex -= locked;
25013             }
25014         return source.rows[rowIndex].childNodes[colIndex];
25015         },
25016
25017         getCellText : function(rowIndex, colIndex){
25018             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
25019         },
25020
25021         getCellBox : function(cell){
25022             var b = this.fly(cell).getBox();
25023         if(Roo.isOpera){ // opera fails to report the Y
25024             b.y = cell.offsetTop + this.mainBody.getY();
25025         }
25026         return b;
25027     },
25028
25029     getCellIndex : function(cell){
25030         var id = String(cell.className).match(this.cellRE);
25031         if(id){
25032             return parseInt(id[1], 10);
25033         }
25034         return 0;
25035     },
25036
25037     findHeaderIndex : function(n){
25038         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
25039         return r ? this.getCellIndex(r) : false;
25040     },
25041
25042     findHeaderCell : function(n){
25043         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
25044         return r ? r : false;
25045     },
25046
25047     findRowIndex : function(n){
25048         if(!n){
25049             return false;
25050         }
25051         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
25052         return r ? r.rowIndex : false;
25053     },
25054
25055     findCellIndex : function(node){
25056         var stop = this.el.dom;
25057         while(node && node != stop){
25058             if(this.findRE.test(node.className)){
25059                 return this.getCellIndex(node);
25060             }
25061             node = node.parentNode;
25062         }
25063         return false;
25064     },
25065
25066     getColumnId : function(index){
25067             return this.cm.getColumnId(index);
25068         },
25069
25070         getSplitters : function(){
25071             if(this.splitterSelector){
25072                return Roo.DomQuery.select(this.splitterSelector);
25073             }else{
25074                 return null;
25075             }
25076         },
25077
25078         getSplitter : function(index){
25079             return this.getSplitters()[index];
25080         },
25081
25082     onRowOver : function(e, t){
25083         var row;
25084         if((row = this.findRowIndex(t)) !== false){
25085             this.getRowComposite(row).addClass("x-grid-row-over");
25086         }
25087     },
25088
25089     onRowOut : function(e, t){
25090         var row;
25091         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
25092             this.getRowComposite(row).removeClass("x-grid-row-over");
25093         }
25094     },
25095
25096     renderHeaders : function(){
25097             var cm = this.cm;
25098         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
25099         var cb = [], lb = [], sb = [], lsb = [], p = {};
25100         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
25101             p.cellId = "x-grid-hd-0-" + i;
25102             p.splitId = "x-grid-csplit-0-" + i;
25103             p.id = cm.getColumnId(i);
25104             p.title = cm.getColumnTooltip(i) || "";
25105             p.value = cm.getColumnHeader(i) || "";
25106             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
25107             if(!cm.isLocked(i)){
25108                 cb[cb.length] = ct.apply(p);
25109                 sb[sb.length] = st.apply(p);
25110             }else{
25111                 lb[lb.length] = ct.apply(p);
25112                 lsb[lsb.length] = st.apply(p);
25113             }
25114         }
25115         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
25116                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
25117         },
25118
25119         updateHeaders : function(){
25120         var html = this.renderHeaders();
25121         this.lockedHd.update(html[0]);
25122         this.mainHd.update(html[1]);
25123     },
25124
25125     /**
25126      * Focuses the specified row.
25127      * @param {Number} row The row index
25128      */
25129     focusRow : function(row){
25130         var x = this.scroller.dom.scrollLeft;
25131         this.focusCell(row, 0, false);
25132         this.scroller.dom.scrollLeft = x;
25133     },
25134
25135     /**
25136      * Focuses the specified cell.
25137      * @param {Number} row The row index
25138      * @param {Number} col The column index
25139      * @param {Boolean} hscroll false to disable horizontal scrolling
25140      */
25141     focusCell : function(row, col, hscroll){
25142         var el = this.ensureVisible(row, col, hscroll);
25143         this.focusEl.alignTo(el, "tl-tl");
25144         if(Roo.isGecko){
25145             this.focusEl.focus();
25146         }else{
25147             this.focusEl.focus.defer(1, this.focusEl);
25148         }
25149     },
25150
25151     /**
25152      * Scrolls the specified cell into view
25153      * @param {Number} row The row index
25154      * @param {Number} col The column index
25155      * @param {Boolean} hscroll false to disable horizontal scrolling
25156      */
25157     ensureVisible : function(row, col, hscroll){
25158         if(typeof row != "number"){
25159             row = row.rowIndex;
25160         }
25161         if(row < 0 && row >= this.ds.getCount()){
25162             return;
25163         }
25164         col = (col !== undefined ? col : 0);
25165         var cm = this.grid.colModel;
25166         while(cm.isHidden(col)){
25167             col++;
25168         }
25169
25170         var el = this.getCell(row, col);
25171         if(!el){
25172             return;
25173         }
25174         var c = this.scroller.dom;
25175
25176         var ctop = parseInt(el.offsetTop, 10);
25177         var cleft = parseInt(el.offsetLeft, 10);
25178         var cbot = ctop + el.offsetHeight;
25179         var cright = cleft + el.offsetWidth;
25180
25181         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
25182         var stop = parseInt(c.scrollTop, 10);
25183         var sleft = parseInt(c.scrollLeft, 10);
25184         var sbot = stop + ch;
25185         var sright = sleft + c.clientWidth;
25186
25187         if(ctop < stop){
25188                 c.scrollTop = ctop;
25189         }else if(cbot > sbot){
25190             c.scrollTop = cbot-ch;
25191         }
25192
25193         if(hscroll !== false){
25194             if(cleft < sleft){
25195                 c.scrollLeft = cleft;
25196             }else if(cright > sright){
25197                 c.scrollLeft = cright-c.clientWidth;
25198             }
25199         }
25200         return el;
25201     },
25202
25203     updateColumns : function(){
25204         this.grid.stopEditing();
25205         var cm = this.grid.colModel, colIds = this.getColumnIds();
25206         //var totalWidth = cm.getTotalWidth();
25207         var pos = 0;
25208         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
25209             //if(cm.isHidden(i)) continue;
25210             var w = cm.getColumnWidth(i);
25211             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
25212             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
25213         }
25214         this.updateSplitters();
25215     },
25216
25217     generateRules : function(cm){
25218         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
25219         Roo.util.CSS.removeStyleSheet(rulesId);
25220         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
25221             var cid = cm.getColumnId(i);
25222             var align = '';
25223             if(cm.config[i].align){
25224                 align = 'text-align:'+cm.config[i].align+';';
25225             }
25226             var hidden = '';
25227             if(cm.isHidden(i)){
25228                 hidden = 'display:none;';
25229             }
25230             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
25231             ruleBuf.push(
25232                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
25233                     this.hdSelector, cid, " {\n", align, width, "}\n",
25234                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
25235                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
25236         }
25237         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
25238     },
25239
25240     updateSplitters : function(){
25241         var cm = this.cm, s = this.getSplitters();
25242         if(s){ // splitters not created yet
25243             var pos = 0, locked = true;
25244             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
25245                 if(cm.isHidden(i)) continue;
25246                 var w = cm.getColumnWidth(i);
25247                 if(!cm.isLocked(i) && locked){
25248                     pos = 0;
25249                     locked = false;
25250                 }
25251                 pos += w;
25252                 s[i].style.left = (pos-this.splitOffset) + "px";
25253             }
25254         }
25255     },
25256
25257     handleHiddenChange : function(colModel, colIndex, hidden){
25258         if(hidden){
25259             this.hideColumn(colIndex);
25260         }else{
25261             this.unhideColumn(colIndex);
25262         }
25263     },
25264
25265     hideColumn : function(colIndex){
25266         var cid = this.getColumnId(colIndex);
25267         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
25268         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
25269         if(Roo.isSafari){
25270             this.updateHeaders();
25271         }
25272         this.updateSplitters();
25273         this.layout();
25274     },
25275
25276     unhideColumn : function(colIndex){
25277         var cid = this.getColumnId(colIndex);
25278         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
25279         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
25280
25281         if(Roo.isSafari){
25282             this.updateHeaders();
25283         }
25284         this.updateSplitters();
25285         this.layout();
25286     },
25287
25288     insertRows : function(dm, firstRow, lastRow, isUpdate){
25289         if(firstRow == 0 && lastRow == dm.getCount()-1){
25290             this.refresh();
25291         }else{
25292             if(!isUpdate){
25293                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
25294             }
25295             var s = this.getScrollState();
25296             var markup = this.renderRows(firstRow, lastRow);
25297             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
25298             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
25299             this.restoreScroll(s);
25300             if(!isUpdate){
25301                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
25302                 this.syncRowHeights(firstRow, lastRow);
25303                 this.stripeRows(firstRow);
25304                 this.layout();
25305             }
25306         }
25307     },
25308
25309     bufferRows : function(markup, target, index){
25310         var before = null, trows = target.rows, tbody = target.tBodies[0];
25311         if(index < trows.length){
25312             before = trows[index];
25313         }
25314         var b = document.createElement("div");
25315         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
25316         var rows = b.firstChild.rows;
25317         for(var i = 0, len = rows.length; i < len; i++){
25318             if(before){
25319                 tbody.insertBefore(rows[0], before);
25320             }else{
25321                 tbody.appendChild(rows[0]);
25322             }
25323         }
25324         b.innerHTML = "";
25325         b = null;
25326     },
25327
25328     deleteRows : function(dm, firstRow, lastRow){
25329         if(dm.getRowCount()<1){
25330             this.fireEvent("beforerefresh", this);
25331             this.mainBody.update("");
25332             this.lockedBody.update("");
25333             this.fireEvent("refresh", this);
25334         }else{
25335             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
25336             var bt = this.getBodyTable();
25337             var tbody = bt.firstChild;
25338             var rows = bt.rows;
25339             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
25340                 tbody.removeChild(rows[firstRow]);
25341             }
25342             this.stripeRows(firstRow);
25343             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
25344         }
25345     },
25346
25347     updateRows : function(dataSource, firstRow, lastRow){
25348         var s = this.getScrollState();
25349         this.refresh();
25350         this.restoreScroll(s);
25351     },
25352
25353     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
25354         if(!noRefresh){
25355            this.refresh();
25356         }
25357         this.updateHeaderSortState();
25358     },
25359
25360     getScrollState : function(){
25361         var sb = this.scroller.dom;
25362         return {left: sb.scrollLeft, top: sb.scrollTop};
25363     },
25364
25365     stripeRows : function(startRow){
25366         if(!this.grid.stripeRows || this.ds.getCount() < 1){
25367             return;
25368         }
25369         startRow = startRow || 0;
25370         var rows = this.getBodyTable().rows;
25371         var lrows = this.getLockedTable().rows;
25372         var cls = ' x-grid-row-alt ';
25373         for(var i = startRow, len = rows.length; i < len; i++){
25374             var row = rows[i], lrow = lrows[i];
25375             var isAlt = ((i+1) % 2 == 0);
25376             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
25377             if(isAlt == hasAlt){
25378                 continue;
25379             }
25380             if(isAlt){
25381                 row.className += " x-grid-row-alt";
25382             }else{
25383                 row.className = row.className.replace("x-grid-row-alt", "");
25384             }
25385             if(lrow){
25386                 lrow.className = row.className;
25387             }
25388         }
25389     },
25390
25391     restoreScroll : function(state){
25392         var sb = this.scroller.dom;
25393         sb.scrollLeft = state.left;
25394         sb.scrollTop = state.top;
25395         this.syncScroll();
25396     },
25397
25398     syncScroll : function(){
25399         var sb = this.scroller.dom;
25400         var sh = this.mainHd.dom;
25401         var bs = this.mainBody.dom;
25402         var lv = this.lockedBody.dom;
25403         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
25404         lv.scrollTop = bs.scrollTop = sb.scrollTop;
25405     },
25406
25407     handleScroll : function(e){
25408         this.syncScroll();
25409         var sb = this.scroller.dom;
25410         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
25411         e.stopEvent();
25412     },
25413
25414     handleWheel : function(e){
25415         var d = e.getWheelDelta();
25416         this.scroller.dom.scrollTop -= d*22;
25417         // set this here to prevent jumpy scrolling on large tables
25418         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
25419         e.stopEvent();
25420     },
25421
25422     renderRows : function(startRow, endRow){
25423         // pull in all the crap needed to render rows
25424         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
25425         var colCount = cm.getColumnCount();
25426
25427         if(ds.getCount() < 1){
25428             return ["", ""];
25429         }
25430
25431         // build a map for all the columns
25432         var cs = [];
25433         for(var i = 0; i < colCount; i++){
25434             var name = cm.getDataIndex(i);
25435             cs[i] = {
25436                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
25437                 renderer : cm.getRenderer(i),
25438                 id : cm.getColumnId(i),
25439                 locked : cm.isLocked(i)
25440             };
25441         }
25442
25443         startRow = startRow || 0;
25444         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
25445
25446         // records to render
25447         var rs = ds.getRange(startRow, endRow);
25448
25449         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
25450     },
25451
25452     // As much as I hate to duplicate code, this was branched because FireFox really hates
25453     // [].join("") on strings. The performance difference was substantial enough to
25454     // branch this function
25455     doRender : Roo.isGecko ?
25456             function(cs, rs, ds, startRow, colCount, stripe){
25457                 var ts = this.templates, ct = ts.cell, rt = ts.row;
25458                 // buffers
25459                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
25460                 for(var j = 0, len = rs.length; j < len; j++){
25461                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
25462                     for(var i = 0; i < colCount; i++){
25463                         c = cs[i];
25464                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
25465                         p.id = c.id;
25466                         p.css = p.attr = "";
25467                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
25468                         if(p.value == undefined || p.value === "") p.value = "&#160;";
25469                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
25470                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
25471                         }
25472                         var markup = ct.apply(p);
25473                         if(!c.locked){
25474                             cb+= markup;
25475                         }else{
25476                             lcb+= markup;
25477                         }
25478                     }
25479                     var alt = [];
25480                     if(stripe && ((rowIndex+1) % 2 == 0)){
25481                         alt[0] = "x-grid-row-alt";
25482                     }
25483                     if(r.dirty){
25484                         alt[1] = " x-grid-dirty-row";
25485                     }
25486                     rp.cells = lcb;
25487                     if(this.getRowClass){
25488                         alt[2] = this.getRowClass(r, rowIndex);
25489                     }
25490                     rp.alt = alt.join(" ");
25491                     lbuf+= rt.apply(rp);
25492                     rp.cells = cb;
25493                     buf+=  rt.apply(rp);
25494                 }
25495                 return [lbuf, buf];
25496             } :
25497             function(cs, rs, ds, startRow, colCount, stripe){
25498                 var ts = this.templates, ct = ts.cell, rt = ts.row;
25499                 // buffers
25500                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
25501                 for(var j = 0, len = rs.length; j < len; j++){
25502                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
25503                     for(var i = 0; i < colCount; i++){
25504                         c = cs[i];
25505                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
25506                         p.id = c.id;
25507                         p.css = p.attr = "";
25508                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
25509                         if(p.value == undefined || p.value === "") p.value = "&#160;";
25510                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
25511                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
25512                         }
25513                         var markup = ct.apply(p);
25514                         if(!c.locked){
25515                             cb[cb.length] = markup;
25516                         }else{
25517                             lcb[lcb.length] = markup;
25518                         }
25519                     }
25520                     var alt = [];
25521                     if(stripe && ((rowIndex+1) % 2 == 0)){
25522                         alt[0] = "x-grid-row-alt";
25523                     }
25524                     if(r.dirty){
25525                         alt[1] = " x-grid-dirty-row";
25526                     }
25527                     rp.cells = lcb;
25528                     if(this.getRowClass){
25529                         alt[2] = this.getRowClass(r, rowIndex);
25530                     }
25531                     rp.alt = alt.join(" ");
25532                     rp.cells = lcb.join("");
25533                     lbuf[lbuf.length] = rt.apply(rp);
25534                     rp.cells = cb.join("");
25535                     buf[buf.length] =  rt.apply(rp);
25536                 }
25537                 return [lbuf.join(""), buf.join("")];
25538             },
25539
25540     renderBody : function(){
25541         var markup = this.renderRows();
25542         var bt = this.templates.body;
25543         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
25544     },
25545
25546     /**
25547      * Refreshes the grid
25548      * @param {Boolean} headersToo
25549      */
25550     refresh : function(headersToo){
25551         this.fireEvent("beforerefresh", this);
25552         this.grid.stopEditing();
25553         var result = this.renderBody();
25554         this.lockedBody.update(result[0]);
25555         this.mainBody.update(result[1]);
25556         if(headersToo === true){
25557             this.updateHeaders();
25558             this.updateColumns();
25559             this.updateSplitters();
25560             this.updateHeaderSortState();
25561         }
25562         this.syncRowHeights();
25563         this.layout();
25564         this.fireEvent("refresh", this);
25565     },
25566
25567     handleColumnMove : function(cm, oldIndex, newIndex){
25568         this.indexMap = null;
25569         var s = this.getScrollState();
25570         this.refresh(true);
25571         this.restoreScroll(s);
25572         this.afterMove(newIndex);
25573     },
25574
25575     afterMove : function(colIndex){
25576         if(this.enableMoveAnim && Roo.enableFx){
25577             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
25578         }
25579     },
25580
25581     updateCell : function(dm, rowIndex, dataIndex){
25582         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
25583         if(typeof colIndex == "undefined"){ // not present in grid
25584             return;
25585         }
25586         var cm = this.grid.colModel;
25587         var cell = this.getCell(rowIndex, colIndex);
25588         var cellText = this.getCellText(rowIndex, colIndex);
25589
25590         var p = {
25591             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
25592             id : cm.getColumnId(colIndex),
25593             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
25594         };
25595         var renderer = cm.getRenderer(colIndex);
25596         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
25597         if(typeof val == "undefined" || val === "") val = "&#160;";
25598         cellText.innerHTML = val;
25599         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
25600         this.syncRowHeights(rowIndex, rowIndex);
25601     },
25602
25603     calcColumnWidth : function(colIndex, maxRowsToMeasure){
25604         var maxWidth = 0;
25605         if(this.grid.autoSizeHeaders){
25606             var h = this.getHeaderCellMeasure(colIndex);
25607             maxWidth = Math.max(maxWidth, h.scrollWidth);
25608         }
25609         var tb, index;
25610         if(this.cm.isLocked(colIndex)){
25611             tb = this.getLockedTable();
25612             index = colIndex;
25613         }else{
25614             tb = this.getBodyTable();
25615             index = colIndex - this.cm.getLockedCount();
25616         }
25617         if(tb && tb.rows){
25618             var rows = tb.rows;
25619             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
25620             for(var i = 0; i < stopIndex; i++){
25621                 var cell = rows[i].childNodes[index].firstChild;
25622                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
25623             }
25624         }
25625         return maxWidth + /*margin for error in IE*/ 5;
25626     },
25627     /**
25628      * Autofit a column to its content.
25629      * @param {Number} colIndex
25630      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
25631      */
25632      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
25633          if(this.cm.isHidden(colIndex)){
25634              return; // can't calc a hidden column
25635          }
25636         if(forceMinSize){
25637             var cid = this.cm.getColumnId(colIndex);
25638             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
25639            if(this.grid.autoSizeHeaders){
25640                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
25641            }
25642         }
25643         var newWidth = this.calcColumnWidth(colIndex);
25644         this.cm.setColumnWidth(colIndex,
25645             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
25646         if(!suppressEvent){
25647             this.grid.fireEvent("columnresize", colIndex, newWidth);
25648         }
25649     },
25650
25651     /**
25652      * Autofits all columns to their content and then expands to fit any extra space in the grid
25653      */
25654      autoSizeColumns : function(){
25655         var cm = this.grid.colModel;
25656         var colCount = cm.getColumnCount();
25657         for(var i = 0; i < colCount; i++){
25658             this.autoSizeColumn(i, true, true);
25659         }
25660         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
25661             this.fitColumns();
25662         }else{
25663             this.updateColumns();
25664             this.layout();
25665         }
25666     },
25667
25668     /**
25669      * Autofits all columns to the grid's width proportionate with their current size
25670      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
25671      */
25672     fitColumns : function(reserveScrollSpace){
25673         var cm = this.grid.colModel;
25674         var colCount = cm.getColumnCount();
25675         var cols = [];
25676         var width = 0;
25677         var i, w;
25678         for (i = 0; i < colCount; i++){
25679             if(!cm.isHidden(i) && !cm.isFixed(i)){
25680                 w = cm.getColumnWidth(i);
25681                 cols.push(i);
25682                 cols.push(w);
25683                 width += w;
25684             }
25685         }
25686         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
25687         if(reserveScrollSpace){
25688             avail -= 17;
25689         }
25690         var frac = (avail - cm.getTotalWidth())/width;
25691         while (cols.length){
25692             w = cols.pop();
25693             i = cols.pop();
25694             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
25695         }
25696         this.updateColumns();
25697         this.layout();
25698     },
25699
25700     onRowSelect : function(rowIndex){
25701         var row = this.getRowComposite(rowIndex);
25702         row.addClass("x-grid-row-selected");
25703     },
25704
25705     onRowDeselect : function(rowIndex){
25706         var row = this.getRowComposite(rowIndex);
25707         row.removeClass("x-grid-row-selected");
25708     },
25709
25710     onCellSelect : function(row, col){
25711         var cell = this.getCell(row, col);
25712         if(cell){
25713             Roo.fly(cell).addClass("x-grid-cell-selected");
25714         }
25715     },
25716
25717     onCellDeselect : function(row, col){
25718         var cell = this.getCell(row, col);
25719         if(cell){
25720             Roo.fly(cell).removeClass("x-grid-cell-selected");
25721         }
25722     },
25723
25724     updateHeaderSortState : function(){
25725         var state = this.ds.getSortState();
25726         if(!state){
25727             return;
25728         }
25729         this.sortState = state;
25730         var sortColumn = this.cm.findColumnIndex(state.field);
25731         if(sortColumn != -1){
25732             var sortDir = state.direction;
25733             var sc = this.sortClasses;
25734             var hds = this.el.select(this.headerSelector).removeClass(sc);
25735             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
25736         }
25737     },
25738
25739     handleHeaderClick : function(g, index){
25740         if(this.headersDisabled){
25741             return;
25742         }
25743         var dm = g.dataSource, cm = g.colModel;
25744             if(!cm.isSortable(index)){
25745             return;
25746         }
25747             g.stopEditing();
25748         dm.sort(cm.getDataIndex(index));
25749     },
25750
25751
25752     destroy : function(){
25753         if(this.colMenu){
25754             this.colMenu.removeAll();
25755             Roo.menu.MenuMgr.unregister(this.colMenu);
25756             this.colMenu.getEl().remove();
25757             delete this.colMenu;
25758         }
25759         if(this.hmenu){
25760             this.hmenu.removeAll();
25761             Roo.menu.MenuMgr.unregister(this.hmenu);
25762             this.hmenu.getEl().remove();
25763             delete this.hmenu;
25764         }
25765         if(this.grid.enableColumnMove){
25766             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
25767             if(dds){
25768                 for(var dd in dds){
25769                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
25770                         var elid = dds[dd].dragElId;
25771                         dds[dd].unreg();
25772                         Roo.get(elid).remove();
25773                     } else if(dds[dd].config.isTarget){
25774                         dds[dd].proxyTop.remove();
25775                         dds[dd].proxyBottom.remove();
25776                         dds[dd].unreg();
25777                     }
25778                     if(Roo.dd.DDM.locationCache[dd]){
25779                         delete Roo.dd.DDM.locationCache[dd];
25780                     }
25781                 }
25782                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
25783             }
25784         }
25785         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
25786         this.bind(null, null);
25787         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
25788     },
25789
25790     handleLockChange : function(){
25791         this.refresh(true);
25792     },
25793
25794     onDenyColumnLock : function(){
25795
25796     },
25797
25798     onDenyColumnHide : function(){
25799
25800     },
25801
25802     handleHdMenuClick : function(item){
25803         var index = this.hdCtxIndex;
25804         var cm = this.cm, ds = this.ds;
25805         switch(item.id){
25806             case "asc":
25807                 ds.sort(cm.getDataIndex(index), "ASC");
25808                 break;
25809             case "desc":
25810                 ds.sort(cm.getDataIndex(index), "DESC");
25811                 break;
25812             case "lock":
25813                 var lc = cm.getLockedCount();
25814                 if(cm.getColumnCount(true) <= lc+1){
25815                     this.onDenyColumnLock();
25816                     return;
25817                 }
25818                 if(lc != index){
25819                     cm.setLocked(index, true, true);
25820                     cm.moveColumn(index, lc);
25821                     this.grid.fireEvent("columnmove", index, lc);
25822                 }else{
25823                     cm.setLocked(index, true);
25824                 }
25825             break;
25826             case "unlock":
25827                 var lc = cm.getLockedCount();
25828                 if((lc-1) != index){
25829                     cm.setLocked(index, false, true);
25830                     cm.moveColumn(index, lc-1);
25831                     this.grid.fireEvent("columnmove", index, lc-1);
25832                 }else{
25833                     cm.setLocked(index, false);
25834                 }
25835             break;
25836             default:
25837                 index = cm.getIndexById(item.id.substr(4));
25838                 if(index != -1){
25839                     if(item.checked && cm.getColumnCount(true) <= 1){
25840                         this.onDenyColumnHide();
25841                         return false;
25842                     }
25843                     cm.setHidden(index, item.checked);
25844                 }
25845         }
25846         return true;
25847     },
25848
25849     beforeColMenuShow : function(){
25850         var cm = this.cm,  colCount = cm.getColumnCount();
25851         this.colMenu.removeAll();
25852         for(var i = 0; i < colCount; i++){
25853             this.colMenu.add(new Roo.menu.CheckItem({
25854                 id: "col-"+cm.getColumnId(i),
25855                 text: cm.getColumnHeader(i),
25856                 checked: !cm.isHidden(i),
25857                 hideOnClick:false
25858             }));
25859         }
25860     },
25861
25862     handleHdCtx : function(g, index, e){
25863         e.stopEvent();
25864         var hd = this.getHeaderCell(index);
25865         this.hdCtxIndex = index;
25866         var ms = this.hmenu.items, cm = this.cm;
25867         ms.get("asc").setDisabled(!cm.isSortable(index));
25868         ms.get("desc").setDisabled(!cm.isSortable(index));
25869         if(this.grid.enableColLock !== false){
25870             ms.get("lock").setDisabled(cm.isLocked(index));
25871             ms.get("unlock").setDisabled(!cm.isLocked(index));
25872         }
25873         this.hmenu.show(hd, "tl-bl");
25874     },
25875
25876     handleHdOver : function(e){
25877         var hd = this.findHeaderCell(e.getTarget());
25878         if(hd && !this.headersDisabled){
25879             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
25880                this.fly(hd).addClass("x-grid-hd-over");
25881             }
25882         }
25883     },
25884
25885     handleHdOut : function(e){
25886         var hd = this.findHeaderCell(e.getTarget());
25887         if(hd){
25888             this.fly(hd).removeClass("x-grid-hd-over");
25889         }
25890     },
25891
25892     handleSplitDblClick : function(e, t){
25893         var i = this.getCellIndex(t);
25894         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
25895             this.autoSizeColumn(i, true);
25896             this.layout();
25897         }
25898     },
25899
25900     render : function(){
25901
25902         var cm = this.cm;
25903         var colCount = cm.getColumnCount();
25904
25905         if(this.grid.monitorWindowResize === true){
25906             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
25907         }
25908         var header = this.renderHeaders();
25909         var body = this.templates.body.apply({rows:""});
25910         var html = this.templates.master.apply({
25911             lockedBody: body,
25912             body: body,
25913             lockedHeader: header[0],
25914             header: header[1]
25915         });
25916
25917         //this.updateColumns();
25918
25919         this.grid.getGridEl().dom.innerHTML = html;
25920
25921         this.initElements();
25922
25923         this.scroller.on("scroll", this.handleScroll, this);
25924         this.lockedBody.on("mousewheel", this.handleWheel, this);
25925         this.mainBody.on("mousewheel", this.handleWheel, this);
25926
25927         this.mainHd.on("mouseover", this.handleHdOver, this);
25928         this.mainHd.on("mouseout", this.handleHdOut, this);
25929         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
25930                 {delegate: "."+this.splitClass});
25931
25932         this.lockedHd.on("mouseover", this.handleHdOver, this);
25933         this.lockedHd.on("mouseout", this.handleHdOut, this);
25934         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
25935                 {delegate: "."+this.splitClass});
25936
25937         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
25938             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
25939         }
25940
25941         this.updateSplitters();
25942
25943         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
25944             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
25945             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
25946         }
25947
25948         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
25949             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
25950             this.hmenu.add(
25951                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
25952                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
25953             );
25954             if(this.grid.enableColLock !== false){
25955                 this.hmenu.add('-',
25956                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
25957                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
25958                 );
25959             }
25960             if(this.grid.enableColumnHide !== false){
25961
25962                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
25963                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
25964                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
25965
25966                 this.hmenu.add('-',
25967                     {id:"columns", text: this.columnsText, menu: this.colMenu}
25968                 );
25969             }
25970             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
25971
25972             this.grid.on("headercontextmenu", this.handleHdCtx, this);
25973         }
25974
25975         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
25976             this.dd = new Roo.grid.GridDragZone(this.grid, {
25977                 ddGroup : this.grid.ddGroup || 'GridDD'
25978             });
25979         }
25980
25981         /*
25982         for(var i = 0; i < colCount; i++){
25983             if(cm.isHidden(i)){
25984                 this.hideColumn(i);
25985             }
25986             if(cm.config[i].align){
25987                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
25988                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
25989             }
25990         }*/
25991         
25992         this.updateHeaderSortState();
25993
25994         this.beforeInitialResize();
25995         this.layout(true);
25996
25997         // two part rendering gives faster view to the user
25998         this.renderPhase2.defer(1, this);
25999     },
26000
26001     renderPhase2 : function(){
26002         // render the rows now
26003         this.refresh();
26004         if(this.grid.autoSizeColumns){
26005             this.autoSizeColumns();
26006         }
26007     },
26008
26009     beforeInitialResize : function(){
26010
26011     },
26012
26013     onColumnSplitterMoved : function(i, w){
26014         this.userResized = true;
26015         var cm = this.grid.colModel;
26016         cm.setColumnWidth(i, w, true);
26017         var cid = cm.getColumnId(i);
26018         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
26019         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
26020         this.updateSplitters();
26021         this.layout();
26022         this.grid.fireEvent("columnresize", i, w);
26023     },
26024
26025     syncRowHeights : function(startIndex, endIndex){
26026         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
26027             startIndex = startIndex || 0;
26028             var mrows = this.getBodyTable().rows;
26029             var lrows = this.getLockedTable().rows;
26030             var len = mrows.length-1;
26031             endIndex = Math.min(endIndex || len, len);
26032             for(var i = startIndex; i <= endIndex; i++){
26033                 var m = mrows[i], l = lrows[i];
26034                 var h = Math.max(m.offsetHeight, l.offsetHeight);
26035                 m.style.height = l.style.height = h + "px";
26036             }
26037         }
26038     },
26039
26040     layout : function(initialRender, is2ndPass){
26041         var g = this.grid;
26042         var auto = g.autoHeight;
26043         var scrollOffset = 16;
26044         var c = g.getGridEl(), cm = this.cm,
26045                 expandCol = g.autoExpandColumn,
26046                 gv = this;
26047         //c.beginMeasure();
26048
26049         if(!c.dom.offsetWidth){ // display:none?
26050             if(initialRender){
26051                 this.lockedWrap.show();
26052                 this.mainWrap.show();
26053             }
26054             return;
26055         }
26056
26057         var hasLock = this.cm.isLocked(0);
26058
26059         var tbh = this.headerPanel.getHeight();
26060         var bbh = this.footerPanel.getHeight();
26061
26062         if(auto){
26063             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
26064             var newHeight = ch + c.getBorderWidth("tb");
26065             if(g.maxHeight){
26066                 newHeight = Math.min(g.maxHeight, newHeight);
26067             }
26068             c.setHeight(newHeight);
26069         }
26070
26071         if(g.autoWidth){
26072             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
26073         }
26074
26075         var s = this.scroller;
26076
26077         var csize = c.getSize(true);
26078
26079         this.el.setSize(csize.width, csize.height);
26080
26081         this.headerPanel.setWidth(csize.width);
26082         this.footerPanel.setWidth(csize.width);
26083
26084         var hdHeight = this.mainHd.getHeight();
26085         var vw = csize.width;
26086         var vh = csize.height - (tbh + bbh);
26087
26088         s.setSize(vw, vh);
26089
26090         var bt = this.getBodyTable();
26091         var ltWidth = hasLock ?
26092                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
26093
26094         var scrollHeight = bt.offsetHeight;
26095         var scrollWidth = ltWidth + bt.offsetWidth;
26096         var vscroll = false, hscroll = false;
26097
26098         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
26099
26100         var lw = this.lockedWrap, mw = this.mainWrap;
26101         var lb = this.lockedBody, mb = this.mainBody;
26102
26103         setTimeout(function(){
26104             var t = s.dom.offsetTop;
26105             var w = s.dom.clientWidth,
26106                 h = s.dom.clientHeight;
26107
26108             lw.setTop(t);
26109             lw.setSize(ltWidth, h);
26110
26111             mw.setLeftTop(ltWidth, t);
26112             mw.setSize(w-ltWidth, h);
26113
26114             lb.setHeight(h-hdHeight);
26115             mb.setHeight(h-hdHeight);
26116
26117             if(is2ndPass !== true && !gv.userResized && expandCol){
26118                 // high speed resize without full column calculation
26119                 
26120                 var ci = cm.getIndexById(expandCol);
26121                 if (ci < 0) {
26122                     ci = cm.findColumnIndex(expandCol);
26123                 }
26124                 ci = Math.max(0, ci); // make sure it's got at least the first col.
26125                 var expandId = cm.getColumnId(ci);
26126                 var  tw = cm.getTotalWidth(false);
26127                 var currentWidth = cm.getColumnWidth(ci);
26128                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
26129                 if(currentWidth != cw){
26130                     cm.setColumnWidth(ci, cw, true);
26131                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
26132                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
26133                     gv.updateSplitters();
26134                     gv.layout(false, true);
26135                 }
26136             }
26137
26138             if(initialRender){
26139                 lw.show();
26140                 mw.show();
26141             }
26142             //c.endMeasure();
26143         }, 10);
26144     },
26145
26146     onWindowResize : function(){
26147         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
26148             return;
26149         }
26150         this.layout();
26151     },
26152
26153     appendFooter : function(parentEl){
26154         return null;
26155     },
26156
26157     sortAscText : "Sort Ascending",
26158     sortDescText : "Sort Descending",
26159     lockText : "Lock Column",
26160     unlockText : "Unlock Column",
26161     columnsText : "Columns"
26162 });
26163
26164
26165 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
26166     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
26167     this.proxy.el.addClass('x-grid3-col-dd');
26168 };
26169
26170 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
26171     handleMouseDown : function(e){
26172
26173     },
26174
26175     callHandleMouseDown : function(e){
26176         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
26177     }
26178 });