15 //>>css.structure: ../../themes/base/core.css |
15 //>>css.structure: ../../themes/base/core.css |
16 //>>css.structure: ../../themes/base/dialog.css |
16 //>>css.structure: ../../themes/base/dialog.css |
17 //>>css.theme: ../../themes/base/theme.css |
17 //>>css.theme: ../../themes/base/theme.css |
18 |
18 |
19 ( function( factory ) { |
19 ( function( factory ) { |
|
20 "use strict"; |
|
21 |
20 if ( typeof define === "function" && define.amd ) { |
22 if ( typeof define === "function" && define.amd ) { |
21 |
23 |
22 // AMD. Register as an anonymous module. |
24 // AMD. Register as an anonymous module. |
23 define( [ |
25 define( [ |
24 "jquery", |
26 "jquery", |
279 this._show( this.uiDialog, this.options.show, function() { |
282 this._show( this.uiDialog, this.options.show, function() { |
280 that._focusTabbable(); |
283 that._focusTabbable(); |
281 that._trigger( "focus" ); |
284 that._trigger( "focus" ); |
282 } ); |
285 } ); |
283 |
286 |
284 // Track the dialog immediately upon openening in case a focus event |
287 // Track the dialog immediately upon opening in case a focus event |
285 // somehow occurs outside of the dialog before an element inside the |
288 // somehow occurs outside of the dialog before an element inside the |
286 // dialog is focused (#10152) |
289 // dialog is focused (#10152) |
287 this._makeFocusTarget(); |
290 this._makeFocusTarget(); |
288 |
291 |
289 this._trigger( "open" ); |
292 this._trigger( "open" ); |
315 hasFocus = this.uiDialog; |
318 hasFocus = this.uiDialog; |
316 } |
319 } |
317 hasFocus.eq( 0 ).trigger( "focus" ); |
320 hasFocus.eq( 0 ).trigger( "focus" ); |
318 }, |
321 }, |
319 |
322 |
|
323 _restoreTabbableFocus: function() { |
|
324 var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ), |
|
325 isActive = this.uiDialog[ 0 ] === activeElement || |
|
326 $.contains( this.uiDialog[ 0 ], activeElement ); |
|
327 if ( !isActive ) { |
|
328 this._focusTabbable(); |
|
329 } |
|
330 }, |
|
331 |
320 _keepFocus: function( event ) { |
332 _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(); |
333 event.preventDefault(); |
330 checkFocus.call( this ); |
334 this._restoreTabbableFocus(); |
331 |
335 |
332 // support: IE |
336 // support: IE |
333 // IE <= 8 doesn't prevent moving focus even with event.preventDefault() |
337 // IE <= 8 doesn't prevent moving focus even with event.preventDefault() |
334 // so we check again later |
338 // so we check again later |
335 this._delay( checkFocus ); |
339 this._delay( this._restoreTabbableFocus ); |
336 }, |
340 }, |
337 |
341 |
338 _createWrapper: function() { |
342 _createWrapper: function() { |
339 this.uiDialog = $( "<div>" ) |
343 this.uiDialog = $( "<div>" ) |
340 .hide() |
344 .hide() |
348 |
352 |
349 this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" ); |
353 this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" ); |
350 this._on( this.uiDialog, { |
354 this._on( this.uiDialog, { |
351 keydown: function( event ) { |
355 keydown: function( event ) { |
352 if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && |
356 if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && |
353 event.keyCode === $.ui.keyCode.ESCAPE ) { |
357 event.keyCode === $.ui.keyCode.ESCAPE ) { |
354 event.preventDefault(); |
358 event.preventDefault(); |
355 this.close( event ); |
359 this.close( event ); |
356 return; |
360 return; |
357 } |
361 } |
358 |
362 |
359 // Prevent tabbing out of dialogs |
363 // Prevent tabbing out of dialogs |
360 if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) { |
364 if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) { |
361 return; |
365 return; |
362 } |
366 } |
363 var tabbables = this.uiDialog.find( ":tabbable" ), |
367 var tabbables = this.uiDialog.find( ":tabbable" ), |
364 first = tabbables.filter( ":first" ), |
368 first = tabbables.first(), |
365 last = tabbables.filter( ":last" ); |
369 last = tabbables.last(); |
366 |
370 |
367 if ( ( event.target === last[ 0 ] || event.target === this.uiDialog[ 0 ] ) && |
371 if ( ( event.target === last[ 0 ] || event.target === this.uiDialog[ 0 ] ) && |
368 !event.shiftKey ) { |
372 !event.shiftKey ) { |
369 this._delay( function() { |
373 this._delay( function() { |
370 first.trigger( "focus" ); |
374 first.trigger( "focus" ); |
371 } ); |
375 } ); |
372 event.preventDefault(); |
376 event.preventDefault(); |
373 } else if ( ( event.target === first[ 0 ] || |
377 } else if ( ( event.target === first[ 0 ] || |
374 event.target === this.uiDialog[ 0 ] ) && event.shiftKey ) { |
378 event.target === this.uiDialog[ 0 ] ) && event.shiftKey ) { |
375 this._delay( function() { |
379 this._delay( function() { |
376 last.trigger( "focus" ); |
380 last.trigger( "focus" ); |
377 } ); |
381 } ); |
378 event.preventDefault(); |
382 event.preventDefault(); |
379 } |
383 } |
471 |
475 |
472 // If we already have a button pane, remove it |
476 // If we already have a button pane, remove it |
473 this.uiDialogButtonPane.remove(); |
477 this.uiDialogButtonPane.remove(); |
474 this.uiButtonSet.empty(); |
478 this.uiButtonSet.empty(); |
475 |
479 |
476 if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) { |
480 if ( $.isEmptyObject( buttons ) || ( Array.isArray( buttons ) && !buttons.length ) ) { |
477 this._removeClass( this.uiDialog, "ui-dialog-buttons" ); |
481 this._removeClass( this.uiDialog, "ui-dialog-buttons" ); |
478 return; |
482 return; |
479 } |
483 } |
480 |
484 |
481 $.each( buttons, function( name, props ) { |
485 $.each( buttons, function( name, props ) { |
482 var click, buttonOptions; |
486 var click, buttonOptions; |
483 props = $.isFunction( props ) ? |
487 props = typeof props === "function" ? |
484 { click: props, text: name } : |
488 { click: props, text: name } : |
485 props; |
489 props; |
486 |
490 |
487 // Default to a non-submitting button |
491 // Default to a non-submitting button |
488 props = $.extend( { type: "button" }, props ); |
492 props = $.extend( { type: "button" }, props ); |
843 _createOverlay: function() { |
847 _createOverlay: function() { |
844 if ( !this.options.modal ) { |
848 if ( !this.options.modal ) { |
845 return; |
849 return; |
846 } |
850 } |
847 |
851 |
|
852 var jqMinor = $.fn.jquery.substring( 0, 4 ); |
|
853 |
848 // We use a delay in case the overlay is created from an |
854 // We use a delay in case the overlay is created from an |
849 // event that we're going to be cancelling (#2804) |
855 // event that we're going to be cancelling (#2804) |
850 var isOpening = true; |
856 var isOpening = true; |
851 this._delay( function() { |
857 this._delay( function() { |
852 isOpening = false; |
858 isOpening = false; |
853 } ); |
859 } ); |
854 |
860 |
855 if ( !this.document.data( "ui-dialog-overlays" ) ) { |
861 if ( !this.document.data( "ui-dialog-overlays" ) ) { |
856 |
862 |
857 // Prevent use of anchors and inputs |
863 // Prevent use of anchors and inputs |
858 // Using _on() for an event handler shared across many instances is |
864 // This doesn't use `_on()` because it is a shared event handler |
859 // safe because the dialogs stack and must be closed in reverse order |
865 // across all open modal dialogs. |
860 this._on( this.document, { |
866 this.document.on( "focusin.ui-dialog", function( event ) { |
861 focusin: function( event ) { |
867 if ( isOpening ) { |
862 if ( isOpening ) { |
868 return; |
863 return; |
869 } |
864 } |
870 |
865 |
871 var instance = this._trackingInstances()[ 0 ]; |
866 if ( !this._allowInteraction( event ) ) { |
872 if ( !instance._allowInteraction( event ) ) { |
867 event.preventDefault(); |
873 event.preventDefault(); |
868 this._trackingInstances()[ 0 ]._focusTabbable(); |
874 instance._focusTabbable(); |
|
875 |
|
876 // Support: jQuery >=3.4 <3.6 only |
|
877 // Focus re-triggering in jQuery 3.4/3.5 makes the original element |
|
878 // have its focus event propagated last, breaking the re-targeting. |
|
879 // Trigger focus in a delay in addition if needed to avoid the issue |
|
880 // See https://github.com/jquery/jquery/issues/4382 |
|
881 if ( jqMinor === "3.4." || jqMinor === "3.5." ) { |
|
882 instance._delay( instance._restoreTabbableFocus ); |
869 } |
883 } |
870 } |
884 } |
871 } ); |
885 }.bind( this ) ); |
872 } |
886 } |
873 |
887 |
874 this.overlay = $( "<div>" ) |
888 this.overlay = $( "<div>" ) |
875 .appendTo( this._appendTo() ); |
889 .appendTo( this._appendTo() ); |
876 |
890 |
889 |
903 |
890 if ( this.overlay ) { |
904 if ( this.overlay ) { |
891 var overlays = this.document.data( "ui-dialog-overlays" ) - 1; |
905 var overlays = this.document.data( "ui-dialog-overlays" ) - 1; |
892 |
906 |
893 if ( !overlays ) { |
907 if ( !overlays ) { |
894 this._off( this.document, "focusin" ); |
908 this.document.off( "focusin.ui-dialog" ); |
895 this.document.removeData( "ui-dialog-overlays" ); |
909 this.document.removeData( "ui-dialog-overlays" ); |
896 } else { |
910 } else { |
897 this.document.data( "ui-dialog-overlays", overlays ); |
911 this.document.data( "ui-dialog-overlays", overlays ); |
898 } |
912 } |
899 |
913 |