text: define([
  	"require",
  	"dojo/_base/array", // array.forEach array.indexOf array.map
  	"dojo/aspect",
  	"dojo/_base/declare", // declare
  	"dojo/Deferred", // Deferred
  	"dojo/dom", // dom.isDescendant
  	"dojo/dom-class", // domClass.add domClass.contains
  	"dojo/dom-geometry", // domGeometry.position
  	"dojo/dom-style", // domStyle.set
  	"dojo/_base/fx", // fx.fadeIn fx.fadeOut
  	"dojo/i18n", // i18n.getLocalization
  	"dojo/keys",
  	"dojo/_base/lang", // lang.mixin lang.hitch
  	"dojo/on",
  	"dojo/ready",
  	"dojo/sniff", // has("ie") has("opera") has("dijit-legacy-requires")
  	"dojo/window", // winUtils.getBox, winUtils.get
  	"dojo/dnd/Moveable", // Moveable
  	"dojo/dnd/TimedMoveable", // TimedMoveable
  	"./focus",
  	"./_base/manager", // manager.defaultDuration
  	"./_Widget",
  	"./_TemplatedMixin",
  	"./_CssStateMixin",
  	"./form/_FormMixin",
  	"./_DialogMixin",
  	"./DialogUnderlay",
  	"./layout/ContentPane",
  	"dojo/text!./templates/Dialog.html",
  	"dojo/i18n!./nls/common"
  ], function(require, array, aspect, declare, Deferred,
  			dom, domClass, domGeometry, domStyle, fx, i18n, keys, lang, on, ready, has, winUtils,
  			Moveable, TimedMoveable, focus, manager, _Widget, _TemplatedMixin, _CssStateMixin, _FormMixin, _DialogMixin,
  			DialogUnderlay, ContentPane, template){
  
  	// module:
  	//		dijit/Dialog
  
  	/*=====
  	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.
  	};
  	=====*/
  
  	var _DialogBase = declare("dijit._DialogBase" + (has("dojo-bidi") ? "_NoBidi" : ""), [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], {
  		templateString: template,
  
  		baseClass: "dijitDialog",
  
  		cssStateNodes: {
  			closeButtonNode: "dijitDialogCloseIcon"
  		},
  
  		// Map widget attributes to DOMNode attributes.
  		_setTitleAttr: { node: "titleNode", type: "innerHTML" },
  
  		// 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: manager.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,
  
  		_setDraggableAttr: function(/*Boolean*/ val){
  			// Avoid _WidgetBase behavior of copying draggable attribute to this.domNode,
  			// as that prevents text select on modern browsers (#14452)
  			this._set("draggable", val);
  		},
  
  		// 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 data-dojo-type="dijit/Dialog" aria-describedby="intro" .....>
  		//	|		<div id="intro">Introductory text</div>
  		//	|		<div>rest of dialog contents</div>
  		//	|	</div>
  		"aria-describedby": "",
  
  		// maxRatio: Number
  		//		Maximum size to allow the dialog to expand to, relative to viewport size
  		maxRatio: 0.9,
  
  		postMixInProperties: function(){
  			var _nlsResources = i18n.getLocalization("dijit", "common");
  			lang.mixin(this, _nlsResources);
  			this.inherited(arguments);
  		},
  
  		postCreate: function(){
  			domStyle.set(this.domNode, {
  				display: "none",
  				position: "absolute"
  			});
  			this.ownerDocumentBody.appendChild(this.domNode);
  
  			this.inherited(arguments);
  
  			aspect.after(this, "onExecute", lang.hitch(this, "hide"), true);
  			aspect.after(this, "onCancel", lang.hitch(this, "hide"), true);
  
  			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 && DialogLevelManager.isTop(this)){
  				this._getFocusItems(this.domNode);
  				focus.focus(this._firstFocusItem);
  			}
  			this.inherited(arguments);
  		},
  
  		focus: function(){
  			this._getFocusItems(this.domNode);
  			focus.focus(this._firstFocusItem);
  		},
  
  		_endDrag: function(){
  			// summary:
  			//		Called after dragging the Dialog. Saves the position of the dialog in the viewport,
  			//		and also adjust position to be fully within the viewport, so user doesn't lose access to handle
  			var nodePosition = domGeometry.position(this.domNode),
  				viewport = winUtils.getBox(this.ownerDocument);
  			nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
  			nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
  			this._relativePosition = nodePosition;
  			this._position();
  		},
  
  		_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 = new ((has("ie") == 6) ? TimedMoveable // prevent overload, see #5285
  					: Moveable)(node, { handle: this.titleBar });
  				aspect.after(this._moveable, "onMoveStop", lang.hitch(this, "_endDrag"), true);
  			}else{
  				domClass.add(node, "dijitDialogFixed");
  			}
  
  			this.underlayAttrs = {
  				dialogId: this.id,
  				"class": array.map(this["class"].split(/\s/),function(s){
  					return s + "_underlay";
  				}).join(" "),
  				ownerDocument: this.ownerDocument
  			};
  		},
  
  		_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 domGeometry.position(this.domNode) call below.
  			if(this._singleChild){
  				if(typeof this._singleChildOriginalStyle != "undefined"){
  					this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
  					delete this._singleChildOriginalStyle;
  				}
  			}else{
  				domStyle.set(this.containerNode, {
  					width: "auto",
  					height: "auto"
  				});
  			}
  
  			var bb = domGeometry.position(this.domNode);
  
  			// Get viewport size but then reduce it by a bit; Dialog should always have some space around it
  			// to indicate that it's a popup.  This will also compensate for possible scrollbars on viewport.
  			var viewport = winUtils.getBox(this.ownerDocument);
  			viewport.w *= this.maxRatio;
  			viewport.h *= this.maxRatio;
  
  			if(bb.w >= viewport.w || bb.h >= viewport.h){
  				// Reduce size of dialog contents so that dialog fits in viewport
  
  				var containerSize = domGeometry.position(this.containerNode),
  					w = Math.min(bb.w, viewport.w) - (bb.w - containerSize.w),
  					h = Math.min(bb.h, viewport.h) - (bb.h - containerSize.h);
  
  				if(this._singleChild && this._singleChild.resize){
  					if(typeof this._singleChildOriginalStyle == "undefined"){
  						this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
  					}
  					this._singleChild.resize({w: w, h: h});
  				}else{
  					domStyle.set(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 the dialog in the viewport.  If no relative offset
  			//		in the viewport has been determined (by dragging, for instance),
  			//		center the dialog.  Otherwise, use the Dialog's stored relative offset,
  			//		adjusted by the viewport's scroll.
  			if(!domClass.contains(this.ownerDocumentBody, "dojoMove")){    // don't do anything if called during auto-scroll
  				var node = this.domNode,
  					viewport = winUtils.getBox(this.ownerDocument),
  					p = this._relativePosition,
  					bb = p ? null : domGeometry.position(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))
  					;
  				domStyle.set(node, {
  					left: l + "px",
  					top: t + "px"
  				});
  			}
  		},
  
  		_onKey: function(/*Event*/ evt){
  			// summary:
  			//		Handles the keyboard events for accessibility reasons
  			// tags:
  			//		private
  
  			if(evt.keyCode == keys.TAB){
  				this._getFocusItems(this.domNode);
  				var node = evt.target;
  				if(this._firstFocusItem == this._lastFocusItem){
  					// don't move focus anywhere, but don't allow browser to move focus off of dialog either
  					evt.stopPropagation();
  					evt.preventDefault();
  				}else if(node == this._firstFocusItem && evt.shiftKey){
  					// if we are shift-tabbing from first focusable item in dialog, send focus to last item
  					focus.focus(this._lastFocusItem);
  					evt.stopPropagation();
  					evt.preventDefault();
  				}else if(node == this._lastFocusItem && !evt.shiftKey){
  					// if we are tabbing from last focusable item in dialog, send focus to first item
  					focus.focus(this._firstFocusItem);
  					evt.stopPropagation();
  					evt.preventDefault();
  				}
  			}else if(evt.keyCode == keys.ESCAPE){
  				this.onCancel();
  				evt.stopPropagation();
  				evt.preventDefault();
  			}
  		},
  
  		show: function(){
  			// summary:
  			//		Display the dialog
  			// returns: dojo/promise/Promise
  			//		Promise 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){
  				// There's a hide() operation in progress, so cancel it, but still call DialogLevelManager.hide()
  				// as though the hide() completed, in preparation for the DialogLevelManager.show() call below.
  				this._fadeOutDeferred.cancel();
  				DialogLevelManager.hide(this);
  			}
  
  			// Recenter Dialog if user scrolls browser.  Connecting to document doesn't work on IE, need to use window.
  			var win = winUtils.get(this.ownerDocument);
  			this._modalconnects.push(on(win, "scroll", lang.hitch(this, "resize")));
  
  			this._modalconnects.push(on(this.domNode, "keydown", lang.hitch(this, "_onKey")));
  
  			domStyle.set(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 Deferred(lang.hitch(this, function(){
  				fadeIn.stop();
  				delete this._fadeInDeferred;
  			}));
  
  			// If delay is 0, code below will delete this._fadeInDeferred instantly, so grab promise while we can.
  			var promise = this._fadeInDeferred.promise;
  
  			fadeIn = fx.fadeIn({
  				node: this.domNode,
  				duration: this.duration,
  				beforeBegin: lang.hitch(this, function(){
  					DialogLevelManager.show(this, this.underlayAttrs);
  				}),
  				onEnd: lang.hitch(this, function(){
  					if(this.autofocus && 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);
  						focus.focus(this._firstFocusItem);
  					}
  					this._fadeInDeferred.resolve(true);
  					delete this._fadeInDeferred;
  				})
  			}).play();
  
  			return promise;
  		},
  
  		hide: function(){
  			// summary:
  			//		Hide the dialog
  			// returns: dojo/promise/Promise
  			//		Promise object that resolves when the display animation is complete
  
  			// If we haven't been initialized yet then we aren't showing and we can just return.
  			// Likewise if we are already hidden, or are currently fading out.
  			if(!this._alreadyInitialized || !this.open){
  				return;
  			}
  			if(this._fadeInDeferred){
  				this._fadeInDeferred.cancel();
  			}
  
  			// fade-in Animation object, setup below
  			var fadeOut;
  
  			this._fadeOutDeferred = new Deferred(lang.hitch(this, function(){
  				fadeOut.stop();
  				delete this._fadeOutDeferred;
  			}));
  
  			// fire onHide when the promise resolves.
  			this._fadeOutDeferred.then(lang.hitch(this, 'onHide'));
  
  			// If delay is 0, code below will delete this._fadeOutDeferred instantly, so grab promise while we can.
  			var promise = this._fadeOutDeferred.promise;
  
  			fadeOut = fx.fadeOut({
  				node: this.domNode,
  				duration: this.duration,
  				onEnd: lang.hitch(this, function(){
  					this.domNode.style.display = "none";
  					DialogLevelManager.hide(this);
  					this._fadeOutDeferred.resolve(true);
  					delete this._fadeOutDeferred;
  				})
  			}).play();
  
  			if(this._scrollConnected){
  				this._scrollConnected = false;
  			}
  			var h;
  			while(h = this._modalconnects.pop()){
  				h.remove();
  			}
  
  			if(this._relativePosition){
  				delete this._relativePosition;
  			}
  			this._set("open", false);
  
  			return promise;
  		},
  
  		resize: function(){
  			// summary:
  			//		Called when viewport scrolled or size changed.  Adjust Dialog as necessary to keep it visible.
  			// tags:
  			//		private
  			if(this.domNode.style.display != "none"){
  				this._size();
  				if(!has("touch")){
  					// If the user has scrolled the display then reposition the Dialog.  But don't do it for touch
  					// devices, because it will counteract when a keyboard pops up and then the browser auto-scrolls
  					// the focused node into view.
  					this._position();
  				}
  			}
  		},
  
  		destroy: function(){
  			if(this._fadeInDeferred){
  				this._fadeInDeferred.cancel();
  			}
  			if(this._fadeOutDeferred){
  				this._fadeOutDeferred.cancel();
  			}
  			if(this._moveable){
  				this._moveable.destroy();
  			}
  			var h;
  			while(h = this._modalconnects.pop()){
  				h.remove();
  			}
  
  			DialogLevelManager.hide(this);
  
  			this.inherited(arguments);
  		}
  	});
  
  	if(has("dojo-bidi")){
  		_DialogBase = declare("dijit._DialogBase", _DialogBase, {
  			_setTitleAttr: function(/*String*/ title){
  				this._set("title", title);
  				this.titleNode.innerHTML = title;
  				this.applyTextDir(this.titleNode);
  			},
  
  			_setTextDirAttr: function(textDir){
  				if(this._created && this.textDir != textDir){
  					this._set("textDir", textDir);
  					this.set("title", this.title);
  				}
  			}
  		});
  	}
  
  	var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {
  		// 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 data-dojo-type="dijit/Dialog" data-dojo-props="href: 'test.html'"></div>
  		// example:
  		// |	var foo = new Dialog({ title: "test dialog", content: "test content" });
  		// |	foo.placeAt(win.body());
  		// |	foo.startup();
  	});
  	Dialog._DialogBase = _DialogBase;	// for monkey patching and dojox/widget/DialogSimple
  
  	var DialogLevelManager = Dialog._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.
  
  		_beginZIndex: 950,
  
  		show: function(/*dijit/_WidgetBase*/ 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.
  
  			// Save current focus
  			ds[ds.length - 1].focus = focus.curNode;
  
  			// Set z-index a bit above previous dialog
  			var zIndex = ds[ds.length - 1].dialog ? ds[ds.length - 1].zIndex + 2 : Dialog._DialogLevelManager._beginZIndex;
  			domStyle.set(dialog.domNode, 'zIndex', zIndex);
  
  			// Display the underlay, or if already displayed then adjust for this new dialog
  			DialogUnderlay.show(underlayAttrs, zIndex - 1, lang.hitch(dialog, "focus"));
  
  			ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
  		},
  
  		hide: function(/*dijit/_WidgetBase*/ 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.
  
  			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.
  					DialogUnderlay.hide();
  				}else{
  					// Popping back to previous dialog, adjust underlay.
  					DialogUnderlay.show(pd.underlayAttrs, pd.zIndex - 1);
  				}
  
  				// 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(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
  						pd.dialog._getFocusItems(pd.dialog.domNode);
  						focus = pd.dialog._firstFocusItem;
  					}
  
  					if(focus){
  						// Refocus the button that spawned the Dialog.   This will fail in corner cases including
  						// page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
  						// before this code runs.  (#15058)
  						try{
  							focus.focus();
  						}catch(e){
  						}
  					}
  				}
  			}else{
  				// Removing a dialog out of order (#9944, #10705).
  				// Don't need to mess with underlay or z-index or anything.
  				var idx = array.indexOf(array.map(ds, function(elem){
  					return elem.dialog;
  				}), dialog);
  				if(idx != -1){
  					ds.splice(idx, 1);
  				}
  			}
  		},
  
  		isTop: function(/*dijit/_WidgetBase*/ dialog){
  			// summary:
  			//		Returns true if specified Dialog is the top in the task
  			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)
  	// }
  	var ds = Dialog._dialogStack = [
  		{dialog: null, focus: null, underlayAttrs: null}    // entry for stuff at z-index: 0
  	];
  
  	// If focus was accidentally removed from the dialog, such as if the user clicked a blank
  	// area of the screen, or clicked the browser's address bar and then tabbed into the page,
  	// then refocus.   Won't do anything if focus was removed because the Dialog was closed, or
  	// because a new Dialog popped up on top of the old one, or when focus moves to popups
  	focus.watch("curNode", function(attr, oldNode, node){
  		var topDialog = ds[ds.length - 1].dialog;
  		if(node && topDialog){	// if no dialogs, ds.length==1 but ds[ds.length-1].dialog is null
  			// If the node that was focused is inside the dialog or in a popup, even a context menu that isn't
  			// technically a descendant of the the dialog, don't do anything.
  			do{
  				if(node == topDialog.domNode || domClass.contains(node, "dijitPopup")){ return; }
  			}while(node = node.parentNode);
  
  			// Otherwise, return focus to the dialog.  Use a delay to avoid confusing dijit/focus code's
  			// own tracking of focus.
  			topDialog.focus();
  		}
  	});
  
  	// Back compat w/1.6, remove for 2.0
  	if(has("dijit-legacy-requires")){
  		ready(0, function(){
  			var requires = ["dijit/TooltipDialog"];
  			require(requires);	// use indirection so modules not rolled into a build
  		});
  	}
  
  	return Dialog;
  });
  