diff options
Diffstat (limited to 'lib/dijit/Dialog.js')
-rw-r--r-- | lib/dijit/Dialog.js | 827 |
1 files changed, 591 insertions, 236 deletions
diff --git a/lib/dijit/Dialog.js b/lib/dijit/Dialog.js index a35d523bf..22d2dbedf 100644 --- a/lib/dijit/Dialog.js +++ b/lib/dijit/Dialog.js @@ -1,12 +1,12 @@ /* - Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. + Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ -if(!dojo._hasResource["dijit.Dialog"]){ -dojo._hasResource["dijit.Dialog"]=true; +if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.Dialog"] = true; dojo.provide("dijit.Dialog"); dojo.require("dojo.dnd.move"); dojo.require("dojo.dnd.TimedMoveable"); @@ -19,238 +19,593 @@ dojo.require("dijit.form._FormMixin"); dojo.require("dijit._DialogMixin"); dojo.require("dijit.DialogUnderlay"); dojo.require("dijit.layout.ContentPane"); -dojo.requireLocalization("dijit","common",null,"ROOT,ar,ca,cs,da,de,el,es,fi,fr,he,hu,it,ja,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw"); -dojo.declare("dijit._DialogBase",[dijit._Templated,dijit.form._FormMixin,dijit._DialogMixin,dijit._CssStateMixin],{templateString:dojo.cache("dijit","templates/Dialog.html","<div class=\"dijitDialog\" tabindex=\"-1\" waiRole=\"dialog\" waiState=\"labelledby-${id}_title\">\n\t<div dojoAttachPoint=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span dojoAttachPoint=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span dojoAttachPoint=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" dojoAttachEvent=\"onclick: onCancel\" title=\"${buttonCancel}\">\n\t\t<span dojoAttachPoint=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div dojoAttachPoint=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"),baseClass:"dijitDialog",cssStateNodes:{closeButtonNode:"dijitDialogCloseIcon"},attributeMap:dojo.delegate(dijit._Widget.prototype.attributeMap,{title:[{node:"titleNode",type:"innerHTML"},{node:"titleBar",type:"attribute"}],"aria-describedby":""}),open:false,duration:dijit.defaultDuration,refocus:true,autofocus:true,_firstFocusItem:null,_lastFocusItem:null,doLayout:false,draggable:true,"aria-describedby":"",postMixInProperties:function(){ -var _1=dojo.i18n.getLocalization("dijit","common"); -dojo.mixin(this,_1); -this.inherited(arguments); -},postCreate:function(){ -dojo.style(this.domNode,{display:"none",position:"absolute"}); -dojo.body().appendChild(this.domNode); -this.inherited(arguments); -this.connect(this,"onExecute","hide"); -this.connect(this,"onCancel","hide"); -this._modalconnects=[]; -},onLoad:function(){ -this._position(); -if(this.autofocus){ -this._getFocusItems(this.domNode); -dijit.focus(this._firstFocusItem); -} -this.inherited(arguments); -},_endDrag:function(e){ -if(e&&e.node&&e.node===this.domNode){ -this._relativePosition=dojo.position(e.node); -} -},_setup:function(){ -var _2=this.domNode; -if(this.titleBar&&this.draggable){ -this._moveable=(dojo.isIE==6)?new dojo.dnd.TimedMoveable(_2,{handle:this.titleBar}):new dojo.dnd.Moveable(_2,{handle:this.titleBar,timeout:0}); -dojo.subscribe("/dnd/move/stop",this,"_endDrag"); -}else{ -dojo.addClass(_2,"dijitDialogFixed"); -} -this.underlayAttrs={dialogId:this.id,"class":dojo.map(this["class"].split(/\s/),function(s){ -return s+"_underlay"; -}).join(" ")}; -this._fadeIn=dojo.fadeIn({node:_2,duration:this.duration,beforeBegin:dojo.hitch(this,function(){ -var _3=dijit._underlay; -if(!_3){ -_3=dijit._underlay=new dijit.DialogUnderlay(this.underlayAttrs); -}else{ -_3.set(this.underlayAttrs); -} -var ds=dijit._dialogStack,_4=948+ds.length*2; -if(ds.length==1){ -_3.show(); -} -dojo.style(dijit._underlay.domNode,"zIndex",_4); -dojo.style(this.domNode,"zIndex",_4+1); -}),onEnd:dojo.hitch(this,function(){ -if(this.autofocus){ -this._getFocusItems(this.domNode); -dijit.focus(this._firstFocusItem); -} -})}); -this._fadeOut=dojo.fadeOut({node:_2,duration:this.duration,onEnd:dojo.hitch(this,function(){ -_2.style.display="none"; -var ds=dijit._dialogStack; -if(ds.length==0){ -dijit._underlay.hide(); -}else{ -dojo.style(dijit._underlay.domNode,"zIndex",948+ds.length*2); -dijit._underlay.set(ds[ds.length-1].underlayAttrs); -} -if(this.refocus){ -var _5=this._savedFocus; -if(ds.length>0){ -var pd=ds[ds.length-1]; -if(!dojo.isDescendant(_5.node,pd.domNode)){ -pd._getFocusItems(pd.domNode); -_5=pd._firstFocusItem; -} -} -dijit.focus(_5); -} -})}); -},uninitialize:function(){ -var _6=false; -if(this._fadeIn&&this._fadeIn.status()=="playing"){ -_6=true; -this._fadeIn.stop(); -} -if(this._fadeOut&&this._fadeOut.status()=="playing"){ -_6=true; -this._fadeOut.stop(); -} -if((this.open||_6)&&!dijit._underlay._destroyed){ -dijit._underlay.hide(); -} -if(this._moveable){ -this._moveable.destroy(); -} -this.inherited(arguments); -},_size:function(){ -this._checkIfSingleChild(); -if(this._singleChild){ -if(this._singleChildOriginalStyle){ -this._singleChild.domNode.style.cssText=this._singleChildOriginalStyle; -} -delete this._singleChildOriginalStyle; -}else{ -dojo.style(this.containerNode,{width:"auto",height:"auto"}); -} -var mb=dojo.marginBox(this.domNode); -var _7=dojo.window.getBox(); -if(mb.w>=_7.w||mb.h>=_7.h){ -var w=Math.min(mb.w,Math.floor(_7.w*0.75)),h=Math.min(mb.h,Math.floor(_7.h*0.75)); -if(this._singleChild&&this._singleChild.resize){ -this._singleChildOriginalStyle=this._singleChild.domNode.style.cssText; -this._singleChild.resize({w:w,h:h}); -}else{ -dojo.style(this.containerNode,{width:w+"px",height:h+"px",overflow:"auto",position:"relative"}); -} -}else{ -if(this._singleChild&&this._singleChild.resize){ -this._singleChild.resize(); -} -} -},_position:function(){ -if(!dojo.hasClass(dojo.body(),"dojoMove")){ -var _8=this.domNode,_9=dojo.window.getBox(),p=this._relativePosition,bb=p?null:dojo._getBorderBox(_8),l=Math.floor(_9.l+(p?p.x:(_9.w-bb.w)/2)),t=Math.floor(_9.t+(p?p.y:(_9.h-bb.h)/2)); -dojo.style(_8,{left:l+"px",top:t+"px"}); -} -},_onKey:function(_a){ -var ds=dijit._dialogStack; -if(ds[ds.length-1]!=this){ -return; -} -if(_a.charOrCode){ -var dk=dojo.keys; -var _b=_a.target; -if(_a.charOrCode===dk.TAB){ -this._getFocusItems(this.domNode); -} -var _c=(this._firstFocusItem==this._lastFocusItem); -if(_b==this._firstFocusItem&&_a.shiftKey&&_a.charOrCode===dk.TAB){ -if(!_c){ -dijit.focus(this._lastFocusItem); -} -dojo.stopEvent(_a); -}else{ -if(_b==this._lastFocusItem&&_a.charOrCode===dk.TAB&&!_a.shiftKey){ -if(!_c){ -dijit.focus(this._firstFocusItem); -} -dojo.stopEvent(_a); -}else{ -while(_b){ -if(_b==this.domNode||dojo.hasClass(_b,"dijitPopup")){ -if(_a.charOrCode==dk.ESCAPE){ -this.onCancel(); -}else{ -return; -} -} -_b=_b.parentNode; -} -if(_a.charOrCode!==dk.TAB){ -dojo.stopEvent(_a); -}else{ -if(!dojo.isOpera){ -try{ -this._firstFocusItem.focus(); -} -catch(e){ -} -} -} -} -} -} -},show:function(){ -if(this.open){ -return; -} -if(!this._alreadyInitialized){ -this._setup(); -this._alreadyInitialized=true; -} -if(this._fadeOut.status()=="playing"){ -this._fadeOut.stop(); -} -this._modalconnects.push(dojo.connect(window,"onscroll",this,"layout")); -this._modalconnects.push(dojo.connect(window,"onresize",this,function(){ -var _d=dojo.window.getBox(); -if(!this._oldViewport||_d.h!=this._oldViewport.h||_d.w!=this._oldViewport.w){ -this.layout(); -this._oldViewport=_d; -} -})); -this._modalconnects.push(dojo.connect(dojo.doc.documentElement,"onkeypress",this,"_onKey")); -dojo.style(this.domNode,{opacity:0,display:""}); -this.open=true; -this._onShow(); -this._size(); -this._position(); -dijit._dialogStack.push(this); -this._fadeIn.play(); -this._savedFocus=dijit.getFocus(this); -},hide:function(){ -var ds=dijit._dialogStack; -if(!this._alreadyInitialized||this!=ds[ds.length-1]){ -return; -} -if(this._fadeIn.status()=="playing"){ -this._fadeIn.stop(); -} -ds.pop(); -this._fadeOut.play(); -if(this._scrollConnected){ -this._scrollConnected=false; -} -dojo.forEach(this._modalconnects,dojo.disconnect); -this._modalconnects=[]; -if(this._relativePosition){ -delete this._relativePosition; -} -this.open=false; -this.onHide(); -},layout:function(){ -if(this.domNode.style.display!="none"){ -if(dijit._underlay){ -dijit._underlay.layout(); -} -this._position(); -} -},destroy:function(){ -dojo.forEach(this._modalconnects,dojo.disconnect); -if(this.refocus&&this.open){ -setTimeout(dojo.hitch(dijit,"focus",this._savedFocus),25); -} -this.inherited(arguments); -}}); -dojo.declare("dijit.Dialog",[dijit.layout.ContentPane,dijit._DialogBase],{}); -dijit._dialogStack=[]; +dojo.requireLocalization("dijit", "common", null, "ROOT,ar,ca,cs,da,de,el,es,fi,fr,he,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw"); dojo.require("dijit.TooltipDialog"); + + +// dijit/TooltipDialog required for back-compat. TODO: remove in 2.0 + +/*===== +dijit._underlay = function(kwArgs){ + // summary: + // A shared instance of a `dijit.DialogUnderlay` + // + // description: + // A shared instance of a `dijit.DialogUnderlay` created and + // used by `dijit.Dialog`, though never created until some Dialog + // or subclass thereof is shown. +}; +=====*/ +dojo.declare( + "dijit._DialogBase", + [dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin, dijit._CssStateMixin], + { + // summary: + // A modal dialog Widget + // + // description: + // Pops up a modal dialog window, blocking access to the screen + // and also graying out the screen Dialog is extended from + // ContentPane so it supports all the same parameters (href, etc.) + // + // example: + // | <div dojoType="dijit.Dialog" href="test.html"></div> + // + // example: + // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" }; + // | dojo.body().appendChild(foo.domNode); + // | foo.startup(); + + templateString: dojo.cache("dijit", "templates/Dialog.html", "<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div dojoAttachPoint=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span dojoAttachPoint=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span dojoAttachPoint=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" dojoAttachEvent=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span dojoAttachPoint=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div dojoAttachPoint=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"), + + baseClass: "dijitDialog", + + cssStateNodes: { + closeButtonNode: "dijitDialogCloseIcon" + }, + + attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { + title: [ + { node: "titleNode", type: "innerHTML" }, + { node: "titleBar", type: "attribute" } + ], + "aria-describedby":"" + }), + + // open: [readonly] Boolean + // True if Dialog is currently displayed on screen. + open: false, + + // duration: Integer + // The time in milliseconds it takes the dialog to fade in and out + duration: dijit.defaultDuration, + + // refocus: Boolean + // A Toggle to modify the default focus behavior of a Dialog, which + // is to re-focus the element which had focus before being opened. + // False will disable refocusing. Default: true + refocus: true, + + // autofocus: Boolean + // A Toggle to modify the default focus behavior of a Dialog, which + // is to focus on the first dialog element after opening the dialog. + // False will disable autofocusing. Default: true + autofocus: true, + + // _firstFocusItem: [private readonly] DomNode + // The pointer to the first focusable node in the dialog. + // Set by `dijit._DialogMixin._getFocusItems`. + _firstFocusItem: null, + + // _lastFocusItem: [private readonly] DomNode + // The pointer to which node has focus prior to our dialog. + // Set by `dijit._DialogMixin._getFocusItems`. + _lastFocusItem: null, + + // doLayout: [protected] Boolean + // Don't change this parameter from the default value. + // This ContentPane parameter doesn't make sense for Dialog, since Dialog + // is never a child of a layout container, nor can you specify the size of + // Dialog in order to control the size of an inner widget. + doLayout: false, + + // draggable: Boolean + // Toggles the moveable aspect of the Dialog. If true, Dialog + // can be dragged by it's title. If false it will remain centered + // in the viewport. + draggable: true, + + //aria-describedby: String + // Allows the user to add an aria-describedby attribute onto the dialog. The value should + // be the id of the container element of text that describes the dialog purpose (usually + // the first text in the dialog). + // <div dojoType="dijit.Dialog" aria-describedby="intro" .....> + // <div id="intro">Introductory text</div> + // <div>rest of dialog contents</div> + // </div> + "aria-describedby":"", + + postMixInProperties: function(){ + var _nlsResources = dojo.i18n.getLocalization("dijit", "common"); + dojo.mixin(this, _nlsResources); + this.inherited(arguments); + }, + + postCreate: function(){ + dojo.style(this.domNode, { + display: "none", + position:"absolute" + }); + dojo.body().appendChild(this.domNode); + + this.inherited(arguments); + + this.connect(this, "onExecute", "hide"); + this.connect(this, "onCancel", "hide"); + this._modalconnects = []; + }, + + onLoad: function(){ + // summary: + // Called when data has been loaded from an href. + // Unlike most other callbacks, this function can be connected to (via `dojo.connect`) + // but should *not* be overridden. + // tags: + // callback + + // when href is specified we need to reposition the dialog after the data is loaded + // and find the focusable elements + this._position(); + if(this.autofocus && dijit._DialogLevelManager.isTop(this)){ + this._getFocusItems(this.domNode); + dijit.focus(this._firstFocusItem); + } + this.inherited(arguments); + }, + + _endDrag: function(e){ + // summary: + // Called after dragging the Dialog. Saves the position of the dialog in the viewport. + // tags: + // private + if(e && e.node && e.node === this.domNode){ + this._relativePosition = dojo.position(e.node); + } + }, + + _setup: function(){ + // summary: + // Stuff we need to do before showing the Dialog for the first + // time (but we defer it until right beforehand, for + // performance reasons). + // tags: + // private + + var node = this.domNode; + + if(this.titleBar && this.draggable){ + this._moveable = (dojo.isIE == 6) ? + new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) : // prevent overload, see #5285 + new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 }); + this._dndListener = dojo.subscribe("/dnd/move/stop",this,"_endDrag"); + }else{ + dojo.addClass(node,"dijitDialogFixed"); + } + + this.underlayAttrs = { + dialogId: this.id, + "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ") + }; + }, + + _size: function(){ + // summary: + // If necessary, shrink dialog contents so dialog fits in viewport + // tags: + // private + + this._checkIfSingleChild(); + + // If we resized the dialog contents earlier, reset them back to original size, so + // that if the user later increases the viewport size, the dialog can display w/out a scrollbar. + // Need to do this before the dojo.marginBox(this.domNode) call below. + if(this._singleChild){ + if(this._singleChildOriginalStyle){ + this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle; + } + delete this._singleChildOriginalStyle; + }else{ + dojo.style(this.containerNode, { + width:"auto", + height:"auto" + }); + } + + var mb = dojo._getMarginSize(this.domNode); + var viewport = dojo.window.getBox(); + if(mb.w >= viewport.w || mb.h >= viewport.h){ + // Reduce size of dialog contents so that dialog fits in viewport + + var w = Math.min(mb.w, Math.floor(viewport.w * 0.75)), + h = Math.min(mb.h, Math.floor(viewport.h * 0.75)); + + if(this._singleChild && this._singleChild.resize){ + this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText; + this._singleChild.resize({w: w, h: h}); + }else{ + dojo.style(this.containerNode, { + width: w + "px", + height: h + "px", + overflow: "auto", + position: "relative" // workaround IE bug moving scrollbar or dragging dialog + }); + } + }else{ + if(this._singleChild && this._singleChild.resize){ + this._singleChild.resize(); + } + } + }, + + _position: function(){ + // summary: + // Position modal dialog in the viewport. If no relative offset + // in the viewport has been determined (by dragging, for instance), + // center the node. Otherwise, use the Dialog's stored relative offset, + // and position the node to top: left: values based on the viewport. + // tags: + // private + if(!dojo.hasClass(dojo.body(),"dojoMove")){ + var node = this.domNode, + viewport = dojo.window.getBox(), + p = this._relativePosition, + bb = p ? null : dojo._getBorderBox(node), + l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)), + t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2)) + ; + dojo.style(node,{ + left: l + "px", + top: t + "px" + }); + } + }, + + _onKey: function(/*Event*/ evt){ + // summary: + // Handles the keyboard events for accessibility reasons + // tags: + // private + + if(evt.charOrCode){ + var dk = dojo.keys; + var node = evt.target; + if(evt.charOrCode === dk.TAB){ + this._getFocusItems(this.domNode); + } + var singleFocusItem = (this._firstFocusItem == this._lastFocusItem); + // see if we are shift-tabbing from first focusable item on dialog + if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){ + if(!singleFocusItem){ + dijit.focus(this._lastFocusItem); // send focus to last item in dialog + } + dojo.stopEvent(evt); + }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){ + if(!singleFocusItem){ + dijit.focus(this._firstFocusItem); // send focus to first item in dialog + } + dojo.stopEvent(evt); + }else{ + // see if the key is for the dialog + while(node){ + if(node == this.domNode || dojo.hasClass(node, "dijitPopup")){ + if(evt.charOrCode == dk.ESCAPE){ + this.onCancel(); + }else{ + return; // just let it go + } + } + node = node.parentNode; + } + // this key is for the disabled document window + if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y + dojo.stopEvent(evt); + // opera won't tab to a div + }else if(!dojo.isOpera){ + try{ + this._firstFocusItem.focus(); + }catch(e){ /*squelch*/ } + } + } + } + }, + + show: function(){ + // summary: + // Display the dialog + // returns: dojo.Deferred + // Deferred object that resolves when the display animation is complete + + if(this.open){ return; } + + if(!this._started){ + this.startup(); + } + + // first time we show the dialog, there's some initialization stuff to do + if(!this._alreadyInitialized){ + this._setup(); + this._alreadyInitialized=true; + } + + if(this._fadeOutDeferred){ + this._fadeOutDeferred.cancel(); + } + + this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout")); + this._modalconnects.push(dojo.connect(window, "onresize", this, function(){ + // IE gives spurious resize events and can actually get stuck + // in an infinite loop if we don't ignore them + var viewport = dojo.window.getBox(); + if(!this._oldViewport || + viewport.h != this._oldViewport.h || + viewport.w != this._oldViewport.w){ + this.layout(); + this._oldViewport = viewport; + } + })); + this._modalconnects.push(dojo.connect(this.domNode, "onkeypress", this, "_onKey")); + + dojo.style(this.domNode, { + opacity:0, + display:"" + }); + + this._set("open", true); + this._onShow(); // lazy load trigger + + this._size(); + this._position(); + + // fade-in Animation object, setup below + var fadeIn; + + this._fadeInDeferred = new dojo.Deferred(dojo.hitch(this, function(){ + fadeIn.stop(); + delete this._fadeInDeferred; + })); + + fadeIn = dojo.fadeIn({ + node: this.domNode, + duration: this.duration, + beforeBegin: dojo.hitch(this, function(){ + dijit._DialogLevelManager.show(this, this.underlayAttrs); + }), + onEnd: dojo.hitch(this, function(){ + if(this.autofocus && dijit._DialogLevelManager.isTop(this)){ + // find focusable items each time dialog is shown since if dialog contains a widget the + // first focusable items can change + this._getFocusItems(this.domNode); + dijit.focus(this._firstFocusItem); + } + this._fadeInDeferred.callback(true); + delete this._fadeInDeferred; + }) + }).play(); + + return this._fadeInDeferred; + }, + + hide: function(){ + // summary: + // Hide the dialog + // returns: dojo.Deferred + // Deferred object that resolves when the hide animation is complete + + // if we haven't been initialized yet then we aren't showing and we can just return + if(!this._alreadyInitialized){ + return; + } + if(this._fadeInDeferred){ + this._fadeInDeferred.cancel(); + } + + // fade-in Animation object, setup below + var fadeOut; + + this._fadeOutDeferred = new dojo.Deferred(dojo.hitch(this, function(){ + fadeOut.stop(); + delete this._fadeOutDeferred; + })); + + fadeOut = dojo.fadeOut({ + node: this.domNode, + duration: this.duration, + onEnd: dojo.hitch(this, function(){ + this.domNode.style.display = "none"; + dijit._DialogLevelManager.hide(this); + this.onHide(); + this._fadeOutDeferred.callback(true); + delete this._fadeOutDeferred; + }) + }).play(); + + if(this._scrollConnected){ + this._scrollConnected = false; + } + dojo.forEach(this._modalconnects, dojo.disconnect); + this._modalconnects = []; + + if(this._relativePosition){ + delete this._relativePosition; + } + this._set("open", false); + + return this._fadeOutDeferred; + }, + + layout: function(){ + // summary: + // Position the Dialog and the underlay + // tags: + // private + if(this.domNode.style.display != "none"){ + if(dijit._underlay){ // avoid race condition during show() + dijit._underlay.layout(); + } + this._position(); + } + }, + + destroy: function(){ + if(this._fadeInDeferred){ + this._fadeInDeferred.cancel(); + } + if(this._fadeOutDeferred){ + this._fadeOutDeferred.cancel(); + } + if(this._moveable){ + this._moveable.destroy(); + } + if(this._dndListener){ + dojo.unsubscribe(this._dndListener); + } + dojo.forEach(this._modalconnects, dojo.disconnect); + + dijit._DialogLevelManager.hide(this); + + this.inherited(arguments); + } + } +); + +dojo.declare( + "dijit.Dialog", + [dijit.layout.ContentPane, dijit._DialogBase], + {} +); + +dijit._DialogLevelManager = { + // summary: + // Controls the various active "levels" on the page, starting with the + // stuff initially visible on the page (at z-index 0), and then having an entry for + // each Dialog shown. + + show: function(/*dijit._Widget*/ dialog, /*Object*/ underlayAttrs){ + // summary: + // Call right before fade-in animation for new dialog. + // Saves current focus, displays/adjusts underlay for new dialog, + // and sets the z-index of the dialog itself. + // + // New dialog will be displayed on top of all currently displayed dialogs. + // + // Caller is responsible for setting focus in new dialog after the fade-in + // animation completes. + + var ds = dijit._dialogStack; + + // Save current focus + ds[ds.length-1].focus = dijit.getFocus(dialog); + + // Display the underlay, or if already displayed then adjust for this new dialog + var underlay = dijit._underlay; + if(!underlay || underlay._destroyed){ + underlay = dijit._underlay = new dijit.DialogUnderlay(underlayAttrs); + }else{ + underlay.set(dialog.underlayAttrs); + } + + // Set z-index a bit above previous dialog + var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : 950; + if(ds.length == 1){ // first dialog + underlay.show(); + } + dojo.style(dijit._underlay.domNode, 'zIndex', zIndex - 1); + + // Dialog + dojo.style(dialog.domNode, 'zIndex', zIndex); + + ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex}); + }, + + hide: function(/*dijit._Widget*/ dialog){ + // summary: + // Called when the specified dialog is hidden/destroyed, after the fade-out + // animation ends, in order to reset page focus, fix the underlay, etc. + // If the specified dialog isn't open then does nothing. + // + // Caller is responsible for either setting display:none on the dialog domNode, + // or calling dijit.popup.hide(), or removing it from the page DOM. + + var ds = dijit._dialogStack; + + if(ds[ds.length-1].dialog == dialog){ + // Removing the top (or only) dialog in the stack, return focus + // to previous dialog + + ds.pop(); + + var pd = ds[ds.length-1]; // the new active dialog (or the base page itself) + + // Adjust underlay + if(ds.length == 1){ + // Returning to original page. + // Hide the underlay, unless the underlay widget has already been destroyed + // because we are being called during page unload (when all widgets are destroyed) + if(!dijit._underlay._destroyed){ + dijit._underlay.hide(); + } + }else{ + // Popping back to previous dialog, adjust underlay + dojo.style(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1); + dijit._underlay.set(pd.underlayAttrs); + } + + // Adjust focus + if(dialog.refocus){ + // If we are returning control to a previous dialog but for some reason + // that dialog didn't have a focused field, set focus to first focusable item. + // This situation could happen if two dialogs appeared at nearly the same time, + // since a dialog doesn't set it's focus until the fade-in is finished. + var focus = pd.focus; + if(!focus || (pd.dialog && !dojo.isDescendant(focus.node, pd.dialog.domNode))){ + pd.dialog._getFocusItems(pd.dialog.domNode); + focus = pd.dialog._firstFocusItem; + } + + try{ + dijit.focus(focus); + }catch(e){ + /* focus() will fail if user opened the dialog by clicking a non-focusable element */ + } + } + }else{ + // Removing a dialog out of order (#9944, #10705). + // Don't need to mess with underlay or z-index or anything. + var idx = dojo.indexOf(dojo.map(ds, function(elem){return elem.dialog}), dialog); + if(idx != -1){ + ds.splice(idx, 1); + } + } + }, + + isTop: function(/*dijit._Widget*/ dialog){ + // summary: + // Returns true if specified Dialog is the top in the task + var ds = dijit._dialogStack; + return ds[ds.length-1].dialog == dialog; + } +}; + +// Stack representing the various active "levels" on the page, starting with the +// stuff initially visible on the page (at z-index 0), and then having an entry for +// each Dialog shown. +// Each element in stack has form { +// dialog: dialogWidget, +// focus: returnFromGetFocus(), +// underlayAttrs: attributes to set on underlay (when this widget is active) +// } +dijit._dialogStack = [ + {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0 +]; + } |