Fixes #3534 - Dialog: Modal dialog disables all input elements on page.
Fixes #4671 - Dialog: Modal Dialog disables vertical scroll bar in Chrome & Safari.
Fixes #4995 - Dialog: Modal Dialog's overlay dissapears in IE when content is tall.
Fixes #5388 - Dialog: Don't change z-index when already at the top.
Fixes #5466 - Dialog: "modal" Dialog Incorrectly Cancels Input Events.
Fixes #5762 - Dialog: Get rid of z-index workaround, document it instead.
Fixes #6267 - Dialog: checkboxes that inherit a z-index < jqueryui.dialog z-index don't work.
Fixes #7051 - Dialog: modal prevents tab key from moving focus off slider handle.
Fixes #7107 - Dialog: Modal dialog event loss with high zindex child elements (FF 3.6).
Fixes #7120 - Dialog: Modal operation interrupts drag drop marker functionality on gmaps.
Fixes #8172 - Dialog: Change event cancelled when opening modal dialog from another modal dialog.
Fixes #8583 - Dialog: Mouse event wrongly stopped.
Fixes #8722 - Dialog: Remove stack option.
Fixes #8729 - Dialog: Remove zIndex option.
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">
-
+ <div id="dialog1"></div>
+ <div id="dialog2"></div>
</div>
</body>
</html>
stack: true,
title: '',
width: 300,
- zIndex: 1000,
// callbacks
create: null
});
test("moveToTop", function() {
- expect( 3 );
-
- var d1, d2, dlg1, dlg2,
- expected = $('<div></div>').dialog(),
- actual = expected.dialog('moveToTop');
- equal(actual, expected, 'moveToTop is chainable');
-
- d1 = $('<div></div>').dialog();
- dlg1 = d1.parents('.ui-dialog');
- d1.dialog('close');
- d1.dialog('open');
- d2 = $('<div></div>').dialog();
- dlg2 = d2.parents('.ui-dialog');
- d2.dialog('close');
- d2.dialog('open');
- ok(dlg1.css('zIndex') < dlg2.css('zIndex'), 'dialog 1 under dialog 2 before moveToTop method called');
- d1.dialog('moveToTop');
- ok(dlg1.css('zIndex') > dlg2.css('zIndex'), 'dialog 1 above dialog 2 after moveToTop method called');
+ expect( 5 );
+ function order() {
+ var actual = $( ".ui-dialog" ).map(function() {
+ return +$( this ).find( ".ui-dialog-content" ).attr( "id" ).replace( /\D+/, "" );
+ }).get().reverse();
+ deepEqual( actual, $.makeArray( arguments ) );
+ }
+ var dialog1, dialog2,
+ focusOn = "dialog1";
+ dialog1 = $( "#dialog1" ).dialog({
+ focus: function() {
+ equal( focusOn, "dialog1" );
+ }
+ });
+ focusOn = "dialog2";
+ dialog2 = $( "#dialog2" ).dialog({
+ focus: function() {
+ equal( focusOn, "dialog2" );
+ }
+ });
+ order( 2, 1 );
+ focusOn = "dialog1";
+ dialog1.dialog( "moveToTop" );
+ order( 1, 2 );
});
test("open", function() {
'<input type="radio" name="radio" id="b" value="b">b</input></form>').appendTo( "body" ).dialog({autoOpen: false});
d1.find('#b').prop( "checked", true );
- equal($('input:checked').val(), 'b', "checkbox b is checked");
+ equal(d1.find('input:checked').val(), 'b', "checkbox b is checked");
d1.dialog('open');
- equal($('input:checked').val(), 'b', "checkbox b is checked");
+ equal(d1.find('input:checked').val(), 'b', "checkbox b is checked");
d1.remove();
});
d1.add(d2).remove();
});
-test("#6966: Escape key closes all dialogs, not the top one", function(){
- expect(24);
- // test with close function removing dialog triggered through the overlay
- d1 = $('<div title="dialog 1">Dialog 1</div>').dialog({modal: true, close: function(){ d1.remove(); }});
- d2 = $('<div title="dialog 2">Dialog 2</div>').dialog({modal: true, close: function(){ d2.remove(); }});
-
- ok(d1.data('dialog') && d1.dialog('isOpen'), 'first dialog is open');
- ok(d2.data('dialog') && d2.dialog('isOpen'), 'second dialog is open');
-
- $( document ).simulate('keydown', {keyCode: $.ui.keyCode.ESCAPE});
- ok(d1.data('dialog') && d1.dialog('isOpen'), 'first dialog still open');
- ok(!d2.data('dialog'), 'second dialog is closed');
-
- $( document ).simulate('keydown', {keyCode: $.ui.keyCode.ESCAPE});
- ok(!d1.data('dialog'), 'first dialog is closed');
- ok(!d2.data('dialog'), 'second dialog is closed');
-
- d2.remove();
- d1.remove();
-
- // test with close function removing dialog triggered through the dialog
- d1 = $('<div title="dialog 1">Dialog 1</div>').dialog({modal: true, close: function(){ d1.remove(); }});
- d2 = $('<div title="dialog 2">Dialog 2</div>').dialog({modal: true, close: function(){ d2.remove(); }});
-
- ok(d1.data('dialog') && d1.dialog('isOpen'), 'first dialog is open');
- ok(d2.data('dialog') && d2.dialog('isOpen'), 'second dialog is open');
-
- d2.simulate('keydown', {keyCode: $.ui.keyCode.ESCAPE});
- ok(d1.data('dialog') && d1.dialog('isOpen'), 'first dialog still open');
- ok(!d2.data('dialog'), 'second dialog is closed');
-
- d1.simulate('keydown', {keyCode: $.ui.keyCode.ESCAPE});
- ok(!d1.data('dialog'), 'first dialog is closed');
- ok(!d2.data('dialog'), 'second dialog is closed');
-
- d2.remove();
- d1.remove();
-
- // test without close function removing dialog
- d1 = $('<div title="dialog 1">Dialog 1</div>').dialog({modal: true});
- d2 = $('<div title="dialog 2">Dialog 2</div>').dialog({modal: true});
-
- ok(d1.dialog("isOpen"), 'first dialog is open');
- ok(d2.dialog("isOpen"), 'second dialog is open');
-
- d2.simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE});
- ok(d1.dialog("isOpen"), 'first dialog still open');
- ok(!d2.dialog("isOpen"), 'second dialog is closed');
-
- d1.simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE});
- ok(!d1.dialog("isOpen"), 'first dialog is closed');
- ok(!d2.dialog("isOpen"), 'second dialog is closed');
-
- d2.remove();
- d1.remove();
-
- // test without close function removing dialog triggered through the overlay
- d1 = $('<div title="dialog 1">Dialog 1</div>').dialog({modal: true});
- d2 = $('<div title="dialog 2">Dialog 2</div>').dialog({modal: true});
-
- ok(d1.dialog("isOpen"), 'first dialog is open');
- ok(d2.dialog("isOpen"), 'second dialog is open');
-
- $( document ).simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE});
- ok(d1.dialog("isOpen"), 'first dialog still open');
- ok(!d2.dialog("isOpen"), 'second dialog is closed');
-
- $( document ).simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE});
- ok(!d1.dialog("isOpen"), 'first dialog is closed');
- ok(!d2.dialog("isOpen"), 'second dialog is closed');
-
- d2.remove();
- d1.remove();
-});
-
test("#4980: Destroy should place element back in original DOM position", function(){
expect( 2 );
container = $('<div id="container"><div id="modal">Content</div></div>');
--- /dev/null
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Dialog Visual Test</title>
+ <link rel="stylesheet" href="../../../themes/base/jquery.ui.all.css">
+ <script src="../../../jquery-1.8.2.js"></script>
+ <script src="../../../external/jquery.bgiframe-2.1.2.js"></script>
+ <script src="../../../ui/jquery.ui.core.js"></script>
+ <script src="../../../ui/jquery.ui.widget.js"></script>
+ <script src="../../../ui/jquery.ui.mouse.js"></script>
+ <script src="../../../ui/jquery.ui.draggable.js"></script>
+ <script src="../../../ui/jquery.ui.position.js"></script>
+ <script src="../../../ui/jquery.ui.resizable.js"></script>
+ <script src="../../../ui/jquery.ui.dialog.js"></script>
+
+ <!-- stuff needed to make things complex -->
+ <script src="../../../ui/jquery.ui.datepicker.js"></script>
+ <script src="../../../ui/jquery.ui.menu.js"></script>
+ <script src="../../../ui/jquery.ui.autocomplete.js"></script>
+ <script src="../../../ui/jquery.ui.tooltip.js"></script>
+
+ <script>
+ $(function() {
+ $( "#dialog" ).dialog({
+ modal: true,
+ height: 300,
+ width: 500
+ });
+
+ var datepickerDialog = $( "#dialog-datepicker" ).dialog({
+ autoOpen: false,
+ modal: true,
+ }),
+
+ autocompleteDialog = $( "#dialog-autocomplete" ).dialog({
+ autoOpen: false,
+ modal: false,
+ width: 600
+ });
+
+ $( "#open-datepicker" ).click(function() {
+ datepickerDialog.dialog( "open" );
+ });
+
+ $( "#open-autocomplete" ).click(function() {
+ autocompleteDialog.dialog( "open" );
+ });
+
+ $( "#datepicker" ).datepicker();
+
+ $( "#autocomplete" ).autocomplete({
+ source: [
+ "ActionScript",
+ "AppleScript",
+ "Asp",
+ "BASIC",
+ "Scheme"
+ ]
+ });
+
+ $( document ).tooltip();
+ });
+ </script>
+</head>
+<body>
+
+<p>WHAT: A modal dialog opening another modal dialog (including a datepicker), opening a non-modal dialog (including an autocomplete with tooltip applied). A regular link on the page, outside of the dialogs.</p>
+<p>EXPECTED: As long as a modal dialog is open, focus stays within the dialogs. Both mouse and keyboard interactions are captured and restricted to the dialog. When the nested modal dialog is open, the first modal dialog can't be interacted with, until the nested dialog is closed. When the third dialog is open (not modal), switching between the two dialogs is possible, both can be interacted with.</p>
+
+<a href="#">Outside link</a>
+
+<div id="dialog" title="Basic dialog">
+ <p>This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.</p>
+ <p><button id="open-datepicker">Open another window with a datepicker.</button></p>
+</div>
+
+<div id="dialog-datepicker" title="A dialog with a datepicker">
+ <p>Date: <input id="datepicker"></p>
+ <p><button id="open-autocomplete">Open another window with an autocomplete and a tooltip.</button></p>
+</div>
+
+<div id="dialog-autocomplete">
+ <label for="autocomplete">Tags: </label>
+ <input id="autocomplete" title="Try typing something!">
+</div>
+
+</body>
+</html>
.ui-helper-clearfix { zoom: 1; }
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+.ui-front { z-index: 100; }
+
/* Interaction Cues
----------------------------------*/
----------------------------------*/
/* Overlays */
-.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+.ui-widget-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; }
*
* http://docs.jquery.com/UI/Dialog#theming
*/
-.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; outline: 0; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
*/
(function( $, undefined ) {
-var uiDialogClasses = "ui-dialog ui-widget ui-widget-content ui-corner-all ",
+var uiDialogClasses = "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front ",
sizeRelatedOptions = {
buttons: true,
height: true,
},
resizable: true,
show: null,
- stack: true,
title: "",
- width: 300,
- zIndex: 1000
+ width: 300
},
_create: function() {
uiDialog = ( this.uiDialog = $( "<div>" ) )
.addClass( uiDialogClasses + options.dialogClass )
- .css({
- display: "none",
- outline: 0, // TODO: move to stylesheet
- zIndex: options.zIndex
- })
+ .hide()
// setting tabIndex makes the div focusable
.attr( "tabIndex", -1)
.keydown(function( event ) {
}
})
.mousedown(function( event ) {
- that.moveToTop( false, event );
+ that.moveToTop( event );
})
- .appendTo( "body" );
+ .appendTo( this.document[ 0 ].body );
this.element
.show()
},
close: function( event ) {
- var that = this,
- maxZ, thisZ;
+ var that = this;
if ( !this._isOpen ) {
return;
this._trigger( "close", event );
}
- $.ui.dialog.overlay.resize();
-
- // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
- if ( this.options.modal ) {
- maxZ = 0;
- $( ".ui-dialog" ).each(function() {
- if ( this !== that.uiDialog[0] ) {
- thisZ = $( this ).css( "z-index" );
- if ( !isNaN( thisZ ) ) {
- maxZ = Math.max( maxZ, thisZ );
- }
- }
- });
- $.ui.dialog.maxZ = maxZ;
- }
-
return this;
},
return this._isOpen;
},
- // the force parameter allows us to move modal dialogs to their correct
- // position on open
- moveToTop: function( force, event ) {
- var options = this.options,
- saveScroll;
-
- if ( ( options.modal && !force ) ||
- ( !options.stack && !options.modal ) ) {
- return this._trigger( "focus", event );
- }
-
- if ( options.zIndex > $.ui.dialog.maxZ ) {
- $.ui.dialog.maxZ = options.zIndex;
- }
- if ( this.overlay ) {
- $.ui.dialog.maxZ += 1;
- $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ;
- this.overlay.$el.css( "z-index", $.ui.dialog.overlay.maxZ );
+ moveToTop: function( event, silent ) {
+ var moved = this.uiDialog.nextAll( ":visible" ).insertBefore( this.uiDialog );
+ if ( !silent && moved.length ) {
+ this._trigger( "focus", event );
}
-
- // Save and then restore scroll
- // Opera 9.5+ resets when parent z-index is changed.
- // http://bugs.jqueryui.com/ticket/3193
- saveScroll = {
- scrollTop: this.element.scrollTop(),
- scrollLeft: this.element.scrollLeft()
- };
- $.ui.dialog.maxZ += 1;
- this.uiDialog.css( "z-index", $.ui.dialog.maxZ );
- this.element.attr( saveScroll );
- this._trigger( "focus", event );
-
- return this;
},
open: function() {
this._position( options.position );
uiDialog.show( options.show );
this.overlay = options.modal ? new $.ui.dialog.overlay( this ) : null;
- this.moveToTop( true );
+
+ this.moveToTop( null, true );
// set focus to the first tabbable element in the content area or the first button
// if there are no tabbable elements, set focus on the dialog itself
this._isOpen = true;
this._trigger( "open" );
+ this._trigger( "focus" );
return this;
},
$( this )
.removeClass( "ui-dialog-dragging" );
that._trigger( "dragStop", event, filteredUi( ui ) );
- $.ui.dialog.overlay.resize();
}
});
},
options.height = $( this ).height();
options.width = $( this ).width();
that._trigger( "resizeStop", event, filteredUi( ui ) );
- $.ui.dialog.overlay.resize();
}
})
.css( "position", position )
$.extend($.ui.dialog, {
uuid: 0,
- maxZ: 0,
getTitleId: function($el) {
var id = $el.attr( "id" );
instances: [],
// reuse old instances due to IE memory leak with alpha transparency (see #5185)
oldInstances: [],
- maxZ: 0,
- events: $.map(
- "focus,mousedown,mouseup,keydown,keypress,click".split( "," ),
- function( event ) {
- return event + ".dialog-overlay";
- }
- ).join( " " ),
create: function( dialog ) {
- if ( this.instances.length === 0 ) {
- // prevent use of anchors and inputs
- // we use a setTimeout in case the overlay is created from an
- // event that we're going to be cancelling (see #2804)
- setTimeout(function() {
- // handle $(el).dialog().dialog('close') (see #4065)
- if ( $.ui.dialog.overlay.instances.length ) {
- $( document ).bind( $.ui.dialog.overlay.events, function( event ) {
- // stop events if the z-index of the target is < the z-index of the overlay
- // we cannot return true when we don't want to cancel the event (#3523)
- if ( $( event.target ).zIndex() < $.ui.dialog.overlay.maxZ ) {
- return false;
- }
- });
- }
- }, 1 );
-
- // handle window resize
- $( window ).bind( "resize.dialog-overlay", $.ui.dialog.overlay.resize );
- }
- var $el = ( this.oldInstances.pop() || $( "<div>" ).addClass( "ui-widget-overlay" ) );
+ var $el = ( this.oldInstances.pop() || $( "<div>" ).addClass( "ui-widget-overlay ui-front" ) );
- // allow closing by pressing the escape key
- $( document ).bind( "keydown.dialog-overlay", function( event ) {
- var instances = $.ui.dialog.overlay.instances;
- // only react to the event if we're the top overlay
- if ( instances.length !== 0 && instances[ instances.length - 1 ] === $el &&
- dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
- event.keyCode === $.ui.keyCode.ESCAPE ) {
-
- dialog.close( event );
- event.preventDefault();
- }
- });
-
- $el.appendTo( document.body ).css({
- width: this.width(),
- height: this.height()
- });
+ $el.appendTo( document.body );
if ( $.fn.bgiframe ) {
$el.bgiframe();
},
destroy: function( $el ) {
- var indexOf = $.inArray( $el, this.instances ),
- maxZ = 0;
+ var indexOf = $.inArray( $el, this.instances );
if ( indexOf !== -1 ) {
this.oldInstances.push( this.instances.splice( indexOf, 1 )[ 0 ] );
$( [ document, window ] ).unbind( ".dialog-overlay" );
}
- $el.height( 0 ).width( 0 ).remove();
-
- // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
- $.each( this.instances, function() {
- maxZ = Math.max( maxZ, this.css( "z-index" ) );
- });
- this.maxZ = maxZ;
- },
-
- height: function() {
- var scrollHeight,
- offsetHeight;
- // handle IE
- if ( $.ui.ie ) {
- scrollHeight = Math.max(
- document.documentElement.scrollHeight,
- document.body.scrollHeight
- );
- offsetHeight = Math.max(
- document.documentElement.offsetHeight,
- document.body.offsetHeight
- );
-
- if ( scrollHeight < offsetHeight ) {
- return $( window ).height() + "px";
- } else {
- return scrollHeight + "px";
- }
- // handle "good" browsers
- } else {
- return $( document ).height() + "px";
- }
- },
-
- width: function() {
- var scrollWidth,
- offsetWidth;
- // handle IE
- if ( $.ui.ie ) {
- scrollWidth = Math.max(
- document.documentElement.scrollWidth,
- document.body.scrollWidth
- );
- offsetWidth = Math.max(
- document.documentElement.offsetWidth,
- document.body.offsetWidth
- );
-
- if ( scrollWidth < offsetWidth ) {
- return $( window ).width() + "px";
- } else {
- return scrollWidth + "px";
- }
- // handle "good" browsers
- } else {
- return $( document ).width() + "px";
- }
- },
-
- resize: function() {
- /* If the dialog is draggable and the user drags it past the
- * right edge of the window, the document becomes wider so we
- * need to stretch the overlay. If the user then drags the
- * dialog back to the left, the document will become narrower,
- * so we need to shrink the overlay to the appropriate size.
- * This is handled by shrinking the overlay before setting it
- * to the full document size.
- */
- var $overlays = $( [] );
- $.each( $.ui.dialog.overlay.instances, function() {
- $overlays = $overlays.add( this );
- });
-
- $overlays.css({
- width: 0,
- height: 0
- }).css({
- width: $.ui.dialog.overlay.width(),
- height: $.ui.dialog.overlay.height()
- });
+ $el.remove();
}
});