]>
Commit | Line | Data |
---|---|---|
1354d172 AD |
1 | require({cache:{ |
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", [ | |
4 | "require", | |
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 | |
17 | "dojo/keys", | |
18 | "dojo/_base/lang", // lang.mixin lang.hitch | |
19 | "dojo/on", | |
20 | "dojo/ready", | |
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 | |
26 | "./focus", | |
27 | "./_base/manager", // manager.defaultDuration | |
28 | "./_Widget", | |
29 | "./_TemplatedMixin", | |
30 | "./_CssStateMixin", | |
31 | "./form/_FormMixin", | |
32 | "./_DialogMixin", | |
33 | "./DialogUnderlay", | |
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){ | |
42 | ||
43 | /*===== | |
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; | |
49 | =====*/ | |
50 | ||
51 | ||
52 | // module: | |
53 | // dijit/Dialog | |
54 | // summary: | |
55 | // A modal dialog Widget | |
56 | ||
57 | ||
58 | /*===== | |
59 | dijit._underlay = function(kwArgs){ | |
60 | // summary: | |
61 | // A shared instance of a `dijit.DialogUnderlay` | |
62 | // | |
63 | // description: | |
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. | |
67 | }; | |
68 | =====*/ | |
69 | ||
70 | var _DialogBase = declare("dijit._DialogBase", [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], { | |
71 | // summary: | |
72 | // A modal dialog Widget | |
73 | // | |
74 | // description: | |
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.) | |
78 | // | |
79 | // example: | |
80 | // | <div data-dojo-type="dijit.Dialog" data-dojo-props="href: 'test.html'"></div> | |
81 | // | |
82 | // example: | |
83 | // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" }; | |
84 | // | dojo.body().appendChild(foo.domNode); | |
85 | // | foo.startup(); | |
86 | ||
87 | templateString: template, | |
88 | ||
89 | baseClass: "dijitDialog", | |
90 | ||
91 | cssStateNodes: { | |
92 | closeButtonNode: "dijitDialogCloseIcon" | |
93 | }, | |
94 | ||
95 | // Map widget attributes to DOMNode attributes. | |
96 | _setTitleAttr: [ | |
97 | { node: "titleNode", type: "innerHTML" }, | |
98 | { node: "titleBar", type: "attribute" } | |
99 | ], | |
100 | ||
101 | // open: [readonly] Boolean | |
102 | // True if Dialog is currently displayed on screen. | |
103 | open: false, | |
104 | ||
105 | // duration: Integer | |
106 | // The time in milliseconds it takes the dialog to fade in and out | |
107 | duration: manager.defaultDuration, | |
108 | ||
109 | // refocus: Boolean | |
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 | |
113 | refocus: true, | |
114 | ||
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 | |
119 | autofocus: true, | |
120 | ||
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, | |
125 | ||
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, | |
130 | ||
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. | |
136 | doLayout: false, | |
137 | ||
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 | |
141 | // in the viewport. | |
142 | draggable: true, | |
143 | ||
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> | |
151 | // </div> | |
152 | "aria-describedby":"", | |
153 | ||
154 | postMixInProperties: function(){ | |
155 | var _nlsResources = i18n.getLocalization("dijit", "common"); | |
156 | lang.mixin(this, _nlsResources); | |
157 | this.inherited(arguments); | |
158 | }, | |
159 | ||
160 | postCreate: function(){ | |
161 | domStyle.set(this.domNode, { | |
162 | display: "none", | |
163 | position:"absolute" | |
164 | }); | |
165 | win.body().appendChild(this.domNode); | |
166 | ||
167 | this.inherited(arguments); | |
168 | ||
169 | this.connect(this, "onExecute", "hide"); | |
170 | this.connect(this, "onCancel", "hide"); | |
171 | this._modalconnects = []; | |
172 | }, | |
173 | ||
174 | onLoad: function(){ | |
175 | // summary: | |
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. | |
179 | // tags: | |
180 | // callback | |
181 | ||
182 | // when href is specified we need to reposition the dialog after the data is loaded | |
183 | // and find the focusable elements | |
184 | this._position(); | |
185 | if(this.autofocus && DialogLevelManager.isTop(this)){ | |
186 | this._getFocusItems(this.domNode); | |
187 | focus.focus(this._firstFocusItem); | |
188 | } | |
189 | this.inherited(arguments); | |
190 | }, | |
191 | ||
192 | _endDrag: function(){ | |
193 | // summary: | |
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; | |
201 | this._position(); | |
202 | }, | |
203 | ||
204 | _setup: function(){ | |
205 | // summary: | |
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). | |
209 | // tags: | |
210 | // private | |
211 | ||
212 | var node = this.domNode; | |
213 | ||
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"); | |
218 | }else{ | |
219 | domClass.add(node,"dijitDialogFixed"); | |
220 | } | |
221 | ||
222 | this.underlayAttrs = { | |
223 | dialogId: this.id, | |
224 | "class": array.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ") | |
225 | }; | |
226 | }, | |
227 | ||
228 | _size: function(){ | |
229 | // summary: | |
230 | // If necessary, shrink dialog contents so dialog fits in viewport | |
231 | // tags: | |
232 | // private | |
233 | ||
234 | this._checkIfSingleChild(); | |
235 | ||
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; | |
242 | } | |
243 | delete this._singleChildOriginalStyle; | |
244 | }else{ | |
245 | domStyle.set(this.containerNode, { | |
246 | width:"auto", | |
247 | height:"auto" | |
248 | }); | |
249 | } | |
250 | ||
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 | |
255 | ||
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)); | |
258 | ||
259 | if(this._singleChild && this._singleChild.resize){ | |
260 | this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText; | |
261 | this._singleChild.resize({w: w, h: h}); | |
262 | }else{ | |
263 | domStyle.set(this.containerNode, { | |
264 | width: w + "px", | |
265 | height: h + "px", | |
266 | overflow: "auto", | |
267 | position: "relative" // workaround IE bug moving scrollbar or dragging dialog | |
268 | }); | |
269 | } | |
270 | }else{ | |
271 | if(this._singleChild && this._singleChild.resize){ | |
272 | this._singleChild.resize(); | |
273 | } | |
274 | } | |
275 | }, | |
276 | ||
277 | _position: function(){ | |
278 | // summary: | |
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)) | |
290 | ; | |
291 | domStyle.set(node,{ | |
292 | left: l + "px", | |
293 | top: t + "px" | |
294 | }); | |
295 | } | |
296 | }, | |
297 | ||
298 | _onKey: function(/*Event*/ evt){ | |
299 | // summary: | |
300 | // Handles the keyboard events for accessibility reasons | |
301 | // tags: | |
302 | // private | |
303 | ||
304 | if(evt.charOrCode){ | |
305 | var node = evt.target; | |
306 | if(evt.charOrCode === keys.TAB){ | |
307 | this._getFocusItems(this.domNode); | |
308 | } | |
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 | |
314 | } | |
315 | event.stop(evt); | |
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 | |
319 | } | |
320 | event.stop(evt); | |
321 | }else{ | |
322 | // see if the key is for the dialog | |
323 | while(node){ | |
324 | if(node == this.domNode || domClass.contains(node, "dijitPopup")){ | |
325 | if(evt.charOrCode == keys.ESCAPE){ | |
326 | this.onCancel(); | |
327 | }else{ | |
328 | return; // just let it go | |
329 | } | |
330 | } | |
331 | node = node.parentNode; | |
332 | } | |
333 | // this key is for the disabled document window | |
334 | if(evt.charOrCode !== keys.TAB){ // allow tabbing into the dialog for a11y | |
335 | event.stop(evt); | |
336 | // opera won't tab to a div | |
337 | }else if(!has("opera")){ | |
338 | try{ | |
339 | this._firstFocusItem.focus(); | |
340 | }catch(e){ /*squelch*/ } | |
341 | } | |
342 | } | |
343 | } | |
344 | }, | |
345 | ||
346 | show: function(){ | |
347 | // summary: | |
348 | // Display the dialog | |
349 | // returns: dojo.Deferred | |
350 | // Deferred object that resolves when the display animation is complete | |
351 | ||
352 | if(this.open){ return; } | |
353 | ||
354 | if(!this._started){ | |
355 | this.startup(); | |
356 | } | |
357 | ||
358 | // first time we show the dialog, there's some initialization stuff to do | |
359 | if(!this._alreadyInitialized){ | |
360 | this._setup(); | |
361 | this._alreadyInitialized=true; | |
362 | } | |
363 | ||
364 | if(this._fadeOutDeferred){ | |
365 | this._fadeOutDeferred.cancel(); | |
366 | } | |
367 | ||
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){ | |
376 | this.layout(); | |
377 | this._oldViewport = viewport; | |
378 | } | |
379 | }))); | |
380 | this._modalconnects.push(on(this.domNode, connect._keypress, lang.hitch(this, "_onKey"))); | |
381 | ||
382 | domStyle.set(this.domNode, { | |
383 | opacity:0, | |
384 | display:"" | |
385 | }); | |
386 | ||
387 | this._set("open", true); | |
388 | this._onShow(); // lazy load trigger | |
389 | ||
390 | this._size(); | |
391 | this._position(); | |
392 | ||
393 | // fade-in Animation object, setup below | |
394 | var fadeIn; | |
395 | ||
396 | this._fadeInDeferred = new Deferred(lang.hitch(this, function(){ | |
397 | fadeIn.stop(); | |
398 | delete this._fadeInDeferred; | |
399 | })); | |
400 | ||
401 | fadeIn = fx.fadeIn({ | |
402 | node: this.domNode, | |
403 | duration: this.duration, | |
404 | beforeBegin: lang.hitch(this, function(){ | |
405 | DialogLevelManager.show(this, this.underlayAttrs); | |
406 | }), | |
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); | |
413 | } | |
414 | this._fadeInDeferred.callback(true); | |
415 | delete this._fadeInDeferred; | |
416 | }) | |
417 | }).play(); | |
418 | ||
419 | return this._fadeInDeferred; | |
420 | }, | |
421 | ||
422 | hide: function(){ | |
423 | // summary: | |
424 | // Hide the dialog | |
425 | // returns: dojo.Deferred | |
426 | // Deferred object that resolves when the hide animation is complete | |
427 | ||
428 | // if we haven't been initialized yet then we aren't showing and we can just return | |
429 | if(!this._alreadyInitialized){ | |
430 | return; | |
431 | } | |
432 | if(this._fadeInDeferred){ | |
433 | this._fadeInDeferred.cancel(); | |
434 | } | |
435 | ||
436 | // fade-in Animation object, setup below | |
437 | var fadeOut; | |
438 | ||
439 | this._fadeOutDeferred = new Deferred(lang.hitch(this, function(){ | |
440 | fadeOut.stop(); | |
441 | delete this._fadeOutDeferred; | |
442 | })); | |
443 | // fire onHide when the promise resolves. | |
444 | this._fadeOutDeferred.then(lang.hitch(this, 'onHide')); | |
445 | ||
446 | fadeOut = fx.fadeOut({ | |
447 | node: this.domNode, | |
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; | |
454 | }) | |
455 | }).play(); | |
456 | ||
457 | if(this._scrollConnected){ | |
458 | this._scrollConnected = false; | |
459 | } | |
460 | var h; | |
461 | while(h = this._modalconnects.pop()){ | |
462 | h.remove(); | |
463 | } | |
464 | ||
465 | if(this._relativePosition){ | |
466 | delete this._relativePosition; | |
467 | } | |
468 | this._set("open", false); | |
469 | ||
470 | return this._fadeOutDeferred; | |
471 | }, | |
472 | ||
473 | layout: function(){ | |
474 | // summary: | |
475 | // Position the Dialog and the underlay | |
476 | // tags: | |
477 | // private | |
478 | if(this.domNode.style.display != "none"){ | |
479 | if(dijit._underlay){ // avoid race condition during show() | |
480 | dijit._underlay.layout(); | |
481 | } | |
482 | this._position(); | |
483 | } | |
484 | }, | |
485 | ||
486 | destroy: function(){ | |
487 | if(this._fadeInDeferred){ | |
488 | this._fadeInDeferred.cancel(); | |
489 | } | |
490 | if(this._fadeOutDeferred){ | |
491 | this._fadeOutDeferred.cancel(); | |
492 | } | |
493 | if(this._moveable){ | |
494 | this._moveable.destroy(); | |
495 | } | |
496 | var h; | |
497 | while(h = this._modalconnects.pop()){ | |
498 | h.remove(); | |
499 | } | |
500 | ||
501 | DialogLevelManager.hide(this); | |
502 | ||
503 | this.inherited(arguments); | |
504 | } | |
505 | }); | |
506 | ||
507 | var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {}); | |
508 | Dialog._DialogBase = _DialogBase; // for monkey patching | |
509 | ||
510 | var DialogLevelManager = Dialog._DialogLevelManager = { | |
511 | // summary: | |
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. | |
515 | ||
516 | _beginZIndex: 950, | |
517 | ||
518 | show: function(/*dijit._Widget*/ dialog, /*Object*/ underlayAttrs){ | |
519 | // summary: | |
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. | |
523 | // | |
524 | // New dialog will be displayed on top of all currently displayed dialogs. | |
525 | // | |
526 | // Caller is responsible for setting focus in new dialog after the fade-in | |
527 | // animation completes. | |
528 | ||
529 | // Save current focus | |
530 | ds[ds.length-1].focus = focus.curNode; | |
531 | ||
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); | |
536 | }else{ | |
537 | underlay.set(dialog.underlayAttrs); | |
538 | } | |
539 | ||
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 | |
543 | underlay.show(); | |
544 | } | |
545 | domStyle.set(dijit._underlay.domNode, 'zIndex', zIndex - 1); | |
546 | ||
547 | // Dialog | |
548 | domStyle.set(dialog.domNode, 'zIndex', zIndex); | |
549 | ||
550 | ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex}); | |
551 | }, | |
552 | ||
553 | hide: function(/*dijit._Widget*/ dialog){ | |
554 | // summary: | |
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. | |
558 | // | |
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. | |
561 | ||
562 | if(ds[ds.length-1].dialog == dialog){ | |
563 | // Removing the top (or only) dialog in the stack, return focus | |
564 | // to previous dialog | |
565 | ||
566 | ds.pop(); | |
567 | ||
568 | var pd = ds[ds.length-1]; // the new active dialog (or the base page itself) | |
569 | ||
570 | // Adjust underlay | |
571 | if(ds.length == 1){ | |
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(); | |
577 | } | |
578 | }else{ | |
579 | // Popping back to previous dialog, adjust underlay | |
580 | domStyle.set(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1); | |
581 | dijit._underlay.set(pd.underlayAttrs); | |
582 | } | |
583 | ||
584 | // Adjust focus | |
585 | if(dialog.refocus){ | |
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; | |
594 | } | |
595 | ||
596 | if(focus){ | |
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) | |
600 | try{ | |
601 | focus.focus(); | |
602 | }catch(e){} | |
603 | } | |
604 | } | |
605 | }else{ | |
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); | |
609 | if(idx != -1){ | |
610 | ds.splice(idx, 1); | |
611 | } | |
612 | } | |
613 | }, | |
614 | ||
615 | isTop: function(/*dijit._Widget*/ dialog){ | |
616 | // summary: | |
617 | // Returns true if specified Dialog is the top in the task | |
618 | return ds[ds.length-1].dialog == dialog; | |
619 | } | |
620 | }; | |
621 | ||
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) | |
629 | // } | |
630 | var ds = Dialog._dialogStack = [ | |
631 | {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0 | |
632 | ]; | |
633 | ||
634 | // Back compat w/1.6, remove for 2.0 | |
635 | if(!kernel.isAsync){ | |
636 | ready(0, function(){ | |
637 | var requires = ["dijit/TooltipDialog"]; | |
638 | require(requires); // use indirection so modules not rolled into a build | |
639 | }); | |
640 | } | |
641 | ||
642 | return Dialog; | |
643 | }); |