resources/lib/jquery-ui/ui/widgets/dialog.js   F
last analyzed

Complexity

Total Complexity 163
Complexity/F 2.59

Size

Lines of Code 922
Function Count 63

Duplication

Duplicated Lines 39
Ratio 4.23 %

Importance

Changes 0
Metric Value
cc 0
nc 1140850688
dl 39
loc 922
rs 2.1818
c 0
b 0
f 0
wmc 163
mnd 2
bc 136
fnc 63
bpm 2.1587
cpm 2.5873
noi 0

1 Function

Rating   Name   Duplication   Size   Complexity  
B dialog.js ➔ ?!? 39 897 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like resources/lib/jquery-ui/ui/widgets/dialog.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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