You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

955 rivejä
23 KiB

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