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.

946 lines
23 KiB

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