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