remove debugging code
[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         if(this.modal){
984             this.mask.setStyle("z-index", index);
985         }
986         if(this.shim){
987             this.shim.setStyle("z-index", ++index);
988         }
989         if(this.shadow){
990             this.shadow.setZIndex(++index);
991         }
992         this.el.setStyle("z-index", ++index);
993         if(this.proxy){
994             this.proxy.setStyle("z-index", ++index);
995         }
996         if(this.resizer){
997             this.resizer.proxy.setStyle("z-index", ++index);
998         }
999
1000         this.lastZIndex = index;
1001     },
1002
1003     /**
1004      * Returns the element for this dialog
1005      * @return {Roo.Element} The underlying dialog Element
1006      */
1007     getEl : function(){
1008         return this.el;
1009     }
1010 });
1011
1012 /**
1013  * @class Roo.DialogManager
1014  * Provides global access to BasicDialogs that have been created and
1015  * support for z-indexing (layering) multiple open dialogs.
1016  */
1017 Roo.DialogManager = function(){
1018     var list = {};
1019     var accessList = [];
1020     var front = null;
1021
1022     // private
1023     var sortDialogs = function(d1, d2){
1024         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
1025     };
1026
1027     // private
1028     var orderDialogs = function(){
1029         accessList.sort(sortDialogs);
1030         var seed = Roo.DialogManager.zseed;
1031         for(var i = 0, len = accessList.length; i < len; i++){
1032             var dlg = accessList[i];
1033             if(dlg){
1034                 dlg.setZIndex(seed + (i*10));
1035             }
1036         }
1037     };
1038
1039     return {
1040         /**
1041          * The starting z-index for BasicDialogs (defaults to 9000)
1042          * @type Number The z-index value
1043          */
1044         zseed : 9000,
1045
1046         // private
1047         register : function(dlg){
1048             list[dlg.id] = dlg;
1049             accessList.push(dlg);
1050         },
1051
1052         // private
1053         unregister : function(dlg){
1054             delete list[dlg.id];
1055             var i=0;
1056             var len=0;
1057             if(!accessList.indexOf){
1058                 for(  i = 0, len = accessList.length; i < len; i++){
1059                     if(accessList[i] == dlg){
1060                         accessList.splice(i, 1);
1061                         return;
1062                     }
1063                 }
1064             }else{
1065                  i = accessList.indexOf(dlg);
1066                 if(i != -1){
1067                     accessList.splice(i, 1);
1068                 }
1069             }
1070         },
1071
1072         /**
1073          * Gets a registered dialog by id
1074          * @param {String/Object} id The id of the dialog or a dialog
1075          * @return {Roo.BasicDialog} this
1076          */
1077         get : function(id){
1078             return typeof id == "object" ? id : list[id];
1079         },
1080
1081         /**
1082          * Brings the specified dialog to the front
1083          * @param {String/Object} dlg The id of the dialog or a dialog
1084          * @return {Roo.BasicDialog} this
1085          */
1086         bringToFront : function(dlg){
1087             dlg = this.get(dlg);
1088             if(dlg != front){
1089                 front = dlg;
1090                 dlg._lastAccess = new Date().getTime();
1091                 orderDialogs();
1092             }
1093             return dlg;
1094         },
1095
1096         /**
1097          * Sends the specified dialog to the back
1098          * @param {String/Object} dlg The id of the dialog or a dialog
1099          * @return {Roo.BasicDialog} this
1100          */
1101         sendToBack : function(dlg){
1102             dlg = this.get(dlg);
1103             dlg._lastAccess = -(new Date().getTime());
1104             orderDialogs();
1105             return dlg;
1106         },
1107
1108         /**
1109          * Hides all dialogs
1110          */
1111         hideAll : function(){
1112             for(var id in list){
1113                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
1114                     list[id].hide();
1115                 }
1116             }
1117         }
1118     };
1119 }();
1120
1121 /**
1122  * @class Roo.LayoutDialog
1123  * @extends Roo.BasicDialog
1124  * Dialog which provides adjustments for working with a layout in a Dialog.
1125  * Add your necessary layout config options to the dialog's config.<br>
1126  * Example usage (including a nested layout):
1127  * <pre><code>
1128 if(!dialog){
1129     dialog = new Roo.LayoutDialog("download-dlg", {
1130         modal: true,
1131         width:600,
1132         height:450,
1133         shadow:true,
1134         minWidth:500,
1135         minHeight:350,
1136         autoTabs:true,
1137         proxyDrag:true,
1138         // layout config merges with the dialog config
1139         center:{
1140             tabPosition: "top",
1141             alwaysShowTabs: true
1142         }
1143     });
1144     dialog.addKeyListener(27, dialog.hide, dialog);
1145     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
1146     dialog.addButton("Build It!", this.getDownload, this);
1147
1148     // we can even add nested layouts
1149     var innerLayout = new Roo.BorderLayout("dl-inner", {
1150         east: {
1151             initialSize: 200,
1152             autoScroll:true,
1153             split:true
1154         },
1155         center: {
1156             autoScroll:true
1157         }
1158     });
1159     innerLayout.beginUpdate();
1160     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
1161     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
1162     innerLayout.endUpdate(true);
1163
1164     var layout = dialog.getLayout();
1165     layout.beginUpdate();
1166     layout.add("center", new Roo.ContentPanel("standard-panel",
1167                         {title: "Download the Source", fitToFrame:true}));
1168     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
1169                {title: "Build your own roo.js"}));
1170     layout.getRegion("center").showPanel(sp);
1171     layout.endUpdate();
1172 }
1173 </code></pre>
1174     * @constructor
1175     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
1176     * @param {Object} config configuration options
1177   */
1178 Roo.LayoutDialog = function(el, cfg){
1179     
1180     var config=  cfg;
1181     if (typeof(cfg) == 'undefined') {
1182         config = Roo.apply({}, el);
1183         // not sure why we use documentElement here.. - it should always be body.
1184         // IE7 borks horribly if we use documentElement.
1185         // webkit also does not like documentElement - it creates a body element...
1186         el = Roo.get( document.body || document.documentElement ).createChild();
1187         //config.autoCreate = true;
1188     }
1189     
1190     
1191     config.autoTabs = false;
1192     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
1193     this.body.setStyle({overflow:"hidden", position:"relative"});
1194     this.layout = new Roo.BorderLayout(this.body.dom, config);
1195     this.layout.monitorWindowResize = false;
1196     this.el.addClass("x-dlg-auto-layout");
1197     // fix case when center region overwrites center function
1198     this.center = Roo.BasicDialog.prototype.center;
1199     this.on("show", this.layout.layout, this.layout, true);
1200     if (config.items) {
1201         var xitems = config.items;
1202         delete config.items;
1203         Roo.each(xitems, this.addxtype, this);
1204     }
1205     
1206     
1207 };
1208 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
1209     /**
1210      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
1211      * @deprecated
1212      */
1213     endUpdate : function(){
1214         this.layout.endUpdate();
1215     },
1216
1217     /**
1218      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
1219      *  @deprecated
1220      */
1221     beginUpdate : function(){
1222         this.layout.beginUpdate();
1223     },
1224
1225     /**
1226      * Get the BorderLayout for this dialog
1227      * @return {Roo.BorderLayout}
1228      */
1229     getLayout : function(){
1230         return this.layout;
1231     },
1232
1233     showEl : function(){
1234         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
1235         if(Roo.isIE7){
1236             this.layout.layout();
1237         }
1238     },
1239
1240     // private
1241     // Use the syncHeightBeforeShow config option to control this automatically
1242     syncBodyHeight : function(){
1243         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
1244         if(this.layout){this.layout.layout();}
1245     },
1246     
1247       /**
1248      * Add an xtype element (actually adds to the layout.)
1249      * @return {Object} xdata xtype object data.
1250      */
1251     
1252     addxtype : function(c) {
1253         return this.layout.addxtype(c);
1254     }
1255 });