bootstrap-dialog.js 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  1. /* global define */
  2. /* ================================================
  3. * Make use of Bootstrap's modal more monkey-friendly.
  4. *
  5. * For Bootstrap 3.
  6. *
  7. * javanoob@hotmail.com
  8. *
  9. * https://github.com/nakupanda/bootstrap3-dialog
  10. *
  11. * Licensed under The MIT License.
  12. * ================================================ */
  13. (function(root, factory) {
  14. "use strict";
  15. // CommonJS module is defined
  16. if (typeof module !== 'undefined' && module.exports) {
  17. module.exports = factory(require('jquery')(root));
  18. }
  19. // AMD module is defined
  20. else if (typeof define === "function" && define.amd) {
  21. define("bootstrap-dialog", ["jquery"], function($) {
  22. return factory($);
  23. });
  24. } else {
  25. // planted over the root!
  26. root.BootstrapDialog = factory(root.jQuery);
  27. }
  28. }(this, function($) {
  29. "use strict";
  30. var BootstrapDialog = function(options) {
  31. this.defaultOptions = $.extend(true, {
  32. id: BootstrapDialog.newGuid(),
  33. buttons: [],
  34. data: {},
  35. onshow: null,
  36. onshown: null,
  37. onhide: null,
  38. onhidden: null
  39. }, BootstrapDialog.defaultOptions);
  40. this.indexedButtons = {};
  41. this.registeredButtonHotkeys = {};
  42. this.draggableData = {
  43. isMouseDown: false,
  44. mouseOffset: {}
  45. };
  46. this.realized = false;
  47. this.opened = false;
  48. this.initOptions(options);
  49. this.holdThisInstance();
  50. };
  51. /**
  52. * Some constants.
  53. */
  54. BootstrapDialog.NAMESPACE = 'bootstrap-dialog';
  55. BootstrapDialog.TYPE_DEFAULT = 'type-default';
  56. BootstrapDialog.TYPE_INFO = 'type-info';
  57. BootstrapDialog.TYPE_PRIMARY = 'type-primary';
  58. BootstrapDialog.TYPE_SUCCESS = 'type-success';
  59. BootstrapDialog.TYPE_WARNING = 'type-warning';
  60. BootstrapDialog.TYPE_DANGER = 'type-danger';
  61. BootstrapDialog.DEFAULT_TEXTS = {};
  62. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_DEFAULT] = 'Information';
  63. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_INFO] = 'Information';
  64. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_PRIMARY] = 'Information';
  65. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_SUCCESS] = 'Success';
  66. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_WARNING] = 'Warning';
  67. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_DANGER] = 'Danger';
  68. BootstrapDialog.SIZE_NORMAL = 'size-normal';
  69. BootstrapDialog.SIZE_LARGE = 'size-large';
  70. BootstrapDialog.BUTTON_SIZES = {};
  71. BootstrapDialog.BUTTON_SIZES[BootstrapDialog.SIZE_NORMAL] = '';
  72. BootstrapDialog.BUTTON_SIZES[BootstrapDialog.SIZE_LARGE] = 'btn-lg';
  73. BootstrapDialog.ICON_SPINNER = 'glyphicon glyphicon-asterisk';
  74. BootstrapDialog.ZINDEX_BACKDROP = 1040;
  75. BootstrapDialog.ZINDEX_MODAL = 1050;
  76. /**
  77. * Default options.
  78. */
  79. BootstrapDialog.defaultOptions = {
  80. type: BootstrapDialog.TYPE_PRIMARY,
  81. size: BootstrapDialog.SIZE_NORMAL,
  82. cssClass: '',
  83. title: null,
  84. message: null,
  85. nl2br: true,
  86. closable: true,
  87. closeByBackdrop: true,
  88. closeByKeyboard: true,
  89. spinicon: BootstrapDialog.ICON_SPINNER,
  90. autodestroy: true,
  91. draggable: false,
  92. animate: true
  93. };
  94. /**
  95. * Config default options.
  96. */
  97. BootstrapDialog.configDefaultOptions = function(options) {
  98. BootstrapDialog.defaultOptions = $.extend(true, BootstrapDialog.defaultOptions, options);
  99. };
  100. /**
  101. * Open / Close all created dialogs all at once.
  102. */
  103. BootstrapDialog.dialogs = {};
  104. BootstrapDialog.openAll = function() {
  105. $.each(BootstrapDialog.dialogs, function(id, dialogInstance) {
  106. dialogInstance.open();
  107. });
  108. };
  109. BootstrapDialog.closeAll = function() {
  110. $.each(BootstrapDialog.dialogs, function(id, dialogInstance) {
  111. dialogInstance.close();
  112. });
  113. };
  114. /**
  115. * Move focus to next visible dialog.
  116. */
  117. BootstrapDialog.moveFocus = function() {
  118. var lastDialogInstance = null;
  119. $.each(BootstrapDialog.dialogs, function(id, dialogInstance) {
  120. lastDialogInstance = dialogInstance;
  121. });
  122. if (lastDialogInstance !== null && lastDialogInstance.isRealized()) {
  123. lastDialogInstance.getModal().focus();
  124. }
  125. };
  126. BootstrapDialog.prototype = {
  127. constructor: BootstrapDialog,
  128. initOptions: function(options) {
  129. this.options = $.extend(true, this.defaultOptions, options);
  130. return this;
  131. },
  132. holdThisInstance: function() {
  133. BootstrapDialog.dialogs[this.getId()] = this;
  134. return this;
  135. },
  136. initModalStuff: function() {
  137. this.setModal(this.createModal())
  138. .setModalDialog(this.createModalDialog())
  139. .setModalContent(this.createModalContent())
  140. .setModalHeader(this.createModalHeader())
  141. .setModalBody(this.createModalBody())
  142. .setModalFooter(this.createModalFooter());
  143. this.getModal().append(this.getModalDialog());
  144. this.getModalDialog().append(this.getModalContent());
  145. this.getModalContent()
  146. .append(this.getModalHeader())
  147. .append(this.getModalBody())
  148. .append(this.getModalFooter());
  149. return this;
  150. },
  151. createModal: function() {
  152. var $modal = $('<div class="modal" tabindex="-1"></div>');
  153. $modal.prop('id', this.getId());
  154. return $modal;
  155. },
  156. getModal: function() {
  157. return this.$modal;
  158. },
  159. setModal: function($modal) {
  160. this.$modal = $modal;
  161. return this;
  162. },
  163. createModalDialog: function() {
  164. return $('<div class="modal-dialog"></div>');
  165. },
  166. getModalDialog: function() {
  167. return this.$modalDialog;
  168. },
  169. setModalDialog: function($modalDialog) {
  170. this.$modalDialog = $modalDialog;
  171. return this;
  172. },
  173. createModalContent: function() {
  174. return $('<div class="modal-content"></div>');
  175. },
  176. getModalContent: function() {
  177. return this.$modalContent;
  178. },
  179. setModalContent: function($modalContent) {
  180. this.$modalContent = $modalContent;
  181. return this;
  182. },
  183. createModalHeader: function() {
  184. return $('<div class="modal-header"></div>');
  185. },
  186. getModalHeader: function() {
  187. return this.$modalHeader;
  188. },
  189. setModalHeader: function($modalHeader) {
  190. this.$modalHeader = $modalHeader;
  191. return this;
  192. },
  193. createModalBody: function() {
  194. return $('<div class="modal-body"></div>');
  195. },
  196. getModalBody: function() {
  197. return this.$modalBody;
  198. },
  199. setModalBody: function($modalBody) {
  200. this.$modalBody = $modalBody;
  201. return this;
  202. },
  203. createModalFooter: function() {
  204. return $('<div class="modal-footer"></div>');
  205. },
  206. getModalFooter: function() {
  207. return this.$modalFooter;
  208. },
  209. setModalFooter: function($modalFooter) {
  210. this.$modalFooter = $modalFooter;
  211. return this;
  212. },
  213. createDynamicContent: function(rawContent) {
  214. var content = null;
  215. if (typeof rawContent === 'function') {
  216. content = rawContent.call(rawContent, this);
  217. } else {
  218. content = rawContent;
  219. }
  220. if (typeof content === 'string') {
  221. content = this.formatStringContent(content);
  222. }
  223. return content;
  224. },
  225. formatStringContent: function(content) {
  226. if (this.options.nl2br) {
  227. return content.replace(/\r\n/g, '<br />').replace(/[\r\n]/g, '<br />');
  228. }
  229. return content;
  230. },
  231. setData: function(key, value) {
  232. this.options.data[key] = value;
  233. return this;
  234. },
  235. getData: function(key) {
  236. return this.options.data[key];
  237. },
  238. setId: function(id) {
  239. this.options.id = id;
  240. return this;
  241. },
  242. getId: function() {
  243. return this.options.id;
  244. },
  245. getType: function() {
  246. return this.options.type;
  247. },
  248. setType: function(type) {
  249. this.options.type = type;
  250. return this;
  251. },
  252. getSize: function() {
  253. return this.options.size;
  254. },
  255. setSize: function(size) {
  256. this.options.size = size;
  257. return this;
  258. },
  259. getCssClass: function() {
  260. return this.options.cssClass;
  261. },
  262. setCssClass: function(cssClass) {
  263. this.options.cssClass = cssClass;
  264. return this;
  265. },
  266. getTitle: function() {
  267. return this.options.title;
  268. },
  269. setTitle: function(title) {
  270. this.options.title = title;
  271. this.updateTitle();
  272. return this;
  273. },
  274. updateTitle: function() {
  275. if (this.isRealized()) {
  276. var title = this.getTitle() !== null ? this.createDynamicContent(this.getTitle()) : this.getDefaultText();
  277. this.getModalHeader().find('.' + this.getNamespace('title')).html('').append(title);
  278. }
  279. return this;
  280. },
  281. getMessage: function() {
  282. return this.options.message;
  283. },
  284. setMessage: function(message) {
  285. this.options.message = message;
  286. this.updateMessage();
  287. return this;
  288. },
  289. updateMessage: function() {
  290. if (this.isRealized()) {
  291. var message = this.createDynamicContent(this.getMessage());
  292. this.getModalBody().find('.' + this.getNamespace('message')).html('').append(message);
  293. }
  294. return this;
  295. },
  296. isClosable: function() {
  297. return this.options.closable;
  298. },
  299. setClosable: function(closable) {
  300. this.options.closable = closable;
  301. this.updateClosable();
  302. return this;
  303. },
  304. setCloseByBackdrop: function(closeByBackdrop) {
  305. this.options.closeByBackdrop = closeByBackdrop;
  306. return this;
  307. },
  308. canCloseByBackdrop: function() {
  309. return this.options.closeByBackdrop;
  310. },
  311. setCloseByKeyboard: function(closeByKeyboard) {
  312. this.options.closeByKeyboard = closeByKeyboard;
  313. return this;
  314. },
  315. canCloseByKeyboard: function() {
  316. return this.options.closeByKeyboard;
  317. },
  318. isAnimate: function() {
  319. return this.options.animate;
  320. },
  321. setAnimate: function(animate) {
  322. this.options.animate = animate;
  323. return this;
  324. },
  325. updateAnimate: function() {
  326. if (this.isRealized()) {
  327. this.getModal().toggleClass('fade', this.isAnimate());
  328. }
  329. return this;
  330. },
  331. getSpinicon: function() {
  332. return this.options.spinicon;
  333. },
  334. setSpinicon: function(spinicon) {
  335. this.options.spinicon = spinicon;
  336. return this;
  337. },
  338. addButton: function(button) {
  339. this.options.buttons.push(button);
  340. return this;
  341. },
  342. addButtons: function(buttons) {
  343. var that = this;
  344. $.each(buttons, function(index, button) {
  345. that.addButton(button);
  346. });
  347. return this;
  348. },
  349. getButtons: function() {
  350. return this.options.buttons;
  351. },
  352. setButtons: function(buttons) {
  353. this.options.buttons = buttons;
  354. this.updateButtons();
  355. return this;
  356. },
  357. /**
  358. * If there is id provided for a button option, it will be in dialog.indexedButtons list.
  359. *
  360. * In that case you can use dialog.getButton(id) to find the button.
  361. *
  362. * @param {type} id
  363. * @returns {undefined}
  364. */
  365. getButton: function(id) {
  366. if (typeof this.indexedButtons[id] !== 'undefined') {
  367. return this.indexedButtons[id];
  368. }
  369. return null;
  370. },
  371. getButtonSize: function() {
  372. if (typeof BootstrapDialog.BUTTON_SIZES[this.getSize()] !== 'undefined') {
  373. return BootstrapDialog.BUTTON_SIZES[this.getSize()];
  374. }
  375. return '';
  376. },
  377. updateButtons: function() {
  378. if (this.isRealized()) {
  379. if (this.getButtons().length === 0) {
  380. this.getModalFooter().hide();
  381. } else {
  382. this.getModalFooter().find('.' + this.getNamespace('footer')).html('').append(this.createFooterButtons());
  383. }
  384. }
  385. return this;
  386. },
  387. isAutodestroy: function() {
  388. return this.options.autodestroy;
  389. },
  390. setAutodestroy: function(autodestroy) {
  391. this.options.autodestroy = autodestroy;
  392. },
  393. getDefaultText: function() {
  394. return BootstrapDialog.DEFAULT_TEXTS[this.getType()];
  395. },
  396. getNamespace: function(name) {
  397. return BootstrapDialog.NAMESPACE + '-' + name;
  398. },
  399. createHeaderContent: function() {
  400. var $container = $('<div></div>');
  401. $container.addClass(this.getNamespace('header'));
  402. // title
  403. $container.append(this.createTitleContent());
  404. // Close button
  405. $container.prepend(this.createCloseButton());
  406. return $container;
  407. },
  408. createTitleContent: function() {
  409. var $title = $('<div></div>');
  410. $title.addClass(this.getNamespace('title'));
  411. return $title;
  412. },
  413. createCloseButton: function() {
  414. var $container = $('<div></div>');
  415. $container.addClass(this.getNamespace('close-button'));
  416. var $icon = $('<button class="close">&times;</button>');
  417. $container.append($icon);
  418. $container.on('click', {dialog: this}, function(event) {
  419. event.data.dialog.close();
  420. });
  421. return $container;
  422. },
  423. createBodyContent: function() {
  424. var $container = $('<div></div>');
  425. $container.addClass(this.getNamespace('body'));
  426. // Message
  427. $container.append(this.createMessageContent());
  428. return $container;
  429. },
  430. createMessageContent: function() {
  431. var $message = $('<div></div>');
  432. $message.addClass(this.getNamespace('message'));
  433. return $message;
  434. },
  435. createFooterContent: function() {
  436. var $container = $('<div></div>');
  437. $container.addClass(this.getNamespace('footer'));
  438. return $container;
  439. },
  440. createFooterButtons: function() {
  441. var that = this;
  442. var $container = $('<div></div>');
  443. $container.addClass(this.getNamespace('footer-buttons'));
  444. this.indexedButtons = {};
  445. $.each(this.options.buttons, function(index, button) {
  446. if (!button.id) {
  447. button.id = BootstrapDialog.newGuid();
  448. }
  449. var $button = that.createButton(button);
  450. that.indexedButtons[button.id] = $button;
  451. $container.append($button);
  452. });
  453. return $container;
  454. },
  455. createButton: function(button) {
  456. var $button = $('<button class="btn"></button>');
  457. $button.addClass(this.getButtonSize());
  458. $button.prop('id', button.id);
  459. // Icon
  460. if (typeof button.icon !== 'undefined' && $.trim(button.icon) !== '') {
  461. $button.append(this.createButtonIcon(button.icon));
  462. }
  463. // Label
  464. if (typeof button.label !== 'undefined') {
  465. $button.append(button.label);
  466. }
  467. // Css class
  468. if (typeof button.cssClass !== 'undefined' && $.trim(button.cssClass) !== '') {
  469. $button.addClass(button.cssClass);
  470. } else {
  471. $button.addClass('btn-default');
  472. }
  473. // Hotkey
  474. if (typeof button.hotkey !== 'undefined') {
  475. this.registeredButtonHotkeys[button.hotkey] = $button;
  476. }
  477. // Button on click
  478. $button.on('click', {dialog: this, $button: $button, button: button}, function(event) {
  479. var dialog = event.data.dialog;
  480. var $button = event.data.$button;
  481. var button = event.data.button;
  482. if (typeof button.action === 'function') {
  483. button.action.call($button, dialog);
  484. }
  485. if (button.autospin) {
  486. $button.toggleSpin(true);
  487. }
  488. });
  489. // Dynamically add extra functions to $button
  490. this.enhanceButton($button);
  491. return $button;
  492. },
  493. /**
  494. * Dynamically add extra functions to $button
  495. *
  496. * Using '$this' to reference 'this' is just for better readability.
  497. *
  498. * @param {type} $button
  499. * @returns {_L13.BootstrapDialog.prototype}
  500. */
  501. enhanceButton: function($button) {
  502. $button.dialog = this;
  503. // Enable / Disable
  504. $button.toggleEnable = function(enable) {
  505. var $this = this;
  506. if (typeof enable !== 'undefined') {
  507. $this.prop("disabled", !enable).toggleClass('disabled', !enable);
  508. } else {
  509. $this.prop("disabled", !$this.prop("disabled"));
  510. }
  511. return $this;
  512. };
  513. $button.enable = function() {
  514. var $this = this;
  515. $this.toggleEnable(true);
  516. return $this;
  517. };
  518. $button.disable = function() {
  519. var $this = this;
  520. $this.toggleEnable(false);
  521. return $this;
  522. };
  523. // Icon spinning, helpful for indicating ajax loading status.
  524. $button.toggleSpin = function(spin) {
  525. var $this = this;
  526. var dialog = $this.dialog;
  527. var $icon = $this.find('.' + dialog.getNamespace('button-icon'));
  528. if (typeof spin === 'undefined') {
  529. spin = !($button.find('.icon-spin').length > 0);
  530. }
  531. if (spin) {
  532. $icon.hide();
  533. $button.prepend(dialog.createButtonIcon(dialog.getSpinicon()).addClass('icon-spin'));
  534. } else {
  535. $icon.show();
  536. $button.find('.icon-spin').remove();
  537. }
  538. return $this;
  539. };
  540. $button.spin = function() {
  541. var $this = this;
  542. $this.toggleSpin(true);
  543. return $this;
  544. };
  545. $button.stopSpin = function() {
  546. var $this = this;
  547. $this.toggleSpin(false);
  548. return $this;
  549. };
  550. return this;
  551. },
  552. createButtonIcon: function(icon) {
  553. var $icon = $('<span></span>');
  554. $icon.addClass(this.getNamespace('button-icon')).addClass(icon);
  555. return $icon;
  556. },
  557. /**
  558. * Invoke this only after the dialog is realized.
  559. *
  560. * @param {type} enable
  561. * @returns {undefined}
  562. */
  563. enableButtons: function(enable) {
  564. $.each(this.indexedButtons, function(id, $button) {
  565. $button.toggleEnable(enable);
  566. });
  567. return this;
  568. },
  569. /**
  570. * Invoke this only after the dialog is realized.
  571. *
  572. * @returns {undefined}
  573. */
  574. updateClosable: function() {
  575. if (this.isRealized()) {
  576. // Close button
  577. this.getModalHeader().find('.' + this.getNamespace('close-button')).toggle(this.isClosable());
  578. }
  579. return this;
  580. },
  581. /**
  582. * Set handler for modal event 'show.bs.modal'.
  583. * This is a setter!
  584. */
  585. onShow: function(onshow) {
  586. this.options.onshow = onshow;
  587. return this;
  588. },
  589. /**
  590. * Set handler for modal event 'shown.bs.modal'.
  591. * This is a setter!
  592. */
  593. onShown: function(onshown) {
  594. this.options.onshown = onshown;
  595. return this;
  596. },
  597. /**
  598. * Set handler for modal event 'hide.bs.modal'.
  599. * This is a setter!
  600. */
  601. onHide: function(onhide) {
  602. this.options.onhide = onhide;
  603. return this;
  604. },
  605. /**
  606. * Set handler for modal event 'hidden.bs.modal'.
  607. * This is a setter!
  608. */
  609. onHidden: function(onhidden) {
  610. this.options.onhidden = onhidden;
  611. return this;
  612. },
  613. isRealized: function() {
  614. return this.realized;
  615. },
  616. setRealized: function(realized) {
  617. this.realized = realized;
  618. return this;
  619. },
  620. isOpened: function() {
  621. return this.opened;
  622. },
  623. setOpened: function(opened) {
  624. this.opened = opened;
  625. return this;
  626. },
  627. handleModalEvents: function() {
  628. this.getModal().on('show.bs.modal', {dialog: this}, function(event) {
  629. var dialog = event.data.dialog;
  630. dialog.showPageScrollBar(true);
  631. if (typeof dialog.options.onshow === 'function') {
  632. return dialog.options.onshow(dialog);
  633. }
  634. });
  635. this.getModal().on('shown.bs.modal', {dialog: this}, function(event) {
  636. var dialog = event.data.dialog;
  637. typeof dialog.options.onshown === 'function' && dialog.options.onshown(dialog);
  638. dialog.showPageScrollBar(true);
  639. });
  640. this.getModal().on('hide.bs.modal', {dialog: this}, function(event) {
  641. var dialog = event.data.dialog;
  642. if (typeof dialog.options.onhide === 'function') {
  643. return dialog.options.onhide(dialog);
  644. }
  645. });
  646. this.getModal().on('hidden.bs.modal', {dialog: this}, function(event) {
  647. var dialog = event.data.dialog;
  648. typeof dialog.options.onhidden === 'function' && dialog.options.onhidden(dialog);
  649. dialog.isAutodestroy() && $(this).remove();
  650. dialog.showPageScrollBar(false);
  651. });
  652. // Backdrop, I did't find a way to change bs3 backdrop option after the dialog is popped up, so here's a new wheel.
  653. this.getModal().on('click', {dialog: this}, function(event) {
  654. event.target === this && event.data.dialog.isClosable() && event.data.dialog.canCloseByBackdrop() && event.data.dialog.close();
  655. });
  656. // ESC key support
  657. this.getModal().on('keyup', {dialog: this}, function(event) {
  658. event.which === 27 && event.data.dialog.isClosable() && event.data.dialog.canCloseByKeyboard() && event.data.dialog.close();
  659. });
  660. // Button hotkey
  661. this.getModal().on('keyup', {dialog: this}, function(event) {
  662. var dialog = event.data.dialog;
  663. if (typeof dialog.registeredButtonHotkeys[event.which] !== 'undefined') {
  664. var $button = $(dialog.registeredButtonHotkeys[event.which]);
  665. !$button.prop('disabled') && $button.focus().trigger('click');
  666. }
  667. });
  668. return this;
  669. },
  670. makeModalDraggable: function() {
  671. if (this.options.draggable) {
  672. this.getModalHeader().addClass(this.getNamespace('draggable')).on('mousedown', {dialog: this}, function(event) {
  673. var dialog = event.data.dialog;
  674. dialog.draggableData.isMouseDown = true;
  675. var dialogOffset = dialog.getModalContent().offset();
  676. dialog.draggableData.mouseOffset = {
  677. top: event.clientY - dialogOffset.top,
  678. left: event.clientX - dialogOffset.left
  679. };
  680. });
  681. this.getModal().on('mouseup mouseleave', {dialog: this}, function(event) {
  682. event.data.dialog.draggableData.isMouseDown = false;
  683. });
  684. $('body').on('mousemove', {dialog: this}, function(event) {
  685. var dialog = event.data.dialog;
  686. if (!dialog.draggableData.isMouseDown) {
  687. return;
  688. }
  689. dialog.getModalContent().offset({
  690. top: event.clientY - dialog.draggableData.mouseOffset.top,
  691. left: event.clientX - dialog.draggableData.mouseOffset.left
  692. });
  693. });
  694. }
  695. return this;
  696. },
  697. showPageScrollBar: function(show) {
  698. $(document.body).toggleClass('modal-open', show);
  699. },
  700. /**
  701. * To make multiple opened dialogs look better.
  702. */
  703. updateZIndex: function() {
  704. var dialogCount = 0;
  705. $.each(BootstrapDialog.dialogs, function(dialogId, dialogInstance) {
  706. dialogCount++;
  707. });
  708. if (dialogCount > 1) {
  709. var $modal = this.getModal();
  710. var $backdrop = $modal.data('bs.modal').$backdrop;
  711. $modal.css('z-index', BootstrapDialog.ZINDEX_MODAL + (dialogCount - 1) * 20);
  712. $backdrop.css('z-index', BootstrapDialog.ZINDEX_BACKDROP + (dialogCount - 1) * 20);
  713. }
  714. return this;
  715. },
  716. realize: function() {
  717. this.initModalStuff();
  718. this.getModal().addClass(BootstrapDialog.NAMESPACE)
  719. .addClass(this.getType())
  720. .addClass(this.getSize())
  721. .addClass(this.getCssClass());
  722. this.getModalFooter().append(this.createFooterContent());
  723. this.getModalHeader().append(this.createHeaderContent());
  724. this.getModalBody().append(this.createBodyContent());
  725. this.getModal().modal({
  726. backdrop: 'static',
  727. keyboard: false,
  728. show: false
  729. });
  730. this.makeModalDraggable();
  731. this.handleModalEvents();
  732. this.setRealized(true);
  733. this.updateButtons();
  734. this.updateTitle();
  735. this.updateMessage();
  736. this.updateClosable();
  737. this.updateAnimate();
  738. return this;
  739. },
  740. open: function() {
  741. !this.isRealized() && this.realize();
  742. this.getModal().modal('show');
  743. this.updateZIndex();
  744. this.setOpened(true);
  745. return this;
  746. },
  747. close: function() {
  748. this.getModal().modal('hide');
  749. if (this.isAutodestroy()) {
  750. delete BootstrapDialog.dialogs[this.getId()];
  751. }
  752. this.setOpened(false);
  753. // Move focus to the last visible dialog.
  754. BootstrapDialog.moveFocus();
  755. return this;
  756. }
  757. };
  758. /**
  759. * RFC4122 version 4 compliant unique id creator.
  760. *
  761. * Added by https://github.com/tufanbarisyildirim/
  762. *
  763. * @returns {String}
  764. */
  765. BootstrapDialog.newGuid = function() {
  766. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  767. var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
  768. return v.toString(16);
  769. });
  770. };
  771. /* ================================================
  772. * For lazy people
  773. * ================================================ */
  774. /**
  775. * Shortcut function: show
  776. *
  777. * @param {type} options
  778. * @returns the created dialog instance
  779. */
  780. BootstrapDialog.show = function(options) {
  781. return new BootstrapDialog(options).open();
  782. };
  783. /**
  784. * Alert window
  785. *
  786. * @returns the created dialog instance
  787. */
  788. BootstrapDialog.alert = function() {
  789. var options = {};
  790. var defaultOptions = {
  791. type: BootstrapDialog.TYPE_PRIMARY,
  792. title: null,
  793. message: null,
  794. closable: true,
  795. buttonLabel: 'OK',
  796. callback: null
  797. };
  798. if (typeof arguments[0] === 'object' && arguments[0].constructor === {}.constructor) {
  799. options = $.extend(true, defaultOptions, arguments[0]);
  800. } else {
  801. options = $.extend(true, defaultOptions, {
  802. message: arguments[0],
  803. closable: false,
  804. buttonLabel: 'OK',
  805. callback: typeof arguments[1] !== 'undefined' ? arguments[1] : null
  806. });
  807. }
  808. return new BootstrapDialog({
  809. type: options.type,
  810. title: options.title,
  811. message: options.message,
  812. closable: options.closable,
  813. data: {
  814. callback: options.callback
  815. },
  816. onhide: function(dialog) {
  817. !dialog.getData('btnClicked') && dialog.isClosable() && typeof dialog.getData('callback') === 'function' && dialog.getData('callback')(false);
  818. },
  819. buttons: [{
  820. label: options.buttonLabel,
  821. action: function(dialog) {
  822. dialog.setData('btnClicked', true);
  823. typeof dialog.getData('callback') === 'function' && dialog.getData('callback')(true);
  824. dialog.close();
  825. }
  826. }]
  827. }).open();
  828. };
  829. /**
  830. * Confirm window
  831. *
  832. * @param {type} message
  833. * @param {type} callback
  834. * @returns the created dialog instance
  835. */
  836. BootstrapDialog.confirm = function(message, callback) {
  837. return new BootstrapDialog({
  838. title: 'Confirmation',
  839. message: message,
  840. closable: false,
  841. data: {
  842. 'callback': callback
  843. },
  844. buttons: [{
  845. label: 'Cancel',
  846. action: function(dialog) {
  847. typeof dialog.getData('callback') === 'function' && dialog.getData('callback')(false);
  848. dialog.close();
  849. }
  850. }, {
  851. label: 'OK',
  852. cssClass: 'btn-primary',
  853. action: function(dialog) {
  854. typeof dialog.getData('callback') === 'function' && dialog.getData('callback')(true);
  855. dialog.close();
  856. }
  857. }]
  858. }).open();
  859. };
  860. /**
  861. * Warning window
  862. *
  863. * @param {type} message
  864. * @returns the created dialog instance
  865. */
  866. BootstrapDialog.warning = function(message, callback) {
  867. return new BootstrapDialog({
  868. type: BootstrapDialog.TYPE_WARNING,
  869. message: message
  870. }).open();
  871. };
  872. /**
  873. * Danger window
  874. *
  875. * @param {type} message
  876. * @returns the created dialog instance
  877. */
  878. BootstrapDialog.danger = function(message, callback) {
  879. return new BootstrapDialog({
  880. type: BootstrapDialog.TYPE_DANGER,
  881. message: message
  882. }).open();
  883. };
  884. /**
  885. * Success window
  886. *
  887. * @param {type} message
  888. * @returns the created dialog instance
  889. */
  890. BootstrapDialog.success = function(message, callback) {
  891. return new BootstrapDialog({
  892. type: BootstrapDialog.TYPE_SUCCESS,
  893. message: message
  894. }).open();
  895. };
  896. return BootstrapDialog;
  897. }));