ui.dialog.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. /*
  2. * jQuery UI Dialog 1.7.2
  3. *
  4. * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
  5. * Dual licensed under the MIT (MIT-LICENSE.txt)
  6. * and GPL (GPL-LICENSE.txt) licenses.
  7. *
  8. * http://docs.jquery.com/UI/Dialog
  9. *
  10. * Depends:
  11. * ui.core.js
  12. * ui.draggable.js
  13. * ui.resizable.js
  14. */
  15. (function($) {
  16. var setDataSwitch = {
  17. dragStart: "start.draggable",
  18. drag: "drag.draggable",
  19. dragStop: "stop.draggable",
  20. maxHeight: "maxHeight.resizable",
  21. minHeight: "minHeight.resizable",
  22. maxWidth: "maxWidth.resizable",
  23. minWidth: "minWidth.resizable",
  24. resizeStart: "start.resizable",
  25. resize: "drag.resizable",
  26. resizeStop: "stop.resizable"
  27. },
  28. uiDialogClasses =
  29. 'ui-dialog ' +
  30. 'ui-widget ' +
  31. 'ui-widget-content ' +
  32. 'ui-corner-all ';
  33. $.widget("ui.dialog", {
  34. _init: function() {
  35. this.originalTitle = this.element.attr('title');
  36. var self = this,
  37. options = this.options,
  38. title = options.title || this.originalTitle || ' ',
  39. titleId = $.ui.dialog.getTitleId(this.element),
  40. uiDialog = (this.uiDialog = $('<div/>'))
  41. .appendTo(document.body)
  42. .hide()
  43. .addClass(uiDialogClasses + options.dialogClass)
  44. .css({
  45. position: 'absolute',
  46. overflow: 'hidden',
  47. zIndex: options.zIndex
  48. })
  49. // setting tabIndex makes the div focusable
  50. // setting outline to 0 prevents a border on focus in Mozilla
  51. .attr('tabIndex', -1).css('outline', 0).keydown(function(event) {
  52. (options.closeOnEscape && event.keyCode
  53. && event.keyCode == $.ui.keyCode.ESCAPE && self.close(event));
  54. })
  55. .attr({
  56. role: 'dialog',
  57. 'aria-labelledby': titleId
  58. })
  59. .mousedown(function(event) {
  60. self.moveToTop(false, event);
  61. }),
  62. uiDialogContent = this.element
  63. .show()
  64. .removeAttr('title')
  65. .addClass(
  66. 'ui-dialog-content ' +
  67. 'ui-widget-content')
  68. .appendTo(uiDialog),
  69. uiDialogTitlebar = (this.uiDialogTitlebar = $('<div></div>'))
  70. .addClass(
  71. 'ui-dialog-titlebar ' +
  72. 'ui-widget-header ' +
  73. 'ui-corner-all ' +
  74. 'ui-helper-clearfix'
  75. )
  76. .prependTo(uiDialog),
  77. uiDialogTitlebarClose = $('<a href="#"/>')
  78. .addClass(
  79. 'ui-dialog-titlebar-close ' +
  80. 'ui-corner-all'
  81. )
  82. .attr('role', 'button')
  83. .hover(
  84. function() {
  85. uiDialogTitlebarClose.addClass('ui-state-hover');
  86. },
  87. function() {
  88. uiDialogTitlebarClose.removeClass('ui-state-hover');
  89. }
  90. )
  91. .focus(function() {
  92. uiDialogTitlebarClose.addClass('ui-state-focus');
  93. })
  94. .blur(function() {
  95. uiDialogTitlebarClose.removeClass('ui-state-focus');
  96. })
  97. .mousedown(function(ev) {
  98. ev.stopPropagation();
  99. })
  100. .click(function(event) {
  101. self.close(event);
  102. return false;
  103. })
  104. .appendTo(uiDialogTitlebar),
  105. uiDialogTitlebarCloseText = (this.uiDialogTitlebarCloseText = $('<span/>'))
  106. .addClass(
  107. 'ui-icon ' +
  108. 'ui-icon-closethick'
  109. )
  110. .text(options.closeText)
  111. .appendTo(uiDialogTitlebarClose),
  112. uiDialogTitle = $('<span/>')
  113. .addClass('ui-dialog-title')
  114. .attr('id', titleId)
  115. .html(title)
  116. .prependTo(uiDialogTitlebar);
  117. uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection();
  118. (options.draggable && $.fn.draggable && this._makeDraggable());
  119. (options.resizable && $.fn.resizable && this._makeResizable());
  120. this._createButtons(options.buttons);
  121. this._isOpen = false;
  122. (options.bgiframe && $.fn.bgiframe && uiDialog.bgiframe());
  123. (options.autoOpen && this.open());
  124. },
  125. destroy: function() {
  126. (this.overlay && this.overlay.destroy());
  127. this.uiDialog.hide();
  128. this.element
  129. .unbind('.dialog')
  130. .removeData('dialog')
  131. .removeClass('ui-dialog-content ui-widget-content')
  132. .hide().appendTo('body');
  133. this.uiDialog.remove();
  134. (this.originalTitle && this.element.attr('title', this.originalTitle));
  135. },
  136. close: function(event) {
  137. var self = this;
  138. if (false === self._trigger('beforeclose', event)) {
  139. return;
  140. }
  141. (self.overlay && self.overlay.destroy());
  142. self.uiDialog.unbind('keypress.ui-dialog');
  143. (self.options.hide
  144. ? self.uiDialog.hide(self.options.hide, function() {
  145. self._trigger('close', event);
  146. })
  147. : self.uiDialog.hide() && self._trigger('close', event));
  148. $.ui.dialog.overlay.resize();
  149. self._isOpen = false;
  150. // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
  151. if (self.options.modal) {
  152. var maxZ = 0;
  153. $('.ui-dialog').each(function() {
  154. if (this != self.uiDialog[0]) {
  155. maxZ = Math.max(maxZ, $(this).css('z-index'));
  156. }
  157. });
  158. $.ui.dialog.maxZ = maxZ;
  159. }
  160. },
  161. isOpen: function() {
  162. return this._isOpen;
  163. },
  164. // the force parameter allows us to move modal dialogs to their correct
  165. // position on open
  166. moveToTop: function(force, event) {
  167. if ((this.options.modal && !force)
  168. || (!this.options.stack && !this.options.modal)) {
  169. return this._trigger('focus', event);
  170. }
  171. if (this.options.zIndex > $.ui.dialog.maxZ) {
  172. $.ui.dialog.maxZ = this.options.zIndex;
  173. }
  174. (this.overlay && this.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = ++$.ui.dialog.maxZ));
  175. //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
  176. // http://ui.jquery.com/bugs/ticket/3193
  177. var saveScroll = { scrollTop: this.element.attr('scrollTop'), scrollLeft: this.element.attr('scrollLeft') };
  178. this.uiDialog.css('z-index', ++$.ui.dialog.maxZ);
  179. this.element.attr(saveScroll);
  180. this._trigger('focus', event);
  181. },
  182. open: function() {
  183. if (this._isOpen) { return; }
  184. var options = this.options,
  185. uiDialog = this.uiDialog;
  186. this.overlay = options.modal ? new $.ui.dialog.overlay(this) : null;
  187. (uiDialog.next().length && uiDialog.appendTo('body'));
  188. this._size();
  189. this._position(options.position);
  190. uiDialog.show(options.show);
  191. this.moveToTop(true);
  192. // prevent tabbing out of modal dialogs
  193. (options.modal && uiDialog.bind('keypress.ui-dialog', function(event) {
  194. if (event.keyCode != $.ui.keyCode.TAB) {
  195. return;
  196. }
  197. var tabbables = $(':tabbable', this),
  198. first = tabbables.filter(':first')[0],
  199. last = tabbables.filter(':last')[0];
  200. if (event.target == last && !event.shiftKey) {
  201. setTimeout(function() {
  202. first.focus();
  203. }, 1);
  204. } else if (event.target == first && event.shiftKey) {
  205. setTimeout(function() {
  206. last.focus();
  207. }, 1);
  208. }
  209. }));
  210. // set focus to the first tabbable element in the content area or the first button
  211. // if there are no tabbable elements, set focus on the dialog itself
  212. $([])
  213. .add(uiDialog.find('.ui-dialog-content :tabbable:first'))
  214. .add(uiDialog.find('.ui-dialog-buttonpane :tabbable:first'))
  215. .add(uiDialog)
  216. .filter(':first')
  217. .focus();
  218. this._trigger('open');
  219. this._isOpen = true;
  220. },
  221. _createButtons: function(buttons) {
  222. var self = this,
  223. hasButtons = false,
  224. uiDialogButtonPane = $('<div></div>')
  225. .addClass(
  226. 'ui-dialog-buttonpane ' +
  227. 'ui-widget-content ' +
  228. 'ui-helper-clearfix'
  229. );
  230. // if we already have a button pane, remove it
  231. this.uiDialog.find('.ui-dialog-buttonpane').remove();
  232. (typeof buttons == 'object' && buttons !== null &&
  233. $.each(buttons, function() { return !(hasButtons = true); }));
  234. if (hasButtons) {
  235. $.each(buttons, function(name, fn) {
  236. $('<button type="button"></button>')
  237. .addClass(
  238. 'ui-state-default ' +
  239. 'ui-corner-all'
  240. )
  241. .text(name)
  242. .click(function() { fn.apply(self.element[0], arguments); })
  243. .hover(
  244. function() {
  245. $(this).addClass('ui-state-hover');
  246. },
  247. function() {
  248. $(this).removeClass('ui-state-hover');
  249. }
  250. )
  251. .focus(function() {
  252. $(this).addClass('ui-state-focus');
  253. })
  254. .blur(function() {
  255. $(this).removeClass('ui-state-focus');
  256. })
  257. .appendTo(uiDialogButtonPane);
  258. });
  259. uiDialogButtonPane.appendTo(this.uiDialog);
  260. }
  261. },
  262. _makeDraggable: function() {
  263. var self = this,
  264. options = this.options,
  265. heightBeforeDrag;
  266. this.uiDialog.draggable({
  267. cancel: '.ui-dialog-content',
  268. handle: '.ui-dialog-titlebar',
  269. containment: 'document',
  270. start: function() {
  271. heightBeforeDrag = options.height;
  272. $(this).height($(this).height()).addClass("ui-dialog-dragging");
  273. (options.dragStart && options.dragStart.apply(self.element[0], arguments));
  274. },
  275. drag: function() {
  276. (options.drag && options.drag.apply(self.element[0], arguments));
  277. },
  278. stop: function() {
  279. $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag);
  280. (options.dragStop && options.dragStop.apply(self.element[0], arguments));
  281. $.ui.dialog.overlay.resize();
  282. }
  283. });
  284. },
  285. _makeResizable: function(handles) {
  286. handles = (handles === undefined ? this.options.resizable : handles);
  287. var self = this,
  288. options = this.options,
  289. resizeHandles = typeof handles == 'string'
  290. ? handles
  291. : 'n,e,s,w,se,sw,ne,nw';
  292. this.uiDialog.resizable({
  293. cancel: '.ui-dialog-content',
  294. alsoResize: this.element,
  295. maxWidth: options.maxWidth,
  296. maxHeight: options.maxHeight,
  297. minWidth: options.minWidth,
  298. minHeight: options.minHeight,
  299. start: function() {
  300. $(this).addClass("ui-dialog-resizing");
  301. (options.resizeStart && options.resizeStart.apply(self.element[0], arguments));
  302. },
  303. resize: function() {
  304. (options.resize && options.resize.apply(self.element[0], arguments));
  305. },
  306. handles: resizeHandles,
  307. stop: function() {
  308. $(this).removeClass("ui-dialog-resizing");
  309. options.height = $(this).height();
  310. options.width = $(this).width();
  311. (options.resizeStop && options.resizeStop.apply(self.element[0], arguments));
  312. $.ui.dialog.overlay.resize();
  313. }
  314. })
  315. .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se');
  316. },
  317. _position: function(pos) {
  318. var wnd = $(window), doc = $(document),
  319. pTop = doc.scrollTop(), pLeft = doc.scrollLeft(),
  320. minTop = pTop;
  321. if ($.inArray(pos, ['center','top','right','bottom','left']) >= 0) {
  322. pos = [
  323. pos == 'right' || pos == 'left' ? pos : 'center',
  324. pos == 'top' || pos == 'bottom' ? pos : 'middle'
  325. ];
  326. }
  327. if (pos.constructor != Array) {
  328. pos = ['center', 'middle'];
  329. }
  330. if (pos[0].constructor == Number) {
  331. pLeft += pos[0];
  332. } else {
  333. switch (pos[0]) {
  334. case 'left':
  335. pLeft += 0;
  336. break;
  337. case 'right':
  338. pLeft += wnd.width() - this.uiDialog.outerWidth();
  339. break;
  340. default:
  341. case 'center':
  342. pLeft += (wnd.width() - this.uiDialog.outerWidth()) / 2;
  343. }
  344. }
  345. if (pos[1].constructor == Number) {
  346. pTop += pos[1];
  347. } else {
  348. switch (pos[1]) {
  349. case 'top':
  350. pTop += 0;
  351. break;
  352. case 'bottom':
  353. pTop += wnd.height() - this.uiDialog.outerHeight();
  354. break;
  355. default:
  356. case 'middle':
  357. pTop += (wnd.height() - this.uiDialog.outerHeight()) / 2;
  358. }
  359. }
  360. // prevent the dialog from being too high (make sure the titlebar
  361. // is accessible)
  362. pTop = Math.max(pTop, minTop);
  363. this.uiDialog.css({top: pTop, left: pLeft});
  364. },
  365. _setData: function(key, value){
  366. (setDataSwitch[key] && this.uiDialog.data(setDataSwitch[key], value));
  367. switch (key) {
  368. case "buttons":
  369. this._createButtons(value);
  370. break;
  371. case "closeText":
  372. this.uiDialogTitlebarCloseText.text(value);
  373. break;
  374. case "dialogClass":
  375. this.uiDialog
  376. .removeClass(this.options.dialogClass)
  377. .addClass(uiDialogClasses + value);
  378. break;
  379. case "draggable":
  380. (value
  381. ? this._makeDraggable()
  382. : this.uiDialog.draggable('destroy'));
  383. break;
  384. case "height":
  385. this.uiDialog.height(value);
  386. break;
  387. case "position":
  388. this._position(value);
  389. break;
  390. case "resizable":
  391. var uiDialog = this.uiDialog,
  392. isResizable = this.uiDialog.is(':data(resizable)');
  393. // currently resizable, becoming non-resizable
  394. (isResizable && !value && uiDialog.resizable('destroy'));
  395. // currently resizable, changing handles
  396. (isResizable && typeof value == 'string' &&
  397. uiDialog.resizable('option', 'handles', value));
  398. // currently non-resizable, becoming resizable
  399. (isResizable || this._makeResizable(value));
  400. break;
  401. case "title":
  402. $(".ui-dialog-title", this.uiDialogTitlebar).html(value || '&nbsp;');
  403. break;
  404. case "width":
  405. this.uiDialog.width(value);
  406. break;
  407. }
  408. $.widget.prototype._setData.apply(this, arguments);
  409. },
  410. _size: function() {
  411. /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
  412. * divs will both have width and height set, so we need to reset them
  413. */
  414. var options = this.options;
  415. // reset content sizing
  416. this.element.css({
  417. height: 0,
  418. minHeight: 0,
  419. width: 'auto'
  420. });
  421. // reset wrapper sizing
  422. // determine the height of all the non-content elements
  423. var nonContentHeight = this.uiDialog.css({
  424. height: 'auto',
  425. width: options.width
  426. })
  427. .height();
  428. this.element
  429. .css({
  430. minHeight: Math.max(options.minHeight - nonContentHeight, 0),
  431. height: options.height == 'auto'
  432. ? 'auto'
  433. : Math.max(options.height - nonContentHeight, 0)
  434. });
  435. }
  436. });
  437. $.extend($.ui.dialog, {
  438. version: "1.7.2",
  439. defaults: {
  440. autoOpen: true,
  441. bgiframe: false,
  442. buttons: {},
  443. closeOnEscape: true,
  444. closeText: 'close',
  445. dialogClass: '',
  446. draggable: true,
  447. hide: null,
  448. height: 'auto',
  449. maxHeight: false,
  450. maxWidth: false,
  451. minHeight: 150,
  452. minWidth: 150,
  453. modal: false,
  454. position: 'center',
  455. resizable: true,
  456. show: null,
  457. stack: true,
  458. title: '',
  459. width: 300,
  460. zIndex: 1000
  461. },
  462. getter: 'isOpen',
  463. uuid: 0,
  464. maxZ: 0,
  465. getTitleId: function($el) {
  466. return 'ui-dialog-title-' + ($el.attr('id') || ++this.uuid);
  467. },
  468. overlay: function(dialog) {
  469. this.$el = $.ui.dialog.overlay.create(dialog);
  470. }
  471. });
  472. $.extend($.ui.dialog.overlay, {
  473. instances: [],
  474. maxZ: 0,
  475. events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
  476. function(event) { return event + '.dialog-overlay'; }).join(' '),
  477. create: function(dialog) {
  478. if (this.instances.length === 0) {
  479. // prevent use of anchors and inputs
  480. // we use a setTimeout in case the overlay is created from an
  481. // event that we're going to be cancelling (see #2804)
  482. setTimeout(function() {
  483. // handle $(el).dialog().dialog('close') (see #4065)
  484. if ($.ui.dialog.overlay.instances.length) {
  485. $(document).bind($.ui.dialog.overlay.events, function(event) {
  486. var dialogZ = $(event.target).parents('.ui-dialog').css('zIndex') || 0;
  487. return (dialogZ > $.ui.dialog.overlay.maxZ);
  488. });
  489. }
  490. }, 1);
  491. // allow closing by pressing the escape key
  492. $(document).bind('keydown.dialog-overlay', function(event) {
  493. (dialog.options.closeOnEscape && event.keyCode
  494. && event.keyCode == $.ui.keyCode.ESCAPE && dialog.close(event));
  495. });
  496. // handle window resize
  497. $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
  498. }
  499. var $el = $('<div></div>').appendTo(document.body)
  500. .addClass('ui-widget-overlay').css({
  501. width: this.width(),
  502. height: this.height()
  503. });
  504. (dialog.options.bgiframe && $.fn.bgiframe && $el.bgiframe());
  505. this.instances.push($el);
  506. return $el;
  507. },
  508. destroy: function($el) {
  509. this.instances.splice($.inArray(this.instances, $el), 1);
  510. if (this.instances.length === 0) {
  511. $([document, window]).unbind('.dialog-overlay');
  512. }
  513. $el.remove();
  514. // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
  515. var maxZ = 0;
  516. $.each(this.instances, function() {
  517. maxZ = Math.max(maxZ, this.css('z-index'));
  518. });
  519. this.maxZ = maxZ;
  520. },
  521. height: function() {
  522. // handle IE 6
  523. if ($.browser.msie && $.browser.version < 7) {
  524. var scrollHeight = Math.max(
  525. document.documentElement.scrollHeight,
  526. document.body.scrollHeight
  527. );
  528. var offsetHeight = Math.max(
  529. document.documentElement.offsetHeight,
  530. document.body.offsetHeight
  531. );
  532. if (scrollHeight < offsetHeight) {
  533. return $(window).height() + 'px';
  534. } else {
  535. return scrollHeight + 'px';
  536. }
  537. // handle "good" browsers
  538. } else {
  539. return $(document).height() + 'px';
  540. }
  541. },
  542. width: function() {
  543. // handle IE 6
  544. if ($.browser.msie && $.browser.version < 7) {
  545. var scrollWidth = Math.max(
  546. document.documentElement.scrollWidth,
  547. document.body.scrollWidth
  548. );
  549. var offsetWidth = Math.max(
  550. document.documentElement.offsetWidth,
  551. document.body.offsetWidth
  552. );
  553. if (scrollWidth < offsetWidth) {
  554. return $(window).width() + 'px';
  555. } else {
  556. return scrollWidth + 'px';
  557. }
  558. // handle "good" browsers
  559. } else {
  560. return $(document).width() + 'px';
  561. }
  562. },
  563. resize: function() {
  564. /* If the dialog is draggable and the user drags it past the
  565. * right edge of the window, the document becomes wider so we
  566. * need to stretch the overlay. If the user then drags the
  567. * dialog back to the left, the document will become narrower,
  568. * so we need to shrink the overlay to the appropriate size.
  569. * This is handled by shrinking the overlay before setting it
  570. * to the full document size.
  571. */
  572. var $overlays = $([]);
  573. $.each($.ui.dialog.overlay.instances, function() {
  574. $overlays = $overlays.add(this);
  575. });
  576. $overlays.css({
  577. width: 0,
  578. height: 0
  579. }).css({
  580. width: $.ui.dialog.overlay.width(),
  581. height: $.ui.dialog.overlay.height()
  582. });
  583. }
  584. });
  585. $.extend($.ui.dialog.overlay.prototype, {
  586. destroy: function() {
  587. $.ui.dialog.overlay.destroy(this.$el);
  588. }
  589. });
  590. })(jQuery);