/* * jQuery UI Dialog @VERSION * * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * http://docs.jquery.com/UI/Dialog * * Depends: * ui.core.js * ui.draggable.js * ui.resizable.js */ (function($) { var setDataSwitch = { dragStart: "start.draggable", drag: "drag.draggable", dragStop: "stop.draggable", maxHeight: "maxHeight.resizable", minHeight: "minHeight.resizable", maxWidth: "maxWidth.resizable", minWidth: "minWidth.resizable", resizeStart: "start.resizable", resize: "drag.resizable", resizeStop: "stop.resizable" }; $.widget("ui.dialog", { _init: function() { this.originalTitle = this.element.attr('title'); this.options.title = this.options.title || this.originalTitle; var self = this, options = this.options, title = options.title || ' ', titleId = $.ui.dialog.getTitleId(this.element), uiDialog = (this.uiDialog = $('
')) .appendTo(document.body) .hide() .addClass( 'ui-dialog ' + 'ui-widget ' + 'ui-widget-content ' + 'ui-corner-all ' + options.dialogClass ) .css({ position: 'absolute', overflow: 'hidden', zIndex: options.zIndex }) // setting tabIndex makes the div focusable // setting outline to 0 prevents a border on focus in Mozilla .attr('tabIndex', -1).css('outline', 0).keydown(function(ev) { (options.closeOnEscape && ev.keyCode && ev.keyCode == $.ui.keyCode.ESCAPE && self.close()); }) .attr({ role: 'dialog', 'aria-labelledby': titleId }) .mousedown(function() { self.moveToTop(); }), uiDialogContent = this.element .show() .removeAttr('title') .addClass( 'ui-dialog-content ' + 'ui-widget-content') .appendTo(uiDialog), uiDialogTitlebar = (this.uiDialogTitlebar = $('
')) .addClass( 'ui-dialog-titlebar ' + 'ui-widget-header ' + 'ui-corner-all ' + 'ui-helper-clearfix' ) .prependTo(uiDialog), uiDialogTitlebarClose = $('') .addClass( 'ui-dialog-titlebar-close ' + 'ui-corner-all' ) .attr('role', 'button') .hover( function() { uiDialogTitlebarClose.addClass('ui-state-hover'); }, function() { uiDialogTitlebarClose.removeClass('ui-state-hover'); } ) .focus(function() { uiDialogTitlebarClose.addClass('ui-state-focus'); }) .blur(function() { uiDialogTitlebarClose.removeClass('ui-state-focus'); }) .mousedown(function(ev) { ev.stopPropagation(); }) .click(function() { self.close(); return false; }) .appendTo(uiDialogTitlebar), uiDialogTitlebarCloseText = (this.uiDialogTitlebarCloseText = $('')) .addClass( 'ui-icon ' + 'ui-icon-closethick' ) .text(options.closeText) .appendTo(uiDialogTitlebarClose), uiDialogTitle = $('') .addClass('ui-dialog-title') .attr('id', titleId) .html(title) .prependTo(uiDialogTitlebar), uiDialogButtonPane = (this.uiDialogButtonPane = $('
')) .addClass( 'ui-dialog-buttonpane ' + 'ui-widget-content ' + 'ui-helper-clearfix' ) .appendTo(uiDialog); uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection(); (options.draggable && $.fn.draggable && this._makeDraggable()); (options.resizable && $.fn.resizable && this._makeResizable()); this._createButtons(options.buttons); this._isOpen = false; (options.bgiframe && $.fn.bgiframe && uiDialog.bgiframe()); (options.autoOpen && this.open()); }, destroy: function() { (this.overlay && this.overlay.destroy()); this.uiDialog.hide(); this.element .unbind('.dialog') .removeData('dialog') .removeClass('ui-dialog-content ui-widget-content') .hide().appendTo('body'); this.uiDialog.remove(); (this.originalTitle && this.element.attr('title', this.originalTitle)); }, close: function() { if (false === this._trigger('beforeclose')) { return; } (this.overlay && this.overlay.destroy()); this.uiDialog .hide(this.options.hide) .unbind('keypress.ui-dialog'); this._trigger('close'); $.ui.dialog.overlay.resize(); this._isOpen = false; }, isOpen: function() { return this._isOpen; }, // the force parameter allows us to move modal dialogs to their correct // position on open moveToTop: function(force) { if ((this.options.modal && !force) || (!this.options.stack && !this.options.modal)) { return this._trigger('focus'); } var maxZ = this.options.zIndex, options = this.options; $('.ui-dialog:visible').each(function() { maxZ = Math.max(maxZ, parseInt($(this).css('z-index'), 10) || options.zIndex); }); (this.overlay && this.overlay.$el.css('z-index', ++maxZ)); //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed. // http://ui.jquery.com/bugs/ticket/3193 var saveScroll = { scrollTop: this.element.attr('scrollTop'), scrollLeft: this.element.attr('scrollLeft') }; this.uiDialog.css('z-index', ++maxZ); this.element.attr(saveScroll); this._trigger('focus'); }, open: function() { if (this._isOpen) { return; } this.overlay = this.options.modal ? new $.ui.dialog.overlay(this) : null; (this.uiDialog.next().length && this.uiDialog.appendTo('body')); this._size(); this._position(this.options.position); this.uiDialog.show(this.options.show); this.moveToTop(true); // prevent tabbing out of modal dialogs (this.options.modal && this.uiDialog.bind('keypress.ui-dialog', function(event) { if (event.keyCode != $.ui.keyCode.TAB) { return; } var tabbables = $(':tabbable', this), first = tabbables.filter(':first')[0], last = tabbables.filter(':last')[0]; if (event.target == last && !event.shiftKey) { setTimeout(function() { first.focus(); }, 1); } else if (event.target == first && event.shiftKey) { setTimeout(function() { last.focus(); }, 1); } })); this.uiDialog.find(':tabbable:first').focus(); this._trigger('open'); this._isOpen = true; }, _createButtons: function(buttons) { var self = this, hasButtons = false, uiDialogButtonPane = this.uiDialogButtonPane; // remove any existing buttons uiDialogButtonPane.empty().hide(); $.each(buttons, function() { return !(hasButtons = true); }); if (hasButtons) { uiDialogButtonPane.show(); $.each(buttons, function(name, fn) { $('') .addClass( 'ui-state-default ' + 'ui-corner-all' ) .text(name) .click(function() { fn.apply(self.element[0], arguments); }) .hover( function() { $(this).addClass('ui-state-hover'); }, function() { $(this).removeClass('ui-state-hover'); } ) .focus(function() { $(this).addClass('ui-state-focus'); }) .blur(function() { $(this).removeClass('ui-state-focus'); }) .appendTo(uiDialogButtonPane); }); } }, _makeDraggable: function() { var self = this, options = this.options; this.uiDialog.draggable({ cancel: '.ui-dialog-content', helper: options.dragHelper, handle: '.ui-dialog-titlebar', containment: 'document', start: function() { (options.dragStart && options.dragStart.apply(self.element[0], arguments)); }, drag: function() { (options.drag && options.drag.apply(self.element[0], arguments)); }, stop: function() { (options.dragStop && options.dragStop.apply(self.element[0], arguments)); $.ui.dialog.overlay.resize(); } }); }, _makeResizable: function(handles) { handles = (handles === undefined ? this.options.resizable : handles); var self = this, options = this.options, resizeHandles = typeof handles == 'string' ? handles : 'n,e,s,w,se,sw,ne,nw'; this.uiDialog.resizable({ cancel: '.ui-dialog-content', alsoResize: this.element, helper: options.resizeHelper, maxWidth: options.maxWidth, maxHeight: options.maxHeight, minWidth: options.minWidth, minHeight: options.minHeight, start: function() { (options.resizeStart && options.resizeStart.apply(self.element[0], arguments)); }, resize: function() { (options.resize && options.resize.apply(self.element[0], arguments)); }, handles: resizeHandles, stop: function() { (options.resizeStop && options.resizeStop.apply(self.element[0], arguments)); $.ui.dialog.overlay.resize(); } }) .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se'); }, _position: function(pos) { var wnd = $(window), doc = $(document), pTop = doc.scrollTop(), pLeft = doc.scrollLeft(), minTop = pTop; if ($.inArray(pos, ['center','top','right','bottom','left']) >= 0) { pos = [ pos == 'right' || pos == 'left' ? pos : 'center', pos == 'top' || pos == 'bottom' ? pos : 'middle' ]; } if (pos.constructor != Array) { pos = ['center', 'middle']; } if (pos[0].constructor == Number) { pLeft += pos[0]; } else { switch (pos[0]) { case 'left': pLeft += 0; break; case 'right': pLeft += wnd.width() - this.uiDialog.outerWidth(); break; default: case 'center': pLeft += (wnd.width() - this.uiDialog.outerWidth()) / 2; } } if (pos[1].constructor == Number) { pTop += pos[1]; } else { switch (pos[1]) { case 'top': pTop += 0; break; case 'bottom': // Opera check fixes #3564, can go away with jQuery 1.3 pTop += ($.browser.opera ? window.innerHeight : wnd.height()) - this.uiDialog.outerHeight(); break; default: case 'middle': // Opera check fixes #3564, can go away with jQuery 1.3 pTop += (($.browser.opera ? window.innerHeight : wnd.height()) - this.uiDialog.outerHeight()) / 2; } } // prevent the dialog from being too high (make sure the titlebar // is accessible) pTop = Math.max(pTop, minTop); this.uiDialog.css({top: pTop, left: pLeft}); }, _setData: function(key, value){ (setDataSwitch[key] && this.uiDialog.data(setDataSwitch[key], value)); switch (key) { case "buttons": this._createButtons(value); break; case "closeText": this.uiDialogTitlebarCloseText.text(value); break; case "draggable": (value ? this._makeDraggable() : this.uiDialog.draggable('destroy')); break; case "height": this.uiDialog.height(value); break; case "position": this._position(value); break; case "resizable": var uiDialog = this.uiDialog, isResizable = this.uiDialog.is(':data(resizable)'); // currently resizable, becoming non-resizable (isResizable && !value && uiDialog.resizable('destroy')); // currently resizable, changing handles (isResizable && typeof value == 'string' && uiDialog.resizable('option', 'handles', value)); // currently non-resizable, becoming resizable (isResizable || this._makeResizable(value)); break; case "title": $(".ui-dialog-title", this.uiDialogTitlebar).html(value || ' '); break; case "width": this.uiDialog.width(value); break; } $.widget.prototype._setData.apply(this, arguments); }, _size: function() { /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content * divs will both have width and height set, so we need to reset them */ var options = this.options; // reset content sizing this.element.css({ height: 0, minHeight: 0, width: 'auto' }); // reset wrapper sizing // determine the height of all the non-content elements var nonContentHeight = this.uiDialog.css({ height: 'auto', width: options.width }) .height(); this.element .css({ minHeight: options.minHeight - nonContentHeight, height: options.height == 'auto' ? 'auto' : options.height - nonContentHeight }); } }); $.extend($.ui.dialog, { version: "@VERSION", defaults: { autoOpen: true, bgiframe: false, buttons: {}, closeOnEscape: true, closeText: 'close', draggable: true, height: 'auto', minHeight: 150, minWidth: 150, modal: false, overlay: {}, position: 'center', resizable: true, stack: true, width: 300, zIndex: 1000 }, getter: 'isOpen', uuid: 0, getTitleId: function($el) { return 'ui-dialog-title-' + ($el.attr('id') || ++this.uuid); }, overlay: function(dialog) { this.$el = $.ui.dialog.overlay.create(dialog); } }); $.extend($.ui.dialog.overlay, { instances: [], 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() { $('a, :input').bind($.ui.dialog.overlay.events, function() { // allow use of the element if inside a dialog and // - there are no modal dialogs // - there are modal dialogs, but we are in front of the topmost modal var allow = false; var $dialog = $(this).parents('.ui-dialog'); if ($dialog.length) { var $overlays = $('.ui-dialog-overlay'); if ($overlays.length) { var maxZ = parseInt($overlays.css('z-index'), 10); $overlays.each(function() { maxZ = Math.max(maxZ, parseInt($(this).css('z-index'), 10)); }); allow = parseInt($dialog.css('z-index'), 10) > maxZ; } else { allow = true; } } return allow; }); }, 1); // allow closing by pressing the escape key $(document).bind('keydown.dialog-overlay', function(event) { (dialog.options.closeOnEscape && event.keyCode && event.keyCode == $.ui.keyCode.ESCAPE && dialog.close()); }); // handle window resize $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize); } var $el = $('
').appendTo(document.body) .addClass('ui-dialog-overlay').css($.extend({ borderWidth: 0, margin: 0, padding: 0, position: 'absolute', top: 0, left: 0, width: this.width(), height: this.height() }, dialog.options.overlay)); (dialog.options.bgiframe && $.fn.bgiframe && $el.bgiframe()); this.instances.push($el); return $el; }, destroy: function($el) { this.instances.splice($.inArray(this.instances, $el), 1); if (this.instances.length === 0) { $('a, :input').add([document, window]).unbind('.dialog-overlay'); } $el.remove(); }, height: function() { // handle IE 6 if ($.browser.msie && $.browser.version < 7) { var scrollHeight = Math.max( document.documentElement.scrollHeight, document.body.scrollHeight ); var offsetHeight = Math.max( document.documentElement.offsetHeight, document.body.offsetHeight ); if (scrollHeight < offsetHeight) { return $(window).height() + 'px'; } else { return scrollHeight + 'px'; } // handle Opera } else if ($.browser.opera) { return Math.max( window.innerHeight, $(document).height() ) + 'px'; // handle "good" browsers } else { return $(document).height() + 'px'; } }, width: function() { // handle IE 6 if ($.browser.msie && $.browser.version < 7) { var scrollWidth = Math.max( document.documentElement.scrollWidth, document.body.scrollWidth ); var offsetWidth = Math.max( document.documentElement.offsetWidth, document.body.offsetWidth ); if (scrollWidth < offsetWidth) { return $(window).width() + 'px'; } else { return scrollWidth + 'px'; } // handle Opera } else if ($.browser.opera) { return Math.max( window.innerWidth, $(document).width() ) + '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() }); } }); $.extend($.ui.dialog.overlay.prototype, { destroy: function() { $.ui.dialog.overlay.destroy(this.$el); } }); })(jQuery);