2 * jQuery UI Dialog 1.9.2
5 * Copyright 2012 jQuery Foundation and other contributors
6 * Released under the MIT license.
7 * http://jquery.org/license
9 * http://api.jqueryui.com/dialog/
15 * jquery.ui.draggable.js
17 * jquery.ui.position.js
18 * jquery.ui.resizable.js
20 (function( $, undefined ) {
22 var uiDialogClasses = "ui-dialog ui-widget ui-widget-content ui-corner-all ",
23 sizeRelatedOptions = {
32 resizableRelatedOptions = {
39 $.widget("ui.dialog", {
60 // ensure that the titlebar is never outside the document
61 using: function( pos ) {
62 var topOffset = $( this ).css( pos ).offset().top;
63 if ( topOffset < 0 ) {
64 $( this ).css( "top", pos.top - topOffset );
77 this.originalTitle = this.element.attr( "title" );
78 // #5742 - .attr() might return a DOMElement
79 if ( typeof this.originalTitle !== "string" ) {
80 this.originalTitle = "";
83 parent: this.element.parent(),
84 index: this.element.parent().children().index( this.element )
86 this.options.title = this.options.title || this.originalTitle;
88 options = this.options,
90 title = options.title || " ",
93 uiDialogTitlebarClose,
97 uiDialog = ( this.uiDialog = $( "<div>" ) )
98 .addClass( uiDialogClasses + options.dialogClass )
101 outline: 0, // TODO: move to stylesheet
102 zIndex: options.zIndex
104 // setting tabIndex makes the div focusable
105 .attr( "tabIndex", -1)
106 .keydown(function( event ) {
107 if ( options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
108 event.keyCode === $.ui.keyCode.ESCAPE ) {
110 event.preventDefault();
113 .mousedown(function( event ) {
114 that.moveToTop( false, event );
120 .removeAttr( "title" )
121 .addClass( "ui-dialog-content ui-widget-content" )
122 .appendTo( uiDialog );
124 uiDialogTitlebar = ( this.uiDialogTitlebar = $( "<div>" ) )
125 .addClass( "ui-dialog-titlebar ui-widget-header " +
126 "ui-corner-all ui-helper-clearfix" )
127 .bind( "mousedown", function() {
128 // Dialog isn't getting focus when dragging (#8063)
131 .prependTo( uiDialog );
133 uiDialogTitlebarClose = $( "<a href='#'></a>" )
134 .addClass( "ui-dialog-titlebar-close ui-corner-all" )
135 .attr( "role", "button" )
136 .click(function( event ) {
137 event.preventDefault();
140 .appendTo( uiDialogTitlebar );
142 ( this.uiDialogTitlebarCloseText = $( "<span>" ) )
143 .addClass( "ui-icon ui-icon-closethick" )
144 .text( options.closeText )
145 .appendTo( uiDialogTitlebarClose );
147 uiDialogTitle = $( "<span>" )
149 .addClass( "ui-dialog-title" )
151 .prependTo( uiDialogTitlebar );
153 uiDialogButtonPane = ( this.uiDialogButtonPane = $( "<div>" ) )
154 .addClass( "ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" );
156 ( this.uiButtonSet = $( "<div>" ) )
157 .addClass( "ui-dialog-buttonset" )
158 .appendTo( uiDialogButtonPane );
162 "aria-labelledby": uiDialogTitle.attr( "id" )
165 uiDialogTitlebar.find( "*" ).add( uiDialogTitlebar ).disableSelection();
166 this._hoverable( uiDialogTitlebarClose );
167 this._focusable( uiDialogTitlebarClose );
169 if ( options.draggable && $.fn.draggable ) {
170 this._makeDraggable();
172 if ( options.resizable && $.fn.resizable ) {
173 this._makeResizable();
176 this._createButtons( options.buttons );
177 this._isOpen = false;
179 if ( $.fn.bgiframe ) {
183 // prevent tabbing out of modal dialogs
184 this._on( uiDialog, { keydown: function( event ) {
185 if ( !options.modal || event.keyCode !== $.ui.keyCode.TAB ) {
189 var tabbables = $( ":tabbable", uiDialog ),
190 first = tabbables.filter( ":first" ),
191 last = tabbables.filter( ":last" );
193 if ( event.target === last[0] && !event.shiftKey ) {
196 } else if ( event.target === first[0] && event.shiftKey ) {
204 if ( this.options.autoOpen ) {
209 _destroy: function() {
211 oldPosition = this.oldPosition;
213 if ( this.overlay ) {
214 this.overlay.destroy();
216 this.uiDialog.hide();
218 .removeClass( "ui-dialog-content ui-widget-content" )
221 this.uiDialog.remove();
223 if ( this.originalTitle ) {
224 this.element.attr( "title", this.originalTitle );
227 next = oldPosition.parent.children().eq( oldPosition.index );
228 // Don't try to place the dialog next to itself (#8613)
229 if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
230 next.before( this.element );
232 oldPosition.parent.append( this.element );
237 return this.uiDialog;
240 close: function( event ) {
244 if ( !this._isOpen ) {
248 if ( false === this._trigger( "beforeClose", event ) ) {
252 this._isOpen = false;
254 if ( this.overlay ) {
255 this.overlay.destroy();
258 if ( this.options.hide ) {
259 this._hide( this.uiDialog, this.options.hide, function() {
260 that._trigger( "close", event );
263 this.uiDialog.hide();
264 this._trigger( "close", event );
267 $.ui.dialog.overlay.resize();
269 // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
270 if ( this.options.modal ) {
272 $( ".ui-dialog" ).each(function() {
273 if ( this !== that.uiDialog[0] ) {
274 thisZ = $( this ).css( "z-index" );
275 if ( !isNaN( thisZ ) ) {
276 maxZ = Math.max( maxZ, thisZ );
280 $.ui.dialog.maxZ = maxZ;
290 // the force parameter allows us to move modal dialogs to their correct
292 moveToTop: function( force, event ) {
293 var options = this.options,
296 if ( ( options.modal && !force ) ||
297 ( !options.stack && !options.modal ) ) {
298 return this._trigger( "focus", event );
301 if ( options.zIndex > $.ui.dialog.maxZ ) {
302 $.ui.dialog.maxZ = options.zIndex;
304 if ( this.overlay ) {
305 $.ui.dialog.maxZ += 1;
306 $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ;
307 this.overlay.$el.css( "z-index", $.ui.dialog.overlay.maxZ );
310 // Save and then restore scroll
311 // Opera 9.5+ resets when parent z-index is changed.
312 // http://bugs.jqueryui.com/ticket/3193
314 scrollTop: this.element.scrollTop(),
315 scrollLeft: this.element.scrollLeft()
317 $.ui.dialog.maxZ += 1;
318 this.uiDialog.css( "z-index", $.ui.dialog.maxZ );
319 this.element.attr( saveScroll );
320 this._trigger( "focus", event );
326 if ( this._isOpen ) {
331 options = this.options,
332 uiDialog = this.uiDialog;
335 this._position( options.position );
336 uiDialog.show( options.show );
337 this.overlay = options.modal ? new $.ui.dialog.overlay( this ) : null;
338 this.moveToTop( true );
340 // set focus to the first tabbable element in the content area or the first button
341 // if there are no tabbable elements, set focus on the dialog itself
342 hasFocus = this.element.find( ":tabbable" );
343 if ( !hasFocus.length ) {
344 hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
345 if ( !hasFocus.length ) {
349 hasFocus.eq( 0 ).focus();
352 this._trigger( "open" );
357 _createButtons: function( buttons ) {
361 // if we already have a button pane, remove it
362 this.uiDialogButtonPane.remove();
363 this.uiButtonSet.empty();
365 if ( typeof buttons === "object" && buttons !== null ) {
366 $.each( buttons, function() {
367 return !(hasButtons = true);
371 $.each( buttons, function( name, props ) {
373 props = $.isFunction( props ) ?
374 { click: props, text: name } :
376 // Default to a non-submitting button
377 props = $.extend( { type: "button" }, props );
378 // Change the context for the click callback to be the main element
380 props.click = function() {
381 click.apply( that.element[0], arguments );
383 button = $( "<button></button>", props )
384 .appendTo( that.uiButtonSet );
389 this.uiDialog.addClass( "ui-dialog-buttons" );
390 this.uiDialogButtonPane.appendTo( this.uiDialog );
392 this.uiDialog.removeClass( "ui-dialog-buttons" );
396 _makeDraggable: function() {
398 options = this.options;
400 function filteredUi( ui ) {
402 position: ui.position,
407 this.uiDialog.draggable({
408 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
409 handle: ".ui-dialog-titlebar",
410 containment: "document",
411 start: function( event, ui ) {
413 .addClass( "ui-dialog-dragging" );
414 that._trigger( "dragStart", event, filteredUi( ui ) );
416 drag: function( event, ui ) {
417 that._trigger( "drag", event, filteredUi( ui ) );
419 stop: function( event, ui ) {
421 ui.position.left - that.document.scrollLeft(),
422 ui.position.top - that.document.scrollTop()
425 .removeClass( "ui-dialog-dragging" );
426 that._trigger( "dragStop", event, filteredUi( ui ) );
427 $.ui.dialog.overlay.resize();
432 _makeResizable: function( handles ) {
433 handles = (handles === undefined ? this.options.resizable : handles);
435 options = this.options,
436 // .ui-resizable has position: relative defined in the stylesheet
437 // but dialogs have to use absolute or fixed positioning
438 position = this.uiDialog.css( "position" ),
439 resizeHandles = typeof handles === 'string' ?
441 "n,e,s,w,se,sw,ne,nw";
443 function filteredUi( ui ) {
445 originalPosition: ui.originalPosition,
446 originalSize: ui.originalSize,
447 position: ui.position,
452 this.uiDialog.resizable({
453 cancel: ".ui-dialog-content",
454 containment: "document",
455 alsoResize: this.element,
456 maxWidth: options.maxWidth,
457 maxHeight: options.maxHeight,
458 minWidth: options.minWidth,
459 minHeight: this._minHeight(),
460 handles: resizeHandles,
461 start: function( event, ui ) {
462 $( this ).addClass( "ui-dialog-resizing" );
463 that._trigger( "resizeStart", event, filteredUi( ui ) );
465 resize: function( event, ui ) {
466 that._trigger( "resize", event, filteredUi( ui ) );
468 stop: function( event, ui ) {
469 $( this ).removeClass( "ui-dialog-resizing" );
470 options.height = $( this ).height();
471 options.width = $( this ).width();
472 that._trigger( "resizeStop", event, filteredUi( ui ) );
473 $.ui.dialog.overlay.resize();
476 .css( "position", position )
477 .find( ".ui-resizable-se" )
478 .addClass( "ui-icon ui-icon-grip-diagonal-se" );
481 _minHeight: function() {
482 var options = this.options;
484 if ( options.height === "auto" ) {
485 return options.minHeight;
487 return Math.min( options.minHeight, options.height );
491 _position: function( position ) {
497 // deep extending converts arrays to objects in jQuery <= 1.3.2 :-(
498 // if (typeof position == 'string' || $.isArray(position)) {
499 // myAt = $.isArray(position) ? position : position.split(' ');
501 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
502 myAt = position.split ? position.split( " " ) : [ position[ 0 ], position[ 1 ] ];
503 if ( myAt.length === 1 ) {
504 myAt[ 1 ] = myAt[ 0 ];
507 $.each( [ "left", "top" ], function( i, offsetPosition ) {
508 if ( +myAt[ i ] === myAt[ i ] ) {
509 offset[ i ] = myAt[ i ];
510 myAt[ i ] = offsetPosition;
515 my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
516 myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
521 position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
523 position = $.ui.dialog.prototype.options.position;
526 // need to show the dialog to get the actual offset in the position plugin
527 isVisible = this.uiDialog.is( ":visible" );
529 this.uiDialog.show();
531 this.uiDialog.position( position );
533 this.uiDialog.hide();
537 _setOptions: function( options ) {
539 resizableOptions = {},
542 $.each( options, function( key, value ) {
543 that._setOption( key, value );
545 if ( key in sizeRelatedOptions ) {
548 if ( key in resizableRelatedOptions ) {
549 resizableOptions[ key ] = value;
556 if ( this.uiDialog.is( ":data(resizable)" ) ) {
557 this.uiDialog.resizable( "option", resizableOptions );
561 _setOption: function( key, value ) {
562 var isDraggable, isResizable,
563 uiDialog = this.uiDialog;
567 this._createButtons( value );
570 // ensure that we always pass a string
571 this.uiDialogTitlebarCloseText.text( "" + value );
575 .removeClass( this.options.dialogClass )
576 .addClass( uiDialogClasses + value );
580 uiDialog.addClass( "ui-dialog-disabled" );
582 uiDialog.removeClass( "ui-dialog-disabled" );
586 isDraggable = uiDialog.is( ":data(draggable)" );
587 if ( isDraggable && !value ) {
588 uiDialog.draggable( "destroy" );
591 if ( !isDraggable && value ) {
592 this._makeDraggable();
596 this._position( value );
599 // currently resizable, becoming non-resizable
600 isResizable = uiDialog.is( ":data(resizable)" );
601 if ( isResizable && !value ) {
602 uiDialog.resizable( "destroy" );
605 // currently resizable, changing handles
606 if ( isResizable && typeof value === "string" ) {
607 uiDialog.resizable( "option", "handles", value );
610 // currently non-resizable, becoming resizable
611 if ( !isResizable && value !== false ) {
612 this._makeResizable( value );
616 // convert whatever was passed in o a string, for html() to not throw up
617 $( ".ui-dialog-title", this.uiDialogTitlebar )
618 .html( "" + ( value || " " ) );
622 this._super( key, value );
626 /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
627 * divs will both have width and height set, so we need to reset them
629 var nonContentHeight, minContentHeight, autoHeight,
630 options = this.options,
631 isVisible = this.uiDialog.is( ":visible" );
633 // reset content sizing
634 this.element.show().css({
640 if ( options.minWidth > options.width ) {
641 options.width = options.minWidth;
644 // reset wrapper sizing
645 // determine the height of all the non-content elements
646 nonContentHeight = this.uiDialog.css({
651 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
653 if ( options.height === "auto" ) {
654 // only needed for IE6 support
655 if ( $.support.minHeight ) {
657 minHeight: minContentHeight,
661 this.uiDialog.show();
662 autoHeight = this.element.css( "height", "auto" ).height();
664 this.uiDialog.hide();
666 this.element.height( Math.max( autoHeight, minContentHeight ) );
669 this.element.height( Math.max( options.height - nonContentHeight, 0 ) );
672 if (this.uiDialog.is( ":data(resizable)" ) ) {
673 this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
678 $.extend($.ui.dialog, {
682 getTitleId: function($el) {
683 var id = $el.attr( "id" );
688 return "ui-dialog-title-" + id;
691 overlay: function( dialog ) {
692 this.$el = $.ui.dialog.overlay.create( dialog );
696 $.extend( $.ui.dialog.overlay, {
698 // reuse old instances due to IE memory leak with alpha transparency (see #5185)
702 "focus,mousedown,mouseup,keydown,keypress,click".split( "," ),
704 return event + ".dialog-overlay";
707 create: function( dialog ) {
708 if ( this.instances.length === 0 ) {
709 // prevent use of anchors and inputs
710 // we use a setTimeout in case the overlay is created from an
711 // event that we're going to be cancelling (see #2804)
712 setTimeout(function() {
713 // handle $(el).dialog().dialog('close') (see #4065)
714 if ( $.ui.dialog.overlay.instances.length ) {
715 $( document ).bind( $.ui.dialog.overlay.events, function( event ) {
716 // stop events if the z-index of the target is < the z-index of the overlay
717 // we cannot return true when we don't want to cancel the event (#3523)
718 if ( $( event.target ).zIndex() < $.ui.dialog.overlay.maxZ ) {
725 // handle window resize
726 $( window ).bind( "resize.dialog-overlay", $.ui.dialog.overlay.resize );
729 var $el = ( this.oldInstances.pop() || $( "<div>" ).addClass( "ui-widget-overlay" ) );
731 // allow closing by pressing the escape key
732 $( document ).bind( "keydown.dialog-overlay", function( event ) {
733 var instances = $.ui.dialog.overlay.instances;
734 // only react to the event if we're the top overlay
735 if ( instances.length !== 0 && instances[ instances.length - 1 ] === $el &&
736 dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
737 event.keyCode === $.ui.keyCode.ESCAPE ) {
739 dialog.close( event );
740 event.preventDefault();
744 $el.appendTo( document.body ).css({
746 height: this.height()
749 if ( $.fn.bgiframe ) {
753 this.instances.push( $el );
757 destroy: function( $el ) {
758 var indexOf = $.inArray( $el, this.instances ),
761 if ( indexOf !== -1 ) {
762 this.oldInstances.push( this.instances.splice( indexOf, 1 )[ 0 ] );
765 if ( this.instances.length === 0 ) {
766 $( [ document, window ] ).unbind( ".dialog-overlay" );
769 $el.height( 0 ).width( 0 ).remove();
771 // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
772 $.each( this.instances, function() {
773 maxZ = Math.max( maxZ, this.css( "z-index" ) );
783 scrollHeight = Math.max(
784 document.documentElement.scrollHeight,
785 document.body.scrollHeight
787 offsetHeight = Math.max(
788 document.documentElement.offsetHeight,
789 document.body.offsetHeight
792 if ( scrollHeight < offsetHeight ) {
793 return $( window ).height() + "px";
795 return scrollHeight + "px";
797 // handle "good" browsers
799 return $( document ).height() + "px";
808 scrollWidth = Math.max(
809 document.documentElement.scrollWidth,
810 document.body.scrollWidth
812 offsetWidth = Math.max(
813 document.documentElement.offsetWidth,
814 document.body.offsetWidth
817 if ( scrollWidth < offsetWidth ) {
818 return $( window ).width() + "px";
820 return scrollWidth + "px";
822 // handle "good" browsers
824 return $( document ).width() + "px";
829 /* If the dialog is draggable and the user drags it past the
830 * right edge of the window, the document becomes wider so we
831 * need to stretch the overlay. If the user then drags the
832 * dialog back to the left, the document will become narrower,
833 * so we need to shrink the overlay to the appropriate size.
834 * This is handled by shrinking the overlay before setting it
835 * to the full document size.
837 var $overlays = $( [] );
838 $.each( $.ui.dialog.overlay.instances, function() {
839 $overlays = $overlays.add( this );
846 width: $.ui.dialog.overlay.width(),
847 height: $.ui.dialog.overlay.height()
852 $.extend( $.ui.dialog.overlay.prototype, {
853 destroy: function() {
854 $.ui.dialog.overlay.destroy( this.$el );