initial import
[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         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
561         var height = this.size.height - this.getHeaderFooterHeight(false);
562         bd.setHeight(height-bd.getMargins("tb"));
563         var hh = this.header.getHeight();
564         var h = this.size.height-hh;
565         cb.setHeight(h);
566         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
567         bw.setHeight(h-cb.getPadding("tb"));
568         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
569         bd.setWidth(bw.getWidth(true));
570         if(this.tabs){
571             this.tabs.syncHeight();
572             if(Roo.isIE){
573                 this.tabs.el.repaint();
574             }
575         }
576     },
577
578     /**
579      * Restores the previous state of the dialog if Roo.state is configured.
580      * @return {Roo.BasicDialog} this
581      */
582     restoreState : function(){
583         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
584         if(box && box.width){
585             this.xy = [box.x, box.y];
586             this.resizeTo(box.width, box.height);
587         }
588         return this;
589     },
590
591     // private
592     beforeShow : function(){
593         this.expand();
594         if(this.fixedcenter){
595             this.xy = this.el.getCenterXY(true);
596         }
597         if(this.modal){
598             Roo.get(document.body).addClass("x-body-masked");
599             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
600             this.mask.show();
601         }
602         this.constrainXY();
603     },
604
605     // private
606     animShow : function(){
607         var b = Roo.get(this.animateTarget, true).getBox();
608         this.proxy.setSize(b.width, b.height);
609         this.proxy.setLocation(b.x, b.y);
610         this.proxy.show();
611         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
612                     true, .35, this.showEl.createDelegate(this));
613     },
614
615     /**
616      * Shows the dialog.
617      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
618      * @return {Roo.BasicDialog} this
619      */
620     show : function(animateTarget){
621         if (this.fireEvent("beforeshow", this) === false){
622             return;
623         }
624         if(this.syncHeightBeforeShow){
625             this.syncBodyHeight();
626         }else if(this.firstShow){
627             this.firstShow = false;
628             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
629         }
630         this.animateTarget = animateTarget || this.animateTarget;
631         if(!this.el.isVisible()){
632             this.beforeShow();
633             if(this.animateTarget){
634                 this.animShow();
635             }else{
636                 this.showEl();
637             }
638         }
639         return this;
640     },
641
642     // private
643     showEl : function(){
644         this.proxy.hide();
645         this.el.setXY(this.xy);
646         this.el.show();
647         this.adjustAssets(true);
648         this.toFront();
649         this.focus();
650         // IE peekaboo bug - fix found by Dave Fenwick
651         if(Roo.isIE){
652             this.el.repaint();
653         }
654         this.fireEvent("show", this);
655     },
656
657     /**
658      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
659      * dialog itself will receive focus.
660      */
661     focus : function(){
662         if(this.defaultButton){
663             this.defaultButton.focus();
664         }else{
665             this.focusEl.focus();
666         }
667     },
668
669     // private
670     constrainXY : function(){
671         if(this.constraintoviewport !== false){
672             if(!this.viewSize){
673                 if(this.container){
674                     var s = this.container.getSize();
675                     this.viewSize = [s.width, s.height];
676                 }else{
677                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
678                 }
679             }
680             var s = Roo.get(this.container||document).getScroll();
681
682             var x = this.xy[0], y = this.xy[1];
683             var w = this.size.width, h = this.size.height;
684             var vw = this.viewSize[0], vh = this.viewSize[1];
685             // only move it if it needs it
686             var moved = false;
687             // first validate right/bottom
688             if(x + w > vw+s.left){
689                 x = vw - w;
690                 moved = true;
691             }
692             if(y + h > vh+s.top){
693                 y = vh - h;
694                 moved = true;
695             }
696             // then make sure top/left isn't negative
697             if(x < s.left){
698                 x = s.left;
699                 moved = true;
700             }
701             if(y < s.top){
702                 y = s.top;
703                 moved = true;
704             }
705             if(moved){
706                 // cache xy
707                 this.xy = [x, y];
708                 if(this.isVisible()){
709                     this.el.setLocation(x, y);
710                     this.adjustAssets();
711                 }
712             }
713         }
714     },
715
716     // private
717     onDrag : function(){
718         if(!this.proxyDrag){
719             this.xy = this.el.getXY();
720             this.adjustAssets();
721         }
722     },
723
724     // private
725     adjustAssets : function(doShow){
726         var x = this.xy[0], y = this.xy[1];
727         var w = this.size.width, h = this.size.height;
728         if(doShow === true){
729             if(this.shadow){
730                 this.shadow.show(this.el);
731             }
732             if(this.shim){
733                 this.shim.show();
734             }
735         }
736         if(this.shadow && this.shadow.isVisible()){
737             this.shadow.show(this.el);
738         }
739         if(this.shim && this.shim.isVisible()){
740             this.shim.setBounds(x, y, w, h);
741         }
742     },
743
744     // private
745     adjustViewport : function(w, h){
746         if(!w || !h){
747             w = Roo.lib.Dom.getViewWidth();
748             h = Roo.lib.Dom.getViewHeight();
749         }
750         // cache the size
751         this.viewSize = [w, h];
752         if(this.modal && this.mask.isVisible()){
753             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
754             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
755         }
756         if(this.isVisible()){
757             this.constrainXY();
758         }
759     },
760
761     /**
762      * Destroys this dialog and all its supporting elements (including any tabs, shim,
763      * shadow, proxy, mask, etc.)  Also removes all event listeners.
764      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
765      */
766     destroy : function(removeEl){
767         if(this.isVisible()){
768             this.animateTarget = null;
769             this.hide();
770         }
771         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
772         if(this.tabs){
773             this.tabs.destroy(removeEl);
774         }
775         Roo.destroy(
776              this.shim,
777              this.proxy,
778              this.resizer,
779              this.close,
780              this.mask
781         );
782         if(this.dd){
783             this.dd.unreg();
784         }
785         if(this.buttons){
786            for(var i = 0, len = this.buttons.length; i < len; i++){
787                this.buttons[i].destroy();
788            }
789         }
790         this.el.removeAllListeners();
791         if(removeEl === true){
792             this.el.update("");
793             this.el.remove();
794         }
795         Roo.DialogManager.unregister(this);
796     },
797
798     // private
799     startMove : function(){
800         if(this.proxyDrag){
801             this.proxy.show();
802         }
803         if(this.constraintoviewport !== false){
804             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
805         }
806     },
807
808     // private
809     endMove : function(){
810         if(!this.proxyDrag){
811             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
812         }else{
813             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
814             this.proxy.hide();
815         }
816         this.refreshSize();
817         this.adjustAssets();
818         this.focus();
819         this.fireEvent("move", this, this.xy[0], this.xy[1]);
820     },
821
822     /**
823      * Brings this dialog to the front of any other visible dialogs
824      * @return {Roo.BasicDialog} this
825      */
826     toFront : function(){
827         Roo.DialogManager.bringToFront(this);
828         return this;
829     },
830
831     /**
832      * Sends this dialog to the back (under) of any other visible dialogs
833      * @return {Roo.BasicDialog} this
834      */
835     toBack : function(){
836         Roo.DialogManager.sendToBack(this);
837         return this;
838     },
839
840     /**
841      * Centers this dialog in the viewport
842      * @return {Roo.BasicDialog} this
843      */
844     center : function(){
845         var xy = this.el.getCenterXY(true);
846         this.moveTo(xy[0], xy[1]);
847         return this;
848     },
849
850     /**
851      * Moves the dialog's top-left corner to the specified point
852      * @param {Number} x
853      * @param {Number} y
854      * @return {Roo.BasicDialog} this
855      */
856     moveTo : function(x, y){
857         this.xy = [x,y];
858         if(this.isVisible()){
859             this.el.setXY(this.xy);
860             this.adjustAssets();
861         }
862         return this;
863     },
864
865     /**
866      * Aligns the dialog to the specified element
867      * @param {String/HTMLElement/Roo.Element} element The element to align to.
868      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
869      * @param {Array} offsets (optional) Offset the positioning by [x, y]
870      * @return {Roo.BasicDialog} this
871      */
872     alignTo : function(element, position, offsets){
873         this.xy = this.el.getAlignToXY(element, position, offsets);
874         if(this.isVisible()){
875             this.el.setXY(this.xy);
876             this.adjustAssets();
877         }
878         return this;
879     },
880
881     /**
882      * Anchors an element to another element and realigns it when the window is resized.
883      * @param {String/HTMLElement/Roo.Element} element The element to align to.
884      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
885      * @param {Array} offsets (optional) Offset the positioning by [x, y]
886      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
887      * is a number, it is used as the buffer delay (defaults to 50ms).
888      * @return {Roo.BasicDialog} this
889      */
890     anchorTo : function(el, alignment, offsets, monitorScroll){
891         var action = function(){
892             this.alignTo(el, alignment, offsets);
893         };
894         Roo.EventManager.onWindowResize(action, this);
895         var tm = typeof monitorScroll;
896         if(tm != 'undefined'){
897             Roo.EventManager.on(window, 'scroll', action, this,
898                 {buffer: tm == 'number' ? monitorScroll : 50});
899         }
900         action.call(this);
901         return this;
902     },
903
904     /**
905      * Returns true if the dialog is visible
906      * @return {Boolean}
907      */
908     isVisible : function(){
909         return this.el.isVisible();
910     },
911
912     // private
913     animHide : function(callback){
914         var b = Roo.get(this.animateTarget).getBox();
915         this.proxy.show();
916         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
917         this.el.hide();
918         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
919                     this.hideEl.createDelegate(this, [callback]));
920     },
921
922     /**
923      * Hides the dialog.
924      * @param {Function} callback (optional) Function to call when the dialog is hidden
925      * @return {Roo.BasicDialog} this
926      */
927     hide : function(callback){
928         if (this.fireEvent("beforehide", this) === false){
929             return;
930         }
931         if(this.shadow){
932             this.shadow.hide();
933         }
934         if(this.shim) {
935           this.shim.hide();
936         }
937         if(this.animateTarget){
938            this.animHide(callback);
939         }else{
940             this.el.hide();
941             this.hideEl(callback);
942         }
943         return this;
944     },
945
946     // private
947     hideEl : function(callback){
948         this.proxy.hide();
949         if(this.modal){
950             this.mask.hide();
951             Roo.get(document.body).removeClass("x-body-masked");
952         }
953         this.fireEvent("hide", this);
954         if(typeof callback == "function"){
955             callback();
956         }
957     },
958
959     // private
960     hideAction : function(){
961         this.setLeft("-10000px");
962         this.setTop("-10000px");
963         this.setStyle("visibility", "hidden");
964     },
965
966     // private
967     refreshSize : function(){
968         this.size = this.el.getSize();
969         this.xy = this.el.getXY();
970         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
971     },
972
973     // private
974     // z-index is managed by the DialogManager and may be overwritten at any time
975     setZIndex : function(index){
976         if(this.modal){
977             this.mask.setStyle("z-index", index);
978         }
979         if(this.shim){
980             this.shim.setStyle("z-index", ++index);
981         }
982         if(this.shadow){
983             this.shadow.setZIndex(++index);
984         }
985         this.el.setStyle("z-index", ++index);
986         if(this.proxy){
987             this.proxy.setStyle("z-index", ++index);
988         }
989         if(this.resizer){
990             this.resizer.proxy.setStyle("z-index", ++index);
991         }
992
993         this.lastZIndex = index;
994     },
995
996     /**
997      * Returns the element for this dialog
998      * @return {Roo.Element} The underlying dialog Element
999      */
1000     getEl : function(){
1001         return this.el;
1002     }
1003 });
1004
1005 /**
1006  * @class Roo.DialogManager
1007  * Provides global access to BasicDialogs that have been created and
1008  * support for z-indexing (layering) multiple open dialogs.
1009  */
1010 Roo.DialogManager = function(){
1011     var list = {};
1012     var accessList = [];
1013     var front = null;
1014
1015     // private
1016     var sortDialogs = function(d1, d2){
1017         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
1018     };
1019
1020     // private
1021     var orderDialogs = function(){
1022         accessList.sort(sortDialogs);
1023         var seed = Roo.DialogManager.zseed;
1024         for(var i = 0, len = accessList.length; i < len; i++){
1025             var dlg = accessList[i];
1026             if(dlg){
1027                 dlg.setZIndex(seed + (i*10));
1028             }
1029         }
1030     };
1031
1032     return {
1033         /**
1034          * The starting z-index for BasicDialogs (defaults to 9000)
1035          * @type Number The z-index value
1036          */
1037         zseed : 9000,
1038
1039         // private
1040         register : function(dlg){
1041             list[dlg.id] = dlg;
1042             accessList.push(dlg);
1043         },
1044
1045         // private
1046         unregister : function(dlg){
1047             delete list[dlg.id];
1048             var i=0;
1049             var len=0;
1050             if(!accessList.indexOf){
1051                 for(  i = 0, len = accessList.length; i < len; i++){
1052                     if(accessList[i] == dlg){
1053                         accessList.splice(i, 1);
1054                         return;
1055                     }
1056                 }
1057             }else{
1058                  i = accessList.indexOf(dlg);
1059                 if(i != -1){
1060                     accessList.splice(i, 1);
1061                 }
1062             }
1063         },
1064
1065         /**
1066          * Gets a registered dialog by id
1067          * @param {String/Object} id The id of the dialog or a dialog
1068          * @return {Roo.BasicDialog} this
1069          */
1070         get : function(id){
1071             return typeof id == "object" ? id : list[id];
1072         },
1073
1074         /**
1075          * Brings the specified dialog to the front
1076          * @param {String/Object} dlg The id of the dialog or a dialog
1077          * @return {Roo.BasicDialog} this
1078          */
1079         bringToFront : function(dlg){
1080             dlg = this.get(dlg);
1081             if(dlg != front){
1082                 front = dlg;
1083                 dlg._lastAccess = new Date().getTime();
1084                 orderDialogs();
1085             }
1086             return dlg;
1087         },
1088
1089         /**
1090          * Sends the specified dialog to the back
1091          * @param {String/Object} dlg The id of the dialog or a dialog
1092          * @return {Roo.BasicDialog} this
1093          */
1094         sendToBack : function(dlg){
1095             dlg = this.get(dlg);
1096             dlg._lastAccess = -(new Date().getTime());
1097             orderDialogs();
1098             return dlg;
1099         },
1100
1101         /**
1102          * Hides all dialogs
1103          */
1104         hideAll : function(){
1105             for(var id in list){
1106                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
1107                     list[id].hide();
1108                 }
1109             }
1110         }
1111     };
1112 }();
1113
1114 /**
1115  * @class Roo.LayoutDialog
1116  * @extends Roo.BasicDialog
1117  * Dialog which provides adjustments for working with a layout in a Dialog.
1118  * Add your necessary layout config options to the dialog's config.<br>
1119  * Example usage (including a nested layout):
1120  * <pre><code>
1121 if(!dialog){
1122     dialog = new Roo.LayoutDialog("download-dlg", {
1123         modal: true,
1124         width:600,
1125         height:450,
1126         shadow:true,
1127         minWidth:500,
1128         minHeight:350,
1129         autoTabs:true,
1130         proxyDrag:true,
1131         // layout config merges with the dialog config
1132         center:{
1133             tabPosition: "top",
1134             alwaysShowTabs: true
1135         }
1136     });
1137     dialog.addKeyListener(27, dialog.hide, dialog);
1138     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
1139     dialog.addButton("Build It!", this.getDownload, this);
1140
1141     // we can even add nested layouts
1142     var innerLayout = new Roo.BorderLayout("dl-inner", {
1143         east: {
1144             initialSize: 200,
1145             autoScroll:true,
1146             split:true
1147         },
1148         center: {
1149             autoScroll:true
1150         }
1151     });
1152     innerLayout.beginUpdate();
1153     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
1154     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
1155     innerLayout.endUpdate(true);
1156
1157     var layout = dialog.getLayout();
1158     layout.beginUpdate();
1159     layout.add("center", new Roo.ContentPanel("standard-panel",
1160                         {title: "Download the Source", fitToFrame:true}));
1161     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
1162                {title: "Build your own roo.js"}));
1163     layout.getRegion("center").showPanel(sp);
1164     layout.endUpdate();
1165 }
1166 </code></pre>
1167     * @constructor
1168     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
1169     * @param {Object} config configuration options
1170   */
1171 Roo.LayoutDialog = function(el, cfg){
1172     
1173     var config=  cfg;
1174     if (typeof(cfg) == 'undefined') {
1175         config = Roo.apply({}, el);
1176         el = Roo.get( document.documentElement || document.body).createChild();
1177         //config.autoCreate = true;
1178     }
1179     
1180     
1181     config.autoTabs = false;
1182     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
1183     this.body.setStyle({overflow:"hidden", position:"relative"});
1184     this.layout = new Roo.BorderLayout(this.body.dom, config);
1185     this.layout.monitorWindowResize = false;
1186     this.el.addClass("x-dlg-auto-layout");
1187     // fix case when center region overwrites center function
1188     this.center = Roo.BasicDialog.prototype.center;
1189     this.on("show", this.layout.layout, this.layout, true);
1190     if (config.items) {
1191         var xitems = config.items;
1192         delete config.items;
1193         Roo.each(xitems, this.addxtype, this);
1194     }
1195     
1196     
1197 };
1198 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
1199     /**
1200      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
1201      * @deprecated
1202      */
1203     endUpdate : function(){
1204         this.layout.endUpdate();
1205     },
1206
1207     /**
1208      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
1209      *  @deprecated
1210      */
1211     beginUpdate : function(){
1212         this.layout.beginUpdate();
1213     },
1214
1215     /**
1216      * Get the BorderLayout for this dialog
1217      * @return {Roo.BorderLayout}
1218      */
1219     getLayout : function(){
1220         return this.layout;
1221     },
1222
1223     showEl : function(){
1224         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
1225         if(Roo.isIE7){
1226             this.layout.layout();
1227         }
1228     },
1229
1230     // private
1231     // Use the syncHeightBeforeShow config option to control this automatically
1232     syncBodyHeight : function(){
1233         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
1234         if(this.layout){this.layout.layout();}
1235     },
1236     
1237       /**
1238      * Add an xtype element (actually adds to the layout.)
1239      * @return {Object} xdata xtype object data.
1240      */
1241     
1242     addxtype : function(c) {
1243         return this.layout.addxtype(c);
1244     }
1245 });