wp/wp-includes/js/jquery/ui/dialog.js
changeset 18 be944660c56a
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
       
     1 /*!
       
     2  * jQuery UI Dialog 1.12.1
       
     3  * http://jqueryui.com
       
     4  *
       
     5  * Copyright jQuery Foundation and other contributors
       
     6  * Released under the MIT license.
       
     7  * http://jquery.org/license
       
     8  */
       
     9 
       
    10 //>>label: Dialog
       
    11 //>>group: Widgets
       
    12 //>>description: Displays customizable dialog windows.
       
    13 //>>docs: http://api.jqueryui.com/dialog/
       
    14 //>>demos: http://jqueryui.com/dialog/
       
    15 //>>css.structure: ../../themes/base/core.css
       
    16 //>>css.structure: ../../themes/base/dialog.css
       
    17 //>>css.theme: ../../themes/base/theme.css
       
    18 
       
    19 ( function( factory ) {
       
    20 	if ( typeof define === "function" && define.amd ) {
       
    21 
       
    22 		// AMD. Register as an anonymous module.
       
    23 		define( [
       
    24 			"jquery",
       
    25 			"./button",
       
    26 			"./draggable",
       
    27 			"./mouse",
       
    28 			"./resizable",
       
    29 			"./core"
       
    30 		], factory );
       
    31 	} else {
       
    32 
       
    33 		// Browser globals
       
    34 		factory( jQuery );
       
    35 	}
       
    36 }( function( $ ) {
       
    37 
       
    38 $.widget( "ui.dialog", {
       
    39 	version: "1.12.1",
       
    40 	options: {
       
    41 		appendTo: "body",
       
    42 		autoOpen: true,
       
    43 		buttons: [],
       
    44 		classes: {
       
    45 			"ui-dialog": "ui-corner-all",
       
    46 			"ui-dialog-titlebar": "ui-corner-all"
       
    47 		},
       
    48 		closeOnEscape: true,
       
    49 		closeText: "Close",
       
    50 		draggable: true,
       
    51 		hide: null,
       
    52 		height: "auto",
       
    53 		maxHeight: null,
       
    54 		maxWidth: null,
       
    55 		minHeight: 150,
       
    56 		minWidth: 150,
       
    57 		modal: false,
       
    58 		position: {
       
    59 			my: "center",
       
    60 			at: "center",
       
    61 			of: window,
       
    62 			collision: "fit",
       
    63 
       
    64 			// Ensure the titlebar is always visible
       
    65 			using: function( pos ) {
       
    66 				var topOffset = $( this ).css( pos ).offset().top;
       
    67 				if ( topOffset < 0 ) {
       
    68 					$( this ).css( "top", pos.top - topOffset );
       
    69 				}
       
    70 			}
       
    71 		},
       
    72 		resizable: true,
       
    73 		show: null,
       
    74 		title: null,
       
    75 		width: 300,
       
    76 
       
    77 		// Callbacks
       
    78 		beforeClose: null,
       
    79 		close: null,
       
    80 		drag: null,
       
    81 		dragStart: null,
       
    82 		dragStop: null,
       
    83 		focus: null,
       
    84 		open: null,
       
    85 		resize: null,
       
    86 		resizeStart: null,
       
    87 		resizeStop: null
       
    88 	},
       
    89 
       
    90 	sizeRelatedOptions: {
       
    91 		buttons: true,
       
    92 		height: true,
       
    93 		maxHeight: true,
       
    94 		maxWidth: true,
       
    95 		minHeight: true,
       
    96 		minWidth: true,
       
    97 		width: true
       
    98 	},
       
    99 
       
   100 	resizableRelatedOptions: {
       
   101 		maxHeight: true,
       
   102 		maxWidth: true,
       
   103 		minHeight: true,
       
   104 		minWidth: true
       
   105 	},
       
   106 
       
   107 	_create: function() {
       
   108 		this.originalCss = {
       
   109 			display: this.element[ 0 ].style.display,
       
   110 			width: this.element[ 0 ].style.width,
       
   111 			minHeight: this.element[ 0 ].style.minHeight,
       
   112 			maxHeight: this.element[ 0 ].style.maxHeight,
       
   113 			height: this.element[ 0 ].style.height
       
   114 		};
       
   115 		this.originalPosition = {
       
   116 			parent: this.element.parent(),
       
   117 			index: this.element.parent().children().index( this.element )
       
   118 		};
       
   119 		this.originalTitle = this.element.attr( "title" );
       
   120 		if ( this.options.title == null && this.originalTitle != null ) {
       
   121 			this.options.title = this.originalTitle;
       
   122 		}
       
   123 
       
   124 		// Dialogs can't be disabled
       
   125 		if ( this.options.disabled ) {
       
   126 			this.options.disabled = false;
       
   127 		}
       
   128 
       
   129 		this._createWrapper();
       
   130 
       
   131 		this.element
       
   132 			.show()
       
   133 			.removeAttr( "title" )
       
   134 			.appendTo( this.uiDialog );
       
   135 
       
   136 		this._addClass( "ui-dialog-content", "ui-widget-content" );
       
   137 
       
   138 		this._createTitlebar();
       
   139 		this._createButtonPane();
       
   140 
       
   141 		if ( this.options.draggable && $.fn.draggable ) {
       
   142 			this._makeDraggable();
       
   143 		}
       
   144 		if ( this.options.resizable && $.fn.resizable ) {
       
   145 			this._makeResizable();
       
   146 		}
       
   147 
       
   148 		this._isOpen = false;
       
   149 
       
   150 		this._trackFocus();
       
   151 	},
       
   152 
       
   153 	_init: function() {
       
   154 		if ( this.options.autoOpen ) {
       
   155 			this.open();
       
   156 		}
       
   157 	},
       
   158 
       
   159 	_appendTo: function() {
       
   160 		var element = this.options.appendTo;
       
   161 		if ( element && ( element.jquery || element.nodeType ) ) {
       
   162 			return $( element );
       
   163 		}
       
   164 		return this.document.find( element || "body" ).eq( 0 );
       
   165 	},
       
   166 
       
   167 	_destroy: function() {
       
   168 		var next,
       
   169 			originalPosition = this.originalPosition;
       
   170 
       
   171 		this._untrackInstance();
       
   172 		this._destroyOverlay();
       
   173 
       
   174 		this.element
       
   175 			.removeUniqueId()
       
   176 			.css( this.originalCss )
       
   177 
       
   178 			// Without detaching first, the following becomes really slow
       
   179 			.detach();
       
   180 
       
   181 		this.uiDialog.remove();
       
   182 
       
   183 		if ( this.originalTitle ) {
       
   184 			this.element.attr( "title", this.originalTitle );
       
   185 		}
       
   186 
       
   187 		next = originalPosition.parent.children().eq( originalPosition.index );
       
   188 
       
   189 		// Don't try to place the dialog next to itself (#8613)
       
   190 		if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
       
   191 			next.before( this.element );
       
   192 		} else {
       
   193 			originalPosition.parent.append( this.element );
       
   194 		}
       
   195 	},
       
   196 
       
   197 	widget: function() {
       
   198 		return this.uiDialog;
       
   199 	},
       
   200 
       
   201 	disable: $.noop,
       
   202 	enable: $.noop,
       
   203 
       
   204 	close: function( event ) {
       
   205 		var that = this;
       
   206 
       
   207 		if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
       
   208 			return;
       
   209 		}
       
   210 
       
   211 		this._isOpen = false;
       
   212 		this._focusedElement = null;
       
   213 		this._destroyOverlay();
       
   214 		this._untrackInstance();
       
   215 
       
   216 		if ( !this.opener.filter( ":focusable" ).trigger( "focus" ).length ) {
       
   217 
       
   218 			// Hiding a focused element doesn't trigger blur in WebKit
       
   219 			// so in case we have nothing to focus on, explicitly blur the active element
       
   220 			// https://bugs.webkit.org/show_bug.cgi?id=47182
       
   221 			$.ui.safeBlur( $.ui.safeActiveElement( this.document[ 0 ] ) );
       
   222 		}
       
   223 
       
   224 		this._hide( this.uiDialog, this.options.hide, function() {
       
   225 			that._trigger( "close", event );
       
   226 		} );
       
   227 	},
       
   228 
       
   229 	isOpen: function() {
       
   230 		return this._isOpen;
       
   231 	},
       
   232 
       
   233 	moveToTop: function() {
       
   234 		this._moveToTop();
       
   235 	},
       
   236 
       
   237 	_moveToTop: function( event, silent ) {
       
   238 		var moved = false,
       
   239 			zIndices = this.uiDialog.siblings( ".ui-front:visible" ).map( function() {
       
   240 				return +$( this ).css( "z-index" );
       
   241 			} ).get(),
       
   242 			zIndexMax = Math.max.apply( null, zIndices );
       
   243 
       
   244 		if ( zIndexMax >= +this.uiDialog.css( "z-index" ) ) {
       
   245 			this.uiDialog.css( "z-index", zIndexMax + 1 );
       
   246 			moved = true;
       
   247 		}
       
   248 
       
   249 		if ( moved && !silent ) {
       
   250 			this._trigger( "focus", event );
       
   251 		}
       
   252 		return moved;
       
   253 	},
       
   254 
       
   255 	open: function() {
       
   256 		var that = this;
       
   257 		if ( this._isOpen ) {
       
   258 			if ( this._moveToTop() ) {
       
   259 				this._focusTabbable();
       
   260 			}
       
   261 			return;
       
   262 		}
       
   263 
       
   264 		this._isOpen = true;
       
   265 		this.opener = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
       
   266 
       
   267 		this._size();
       
   268 		this._position();
       
   269 		this._createOverlay();
       
   270 		this._moveToTop( null, true );
       
   271 
       
   272 		// Ensure the overlay is moved to the top with the dialog, but only when
       
   273 		// opening. The overlay shouldn't move after the dialog is open so that
       
   274 		// modeless dialogs opened after the modal dialog stack properly.
       
   275 		if ( this.overlay ) {
       
   276 			this.overlay.css( "z-index", this.uiDialog.css( "z-index" ) - 1 );
       
   277 		}
       
   278 
       
   279 		this._show( this.uiDialog, this.options.show, function() {
       
   280 			that._focusTabbable();
       
   281 			that._trigger( "focus" );
       
   282 		} );
       
   283 
       
   284 		// Track the dialog immediately upon openening in case a focus event
       
   285 		// somehow occurs outside of the dialog before an element inside the
       
   286 		// dialog is focused (#10152)
       
   287 		this._makeFocusTarget();
       
   288 
       
   289 		this._trigger( "open" );
       
   290 	},
       
   291 
       
   292 	_focusTabbable: function() {
       
   293 
       
   294 		// Set focus to the first match:
       
   295 		// 1. An element that was focused previously
       
   296 		// 2. First element inside the dialog matching [autofocus]
       
   297 		// 3. Tabbable element inside the content element
       
   298 		// 4. Tabbable element inside the buttonpane
       
   299 		// 5. The close button
       
   300 		// 6. The dialog itself
       
   301 		var hasFocus = this._focusedElement;
       
   302 		if ( !hasFocus ) {
       
   303 			hasFocus = this.element.find( "[autofocus]" );
       
   304 		}
       
   305 		if ( !hasFocus.length ) {
       
   306 			hasFocus = this.element.find( ":tabbable" );
       
   307 		}
       
   308 		if ( !hasFocus.length ) {
       
   309 			hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
       
   310 		}
       
   311 		if ( !hasFocus.length ) {
       
   312 			hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" );
       
   313 		}
       
   314 		if ( !hasFocus.length ) {
       
   315 			hasFocus = this.uiDialog;
       
   316 		}
       
   317 		hasFocus.eq( 0 ).trigger( "focus" );
       
   318 	},
       
   319 
       
   320 	_keepFocus: function( event ) {
       
   321 		function checkFocus() {
       
   322 			var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
       
   323 				isActive = this.uiDialog[ 0 ] === activeElement ||
       
   324 					$.contains( this.uiDialog[ 0 ], activeElement );
       
   325 			if ( !isActive ) {
       
   326 				this._focusTabbable();
       
   327 			}
       
   328 		}
       
   329 		event.preventDefault();
       
   330 		checkFocus.call( this );
       
   331 
       
   332 		// support: IE
       
   333 		// IE <= 8 doesn't prevent moving focus even with event.preventDefault()
       
   334 		// so we check again later
       
   335 		this._delay( checkFocus );
       
   336 	},
       
   337 
       
   338 	_createWrapper: function() {
       
   339 		this.uiDialog = $( "<div>" )
       
   340 			.hide()
       
   341 			.attr( {
       
   342 
       
   343 				// Setting tabIndex makes the div focusable
       
   344 				tabIndex: -1,
       
   345 				role: "dialog"
       
   346 			} )
       
   347 			.appendTo( this._appendTo() );
       
   348 
       
   349 		this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" );
       
   350 		this._on( this.uiDialog, {
       
   351 			keydown: function( event ) {
       
   352 				if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
       
   353 						event.keyCode === $.ui.keyCode.ESCAPE ) {
       
   354 					event.preventDefault();
       
   355 					this.close( event );
       
   356 					return;
       
   357 				}
       
   358 
       
   359 				// Prevent tabbing out of dialogs
       
   360 				if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) {
       
   361 					return;
       
   362 				}
       
   363 				var tabbables = this.uiDialog.find( ":tabbable" ),
       
   364 					first = tabbables.filter( ":first" ),
       
   365 					last = tabbables.filter( ":last" );
       
   366 
       
   367 				if ( ( event.target === last[ 0 ] || event.target === this.uiDialog[ 0 ] ) &&
       
   368 						!event.shiftKey ) {
       
   369 					this._delay( function() {
       
   370 						first.trigger( "focus" );
       
   371 					} );
       
   372 					event.preventDefault();
       
   373 				} else if ( ( event.target === first[ 0 ] ||
       
   374 						event.target === this.uiDialog[ 0 ] ) && event.shiftKey ) {
       
   375 					this._delay( function() {
       
   376 						last.trigger( "focus" );
       
   377 					} );
       
   378 					event.preventDefault();
       
   379 				}
       
   380 			},
       
   381 			mousedown: function( event ) {
       
   382 				if ( this._moveToTop( event ) ) {
       
   383 					this._focusTabbable();
       
   384 				}
       
   385 			}
       
   386 		} );
       
   387 
       
   388 		// We assume that any existing aria-describedby attribute means
       
   389 		// that the dialog content is marked up properly
       
   390 		// otherwise we brute force the content as the description
       
   391 		if ( !this.element.find( "[aria-describedby]" ).length ) {
       
   392 			this.uiDialog.attr( {
       
   393 				"aria-describedby": this.element.uniqueId().attr( "id" )
       
   394 			} );
       
   395 		}
       
   396 	},
       
   397 
       
   398 	_createTitlebar: function() {
       
   399 		var uiDialogTitle;
       
   400 
       
   401 		this.uiDialogTitlebar = $( "<div>" );
       
   402 		this._addClass( this.uiDialogTitlebar,
       
   403 			"ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" );
       
   404 		this._on( this.uiDialogTitlebar, {
       
   405 			mousedown: function( event ) {
       
   406 
       
   407 				// Don't prevent click on close button (#8838)
       
   408 				// Focusing a dialog that is partially scrolled out of view
       
   409 				// causes the browser to scroll it into view, preventing the click event
       
   410 				if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) {
       
   411 
       
   412 					// Dialog isn't getting focus when dragging (#8063)
       
   413 					this.uiDialog.trigger( "focus" );
       
   414 				}
       
   415 			}
       
   416 		} );
       
   417 
       
   418 		// Support: IE
       
   419 		// Use type="button" to prevent enter keypresses in textboxes from closing the
       
   420 		// dialog in IE (#9312)
       
   421 		this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
       
   422 			.button( {
       
   423 				label: $( "<a>" ).text( this.options.closeText ).html(),
       
   424 				icon: "ui-icon-closethick",
       
   425 				showLabel: false
       
   426 			} )
       
   427 			.appendTo( this.uiDialogTitlebar );
       
   428 
       
   429 		this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" );
       
   430 		this._on( this.uiDialogTitlebarClose, {
       
   431 			click: function( event ) {
       
   432 				event.preventDefault();
       
   433 				this.close( event );
       
   434 			}
       
   435 		} );
       
   436 
       
   437 		uiDialogTitle = $( "<span>" ).uniqueId().prependTo( this.uiDialogTitlebar );
       
   438 		this._addClass( uiDialogTitle, "ui-dialog-title" );
       
   439 		this._title( uiDialogTitle );
       
   440 
       
   441 		this.uiDialogTitlebar.prependTo( this.uiDialog );
       
   442 
       
   443 		this.uiDialog.attr( {
       
   444 			"aria-labelledby": uiDialogTitle.attr( "id" )
       
   445 		} );
       
   446 	},
       
   447 
       
   448 	_title: function( title ) {
       
   449 		if ( this.options.title ) {
       
   450 			title.text( this.options.title );
       
   451 		} else {
       
   452 			title.html( "&#160;" );
       
   453 		}
       
   454 	},
       
   455 
       
   456 	_createButtonPane: function() {
       
   457 		this.uiDialogButtonPane = $( "<div>" );
       
   458 		this._addClass( this.uiDialogButtonPane, "ui-dialog-buttonpane",
       
   459 			"ui-widget-content ui-helper-clearfix" );
       
   460 
       
   461 		this.uiButtonSet = $( "<div>" )
       
   462 			.appendTo( this.uiDialogButtonPane );
       
   463 		this._addClass( this.uiButtonSet, "ui-dialog-buttonset" );
       
   464 
       
   465 		this._createButtons();
       
   466 	},
       
   467 
       
   468 	_createButtons: function() {
       
   469 		var that = this,
       
   470 			buttons = this.options.buttons;
       
   471 
       
   472 		// If we already have a button pane, remove it
       
   473 		this.uiDialogButtonPane.remove();
       
   474 		this.uiButtonSet.empty();
       
   475 
       
   476 		if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) {
       
   477 			this._removeClass( this.uiDialog, "ui-dialog-buttons" );
       
   478 			return;
       
   479 		}
       
   480 
       
   481 		$.each( buttons, function( name, props ) {
       
   482 			var click, buttonOptions;
       
   483 			props = $.isFunction( props ) ?
       
   484 				{ click: props, text: name } :
       
   485 				props;
       
   486 
       
   487 			// Default to a non-submitting button
       
   488 			props = $.extend( { type: "button" }, props );
       
   489 
       
   490 			// Change the context for the click callback to be the main element
       
   491 			click = props.click;
       
   492 			buttonOptions = {
       
   493 				icon: props.icon,
       
   494 				iconPosition: props.iconPosition,
       
   495 				showLabel: props.showLabel,
       
   496 
       
   497 				// Deprecated options
       
   498 				icons: props.icons,
       
   499 				text: props.text
       
   500 			};
       
   501 
       
   502 			delete props.click;
       
   503 			delete props.icon;
       
   504 			delete props.iconPosition;
       
   505 			delete props.showLabel;
       
   506 
       
   507 			// Deprecated options
       
   508 			delete props.icons;
       
   509 			if ( typeof props.text === "boolean" ) {
       
   510 				delete props.text;
       
   511 			}
       
   512 
       
   513 			$( "<button></button>", props )
       
   514 				.button( buttonOptions )
       
   515 				.appendTo( that.uiButtonSet )
       
   516 				.on( "click", function() {
       
   517 					click.apply( that.element[ 0 ], arguments );
       
   518 				} );
       
   519 		} );
       
   520 		this._addClass( this.uiDialog, "ui-dialog-buttons" );
       
   521 		this.uiDialogButtonPane.appendTo( this.uiDialog );
       
   522 	},
       
   523 
       
   524 	_makeDraggable: function() {
       
   525 		var that = this,
       
   526 			options = this.options;
       
   527 
       
   528 		function filteredUi( ui ) {
       
   529 			return {
       
   530 				position: ui.position,
       
   531 				offset: ui.offset
       
   532 			};
       
   533 		}
       
   534 
       
   535 		this.uiDialog.draggable( {
       
   536 			cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
       
   537 			handle: ".ui-dialog-titlebar",
       
   538 			containment: "document",
       
   539 			start: function( event, ui ) {
       
   540 				that._addClass( $( this ), "ui-dialog-dragging" );
       
   541 				that._blockFrames();
       
   542 				that._trigger( "dragStart", event, filteredUi( ui ) );
       
   543 			},
       
   544 			drag: function( event, ui ) {
       
   545 				that._trigger( "drag", event, filteredUi( ui ) );
       
   546 			},
       
   547 			stop: function( event, ui ) {
       
   548 				var left = ui.offset.left - that.document.scrollLeft(),
       
   549 					top = ui.offset.top - that.document.scrollTop();
       
   550 
       
   551 				options.position = {
       
   552 					my: "left top",
       
   553 					at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
       
   554 						"top" + ( top >= 0 ? "+" : "" ) + top,
       
   555 					of: that.window
       
   556 				};
       
   557 				that._removeClass( $( this ), "ui-dialog-dragging" );
       
   558 				that._unblockFrames();
       
   559 				that._trigger( "dragStop", event, filteredUi( ui ) );
       
   560 			}
       
   561 		} );
       
   562 	},
       
   563 
       
   564 	_makeResizable: function() {
       
   565 		var that = this,
       
   566 			options = this.options,
       
   567 			handles = options.resizable,
       
   568 
       
   569 			// .ui-resizable has position: relative defined in the stylesheet
       
   570 			// but dialogs have to use absolute or fixed positioning
       
   571 			position = this.uiDialog.css( "position" ),
       
   572 			resizeHandles = typeof handles === "string" ?
       
   573 				handles :
       
   574 				"n,e,s,w,se,sw,ne,nw";
       
   575 
       
   576 		function filteredUi( ui ) {
       
   577 			return {
       
   578 				originalPosition: ui.originalPosition,
       
   579 				originalSize: ui.originalSize,
       
   580 				position: ui.position,
       
   581 				size: ui.size
       
   582 			};
       
   583 		}
       
   584 
       
   585 		this.uiDialog.resizable( {
       
   586 			cancel: ".ui-dialog-content",
       
   587 			containment: "document",
       
   588 			alsoResize: this.element,
       
   589 			maxWidth: options.maxWidth,
       
   590 			maxHeight: options.maxHeight,
       
   591 			minWidth: options.minWidth,
       
   592 			minHeight: this._minHeight(),
       
   593 			handles: resizeHandles,
       
   594 			start: function( event, ui ) {
       
   595 				that._addClass( $( this ), "ui-dialog-resizing" );
       
   596 				that._blockFrames();
       
   597 				that._trigger( "resizeStart", event, filteredUi( ui ) );
       
   598 			},
       
   599 			resize: function( event, ui ) {
       
   600 				that._trigger( "resize", event, filteredUi( ui ) );
       
   601 			},
       
   602 			stop: function( event, ui ) {
       
   603 				var offset = that.uiDialog.offset(),
       
   604 					left = offset.left - that.document.scrollLeft(),
       
   605 					top = offset.top - that.document.scrollTop();
       
   606 
       
   607 				options.height = that.uiDialog.height();
       
   608 				options.width = that.uiDialog.width();
       
   609 				options.position = {
       
   610 					my: "left top",
       
   611 					at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
       
   612 						"top" + ( top >= 0 ? "+" : "" ) + top,
       
   613 					of: that.window
       
   614 				};
       
   615 				that._removeClass( $( this ), "ui-dialog-resizing" );
       
   616 				that._unblockFrames();
       
   617 				that._trigger( "resizeStop", event, filteredUi( ui ) );
       
   618 			}
       
   619 		} )
       
   620 			.css( "position", position );
       
   621 	},
       
   622 
       
   623 	_trackFocus: function() {
       
   624 		this._on( this.widget(), {
       
   625 			focusin: function( event ) {
       
   626 				this._makeFocusTarget();
       
   627 				this._focusedElement = $( event.target );
       
   628 			}
       
   629 		} );
       
   630 	},
       
   631 
       
   632 	_makeFocusTarget: function() {
       
   633 		this._untrackInstance();
       
   634 		this._trackingInstances().unshift( this );
       
   635 	},
       
   636 
       
   637 	_untrackInstance: function() {
       
   638 		var instances = this._trackingInstances(),
       
   639 			exists = $.inArray( this, instances );
       
   640 		if ( exists !== -1 ) {
       
   641 			instances.splice( exists, 1 );
       
   642 		}
       
   643 	},
       
   644 
       
   645 	_trackingInstances: function() {
       
   646 		var instances = this.document.data( "ui-dialog-instances" );
       
   647 		if ( !instances ) {
       
   648 			instances = [];
       
   649 			this.document.data( "ui-dialog-instances", instances );
       
   650 		}
       
   651 		return instances;
       
   652 	},
       
   653 
       
   654 	_minHeight: function() {
       
   655 		var options = this.options;
       
   656 
       
   657 		return options.height === "auto" ?
       
   658 			options.minHeight :
       
   659 			Math.min( options.minHeight, options.height );
       
   660 	},
       
   661 
       
   662 	_position: function() {
       
   663 
       
   664 		// Need to show the dialog to get the actual offset in the position plugin
       
   665 		var isVisible = this.uiDialog.is( ":visible" );
       
   666 		if ( !isVisible ) {
       
   667 			this.uiDialog.show();
       
   668 		}
       
   669 		this.uiDialog.position( this.options.position );
       
   670 		if ( !isVisible ) {
       
   671 			this.uiDialog.hide();
       
   672 		}
       
   673 	},
       
   674 
       
   675 	_setOptions: function( options ) {
       
   676 		var that = this,
       
   677 			resize = false,
       
   678 			resizableOptions = {};
       
   679 
       
   680 		$.each( options, function( key, value ) {
       
   681 			that._setOption( key, value );
       
   682 
       
   683 			if ( key in that.sizeRelatedOptions ) {
       
   684 				resize = true;
       
   685 			}
       
   686 			if ( key in that.resizableRelatedOptions ) {
       
   687 				resizableOptions[ key ] = value;
       
   688 			}
       
   689 		} );
       
   690 
       
   691 		if ( resize ) {
       
   692 			this._size();
       
   693 			this._position();
       
   694 		}
       
   695 		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
       
   696 			this.uiDialog.resizable( "option", resizableOptions );
       
   697 		}
       
   698 	},
       
   699 
       
   700 	_setOption: function( key, value ) {
       
   701 		var isDraggable, isResizable,
       
   702 			uiDialog = this.uiDialog;
       
   703 
       
   704 		if ( key === "disabled" ) {
       
   705 			return;
       
   706 		}
       
   707 
       
   708 		this._super( key, value );
       
   709 
       
   710 		if ( key === "appendTo" ) {
       
   711 			this.uiDialog.appendTo( this._appendTo() );
       
   712 		}
       
   713 
       
   714 		if ( key === "buttons" ) {
       
   715 			this._createButtons();
       
   716 		}
       
   717 
       
   718 		if ( key === "closeText" ) {
       
   719 			this.uiDialogTitlebarClose.button( {
       
   720 
       
   721 				// Ensure that we always pass a string
       
   722 				label: $( "<a>" ).text( "" + this.options.closeText ).html()
       
   723 			} );
       
   724 		}
       
   725 
       
   726 		if ( key === "draggable" ) {
       
   727 			isDraggable = uiDialog.is( ":data(ui-draggable)" );
       
   728 			if ( isDraggable && !value ) {
       
   729 				uiDialog.draggable( "destroy" );
       
   730 			}
       
   731 
       
   732 			if ( !isDraggable && value ) {
       
   733 				this._makeDraggable();
       
   734 			}
       
   735 		}
       
   736 
       
   737 		if ( key === "position" ) {
       
   738 			this._position();
       
   739 		}
       
   740 
       
   741 		if ( key === "resizable" ) {
       
   742 
       
   743 			// currently resizable, becoming non-resizable
       
   744 			isResizable = uiDialog.is( ":data(ui-resizable)" );
       
   745 			if ( isResizable && !value ) {
       
   746 				uiDialog.resizable( "destroy" );
       
   747 			}
       
   748 
       
   749 			// Currently resizable, changing handles
       
   750 			if ( isResizable && typeof value === "string" ) {
       
   751 				uiDialog.resizable( "option", "handles", value );
       
   752 			}
       
   753 
       
   754 			// Currently non-resizable, becoming resizable
       
   755 			if ( !isResizable && value !== false ) {
       
   756 				this._makeResizable();
       
   757 			}
       
   758 		}
       
   759 
       
   760 		if ( key === "title" ) {
       
   761 			this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) );
       
   762 		}
       
   763 	},
       
   764 
       
   765 	_size: function() {
       
   766 
       
   767 		// If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
       
   768 		// divs will both have width and height set, so we need to reset them
       
   769 		var nonContentHeight, minContentHeight, maxContentHeight,
       
   770 			options = this.options;
       
   771 
       
   772 		// Reset content sizing
       
   773 		this.element.show().css( {
       
   774 			width: "auto",
       
   775 			minHeight: 0,
       
   776 			maxHeight: "none",
       
   777 			height: 0
       
   778 		} );
       
   779 
       
   780 		if ( options.minWidth > options.width ) {
       
   781 			options.width = options.minWidth;
       
   782 		}
       
   783 
       
   784 		// Reset wrapper sizing
       
   785 		// determine the height of all the non-content elements
       
   786 		nonContentHeight = this.uiDialog.css( {
       
   787 			height: "auto",
       
   788 			width: options.width
       
   789 		} )
       
   790 			.outerHeight();
       
   791 		minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
       
   792 		maxContentHeight = typeof options.maxHeight === "number" ?
       
   793 			Math.max( 0, options.maxHeight - nonContentHeight ) :
       
   794 			"none";
       
   795 
       
   796 		if ( options.height === "auto" ) {
       
   797 			this.element.css( {
       
   798 				minHeight: minContentHeight,
       
   799 				maxHeight: maxContentHeight,
       
   800 				height: "auto"
       
   801 			} );
       
   802 		} else {
       
   803 			this.element.height( Math.max( 0, options.height - nonContentHeight ) );
       
   804 		}
       
   805 
       
   806 		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
       
   807 			this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
       
   808 		}
       
   809 	},
       
   810 
       
   811 	_blockFrames: function() {
       
   812 		this.iframeBlocks = this.document.find( "iframe" ).map( function() {
       
   813 			var iframe = $( this );
       
   814 
       
   815 			return $( "<div>" )
       
   816 				.css( {
       
   817 					position: "absolute",
       
   818 					width: iframe.outerWidth(),
       
   819 					height: iframe.outerHeight()
       
   820 				} )
       
   821 				.appendTo( iframe.parent() )
       
   822 				.offset( iframe.offset() )[ 0 ];
       
   823 		} );
       
   824 	},
       
   825 
       
   826 	_unblockFrames: function() {
       
   827 		if ( this.iframeBlocks ) {
       
   828 			this.iframeBlocks.remove();
       
   829 			delete this.iframeBlocks;
       
   830 		}
       
   831 	},
       
   832 
       
   833 	_allowInteraction: function( event ) {
       
   834 		if ( $( event.target ).closest( ".ui-dialog" ).length ) {
       
   835 			return true;
       
   836 		}
       
   837 
       
   838 		// TODO: Remove hack when datepicker implements
       
   839 		// the .ui-front logic (#8989)
       
   840 		return !!$( event.target ).closest( ".ui-datepicker" ).length;
       
   841 	},
       
   842 
       
   843 	_createOverlay: function() {
       
   844 		if ( !this.options.modal ) {
       
   845 			return;
       
   846 		}
       
   847 
       
   848 		// We use a delay in case the overlay is created from an
       
   849 		// event that we're going to be cancelling (#2804)
       
   850 		var isOpening = true;
       
   851 		this._delay( function() {
       
   852 			isOpening = false;
       
   853 		} );
       
   854 
       
   855 		if ( !this.document.data( "ui-dialog-overlays" ) ) {
       
   856 
       
   857 			// Prevent use of anchors and inputs
       
   858 			// Using _on() for an event handler shared across many instances is
       
   859 			// safe because the dialogs stack and must be closed in reverse order
       
   860 			this._on( this.document, {
       
   861 				focusin: function( event ) {
       
   862 					if ( isOpening ) {
       
   863 						return;
       
   864 					}
       
   865 
       
   866 					if ( !this._allowInteraction( event ) ) {
       
   867 						event.preventDefault();
       
   868 						this._trackingInstances()[ 0 ]._focusTabbable();
       
   869 					}
       
   870 				}
       
   871 			} );
       
   872 		}
       
   873 
       
   874 		this.overlay = $( "<div>" )
       
   875 			.appendTo( this._appendTo() );
       
   876 
       
   877 		this._addClass( this.overlay, null, "ui-widget-overlay ui-front" );
       
   878 		this._on( this.overlay, {
       
   879 			mousedown: "_keepFocus"
       
   880 		} );
       
   881 		this.document.data( "ui-dialog-overlays",
       
   882 			( this.document.data( "ui-dialog-overlays" ) || 0 ) + 1 );
       
   883 	},
       
   884 
       
   885 	_destroyOverlay: function() {
       
   886 		if ( !this.options.modal ) {
       
   887 			return;
       
   888 		}
       
   889 
       
   890 		if ( this.overlay ) {
       
   891 			var overlays = this.document.data( "ui-dialog-overlays" ) - 1;
       
   892 
       
   893 			if ( !overlays ) {
       
   894 				this._off( this.document, "focusin" );
       
   895 				this.document.removeData( "ui-dialog-overlays" );
       
   896 			} else {
       
   897 				this.document.data( "ui-dialog-overlays", overlays );
       
   898 			}
       
   899 
       
   900 			this.overlay.remove();
       
   901 			this.overlay = null;
       
   902 		}
       
   903 	}
       
   904 } );
       
   905 
       
   906 // DEPRECATED
       
   907 // TODO: switch return back to widget declaration at top of file when this is removed
       
   908 if ( $.uiBackCompat !== false ) {
       
   909 
       
   910 	// Backcompat for dialogClass option
       
   911 	$.widget( "ui.dialog", $.ui.dialog, {
       
   912 		options: {
       
   913 			dialogClass: ""
       
   914 		},
       
   915 		_createWrapper: function() {
       
   916 			this._super();
       
   917 			this.uiDialog.addClass( this.options.dialogClass );
       
   918 		},
       
   919 		_setOption: function( key, value ) {
       
   920 			if ( key === "dialogClass" ) {
       
   921 				this.uiDialog
       
   922 					.removeClass( this.options.dialogClass )
       
   923 					.addClass( value );
       
   924 			}
       
   925 			this._superApply( arguments );
       
   926 		}
       
   927 	} );
       
   928 }
       
   929 
       
   930 return $.ui.dialog;
       
   931 
       
   932 } ) );