]> git.wh0rd.org - tt-rss.git/blob - lib/dijit/Dialog.js.uncompressed.js
update dojo to 1.7.3
[tt-rss.git] / lib / dijit / Dialog.js.uncompressed.js
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 });