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\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\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
17 "dojo/_base/lang", // lang.mixin lang.hitch
20 "dojo/sniff", // has("ie") has("opera") has("dijit-legacy-requires")
21 "dojo/window", // winUtils.getBox, winUtils.get
22 "dojo/dnd/Moveable", // Moveable
23 "dojo/dnd/TimedMoveable", // TimedMoveable
25 "./_base/manager", // manager.defaultDuration
32 "./layout/ContentPane",
33 "dojo/text!./templates/Dialog.html",
34 "./main", // for back-compat, exporting dijit._underlay (remove in 2.0)
35 "dojo/i18n!./nls/common"
36 ], function(require
, array
, connect
, declare
, Deferred
,
37 dom
, domClass
, domGeometry
, domStyle
, event
, fx
, i18n
, keys
, lang
, on
, ready
, has
, winUtils
,
38 Moveable
, TimedMoveable
, focus
, manager
, _Widget
, _TemplatedMixin
, _CssStateMixin
, _FormMixin
, _DialogMixin
,
39 DialogUnderlay
, ContentPane
, template
, dijit
){
45 dijit._underlay = function(kwArgs){
47 // A shared instance of a `dijit.DialogUnderlay`
50 // A shared instance of a `dijit.DialogUnderlay` created and
51 // used by `dijit.Dialog`, though never created until some Dialog
52 // or subclass thereof is shown.
56 var _DialogBase
= declare("dijit._DialogBase", [_TemplatedMixin
, _FormMixin
, _DialogMixin
, _CssStateMixin
], {
57 templateString
: template
,
59 baseClass
: "dijitDialog",
62 closeButtonNode
: "dijitDialogCloseIcon"
65 // Map widget attributes to DOMNode attributes.
67 { node
: "titleNode", type
: "innerHTML" },
68 { node
: "titleBar", type
: "attribute" }
71 // open: [readonly] Boolean
72 // True if Dialog is currently displayed on screen.
76 // The time in milliseconds it takes the dialog to fade in and out
77 duration
: manager
.defaultDuration
,
80 // A Toggle to modify the default focus behavior of a Dialog, which
81 // is to re-focus the element which had focus before being opened.
82 // False will disable refocusing. Default: true
86 // A Toggle to modify the default focus behavior of a Dialog, which
87 // is to focus on the first dialog element after opening the dialog.
88 // False will disable autofocusing. Default: true
91 // _firstFocusItem: [private readonly] DomNode
92 // The pointer to the first focusable node in the dialog.
93 // Set by `dijit/_DialogMixin._getFocusItems()`.
94 _firstFocusItem
: null,
96 // _lastFocusItem: [private readonly] DomNode
97 // The pointer to which node has focus prior to our dialog.
98 // Set by `dijit/_DialogMixin._getFocusItems()`.
101 // doLayout: [protected] Boolean
102 // Don't change this parameter from the default value.
103 // This ContentPane parameter doesn't make sense for Dialog, since Dialog
104 // is never a child of a layout container, nor can you specify the size of
105 // Dialog in order to control the size of an inner widget.
108 // draggable: Boolean
109 // Toggles the moveable aspect of the Dialog. If true, Dialog
110 // can be dragged by it's title. If false it will remain centered
114 _setDraggableAttr: function(/*Boolean*/ val
){
115 // Avoid _WidgetBase behavior of copying draggable attribute to this.domNode,
116 // as that prevents text select on modern browsers (#14452)
117 this._set("draggable", val
);
120 // aria-describedby: String
121 // Allows the user to add an aria-describedby attribute onto the dialog. The value should
122 // be the id of the container element of text that describes the dialog purpose (usually
123 // the first text in the dialog).
124 // | <div data-dojo-type="dijit/Dialog" aria-describedby="intro" .....>
125 // | <div id="intro">Introductory text</div>
126 // | <div>rest of dialog contents</div>
128 "aria-describedby": "",
131 // Maximum size to allow the dialog to expand to, relative to viewport size
134 postMixInProperties: function(){
135 var _nlsResources
= i18n
.getLocalization("dijit", "common");
136 lang
.mixin(this, _nlsResources
);
137 this.inherited(arguments
);
140 postCreate: function(){
141 domStyle
.set(this.domNode
, {
145 this.ownerDocumentBody
.appendChild(this.domNode
);
147 this.inherited(arguments
);
149 this.connect(this, "onExecute", "hide");
150 this.connect(this, "onCancel", "hide");
151 this._modalconnects
= [];
156 // Called when data has been loaded from an href.
157 // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
158 // but should *not* be overridden.
162 // when href is specified we need to reposition the dialog after the data is loaded
163 // and find the focusable elements
165 if(this.autofocus
&& DialogLevelManager
.isTop(this)){
166 this._getFocusItems(this.domNode
);
167 focus
.focus(this._firstFocusItem
);
169 this.inherited(arguments
);
172 _onBlur: function(by
){
173 this.inherited(arguments
);
175 // If focus was accidentally removed from the dialog, such as if the user clicked a blank
176 // area of the screen, or clicked the browser's address bar and then tabbed into the page,
177 // then refocus. Won't do anything if focus was removed because the Dialog was closed, or
178 // because a new Dialog popped up on top of the old one.
179 var refocus
= lang
.hitch(this, function(){
180 if(this.open
&& !this._destroyed
&& DialogLevelManager
.isTop(this)){
181 this._getFocusItems(this.domNode
);
182 focus
.focus(this._firstFocusItem
);
186 // wait for mouse up, and then refocus dialog; otherwise doesn't work
187 on
.once(this.ownerDocument
, "mouseup", refocus
);
193 _endDrag: function(){
195 // Called after dragging the Dialog. Saves the position of the dialog in the viewport,
196 // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
197 var nodePosition
= domGeometry
.position(this.domNode
),
198 viewport
= winUtils
.getBox(this.ownerDocument
);
199 nodePosition
.y
= Math
.min(Math
.max(nodePosition
.y
, 0), (viewport
.h
- nodePosition
.h
));
200 nodePosition
.x
= Math
.min(Math
.max(nodePosition
.x
, 0), (viewport
.w
- nodePosition
.w
));
201 this._relativePosition
= nodePosition
;
207 // Stuff we need to do before showing the Dialog for the first
208 // time (but we defer it until right beforehand, for
209 // performance reasons).
213 var node
= this.domNode
;
215 if(this.titleBar
&& this.draggable
){
216 this._moveable
= new ((has("ie") == 6) ? TimedMoveable
// prevent overload, see #5285
217 : Moveable
)(node
, { handle
: this.titleBar
});
218 this.connect(this._moveable
, "onMoveStop", "_endDrag");
220 domClass
.add(node
,"dijitDialogFixed");
223 this.underlayAttrs
= {
225 "class": array
.map(this["class"].split(/\s/), function(s
){ return s
+"_underlay"; }).join(" "),
226 ownerDocument
: this.ownerDocument
232 // If necessary, shrink dialog contents so dialog fits in viewport
236 this._checkIfSingleChild();
238 // If we resized the dialog contents earlier, reset them back to original size, so
239 // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
240 // Need to do this before the domGeometry.position(this.domNode) call below.
241 if(this._singleChild
){
242 if(typeof this._singleChildOriginalStyle
!= "undefined"){
243 this._singleChild
.domNode
.style
.cssText
= this._singleChildOriginalStyle
;
244 delete this._singleChildOriginalStyle
;
247 domStyle
.set(this.containerNode
, {
253 var bb
= domGeometry
.position(this.domNode
);
255 // Get viewport size but then reduce it by a bit; Dialog should always have some space around it
256 // to indicate that it's a popup. This will also compensate for possible scrollbars on viewport.
257 var viewport
= winUtils
.getBox(this.ownerDocument
);
258 viewport
.w
*= this.maxRatio
;
259 viewport
.h
*= this.maxRatio
;
261 if(bb
.w
>= viewport
.w
|| bb
.h
>= viewport
.h
){
262 // Reduce size of dialog contents so that dialog fits in viewport
264 var containerSize
= domGeometry
.position(this.containerNode
),
265 w
= Math
.min(bb
.w
, viewport
.w
) - (bb
.w
- containerSize
.w
),
266 h
= Math
.min(bb
.h
, viewport
.h
) - (bb
.h
- containerSize
.h
);
268 if(this._singleChild
&& this._singleChild
.resize
){
269 if(typeof this._singleChildOriginalStyle
== "undefined"){
270 this._singleChildOriginalStyle
= this._singleChild
.domNode
.style
.cssText
;
272 this._singleChild
.resize({w
: w
, h
: h
});
274 domStyle
.set(this.containerNode
, {
278 position
: "relative" // workaround IE bug moving scrollbar or dragging dialog
282 if(this._singleChild
&& this._singleChild
.resize
){
283 this._singleChild
.resize();
288 _position: function(){
290 // Position modal dialog in the viewport. If no relative offset
291 // in the viewport has been determined (by dragging, for instance),
292 // center the node. Otherwise, use the Dialog's stored relative offset,
293 // and position the node to top: left: values based on the viewport.
294 if(!domClass
.contains(this.ownerDocumentBody
, "dojoMove")){ // don't do anything if called during auto-scroll
295 var node
= this.domNode
,
296 viewport
= winUtils
.getBox(this.ownerDocument
),
297 p
= this._relativePosition
,
298 bb
= p
? null : domGeometry
.position(node
),
299 l
= Math
.floor(viewport
.l
+ (p
? p
.x
: (viewport
.w
- bb
.w
) / 2)),
300 t
= Math
.floor(viewport
.t
+ (p
? p
.y
: (viewport
.h
- bb
.h
) / 2))
309 _onKey: function(/*Event*/ evt
){
311 // Handles the keyboard events for accessibility reasons
316 var node
= evt
.target
;
317 if(evt
.charOrCode
=== keys
.TAB
){
318 this._getFocusItems(this.domNode
);
320 var singleFocusItem
= (this._firstFocusItem
== this._lastFocusItem
);
321 // see if we are shift-tabbing from first focusable item on dialog
322 if(node
== this._firstFocusItem
&& evt
.shiftKey
&& evt
.charOrCode
=== keys
.TAB
){
323 if(!singleFocusItem
){
324 focus
.focus(this._lastFocusItem
); // send focus to last item in dialog
327 }else if(node
== this._lastFocusItem
&& evt
.charOrCode
=== keys
.TAB
&& !evt
.shiftKey
){
328 if(!singleFocusItem
){
329 focus
.focus(this._firstFocusItem
); // send focus to first item in dialog
333 // see if the key is for the dialog
335 if(node
== this.domNode
|| domClass
.contains(node
, "dijitPopup")){
336 if(evt
.charOrCode
== keys
.ESCAPE
){
339 return; // just let it go
342 node
= node
.parentNode
;
344 // this key is for the disabled document window
345 if(evt
.charOrCode
!== keys
.TAB
){ // allow tabbing into the dialog for a11y
347 // opera won't tab to a div
348 }else if(!has("opera")){
350 this._firstFocusItem
.focus();
351 }catch(e
){ /*squelch*/ }
359 // Display the dialog
360 // returns: dojo/_base/Deferred
361 // Deferred object that resolves when the display animation is complete
363 if(this.open
){ return; }
369 // first time we show the dialog, there's some initialization stuff to do
370 if(!this._alreadyInitialized
){
372 this._alreadyInitialized
=true;
375 if(this._fadeOutDeferred
){
376 this._fadeOutDeferred
.cancel();
379 // Recenter Dialog if user scrolls browser. Connecting to document doesn't work on IE, need to use window.
380 var win
= winUtils
.get(this.ownerDocument
);
381 this._modalconnects
.push(on(win
, "scroll", lang
.hitch(this, "resize")));
383 this._modalconnects
.push(on(this.domNode
, connect
._keypress
, lang
.hitch(this, "_onKey")));
385 domStyle
.set(this.domNode
, {
390 this._set("open", true);
391 this._onShow(); // lazy load trigger
396 // fade-in Animation object, setup below
399 this._fadeInDeferred
= new Deferred(lang
.hitch(this, function(){
401 delete this._fadeInDeferred
;
406 duration
: this.duration
,
407 beforeBegin
: lang
.hitch(this, function(){
408 DialogLevelManager
.show(this, this.underlayAttrs
);
410 onEnd
: lang
.hitch(this, function(){
411 if(this.autofocus
&& DialogLevelManager
.isTop(this)){
412 // find focusable items each time dialog is shown since if dialog contains a widget the
413 // first focusable items can change
414 this._getFocusItems(this.domNode
);
415 focus
.focus(this._firstFocusItem
);
417 this._fadeInDeferred
.resolve(true);
418 delete this._fadeInDeferred
;
422 return this._fadeInDeferred
;
428 // returns: dojo/_base/Deferred
429 // Deferred object that resolves when the hide animation is complete
431 // If we haven't been initialized yet then we aren't showing and we can just return.
432 // Likewise if we are already hidden, or are currently fading out.
433 if(!this._alreadyInitialized
|| !this.open
){
436 if(this._fadeInDeferred
){
437 this._fadeInDeferred
.cancel();
440 // fade-in Animation object, setup below
443 this._fadeOutDeferred
= new Deferred(lang
.hitch(this, function(){
445 delete this._fadeOutDeferred
;
447 // fire onHide when the promise resolves.
448 this._fadeOutDeferred
.then(lang
.hitch(this, 'onHide'));
450 fadeOut
= fx
.fadeOut({
452 duration
: this.duration
,
453 onEnd
: lang
.hitch(this, function(){
454 this.domNode
.style
.display
= "none";
455 DialogLevelManager
.hide(this);
456 this._fadeOutDeferred
.resolve(true);
457 delete this._fadeOutDeferred
;
461 if(this._scrollConnected
){
462 this._scrollConnected
= false;
465 while(h
= this._modalconnects
.pop()){
469 if(this._relativePosition
){
470 delete this._relativePosition
;
472 this._set("open", false);
474 return this._fadeOutDeferred
;
479 // Called when viewport scrolled or size changed. Position the Dialog and the underlay.
482 if(this.domNode
.style
.display
!= "none"){
483 if(DialogUnderlay
._singleton
){ // avoid race condition during show()
484 DialogUnderlay
._singleton
.layout();
492 if(this._fadeInDeferred
){
493 this._fadeInDeferred
.cancel();
495 if(this._fadeOutDeferred
){
496 this._fadeOutDeferred
.cancel();
499 this._moveable
.destroy();
502 while(h
= this._modalconnects
.pop()){
506 DialogLevelManager
.hide(this);
508 this.inherited(arguments
);
512 var Dialog
= declare("dijit.Dialog", [ContentPane
, _DialogBase
], {
514 // A modal dialog Widget.
516 // Pops up a modal dialog window, blocking access to the screen
517 // and also graying out the screen Dialog is extended from
518 // ContentPane so it supports all the same parameters (href, etc.).
520 // | <div data-dojo-type="dijit/Dialog" data-dojo-props="href: 'test.html'"></div>
522 // | var foo = new Dialog({ title: "test dialog", content: "test content" };
523 // | foo.placeAt(win.body());
526 Dialog
._DialogBase
= _DialogBase
; // for monkey patching and dojox/widget/DialogSimple
528 var DialogLevelManager
= Dialog
._DialogLevelManager
= {
530 // Controls the various active "levels" on the page, starting with the
531 // stuff initially visible on the page (at z-index 0), and then having an entry for
532 // each Dialog shown.
536 show: function(/*dijit/_WidgetBase*/ dialog, /*Object*/ underlayAttrs
){
538 // Call right before fade-in animation for new dialog.
539 // Saves current focus, displays/adjusts underlay for new dialog,
540 // and sets the z-index of the dialog itself.
542 // New dialog will be displayed on top of all currently displayed dialogs.
544 // Caller is responsible for setting focus in new dialog after the fade-in
545 // animation completes.
547 // Save current focus
548 ds
[ds
.length
-1].focus
= focus
.curNode
;
550 // Display the underlay, or if already displayed then adjust for this new dialog
551 // TODO: one underlay per document (based on dialog.ownerDocument)
552 var underlay
= DialogUnderlay
._singleton
;
553 if(!underlay
|| underlay
._destroyed
){
554 underlay
= dijit
._underlay
= DialogUnderlay
._singleton
= new DialogUnderlay(underlayAttrs
);
556 underlay
.set(dialog
.underlayAttrs
);
559 // Set z-index a bit above previous dialog
560 var zIndex
= ds
[ds
.length
-1].dialog
? ds
[ds
.length
-1].zIndex
+ 2 : Dialog
._DialogLevelManager
._beginZIndex
;
561 if(ds
.length
== 1){ // first dialog
564 domStyle
.set(DialogUnderlay
._singleton
.domNode
, 'zIndex', zIndex
- 1);
567 domStyle
.set(dialog
.domNode
, 'zIndex', zIndex
);
569 ds
.push({dialog
: dialog
, underlayAttrs
: underlayAttrs
, zIndex
: zIndex
});
572 hide: function(/*dijit/_WidgetBase*/ dialog){
574 // Called when the specified dialog is hidden/destroyed, after the fade-out
575 // animation ends, in order to reset page focus, fix the underlay, etc.
576 // If the specified dialog isn't open then does nothing.
578 // Caller is responsible for either setting display:none on the dialog domNode,
579 // or calling dijit/popup.hide(), or removing it from the page DOM.
581 if(ds[ds.length-1].dialog == dialog){
582 // Removing the top (or only) dialog in the stack, return focus
583 // to previous dialog
587 var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
589 // Adjust underlay, unless the underlay widget has already been destroyed
590 // because we are being called during page unload (when all widgets are destroyed)
591 if(!DialogUnderlay._singleton._destroyed){
593 // Returning to original page. Hide the underlay.
594 DialogUnderlay._singleton.hide();
596 // Popping back to previous dialog, adjust underlay.
597 domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', pd.zIndex - 1);
598 DialogUnderlay._singleton.set(pd.underlayAttrs);
604 // If we are returning control to a previous dialog but for some reason
605 // that dialog didn't have a focused field, set focus to first focusable item.
606 // This situation could happen if two dialogs appeared at nearly the same time,
607 // since a dialog doesn't set it's focus until the fade-in is finished.
608 var focus = pd.focus;
609 if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
610 pd.dialog._getFocusItems(pd.dialog.domNode);
611 focus = pd.dialog._firstFocusItem;
615 // Refocus the button that spawned the Dialog. This will fail in corner cases including
616 // page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
617 // before this code runs. (#15058)
624 // Removing a dialog out of order (#9944, #10705).
625 // Don't need to mess with underlay or z-index or anything.
626 var idx = array.indexOf(array.map(ds, function(elem){return elem.dialog}), dialog);
633 isTop: function(/*dijit/_WidgetBase*/ dialog){
635 // Returns true if specified Dialog is the top in the task
636 return ds[ds.length-1].dialog == dialog;
640 // Stack representing the various active "levels" on the page, starting with the
641 // stuff initially visible on the page (at z-index 0), and then having an entry for
642 // each Dialog shown.
643 // Each element in stack has form {
644 // dialog: dialogWidget,
645 // focus: returnFromGetFocus(),
646 // underlayAttrs: attributes to set on underlay (when this widget is active)
648 var ds = Dialog._dialogStack = [
649 {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
652 // Back compat w/1.6, remove for 2.0
653 if(has("dijit-legacy-requires")){
655 var requires = ["dijit/TooltipDialog"];
656 require(requires); // use indirection so modules not rolled into a build