d1854bdb444d9585435f4885e8384af4e06b84a7
[roojs1] / Roo / BasicDialog.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  * @class Roo.BasicDialog
14  * @extends Roo.util.Observable
15  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
16  * <pre><code>
17 var dlg = new Roo.BasicDialog("my-dlg", {
18     height: 200,
19     width: 300,
20     minHeight: 100,
21     minWidth: 150,
22     modal: true,
23     proxyDrag: true,
24     shadow: true
25 });
26 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
27 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28 dlg.addButton('Cancel', dlg.hide, dlg);
29 dlg.show();
30 </code></pre>
31   <b>A Dialog should always be a direct child of the body element.</b>
32  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33  * @cfg {String} title Default text to display in the title bar (defaults to null)
34  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
35  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
36  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
37  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
38  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
39  * (defaults to null with no animation)
40  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
41  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
42  * property for valid values (defaults to 'all')
43  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
44  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
45  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
46  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
47  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
48  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
49  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
50  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
51  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
52  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
53  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
54  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
55  * draggable = true (defaults to false)
56  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
57  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
58  * shadow (defaults to false)
59  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
60  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
61  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
62  * @cfg {Array} buttons Array of buttons
63  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
64  * @constructor
65  * Create a new BasicDialog.
66  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
67  * @param {Object} config Configuration options
68  */
69 Roo.BasicDialog = function(el, config){
70     this.el = Roo.get(el);
71     var dh = Roo.DomHelper;
72     if(!this.el && config && config.autoCreate){
73         if(typeof config.autoCreate == "object"){
74             if(!config.autoCreate.id){
75                 config.autoCreate.id = el;
76             }
77             this.el = dh.append(document.body,
78                         config.autoCreate, true);
79         }else{
80             this.el = dh.append(document.body,
81                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
82         }
83     }
84     el = this.el;
85     el.setDisplayed(true);
86     el.hide = this.hideAction;
87     this.id = el.id;
88     el.addClass("x-dlg");
89
90     Roo.apply(this, config);
91
92     this.proxy = el.createProxy("x-dlg-proxy");
93     this.proxy.hide = this.hideAction;
94     this.proxy.setOpacity(.5);
95     this.proxy.hide();
96
97     if(config.width){
98         el.setWidth(config.width);
99     }
100     if(config.height){
101         el.setHeight(config.height);
102     }
103     this.size = el.getSize();
104     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
105         this.xy = [config.x,config.y];
106     }else{
107         this.xy = el.getCenterXY(true);
108     }
109     /** The header element @type Roo.Element */
110     this.header = el.child("> .x-dlg-hd");
111     /** The body element @type Roo.Element */
112     this.body = el.child("> .x-dlg-bd");
113     /** The footer element @type Roo.Element */
114     this.footer = el.child("> .x-dlg-ft");
115
116     if(!this.header){
117         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
118     }
119     if(!this.body){
120         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
121     }
122
123     this.header.unselectable();
124     if(this.title){
125         this.header.update(this.title);
126     }
127     // this element allows the dialog to be focused for keyboard event
128     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
129     this.focusEl.swallowEvent("click", true);
130
131     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
132
133     // wrap the body and footer for special rendering
134     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
135     if(this.footer){
136         this.bwrap.dom.appendChild(this.footer.dom);
137     }
138
139     this.bg = this.el.createChild({
140         tag: "div", cls:"x-dlg-bg",
141         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
142     });
143     this.centerBg = this.bg.child("div.x-dlg-bg-center");
144
145
146     if(this.autoScroll !== false && !this.autoTabs){
147         this.body.setStyle("overflow", "auto");
148     }
149
150     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
151
152     if(this.closable !== false){
153         this.el.addClass("x-dlg-closable");
154         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
155         this.close.on("click", this.closeClick, this);
156         this.close.addClassOnOver("x-dlg-close-over");
157     }
158     if(this.collapsible !== false){
159         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
160         this.collapseBtn.on("click", this.collapseClick, this);
161         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
162         this.header.on("dblclick", this.collapseClick, this);
163     }
164     if(this.resizable !== false){
165         this.el.addClass("x-dlg-resizable");
166         this.resizer = new Roo.Resizable(el, {
167             minWidth: this.minWidth || 80,
168             minHeight:this.minHeight || 80,
169             handles: this.resizeHandles || "all",
170             pinned: true
171         });
172         this.resizer.on("beforeresize", this.beforeResize, this);
173         this.resizer.on("resize", this.onResize, this);
174     }
175     if(this.draggable !== false){
176         el.addClass("x-dlg-draggable");
177         if (!this.proxyDrag) {
178             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
179         }
180         else {
181             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
182         }
183         dd.setHandleElId(this.header.id);
184         dd.endDrag = this.endMove.createDelegate(this);
185         dd.startDrag = this.startMove.createDelegate(this);
186         dd.onDrag = this.onDrag.createDelegate(this);
187         dd.scroll = false;
188         this.dd = dd;
189     }
190     if(this.modal){
191         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
192         this.mask.enableDisplayMode("block");
193         this.mask.hide();
194         this.el.addClass("x-dlg-modal");
195     }
196     if(this.shadow){
197         this.shadow = new Roo.Shadow({
198             mode : typeof this.shadow == "string" ? this.shadow : "sides",
199             offset : this.shadowOffset
200         });
201     }else{
202         this.shadowOffset = 0;
203     }
204     if(Roo.useShims && this.shim !== false){
205         this.shim = this.el.createShim();
206         this.shim.hide = this.hideAction;
207         this.shim.hide();
208     }else{
209         this.shim = false;
210     }
211     if(this.autoTabs){
212         this.initTabs();
213     }
214     if (this.buttons) { 
215         var bts= this.buttons;
216         this.buttons = [];
217         Roo.each(bts, function(b) {
218             this.addButton(b);
219         }, this);
220     }
221     
222     
223     this.addEvents({
224         /**
225          * @event keydown
226          * Fires when a key is pressed
227          * @param {Roo.BasicDialog} this
228          * @param {Roo.EventObject} e
229          */
230         "keydown" : true,
231         /**
232          * @event move
233          * Fires when this dialog is moved by the user.
234          * @param {Roo.BasicDialog} this
235          * @param {Number} x The new page X
236          * @param {Number} y The new page Y
237          */
238         "move" : true,
239         /**
240          * @event resize
241          * Fires when this dialog is resized by the user.
242          * @param {Roo.BasicDialog} this
243          * @param {Number} width The new width
244          * @param {Number} height The new height
245          */
246         "resize" : true,
247         /**
248          * @event beforehide
249          * Fires before this dialog is hidden.
250          * @param {Roo.BasicDialog} this
251          */
252         "beforehide" : true,
253         /**
254          * @event hide
255          * Fires when this dialog is hidden.
256          * @param {Roo.BasicDialog} this
257          */
258         "hide" : true,
259         /**
260          * @event beforeshow
261          * Fires before this dialog is shown.
262          * @param {Roo.BasicDialog} this
263          */
264         "beforeshow" : true,
265         /**
266          * @event show
267          * Fires when this dialog is shown.
268          * @param {Roo.BasicDialog} this
269          */
270         "show" : true
271     });
272     el.on("keydown", this.onKeyDown, this);
273     el.on("mousedown", this.toFront, this);
274     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
275     this.el.hide();
276     Roo.DialogManager.register(this);
277     Roo.BasicDialog.superclass.constructor.call(this);
278 };
279
280 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
281     shadowOffset: Roo.isIE ? 6 : 5,
282     minHeight: 80,
283     minWidth: 200,
284     minButtonWidth: 75,
285     defaultButton: null,
286     buttonAlign: "right",
287     tabTag: 'div',
288     firstShow: true,
289
290     /**
291      * Sets the dialog title text
292      * @param {String} text The title text to display
293      * @return {Roo.BasicDialog} this
294      */
295     setTitle : function(text){
296         this.header.update(text);
297         return this;
298     },
299
300     // private
301     closeClick : function(){
302         this.hide();
303     },
304
305     // private
306     collapseClick : function(){
307         this[this.collapsed ? "expand" : "collapse"]();
308     },
309
310     /**
311      * Collapses the dialog to its minimized state (only the title bar is visible).
312      * Equivalent to the user clicking the collapse dialog button.
313      */
314     collapse : function(){
315         if(!this.collapsed){
316             this.collapsed = true;
317             this.el.addClass("x-dlg-collapsed");
318             this.restoreHeight = this.el.getHeight();
319             this.resizeTo(this.el.getWidth(), this.header.getHeight());
320         }
321     },
322
323     /**
324      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
325      * clicking the expand dialog button.
326      */
327     expand : function(){
328         if(this.collapsed){
329             this.collapsed = false;
330             this.el.removeClass("x-dlg-collapsed");
331             this.resizeTo(this.el.getWidth(), this.restoreHeight);
332         }
333     },
334
335     /**
336      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
337      * @return {Roo.TabPanel} The tabs component
338      */
339     initTabs : function(){
340         var tabs = this.getTabs();
341         while(tabs.getTab(0)){
342             tabs.removeTab(0);
343         }
344         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
345             var dom = el.dom;
346             tabs.addTab(Roo.id(dom), dom.title);
347             dom.title = "";
348         });
349         tabs.activate(0);
350         return tabs;
351     },
352
353     // private
354     beforeResize : function(){
355         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
356     },
357
358     // private
359     onResize : function(){
360         this.refreshSize();
361         this.syncBodyHeight();
362         this.adjustAssets();
363         this.focus();
364         this.fireEvent("resize", this, this.size.width, this.size.height);
365     },
366
367     // private
368     onKeyDown : function(e){
369         if(this.isVisible()){
370             this.fireEvent("keydown", this, e);
371         }
372     },
373
374     /**
375      * Resizes the dialog.
376      * @param {Number} width
377      * @param {Number} height
378      * @return {Roo.BasicDialog} this
379      */
380     resizeTo : function(width, height){
381         this.el.setSize(width, height);
382         this.size = {width: width, height: height};
383         this.syncBodyHeight();
384         if(this.fixedcenter){
385             this.center();
386         }
387         if(this.isVisible()){
388             this.constrainXY();
389             this.adjustAssets();
390         }
391         this.fireEvent("resize", this, width, height);
392         return this;
393     },
394
395
396     /**
397      * Resizes the dialog to fit the specified content size.
398      * @param {Number} width
399      * @param {Number} height
400      * @return {Roo.BasicDialog} this
401      */
402     setContentSize : function(w, h){
403         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
404         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
405         //if(!this.el.isBorderBox()){
406             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
407             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
408         //}
409         if(this.tabs){
410             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
411             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
412         }
413         this.resizeTo(w, h);
414         return this;
415     },
416
417     /**
418      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
419      * executed in response to a particular key being pressed while the dialog is active.
420      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
421      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
422      * @param {Function} fn The function to call
423      * @param {Object} scope (optional) The scope of the function
424      * @return {Roo.BasicDialog} this
425      */
426     addKeyListener : function(key, fn, scope){
427         var keyCode, shift, ctrl, alt;
428         if(typeof key == "object" && !(key instanceof Array)){
429             keyCode = key["key"];
430             shift = key["shift"];
431             ctrl = key["ctrl"];
432             alt = key["alt"];
433         }else{
434             keyCode = key;
435         }
436         var handler = function(dlg, e){
437             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
438                 var k = e.getKey();
439                 if(keyCode instanceof Array){
440                     for(var i = 0, len = keyCode.length; i < len; i++){
441                         if(keyCode[i] == k){
442                           fn.call(scope || window, dlg, k, e);
443                           return;
444                         }
445                     }
446                 }else{
447                     if(k == keyCode){
448                         fn.call(scope || window, dlg, k, e);
449                     }
450                 }
451             }
452         };
453         this.on("keydown", handler);
454         return this;
455     },
456
457     /**
458      * Returns the TabPanel component (creates it if it doesn't exist).
459      * Note: If you wish to simply check for the existence of tabs without creating them,
460      * check for a null 'tabs' property.
461      * @return {Roo.TabPanel} The tabs component
462      */
463     getTabs : function(){
464         if(!this.tabs){
465             this.el.addClass("x-dlg-auto-tabs");
466             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
467             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
468         }
469         return this.tabs;
470     },
471
472     /**
473      * Adds a button to the footer section of the dialog.
474      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
475      * object or a valid Roo.DomHelper element config
476      * @param {Function} handler The function called when the button is clicked
477      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
478      * @return {Roo.Button} The new button
479      */
480     addButton : function(config, handler, scope){
481         var dh = Roo.DomHelper;
482         if(!this.footer){
483             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
484         }
485         if(!this.btnContainer){
486             var tb = this.footer.createChild({
487
488                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
489                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
490             }, null, true);
491             this.btnContainer = tb.firstChild.firstChild.firstChild;
492         }
493         var bconfig = {
494             handler: handler,
495             scope: scope,
496             minWidth: this.minButtonWidth,
497             hideParent:true
498         };
499         if(typeof config == "string"){
500             bconfig.text = config;
501         }else{
502             if(config.tag){
503                 bconfig.dhconfig = config;
504             }else{
505                 Roo.apply(bconfig, config);
506             }
507         }
508         var fc = false;
509         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
510             bconfig.position = Math.max(0, bconfig.position);
511             fc = this.btnContainer.childNodes[bconfig.position];
512         }
513          
514         var btn = new Roo.Button(
515             fc ? 
516                 this.btnContainer.insertBefore(document.createElement("td"),fc)
517                 : this.btnContainer.appendChild(document.createElement("td")),
518             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
519             bconfig
520         );
521         this.syncBodyHeight();
522         if(!this.buttons){
523             /**
524              * Array of all the buttons that have been added to this dialog via addButton
525              * @type Array
526              */
527             this.buttons = [];
528         }
529         this.buttons.push(btn);
530         return btn;
531     },
532
533     /**
534      * Sets the default button to be focused when the dialog is displayed.
535      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
536      * @return {Roo.BasicDialog} this
537      */
538     setDefaultButton : function(btn){
539         this.defaultButton = btn;
540         return this;
541     },
542
543     // private
544     getHeaderFooterHeight : function(safe){
545         var height = 0;
546         if(this.header){
547            height += this.header.getHeight();
548         }
549         if(this.footer){
550            var fm = this.footer.getMargins();
551             height += (this.footer.getHeight()+fm.top+fm.bottom);
552         }
553         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
554         height += this.centerBg.getPadding("tb");
555         return height;
556     },
557
558     // private
559     syncBodyHeight : function()
560     {
561         var bd = this.body, // the text
562             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
563             bw = this.bwrap;
564         var height = this.size.height - this.getHeaderFooterHeight(false);
565         bd.setHeight(height-bd.getMargins("tb"));
566         var hh = this.header.getHeight();
567         var h = this.size.height-hh;
568         cb.setHeight(h);
569         
570         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
571         bw.setHeight(h-cb.getPadding("tb"));
572         
573         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
574         bd.setWidth(bw.getWidth(true));
575         if(this.tabs){
576             this.tabs.syncHeight();
577             if(Roo.isIE){
578                 this.tabs.el.repaint();
579             }
580         }
581     },
582
583     /**
584      * Restores the previous state of the dialog if Roo.state is configured.
585      * @return {Roo.BasicDialog} this
586      */
587     restoreState : function(){
588         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
589         if(box && box.width){
590             this.xy = [box.x, box.y];
591             this.resizeTo(box.width, box.height);
592         }
593         return this;
594     },
595
596     // private
597     beforeShow : function(){
598         this.expand();
599         if(this.fixedcenter){
600             this.xy = this.el.getCenterXY(true);
601         }
602         if(this.modal){
603             Roo.get(document.body).addClass("x-body-masked");
604             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
605             this.mask.show();
606         }
607         this.constrainXY();
608     },
609
610     // private
611     animShow : function(){
612         var b = Roo.get(this.animateTarget).getBox();
613         this.proxy.setSize(b.width, b.height);
614         this.proxy.setLocation(b.x, b.y);
615         this.proxy.show();
616         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
617                     true, .35, this.showEl.createDelegate(this));
618     },
619
620     /**
621      * Shows the dialog.
622      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
623      * @return {Roo.BasicDialog} this
624      */
625     show : function(animateTarget){
626         if (this.fireEvent("beforeshow", this) === false){
627             return;
628         }
629         if(this.syncHeightBeforeShow){
630             this.syncBodyHeight();
631         }else if(this.firstShow){
632             this.firstShow = false;
633             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
634         }
635         this.animateTarget = animateTarget || this.animateTarget;
636         if(!this.el.isVisible()){
637             this.beforeShow();
638             if(this.animateTarget && Roo.get(this.animateTarget)){
639                 this.animShow();
640             }else{
641                 this.showEl();
642             }
643         }
644         return this;
645     },
646
647     // private
648     showEl : function(){
649         this.proxy.hide();
650         this.el.setXY(this.xy);
651         this.el.show();
652         this.adjustAssets(true);
653         this.toFront();
654         this.focus();
655         // IE peekaboo bug - fix found by Dave Fenwick
656         if(Roo.isIE){
657             this.el.repaint();
658         }
659         this.fireEvent("show", this);
660     },
661
662     /**
663      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
664      * dialog itself will receive focus.
665      */
666     focus : function(){
667         if(this.defaultButton){
668             this.defaultButton.focus();
669         }else{
670             this.focusEl.focus();
671         }
672     },
673
674     // private
675     constrainXY : function(){
676         if(this.constraintoviewport !== false){
677             if(!this.viewSize){
678                 if(this.container){
679                     var s = this.container.getSize();
680                     this.viewSize = [s.width, s.height];
681                 }else{
682                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
683                 }
684             }
685             var s = Roo.get(this.container||document).getScroll();
686
687             var x = this.xy[0], y = this.xy[1];
688             var w = this.size.width, h = this.size.height;
689             var vw = this.viewSize[0], vh = this.viewSize[1];
690             // only move it if it needs it
691             var moved = false;
692             // first validate right/bottom
693             if(x + w > vw+s.left){
694                 x = vw - w;
695                 moved = true;
696             }
697             if(y + h > vh+s.top){
698                 y = vh - h;
699                 moved = true;
700             }
701             // then make sure top/left isn't negative
702             if(x < s.left){
703                 x = s.left;
704                 moved = true;
705             }
706             if(y < s.top){
707                 y = s.top;
708                 moved = true;
709             }
710             if(moved){
711                 // cache xy
712                 this.xy = [x, y];
713                 if(this.isVisible()){
714                     this.el.setLocation(x, y);
715                     this.adjustAssets();
716                 }
717             }
718         }
719     },
720
721     // private
722     onDrag : function(){
723         if(!this.proxyDrag){
724             this.xy = this.el.getXY();
725             this.adjustAssets();
726         }
727     },
728
729     // private
730     adjustAssets : function(doShow){
731         var x = this.xy[0], y = this.xy[1];
732         var w = this.size.width, h = this.size.height;
733         if(doShow === true){
734             if(this.shadow){
735                 this.shadow.show(this.el);
736             }
737             if(this.shim){
738                 this.shim.show();
739             }
740         }
741         if(this.shadow && this.shadow.isVisible()){
742             this.shadow.show(this.el);
743         }
744         if(this.shim && this.shim.isVisible()){
745             this.shim.setBounds(x, y, w, h);
746         }
747     },
748
749     // private
750     adjustViewport : function(w, h){
751         if(!w || !h){
752             w = Roo.lib.Dom.getViewWidth();
753             h = Roo.lib.Dom.getViewHeight();
754         }
755         // cache the size
756         this.viewSize = [w, h];
757         if(this.modal && this.mask.isVisible()){
758             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
759             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
760         }
761         if(this.isVisible()){
762             this.constrainXY();
763         }
764     },
765
766     /**
767      * Destroys this dialog and all its supporting elements (including any tabs, shim,
768      * shadow, proxy, mask, etc.)  Also removes all event listeners.
769      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
770      */
771     destroy : function(removeEl){
772         if(this.isVisible()){
773             this.animateTarget = null;
774             this.hide();
775         }
776         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
777         if(this.tabs){
778             this.tabs.destroy(removeEl);
779         }
780         Roo.destroy(
781              this.shim,
782              this.proxy,
783              this.resizer,
784              this.close,
785              this.mask
786         );
787         if(this.dd){
788             this.dd.unreg();
789         }
790         if(this.buttons){
791            for(var i = 0, len = this.buttons.length; i < len; i++){
792                this.buttons[i].destroy();
793            }
794         }
795         this.el.removeAllListeners();
796         if(removeEl === true){
797             this.el.update("");
798             this.el.remove();
799         }
800         Roo.DialogManager.unregister(this);
801     },
802
803     // private
804     startMove : function(){
805         if(this.proxyDrag){
806             this.proxy.show();
807         }
808         if(this.constraintoviewport !== false){
809             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
810         }
811     },
812
813     // private
814     endMove : function(){
815         if(!this.proxyDrag){
816             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
817         }else{
818             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
819             this.proxy.hide();
820         }
821         this.refreshSize();
822         this.adjustAssets();
823         this.focus();
824         this.fireEvent("move", this, this.xy[0], this.xy[1]);
825     },
826
827     /**
828      * Brings this dialog to the front of any other visible dialogs
829      * @return {Roo.BasicDialog} this
830      */
831     toFront : function(){
832         Roo.DialogManager.bringToFront(this);
833         return this;
834     },
835
836     /**
837      * Sends this dialog to the back (under) of any other visible dialogs
838      * @return {Roo.BasicDialog} this
839      */
840     toBack : function(){
841         Roo.DialogManager.sendToBack(this);
842         return this;
843     },
844
845     /**
846      * Centers this dialog in the viewport
847      * @return {Roo.BasicDialog} this
848      */
849     center : function(){
850         var xy = this.el.getCenterXY(true);
851         this.moveTo(xy[0], xy[1]);
852         return this;
853     },
854
855     /**
856      * Moves the dialog's top-left corner to the specified point
857      * @param {Number} x
858      * @param {Number} y
859      * @return {Roo.BasicDialog} this
860      */
861     moveTo : function(x, y){
862         this.xy = [x,y];
863         if(this.isVisible()){
864             this.el.setXY(this.xy);
865             this.adjustAssets();
866         }
867         return this;
868     },
869
870     /**
871      * Aligns the dialog to the specified element
872      * @param {String/HTMLElement/Roo.Element} element The element to align to.
873      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
874      * @param {Array} offsets (optional) Offset the positioning by [x, y]
875      * @return {Roo.BasicDialog} this
876      */
877     alignTo : function(element, position, offsets){
878         this.xy = this.el.getAlignToXY(element, position, offsets);
879         if(this.isVisible()){
880             this.el.setXY(this.xy);
881             this.adjustAssets();
882         }
883         return this;
884     },
885
886     /**
887      * Anchors an element to another element and realigns it when the window is resized.
888      * @param {String/HTMLElement/Roo.Element} element The element to align to.
889      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
890      * @param {Array} offsets (optional) Offset the positioning by [x, y]
891      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
892      * is a number, it is used as the buffer delay (defaults to 50ms).
893      * @return {Roo.BasicDialog} this
894      */
895     anchorTo : function(el, alignment, offsets, monitorScroll){
896         var action = function(){
897             this.alignTo(el, alignment, offsets);
898         };
899         Roo.EventManager.onWindowResize(action, this);
900         var tm = typeof monitorScroll;
901         if(tm != 'undefined'){
902             Roo.EventManager.on(window, 'scroll', action, this,
903                 {buffer: tm == 'number' ? monitorScroll : 50});
904         }
905         action.call(this);
906         return this;
907     },
908
909     /**
910      * Returns true if the dialog is visible
911      * @return {Boolean}
912      */
913     isVisible : function(){
914         return this.el.isVisible();
915     },
916
917     // private
918     animHide : function(callback){
919         var b = Roo.get(this.animateTarget).getBox();
920         this.proxy.show();
921         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
922         this.el.hide();
923         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
924                     this.hideEl.createDelegate(this, [callback]));
925     },
926
927     /**
928      * Hides the dialog.
929      * @param {Function} callback (optional) Function to call when the dialog is hidden
930      * @return {Roo.BasicDialog} this
931      */
932     hide : function(callback){
933         if (this.fireEvent("beforehide", this) === false){
934             return;
935         }
936         if(this.shadow){
937             this.shadow.hide();
938         }
939         if(this.shim) {
940           this.shim.hide();
941         }
942         // sometimes animateTarget seems to get set.. causing problems...
943         // this just double checks..
944         if(this.animateTarget && Roo.get(this.animateTarget)) {
945            this.animHide(callback);
946         }else{
947             this.el.hide();
948             this.hideEl(callback);
949         }
950         return this;
951     },
952
953     // private
954     hideEl : function(callback){
955         this.proxy.hide();
956         if(this.modal){
957             this.mask.hide();
958             Roo.get(document.body).removeClass("x-body-masked");
959         }
960         this.fireEvent("hide", this);
961         if(typeof callback == "function"){
962             callback();
963         }
964     },
965
966     // private
967     hideAction : function(){
968         this.setLeft("-10000px");
969         this.setTop("-10000px");
970         this.setStyle("visibility", "hidden");
971     },
972
973     // private
974     refreshSize : function(){
975         this.size = this.el.getSize();
976         this.xy = this.el.getXY();
977         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
978     },
979
980     // private
981     // z-index is managed by the DialogManager and may be overwritten at any time
982     setZIndex : function(index){
983         console.log('run????');
984         console.log(index);
985         
986         if(this.modal){
987             this.mask.setStyle("z-index", index);
988         }
989         if(this.shim){
990             this.shim.setStyle("z-index", ++index);
991         }
992         if(this.shadow){
993             this.shadow.setZIndex(++index);
994         }
995         this.el.setStyle("z-index", ++index);
996         if(this.proxy){
997             this.proxy.setStyle("z-index", ++index);
998         }
999         if(this.resizer){
1000             this.resizer.proxy.setStyle("z-index", ++index);
1001         }
1002
1003         this.lastZIndex = index;
1004         
1005         Roo.BasicDialog.lastZIndex = this.lastZIndex;
1006     },
1007
1008     /**
1009      * Returns the element for this dialog
1010      * @return {Roo.Element} The underlying dialog Element
1011      */
1012     getEl : function(){
1013         return this.el;
1014     }
1015 });
1016
1017 /**
1018  * @class Roo.DialogManager
1019  * Provides global access to BasicDialogs that have been created and
1020  * support for z-indexing (layering) multiple open dialogs.
1021  */
1022 Roo.DialogManager = function(){
1023     var list = {};
1024     var accessList = [];
1025     var front = null;
1026
1027     // private
1028     var sortDialogs = function(d1, d2){
1029         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
1030     };
1031
1032     // private
1033     var orderDialogs = function(){
1034         accessList.sort(sortDialogs);
1035         var seed = Roo.DialogManager.zseed;
1036         for(var i = 0, len = accessList.length; i < len; i++){
1037             var dlg = accessList[i];
1038             if(dlg){
1039                 dlg.setZIndex(seed + (i*10));
1040             }
1041         }
1042     };
1043
1044     return {
1045         /**
1046          * The starting z-index for BasicDialogs (defaults to 9000)
1047          * @type Number The z-index value
1048          */
1049         zseed : 9000,
1050
1051         // private
1052         register : function(dlg){
1053             list[dlg.id] = dlg;
1054             accessList.push(dlg);
1055         },
1056
1057         // private
1058         unregister : function(dlg){
1059             delete list[dlg.id];
1060             var i=0;
1061             var len=0;
1062             if(!accessList.indexOf){
1063                 for(  i = 0, len = accessList.length; i < len; i++){
1064                     if(accessList[i] == dlg){
1065                         accessList.splice(i, 1);
1066                         return;
1067                     }
1068                 }
1069             }else{
1070                  i = accessList.indexOf(dlg);
1071                 if(i != -1){
1072                     accessList.splice(i, 1);
1073                 }
1074             }
1075         },
1076
1077         /**
1078          * Gets a registered dialog by id
1079          * @param {String/Object} id The id of the dialog or a dialog
1080          * @return {Roo.BasicDialog} this
1081          */
1082         get : function(id){
1083             return typeof id == "object" ? id : list[id];
1084         },
1085
1086         /**
1087          * Brings the specified dialog to the front
1088          * @param {String/Object} dlg The id of the dialog or a dialog
1089          * @return {Roo.BasicDialog} this
1090          */
1091         bringToFront : function(dlg){
1092             dlg = this.get(dlg);
1093             if(dlg != front){
1094                 front = dlg;
1095                 dlg._lastAccess = new Date().getTime();
1096                 orderDialogs();
1097             }
1098             return dlg;
1099         },
1100
1101         /**
1102          * Sends the specified dialog to the back
1103          * @param {String/Object} dlg The id of the dialog or a dialog
1104          * @return {Roo.BasicDialog} this
1105          */
1106         sendToBack : function(dlg){
1107             dlg = this.get(dlg);
1108             dlg._lastAccess = -(new Date().getTime());
1109             orderDialogs();
1110             return dlg;
1111         },
1112
1113         /**
1114          * Hides all dialogs
1115          */
1116         hideAll : function(){
1117             for(var id in list){
1118                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
1119                     list[id].hide();
1120                 }
1121             }
1122         }
1123     };
1124 }();
1125
1126 /**
1127  * @class Roo.LayoutDialog
1128  * @extends Roo.BasicDialog
1129  * Dialog which provides adjustments for working with a layout in a Dialog.
1130  * Add your necessary layout config options to the dialog's config.<br>
1131  * Example usage (including a nested layout):
1132  * <pre><code>
1133 if(!dialog){
1134     dialog = new Roo.LayoutDialog("download-dlg", {
1135         modal: true,
1136         width:600,
1137         height:450,
1138         shadow:true,
1139         minWidth:500,
1140         minHeight:350,
1141         autoTabs:true,
1142         proxyDrag:true,
1143         // layout config merges with the dialog config
1144         center:{
1145             tabPosition: "top",
1146             alwaysShowTabs: true
1147         }
1148     });
1149     dialog.addKeyListener(27, dialog.hide, dialog);
1150     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
1151     dialog.addButton("Build It!", this.getDownload, this);
1152
1153     // we can even add nested layouts
1154     var innerLayout = new Roo.BorderLayout("dl-inner", {
1155         east: {
1156             initialSize: 200,
1157             autoScroll:true,
1158             split:true
1159         },
1160         center: {
1161             autoScroll:true
1162         }
1163     });
1164     innerLayout.beginUpdate();
1165     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
1166     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
1167     innerLayout.endUpdate(true);
1168
1169     var layout = dialog.getLayout();
1170     layout.beginUpdate();
1171     layout.add("center", new Roo.ContentPanel("standard-panel",
1172                         {title: "Download the Source", fitToFrame:true}));
1173     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
1174                {title: "Build your own roo.js"}));
1175     layout.getRegion("center").showPanel(sp);
1176     layout.endUpdate();
1177 }
1178 </code></pre>
1179     * @constructor
1180     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
1181     * @param {Object} config configuration options
1182   */
1183 Roo.LayoutDialog = function(el, cfg){
1184     
1185     var config=  cfg;
1186     if (typeof(cfg) == 'undefined') {
1187         config = Roo.apply({}, el);
1188         // not sure why we use documentElement here.. - it should always be body.
1189         // IE7 borks horribly if we use documentElement.
1190         // webkit also does not like documentElement - it creates a body element...
1191         el = Roo.get( document.body || document.documentElement ).createChild();
1192         //config.autoCreate = true;
1193     }
1194     
1195     
1196     config.autoTabs = false;
1197     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
1198     this.body.setStyle({overflow:"hidden", position:"relative"});
1199     this.layout = new Roo.BorderLayout(this.body.dom, config);
1200     this.layout.monitorWindowResize = false;
1201     this.el.addClass("x-dlg-auto-layout");
1202     // fix case when center region overwrites center function
1203     this.center = Roo.BasicDialog.prototype.center;
1204     this.on("show", this.layout.layout, this.layout, true);
1205     if (config.items) {
1206         var xitems = config.items;
1207         delete config.items;
1208         Roo.each(xitems, this.addxtype, this);
1209     }
1210     
1211     
1212 };
1213 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
1214     /**
1215      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
1216      * @deprecated
1217      */
1218     endUpdate : function(){
1219         this.layout.endUpdate();
1220     },
1221
1222     /**
1223      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
1224      *  @deprecated
1225      */
1226     beginUpdate : function(){
1227         this.layout.beginUpdate();
1228     },
1229
1230     /**
1231      * Get the BorderLayout for this dialog
1232      * @return {Roo.BorderLayout}
1233      */
1234     getLayout : function(){
1235         return this.layout;
1236     },
1237
1238     showEl : function(){
1239         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
1240         if(Roo.isIE7){
1241             this.layout.layout();
1242         }
1243     },
1244
1245     // private
1246     // Use the syncHeightBeforeShow config option to control this automatically
1247     syncBodyHeight : function(){
1248         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
1249         if(this.layout){this.layout.layout();}
1250     },
1251     
1252       /**
1253      * Add an xtype element (actually adds to the layout.)
1254      * @return {Object} xdata xtype object data.
1255      */
1256     
1257     addxtype : function(c) {
1258         return this.layout.addxtype(c);
1259     }
1260 });
1261
1262 Roo.apply(Roo.BasicDialog,  {
1263     lastZIndex : 9000
1264 });