2 'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"}});
3 define("dijit/Dialog", [
5 "dojo/_base/array", // array.forEach array.indexOf array.map
6 "dojo/_base/connect", // connect._keypress
7 "dojo/_base/declare", // declare
8 "dojo/_base/Deferred", // Deferred
9 "dojo/dom", // dom.isDescendant
10 "dojo/dom-class", // domClass.add domClass.contains
11 "dojo/dom-geometry", // domGeometry.position
12 "dojo/dom-style", // domStyle.set
13 "dojo/_base/event", // event.stop
14 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
15 "dojo/i18n", // i18n.getLocalization
16 "dojo/_base/kernel", // kernel.isAsync
18 "dojo/_base/lang", // lang.mixin lang.hitch
21 "dojo/_base/sniff", // has("ie") has("opera")
22 "dojo/_base/window", // win.body
23 "dojo/window", // winUtils.getBox
24 "dojo/dnd/Moveable", // Moveable
25 "dojo/dnd/TimedMoveable", // TimedMoveable
27 "./_base/manager", // manager.defaultDuration
34 "./layout/ContentPane",
35 "dojo/text!./templates/Dialog.html",
36 ".", // for back-compat, exporting dijit._underlay (remove in 2.0)
37 "dojo/i18n!./nls/common"
38 ], function(require
, array
, connect
, declare
, Deferred
,
39 dom
, domClass
, domGeometry
, domStyle
, event
, fx
, i18n
, kernel
, keys
, lang
, on
, ready
, has
, win
, winUtils
,
40 Moveable
, TimedMoveable
, focus
, manager
, _Widget
, _TemplatedMixin
, _CssStateMixin
, _FormMixin
, _DialogMixin
,
41 DialogUnderlay
, ContentPane
, template
, dijit
){
44 var _Widget = dijit._Widget;
45 var _TemplatedMixin = dijit._TemplatedMixin;
46 var _CssStateMixin = dijit._CssStateMixin;
47 var _FormMixin = dijit.form._FormMixin;
48 var _DialogMixin = dijit._DialogMixin;
55 // A modal dialog Widget
59 dijit._underlay = function(kwArgs){
61 // A shared instance of a `dijit.DialogUnderlay`
64 // A shared instance of a `dijit.DialogUnderlay` created and
65 // used by `dijit.Dialog`, though never created until some Dialog
66 // or subclass thereof is shown.
70 var _DialogBase
= declare("dijit._DialogBase", [_TemplatedMixin
, _FormMixin
, _DialogMixin
, _CssStateMixin
], {
72 // A modal dialog Widget
75 // Pops up a modal dialog window, blocking access to the screen
76 // and also graying out the screen Dialog is extended from
77 // ContentPane so it supports all the same parameters (href, etc.)
80 // | <div data-dojo-type="dijit.Dialog" data-dojo-props="href: 'test.html'"></div>
83 // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
84 // | dojo.body().appendChild(foo.domNode);
87 templateString
: template
,
89 baseClass
: "dijitDialog",
92 closeButtonNode
: "dijitDialogCloseIcon"
95 // Map widget attributes to DOMNode attributes.
97 { node
: "titleNode", type
: "innerHTML" },
98 { node
: "titleBar", type
: "attribute" }
101 // open: [readonly] Boolean
102 // True if Dialog is currently displayed on screen.
106 // The time in milliseconds it takes the dialog to fade in and out
107 duration
: manager
.defaultDuration
,
110 // A Toggle to modify the default focus behavior of a Dialog, which
111 // is to re-focus the element which had focus before being opened.
112 // False will disable refocusing. Default: true
115 // autofocus: Boolean
116 // A Toggle to modify the default focus behavior of a Dialog, which
117 // is to focus on the first dialog element after opening the dialog.
118 // False will disable autofocusing. Default: true
121 // _firstFocusItem: [private readonly] DomNode
122 // The pointer to the first focusable node in the dialog.
123 // Set by `dijit._DialogMixin._getFocusItems`.
124 _firstFocusItem
: null,
126 // _lastFocusItem: [private readonly] DomNode
127 // The pointer to which node has focus prior to our dialog.
128 // Set by `dijit._DialogMixin._getFocusItems`.
129 _lastFocusItem
: null,
131 // doLayout: [protected] Boolean
132 // Don't change this parameter from the default value.
133 // This ContentPane parameter doesn't make sense for Dialog, since Dialog
134 // is never a child of a layout container, nor can you specify the size of
135 // Dialog in order to control the size of an inner widget.
138 // draggable: Boolean
139 // Toggles the moveable aspect of the Dialog. If true, Dialog
140 // can be dragged by it's title. If false it will remain centered
144 //aria-describedby: String
145 // Allows the user to add an aria-describedby attribute onto the dialog. The value should
146 // be the id of the container element of text that describes the dialog purpose (usually
147 // the first text in the dialog).
148 // <div data-dojo-type="dijit.Dialog" aria-describedby="intro" .....>
149 // <div id="intro">Introductory text</div>
150 // <div>rest of dialog contents</div>
152 "aria-describedby":"",
154 postMixInProperties: function(){
155 var _nlsResources
= i18n
.getLocalization("dijit", "common");
156 lang
.mixin(this, _nlsResources
);
157 this.inherited(arguments
);
160 postCreate: function(){
161 domStyle
.set(this.domNode
, {
165 win
.body().appendChild(this.domNode
);
167 this.inherited(arguments
);
169 this.connect(this, "onExecute", "hide");
170 this.connect(this, "onCancel", "hide");
171 this._modalconnects
= [];
176 // Called when data has been loaded from an href.
177 // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
178 // but should *not* be overridden.
182 // when href is specified we need to reposition the dialog after the data is loaded
183 // and find the focusable elements
185 if(this.autofocus
&& DialogLevelManager
.isTop(this)){
186 this._getFocusItems(this.domNode
);
187 focus
.focus(this._firstFocusItem
);
189 this.inherited(arguments
);
192 _endDrag: function(){
194 // Called after dragging the Dialog. Saves the position of the dialog in the viewport,
195 // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
196 var nodePosition
= domGeometry
.position(this.domNode
),
197 viewport
= winUtils
.getBox();
198 nodePosition
.y
= Math
.min(Math
.max(nodePosition
.y
, 0), (viewport
.h
- nodePosition
.h
));
199 nodePosition
.x
= Math
.min(Math
.max(nodePosition
.x
, 0), (viewport
.w
- nodePosition
.w
));
200 this._relativePosition
= nodePosition
;
206 // Stuff we need to do before showing the Dialog for the first
207 // time (but we defer it until right beforehand, for
208 // performance reasons).
212 var node
= this.domNode
;
214 if(this.titleBar
&& this.draggable
){
215 this._moveable
= new ((has("ie") == 6) ? TimedMoveable
// prevent overload, see #5285
216 : Moveable
)(node
, { handle
: this.titleBar
});
217 this.connect(this._moveable
, "onMoveStop", "_endDrag");
219 domClass
.add(node
,"dijitDialogFixed");
222 this.underlayAttrs
= {
224 "class": array
.map(this["class"].split(/\s/), function(s
){ return s
+"_underlay"; }).join(" ")
230 // If necessary, shrink dialog contents so dialog fits in viewport
234 this._checkIfSingleChild();
236 // If we resized the dialog contents earlier, reset them back to original size, so
237 // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
238 // Need to do this before the domGeometry.position(this.domNode) call below.
239 if(this._singleChild
){
240 if(this._singleChildOriginalStyle
){
241 this._singleChild
.domNode
.style
.cssText
= this._singleChildOriginalStyle
;
243 delete this._singleChildOriginalStyle
;
245 domStyle
.set(this.containerNode
, {
251 var bb
= domGeometry
.position(this.domNode
);
252 var viewport
= winUtils
.getBox();
253 if(bb
.w
>= viewport
.w
|| bb
.h
>= viewport
.h
){
254 // Reduce size of dialog contents so that dialog fits in viewport
256 var w
= Math
.min(bb
.w
, Math
.floor(viewport
.w
* 0.75)),
257 h
= Math
.min(bb
.h
, Math
.floor(viewport
.h
* 0.75));
259 if(this._singleChild
&& this._singleChild
.resize
){
260 this._singleChildOriginalStyle
= this._singleChild
.domNode
.style
.cssText
;
261 this._singleChild
.resize({w
: w
, h
: h
});
263 domStyle
.set(this.containerNode
, {
267 position
: "relative" // workaround IE bug moving scrollbar or dragging dialog
271 if(this._singleChild
&& this._singleChild
.resize
){
272 this._singleChild
.resize();
277 _position: function(){
279 // Position modal dialog in the viewport. If no relative offset
280 // in the viewport has been determined (by dragging, for instance),
281 // center the node. Otherwise, use the Dialog's stored relative offset,
282 // and position the node to top: left: values based on the viewport.
283 if(!domClass
.contains(win
.body(), "dojoMove")){ // don't do anything if called during auto-scroll
284 var node
= this.domNode
,
285 viewport
= winUtils
.getBox(),
286 p
= this._relativePosition
,
287 bb
= p
? null : domGeometry
.position(node
),
288 l
= Math
.floor(viewport
.l
+ (p
? p
.x
: (viewport
.w
- bb
.w
) / 2)),
289 t
= Math
.floor(viewport
.t
+ (p
? p
.y
: (viewport
.h
- bb
.h
) / 2))
298 _onKey: function(/*Event*/ evt
){
300 // Handles the keyboard events for accessibility reasons
305 var node
= evt
.target
;
306 if(evt
.charOrCode
=== keys
.TAB
){
307 this._getFocusItems(this.domNode
);
309 var singleFocusItem
= (this._firstFocusItem
== this._lastFocusItem
);
310 // see if we are shift-tabbing from first focusable item on dialog
311 if(node
== this._firstFocusItem
&& evt
.shiftKey
&& evt
.charOrCode
=== keys
.TAB
){
312 if(!singleFocusItem
){
313 focus
.focus(this._lastFocusItem
); // send focus to last item in dialog
316 }else if(node
== this._lastFocusItem
&& evt
.charOrCode
=== keys
.TAB
&& !evt
.shiftKey
){
317 if(!singleFocusItem
){
318 focus
.focus(this._firstFocusItem
); // send focus to first item in dialog
322 // see if the key is for the dialog
324 if(node
== this.domNode
|| domClass
.contains(node
, "dijitPopup")){
325 if(evt
.charOrCode
== keys
.ESCAPE
){
328 return; // just let it go
331 node
= node
.parentNode
;
333 // this key is for the disabled document window
334 if(evt
.charOrCode
!== keys
.TAB
){ // allow tabbing into the dialog for a11y
336 // opera won't tab to a div
337 }else if(!has("opera")){
339 this._firstFocusItem
.focus();
340 }catch(e
){ /*squelch*/ }
348 // Display the dialog
349 // returns: dojo.Deferred
350 // Deferred object that resolves when the display animation is complete
352 if(this.open
){ return; }
358 // first time we show the dialog, there's some initialization stuff to do
359 if(!this._alreadyInitialized
){
361 this._alreadyInitialized
=true;
364 if(this._fadeOutDeferred
){
365 this._fadeOutDeferred
.cancel();
368 this._modalconnects
.push(on(window
, "scroll", lang
.hitch(this, "layout")));
369 this._modalconnects
.push(on(window
, "resize", lang
.hitch(this, function(){
370 // IE gives spurious resize events and can actually get stuck
371 // in an infinite loop if we don't ignore them
372 var viewport
= winUtils
.getBox();
373 if(!this._oldViewport
||
374 viewport
.h
!= this._oldViewport
.h
||
375 viewport
.w
!= this._oldViewport
.w
){
377 this._oldViewport
= viewport
;
380 this._modalconnects
.push(on(this.domNode
, connect
._keypress
, lang
.hitch(this, "_onKey")));
382 domStyle
.set(this.domNode
, {
387 this._set("open", true);
388 this._onShow(); // lazy load trigger
393 // fade-in Animation object, setup below
396 this._fadeInDeferred
= new Deferred(lang
.hitch(this, function(){
398 delete this._fadeInDeferred
;
403 duration
: this.duration
,
404 beforeBegin
: lang
.hitch(this, function(){
405 DialogLevelManager
.show(this, this.underlayAttrs
);
407 onEnd
: lang
.hitch(this, function(){
408 if(this.autofocus
&& DialogLevelManager
.isTop(this)){
409 // find focusable items each time dialog is shown since if dialog contains a widget the
410 // first focusable items can change
411 this._getFocusItems(this.domNode
);
412 focus
.focus(this._firstFocusItem
);
414 this._fadeInDeferred
.callback(true);
415 delete this._fadeInDeferred
;
419 return this._fadeInDeferred
;
425 // returns: dojo.Deferred
426 // Deferred object that resolves when the hide animation is complete
428 // if we haven't been initialized yet then we aren't showing and we can just return
429 if(!this._alreadyInitialized
){
432 if(this._fadeInDeferred
){
433 this._fadeInDeferred
.cancel();
436 // fade-in Animation object, setup below
439 this._fadeOutDeferred
= new Deferred(lang
.hitch(this, function(){
441 delete this._fadeOutDeferred
;
443 // fire onHide when the promise resolves.
444 this._fadeOutDeferred
.then(lang
.hitch(this, 'onHide'));
446 fadeOut
= fx
.fadeOut({
448 duration
: this.duration
,
449 onEnd
: lang
.hitch(this, function(){
450 this.domNode
.style
.display
= "none";
451 DialogLevelManager
.hide(this);
452 this._fadeOutDeferred
.callback(true);
453 delete this._fadeOutDeferred
;
457 if(this._scrollConnected
){
458 this._scrollConnected
= false;
461 while(h
= this._modalconnects
.pop()){
465 if(this._relativePosition
){
466 delete this._relativePosition
;
468 this._set("open", false);
470 return this._fadeOutDeferred
;
475 // Position the Dialog and the underlay
478 if(this.domNode
.style
.display
!= "none"){
479 if(dijit
._underlay
){ // avoid race condition during show()
480 dijit
._underlay
.layout();
487 if(this._fadeInDeferred
){
488 this._fadeInDeferred
.cancel();
490 if(this._fadeOutDeferred
){
491 this._fadeOutDeferred
.cancel();
494 this._moveable
.destroy();
497 while(h
= this._modalconnects
.pop()){
501 DialogLevelManager
.hide(this);
503 this.inherited(arguments
);
507 var Dialog
= declare("dijit.Dialog", [ContentPane
, _DialogBase
], {});
508 Dialog
._DialogBase
= _DialogBase
; // for monkey patching
510 var DialogLevelManager
= Dialog
._DialogLevelManager
= {
512 // Controls the various active "levels" on the page, starting with the
513 // stuff initially visible on the page (at z-index 0), and then having an entry for
514 // each Dialog shown.
518 show: function(/*dijit._Widget*/ dialog
, /*Object*/ underlayAttrs
){
520 // Call right before fade-in animation for new dialog.
521 // Saves current focus, displays/adjusts underlay for new dialog,
522 // and sets the z-index of the dialog itself.
524 // New dialog will be displayed on top of all currently displayed dialogs.
526 // Caller is responsible for setting focus in new dialog after the fade-in
527 // animation completes.
529 // Save current focus
530 ds
[ds
.length
-1].focus
= focus
.curNode
;
532 // Display the underlay, or if already displayed then adjust for this new dialog
533 var underlay
= dijit
._underlay
;
534 if(!underlay
|| underlay
._destroyed
){
535 underlay
= dijit
._underlay
= new DialogUnderlay(underlayAttrs
);
537 underlay
.set(dialog
.underlayAttrs
);
540 // Set z-index a bit above previous dialog
541 var zIndex
= ds
[ds
.length
-1].dialog
? ds
[ds
.length
-1].zIndex
+ 2 : Dialog
._DialogLevelManager
._beginZIndex
;
542 if(ds
.length
== 1){ // first dialog
545 domStyle
.set(dijit
._underlay
.domNode
, 'zIndex', zIndex
- 1);
548 domStyle
.set(dialog
.domNode
, 'zIndex', zIndex
);
550 ds
.push({dialog
: dialog
, underlayAttrs
: underlayAttrs
, zIndex
: zIndex
});
553 hide: function(/*dijit._Widget*/ dialog
){
555 // Called when the specified dialog is hidden/destroyed, after the fade-out
556 // animation ends, in order to reset page focus, fix the underlay, etc.
557 // If the specified dialog isn't open then does nothing.
559 // Caller is responsible for either setting display:none on the dialog domNode,
560 // or calling dijit.popup.hide(), or removing it from the page DOM.
562 if(ds
[ds
.length
-1].dialog
== dialog
){
563 // Removing the top (or only) dialog in the stack, return focus
564 // to previous dialog
568 var pd
= ds
[ds
.length
-1]; // the new active dialog (or the base page itself)
572 // Returning to original page.
573 // Hide the underlay, unless the underlay widget has already been destroyed
574 // because we are being called during page unload (when all widgets are destroyed)
575 if(!dijit
._underlay
._destroyed
){
576 dijit
._underlay
.hide();
579 // Popping back to previous dialog, adjust underlay
580 domStyle
.set(dijit
._underlay
.domNode
, 'zIndex', pd
.zIndex
- 1);
581 dijit
._underlay
.set(pd
.underlayAttrs
);
586 // If we are returning control to a previous dialog but for some reason
587 // that dialog didn't have a focused field, set focus to first focusable item.
588 // This situation could happen if two dialogs appeared at nearly the same time,
589 // since a dialog doesn't set it's focus until the fade-in is finished.
590 var focus
= pd
.focus
;
591 if(pd
.dialog
&& (!focus
|| !dom
.isDescendant(focus
, pd
.dialog
.domNode
))){
592 pd
.dialog
._getFocusItems(pd
.dialog
.domNode
);
593 focus
= pd
.dialog
._firstFocusItem
;
597 // Refocus the button that spawned the Dialog. This will fail in corner cases including
598 // page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
599 // before this code runs. (#15058)
606 // Removing a dialog out of order (#9944, #10705).
607 // Don't need to mess with underlay or z-index or anything.
608 var idx
= array
.indexOf(array
.map(ds
, function(elem
){return elem
.dialog
}), dialog
);
615 isTop: function(/*dijit._Widget*/ dialog
){
617 // Returns true if specified Dialog is the top in the task
618 return ds
[ds
.length
-1].dialog
== dialog
;
622 // Stack representing the various active "levels" on the page, starting with the
623 // stuff initially visible on the page (at z-index 0), and then having an entry for
624 // each Dialog shown.
625 // Each element in stack has form {
626 // dialog: dialogWidget,
627 // focus: returnFromGetFocus(),
628 // underlayAttrs: attributes to set on underlay (when this widget is active)
630 var ds
= Dialog
._dialogStack
= [
631 {dialog
: null, focus
: null, underlayAttrs
: null} // entry for stuff at z-index: 0
634 // Back compat w/1.6, remove for 2.0
637 var requires
= ["dijit/TooltipDialog"];
638 require(requires
); // use indirection so modules not rolled into a build