bootstrap-dialog.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230
  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. /* ================================================
  31. * Definition of BootstrapDialogModal.
  32. * Extend Bootstrap Modal and override some functions.
  33. * BootstrapDialogModal === Modified Modal.
  34. * ================================================ */
  35. var Modal = $.fn.modal.Constructor;
  36. var BootstrapDialogModal = function(element, options) {
  37. Modal.call(this, element, options);
  38. };
  39. BootstrapDialogModal.getModalVersion = function() {
  40. var version = null;
  41. if (typeof $.fn.modal.Constructor.VERSION === 'undefined') {
  42. version = 'v3.1';
  43. } else if (/3\.2\.\d+/.test($.fn.modal.Constructor.VERSION)) {
  44. version = 'v3.2';
  45. } else {
  46. version = 'v3.3'; // v3.3+
  47. }
  48. return version;
  49. };
  50. BootstrapDialogModal.ORIGINAL_BODY_PADDING = $('body').css('padding-right') || 0;
  51. BootstrapDialogModal.METHODS_TO_OVERRIDE = {};
  52. BootstrapDialogModal.METHODS_TO_OVERRIDE['v3.1'] = {};
  53. BootstrapDialogModal.METHODS_TO_OVERRIDE['v3.2'] = {
  54. hide: function(e) {
  55. if (e) {
  56. e.preventDefault();
  57. }
  58. e = $.Event('hide.bs.modal');
  59. this.$element.trigger(e);
  60. if (!this.isShown || e.isDefaultPrevented()) {
  61. return;
  62. }
  63. this.isShown = false;
  64. // Remove css class 'modal-open' when the last opened dialog is closing.
  65. var openedDialogs = this.getGlobalOpenedDialogs();
  66. if (openedDialogs.length === 0) {
  67. this.$body.removeClass('modal-open');
  68. }
  69. this.resetScrollbar();
  70. this.escape();
  71. $(document).off('focusin.bs.modal');
  72. this.$element
  73. .removeClass('in')
  74. .attr('aria-hidden', true)
  75. .off('click.dismiss.bs.modal');
  76. $.support.transition && this.$element.hasClass('fade') ?
  77. this.$element
  78. .one('bsTransitionEnd', $.proxy(this.hideModal, this))
  79. .emulateTransitionEnd(300) :
  80. this.hideModal();
  81. }
  82. };
  83. BootstrapDialogModal.METHODS_TO_OVERRIDE['v3.3'] = {
  84. /**
  85. * Overrided.
  86. *
  87. * @returns {undefined}
  88. */
  89. setScrollbar: function() {
  90. var bodyPad = BootstrapDialogModal.ORIGINAL_BODY_PADDING;
  91. if (this.bodyIsOverflowing) {
  92. this.$body.css('padding-right', bodyPad + this.scrollbarWidth);
  93. }
  94. },
  95. /**
  96. * Overrided.
  97. *
  98. * @returns {undefined}
  99. */
  100. resetScrollbar: function() {
  101. var openedDialogs = this.getGlobalOpenedDialogs();
  102. if (openedDialogs.length === 0) {
  103. this.$body.css('padding-right', BootstrapDialogModal.ORIGINAL_BODY_PADDING);
  104. }
  105. },
  106. /**
  107. * Overrided.
  108. *
  109. * @returns {undefined}
  110. */
  111. hideModal: function() {
  112. this.$element.hide();
  113. this.backdrop($.proxy(function() {
  114. var openedDialogs = this.getGlobalOpenedDialogs();
  115. if (openedDialogs.length === 0) {
  116. this.$body.removeClass('modal-open');
  117. }
  118. this.resetAdjustments();
  119. this.resetScrollbar();
  120. this.$element.trigger('hidden.bs.modal');
  121. }, this));
  122. }
  123. };
  124. BootstrapDialogModal.prototype = {
  125. constructor: BootstrapDialogModal,
  126. /**
  127. * New function, to get the dialogs that opened by BootstrapDialog.
  128. *
  129. * @returns {undefined}
  130. */
  131. getGlobalOpenedDialogs: function() {
  132. var openedDialogs = [];
  133. $.each(BootstrapDialog.dialogs, function(id, dialogInstance) {
  134. if (dialogInstance.isRealized() && dialogInstance.isOpened()) {
  135. openedDialogs.push(dialogInstance);
  136. }
  137. });
  138. return openedDialogs;
  139. }
  140. };
  141. // Add compatible methods.
  142. BootstrapDialogModal.prototype = $.extend(BootstrapDialogModal.prototype, Modal.prototype, BootstrapDialogModal.METHODS_TO_OVERRIDE[BootstrapDialogModal.getModalVersion()]);
  143. /* ================================================
  144. * Definition of BootstrapDialog.
  145. * ================================================ */
  146. var BootstrapDialog = function(options) {
  147. this.defaultOptions = $.extend(true, {
  148. id: BootstrapDialog.newGuid(),
  149. buttons: [],
  150. data: {},
  151. onshow: null,
  152. onshown: null,
  153. onhide: null,
  154. onhidden: null
  155. }, BootstrapDialog.defaultOptions);
  156. this.indexedButtons = {};
  157. this.registeredButtonHotkeys = {};
  158. this.draggableData = {
  159. isMouseDown: false,
  160. mouseOffset: {}
  161. };
  162. this.realized = false;
  163. this.opened = false;
  164. this.initOptions(options);
  165. this.holdThisInstance();
  166. };
  167. /**
  168. * Some constants.
  169. */
  170. BootstrapDialog.NAMESPACE = 'bootstrap-dialog';
  171. BootstrapDialog.TYPE_DEFAULT = 'type-default';
  172. BootstrapDialog.TYPE_INFO = 'type-info';
  173. BootstrapDialog.TYPE_PRIMARY = 'type-primary';
  174. BootstrapDialog.TYPE_SUCCESS = 'type-success';
  175. BootstrapDialog.TYPE_WARNING = 'type-warning';
  176. BootstrapDialog.TYPE_DANGER = 'type-danger';
  177. BootstrapDialog.DEFAULT_TEXTS = {};
  178. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_DEFAULT] = 'Information';
  179. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_INFO] = 'Information';
  180. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_PRIMARY] = 'Information';
  181. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_SUCCESS] = 'Success';
  182. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_WARNING] = 'Warning';
  183. BootstrapDialog.DEFAULT_TEXTS[BootstrapDialog.TYPE_DANGER] = 'Danger';
  184. BootstrapDialog.DEFAULT_TEXTS['OK'] = 'OK';
  185. BootstrapDialog.DEFAULT_TEXTS['CANCEL'] = 'Cancel';
  186. BootstrapDialog.SIZE_NORMAL = 'size-normal';
  187. BootstrapDialog.SIZE_WIDE = 'size-wide'; // size-wide is equal to modal-lg
  188. BootstrapDialog.SIZE_LARGE = 'size-large';
  189. BootstrapDialog.BUTTON_SIZES = {};
  190. BootstrapDialog.BUTTON_SIZES[BootstrapDialog.SIZE_NORMAL] = '';
  191. BootstrapDialog.BUTTON_SIZES[BootstrapDialog.SIZE_WIDE] = '';
  192. BootstrapDialog.BUTTON_SIZES[BootstrapDialog.SIZE_LARGE] = 'btn-lg';
  193. BootstrapDialog.ICON_SPINNER = 'glyphicon glyphicon-asterisk';
  194. /**
  195. * Default options.
  196. */
  197. BootstrapDialog.defaultOptions = {
  198. type: BootstrapDialog.TYPE_PRIMARY,
  199. size: BootstrapDialog.SIZE_NORMAL,
  200. cssClass: '',
  201. title: null,
  202. message: null,
  203. nl2br: true,
  204. closable: true,
  205. closeByBackdrop: true,
  206. closeByKeyboard: true,
  207. spinicon: BootstrapDialog.ICON_SPINNER,
  208. autodestroy: true,
  209. draggable: false,
  210. animate: true,
  211. description: ''
  212. };
  213. /**
  214. * Config default options.
  215. */
  216. BootstrapDialog.configDefaultOptions = function(options) {
  217. BootstrapDialog.defaultOptions = $.extend(true, BootstrapDialog.defaultOptions, options);
  218. };
  219. /**
  220. * Open / Close all created dialogs all at once.
  221. */
  222. BootstrapDialog.dialogs = {};
  223. BootstrapDialog.openAll = function() {
  224. $.each(BootstrapDialog.dialogs, function(id, dialogInstance) {
  225. dialogInstance.open();
  226. });
  227. };
  228. BootstrapDialog.closeAll = function() {
  229. $.each(BootstrapDialog.dialogs, function(id, dialogInstance) {
  230. dialogInstance.close();
  231. });
  232. };
  233. /**
  234. * Move focus to next visible dialog.
  235. */
  236. BootstrapDialog.moveFocus = function() {
  237. var lastDialogInstance = null;
  238. $.each(BootstrapDialog.dialogs, function(id, dialogInstance) {
  239. lastDialogInstance = dialogInstance;
  240. });
  241. if (lastDialogInstance !== null && lastDialogInstance.isRealized()) {
  242. lastDialogInstance.getModal().focus();
  243. }
  244. };
  245. BootstrapDialog.METHODS_TO_OVERRIDE = {};
  246. BootstrapDialog.METHODS_TO_OVERRIDE['v3.1'] = {
  247. handleModalBackdropEvent: function() {
  248. this.getModal().on('click', {dialog: this}, function(event) {
  249. event.target === this && event.data.dialog.isClosable() && event.data.dialog.canCloseByBackdrop() && event.data.dialog.close();
  250. });
  251. return this;
  252. },
  253. /**
  254. * To make multiple opened dialogs look better.
  255. *
  256. * Will be removed in later version, after Bootstrap Modal >= 3.3.0, updating z-index is unnecessary.
  257. */
  258. updateZIndex: function() {
  259. var zIndexBackdrop = 1040;
  260. var zIndexModal = 1050;
  261. var dialogCount = 0;
  262. $.each(BootstrapDialog.dialogs, function(dialogId, dialogInstance) {
  263. dialogCount++;
  264. });
  265. var $modal = this.getModal();
  266. var $backdrop = $modal.data('bs.modal').$backdrop;
  267. $modal.css('z-index', zIndexModal + (dialogCount - 1) * 20);
  268. $backdrop.css('z-index', zIndexBackdrop + (dialogCount - 1) * 20);
  269. return this;
  270. },
  271. open: function() {
  272. !this.isRealized() && this.realize();
  273. this.getModal().modal('show');
  274. this.updateZIndex();
  275. this.setOpened(true);
  276. return this;
  277. }
  278. };
  279. BootstrapDialog.METHODS_TO_OVERRIDE['v3.2'] = {
  280. handleModalBackdropEvent: BootstrapDialog.METHODS_TO_OVERRIDE['v3.1']['handleModalBackdropEvent'],
  281. updateZIndex: BootstrapDialog.METHODS_TO_OVERRIDE['v3.1']['updateZIndex'],
  282. open: BootstrapDialog.METHODS_TO_OVERRIDE['v3.1']['open']
  283. };
  284. BootstrapDialog.METHODS_TO_OVERRIDE['v3.3'] = {};
  285. BootstrapDialog.prototype = {
  286. constructor: BootstrapDialog,
  287. initOptions: function(options) {
  288. this.options = $.extend(true, this.defaultOptions, options);
  289. return this;
  290. },
  291. holdThisInstance: function() {
  292. BootstrapDialog.dialogs[this.getId()] = this;
  293. return this;
  294. },
  295. initModalStuff: function() {
  296. this.setModal(this.createModal())
  297. .setModalDialog(this.createModalDialog())
  298. .setModalContent(this.createModalContent())
  299. .setModalHeader(this.createModalHeader())
  300. .setModalBody(this.createModalBody())
  301. .setModalFooter(this.createModalFooter());
  302. this.getModal().append(this.getModalDialog());
  303. this.getModalDialog().append(this.getModalContent());
  304. this.getModalContent()
  305. .append(this.getModalHeader())
  306. .append(this.getModalBody())
  307. .append(this.getModalFooter());
  308. return this;
  309. },
  310. createModal: function() {
  311. var $modal = $('<div class="modal" tabindex="-1" role="dialog" aria-hidden="true"></div>');
  312. $modal.prop('id', this.getId()).attr('aria-labelledby', this.getId() + '_title');
  313. return $modal;
  314. },
  315. getModal: function() {
  316. return this.$modal;
  317. },
  318. setModal: function($modal) {
  319. this.$modal = $modal;
  320. return this;
  321. },
  322. createModalDialog: function() {
  323. return $('<div class="modal-dialog"></div>');
  324. },
  325. getModalDialog: function() {
  326. return this.$modalDialog;
  327. },
  328. setModalDialog: function($modalDialog) {
  329. this.$modalDialog = $modalDialog;
  330. return this;
  331. },
  332. createModalContent: function() {
  333. return $('<div class="modal-content"></div>');
  334. },
  335. getModalContent: function() {
  336. return this.$modalContent;
  337. },
  338. setModalContent: function($modalContent) {
  339. this.$modalContent = $modalContent;
  340. return this;
  341. },
  342. createModalHeader: function() {
  343. return $('<div class="modal-header"></div>');
  344. },
  345. getModalHeader: function() {
  346. return this.$modalHeader;
  347. },
  348. setModalHeader: function($modalHeader) {
  349. this.$modalHeader = $modalHeader;
  350. return this;
  351. },
  352. createModalBody: function() {
  353. return $('<div class="modal-body"></div>');
  354. },
  355. getModalBody: function() {
  356. return this.$modalBody;
  357. },
  358. setModalBody: function($modalBody) {
  359. this.$modalBody = $modalBody;
  360. return this;
  361. },
  362. createModalFooter: function() {
  363. return $('<div class="modal-footer"></div>');
  364. },
  365. getModalFooter: function() {
  366. return this.$modalFooter;
  367. },
  368. setModalFooter: function($modalFooter) {
  369. this.$modalFooter = $modalFooter;
  370. return this;
  371. },
  372. createDynamicContent: function(rawContent) {
  373. var content = null;
  374. if (typeof rawContent === 'function') {
  375. content = rawContent.call(rawContent, this);
  376. } else {
  377. content = rawContent;
  378. }
  379. if (typeof content === 'string') {
  380. content = this.formatStringContent(content);
  381. }
  382. return content;
  383. },
  384. formatStringContent: function(content) {
  385. if (this.options.nl2br) {
  386. return content.replace(/\r\n/g, '<br />').replace(/[\r\n]/g, '<br />');
  387. }
  388. return content;
  389. },
  390. setData: function(key, value) {
  391. this.options.data[key] = value;
  392. return this;
  393. },
  394. getData: function(key) {
  395. return this.options.data[key];
  396. },
  397. setId: function(id) {
  398. this.options.id = id;
  399. return this;
  400. },
  401. getId: function() {
  402. return this.options.id;
  403. },
  404. getType: function() {
  405. return this.options.type;
  406. },
  407. setType: function(type) {
  408. this.options.type = type;
  409. this.updateType();
  410. return this;
  411. },
  412. updateType: function() {
  413. if (this.isRealized()) {
  414. var types = [BootstrapDialog.TYPE_DEFAULT,
  415. BootstrapDialog.TYPE_INFO,
  416. BootstrapDialog.TYPE_PRIMARY,
  417. BootstrapDialog.TYPE_SUCCESS,
  418. BootstrapDialog.TYPE_WARNING,
  419. BootstrapDialog.TYPE_DANGER];
  420. this.getModal().removeClass(types.join(' ')).addClass(this.getType());
  421. }
  422. return this;
  423. },
  424. getSize: function() {
  425. return this.options.size;
  426. },
  427. setSize: function(size) {
  428. this.options.size = size;
  429. this.updateSize();
  430. return this;
  431. },
  432. updateSize: function() {
  433. if (this.isRealized()) {
  434. var dialog = this;
  435. // Dialog size
  436. this.getModal().removeClass(BootstrapDialog.SIZE_NORMAL)
  437. .removeClass(BootstrapDialog.SIZE_WIDE)
  438. .removeClass(BootstrapDialog.SIZE_LARGE);
  439. this.getModal().addClass(this.getSize());
  440. // Wider dialog.
  441. this.getModalDialog().removeClass('modal-lg');
  442. if (this.getSize() === BootstrapDialog.SIZE_WIDE) {
  443. this.getModalDialog().addClass('modal-lg');
  444. }
  445. // Button size
  446. $.each(this.options.buttons, function(index, button) {
  447. var $button = dialog.getButton(button.id);
  448. var buttonSizes = ['btn-lg', 'btn-sm', 'btn-xs'];
  449. var sizeClassSpecified = false;
  450. if (typeof button['cssClass'] === 'string') {
  451. var btnClasses = button['cssClass'].split(' ');
  452. $.each(btnClasses, function(index, btnClass) {
  453. if ($.inArray(btnClass, buttonSizes) !== -1) {
  454. sizeClassSpecified = true;
  455. }
  456. });
  457. }
  458. if (!sizeClassSpecified) {
  459. $button.removeClass(buttonSizes.join(' '));
  460. $button.addClass(dialog.getButtonSize());
  461. }
  462. });
  463. }
  464. return this;
  465. },
  466. getCssClass: function() {
  467. return this.options.cssClass;
  468. },
  469. setCssClass: function(cssClass) {
  470. this.options.cssClass = cssClass;
  471. return this;
  472. },
  473. getTitle: function() {
  474. return this.options.title;
  475. },
  476. setTitle: function(title) {
  477. this.options.title = title;
  478. this.updateTitle();
  479. return this;
  480. },
  481. updateTitle: function() {
  482. if (this.isRealized()) {
  483. var title = this.getTitle() !== null ? this.createDynamicContent(this.getTitle()) : this.getDefaultText();
  484. this.getModalHeader().find('.' + this.getNamespace('title')).html('').append(title).prop('id', this.getId() + '_title');
  485. }
  486. return this;
  487. },
  488. getMessage: function() {
  489. return this.options.message;
  490. },
  491. setMessage: function(message) {
  492. this.options.message = message;
  493. this.updateMessage();
  494. return this;
  495. },
  496. updateMessage: function() {
  497. if (this.isRealized()) {
  498. var message = this.createDynamicContent(this.getMessage());
  499. this.getModalBody().find('.' + this.getNamespace('message')).html('').append(message);
  500. }
  501. return this;
  502. },
  503. isClosable: function() {
  504. return this.options.closable;
  505. },
  506. setClosable: function(closable) {
  507. this.options.closable = closable;
  508. this.updateClosable();
  509. return this;
  510. },
  511. setCloseByBackdrop: function(closeByBackdrop) {
  512. this.options.closeByBackdrop = closeByBackdrop;
  513. return this;
  514. },
  515. canCloseByBackdrop: function() {
  516. return this.options.closeByBackdrop;
  517. },
  518. setCloseByKeyboard: function(closeByKeyboard) {
  519. this.options.closeByKeyboard = closeByKeyboard;
  520. return this;
  521. },
  522. canCloseByKeyboard: function() {
  523. return this.options.closeByKeyboard;
  524. },
  525. isAnimate: function() {
  526. return this.options.animate;
  527. },
  528. setAnimate: function(animate) {
  529. this.options.animate = animate;
  530. return this;
  531. },
  532. updateAnimate: function() {
  533. if (this.isRealized()) {
  534. this.getModal().toggleClass('fade', this.isAnimate());
  535. }
  536. return this;
  537. },
  538. getSpinicon: function() {
  539. return this.options.spinicon;
  540. },
  541. setSpinicon: function(spinicon) {
  542. this.options.spinicon = spinicon;
  543. return this;
  544. },
  545. addButton: function(button) {
  546. this.options.buttons.push(button);
  547. return this;
  548. },
  549. addButtons: function(buttons) {
  550. var that = this;
  551. $.each(buttons, function(index, button) {
  552. that.addButton(button);
  553. });
  554. return this;
  555. },
  556. getButtons: function() {
  557. return this.options.buttons;
  558. },
  559. setButtons: function(buttons) {
  560. this.options.buttons = buttons;
  561. this.updateButtons();
  562. return this;
  563. },
  564. /**
  565. * If there is id provided for a button option, it will be in dialog.indexedButtons list.
  566. *
  567. * In that case you can use dialog.getButton(id) to find the button.
  568. *
  569. * @param {type} id
  570. * @returns {undefined}
  571. */
  572. getButton: function(id) {
  573. if (typeof this.indexedButtons[id] !== 'undefined') {
  574. return this.indexedButtons[id];
  575. }
  576. return null;
  577. },
  578. getButtonSize: function() {
  579. if (typeof BootstrapDialog.BUTTON_SIZES[this.getSize()] !== 'undefined') {
  580. return BootstrapDialog.BUTTON_SIZES[this.getSize()];
  581. }
  582. return '';
  583. },
  584. updateButtons: function() {
  585. if (this.isRealized()) {
  586. if (this.getButtons().length === 0) {
  587. this.getModalFooter().hide();
  588. } else {
  589. this.getModalFooter().find('.' + this.getNamespace('footer')).html('').append(this.createFooterButtons());
  590. }
  591. }
  592. return this;
  593. },
  594. isAutodestroy: function() {
  595. return this.options.autodestroy;
  596. },
  597. setAutodestroy: function(autodestroy) {
  598. this.options.autodestroy = autodestroy;
  599. },
  600. getDescription: function() {
  601. return this.options.description;
  602. },
  603. setDescription: function(description) {
  604. this.options.description = description;
  605. return this;
  606. },
  607. getDefaultText: function() {
  608. return BootstrapDialog.DEFAULT_TEXTS[this.getType()];
  609. },
  610. getNamespace: function(name) {
  611. return BootstrapDialog.NAMESPACE + '-' + name;
  612. },
  613. createHeaderContent: function() {
  614. var $container = $('<div></div>');
  615. $container.addClass(this.getNamespace('header'));
  616. // title
  617. $container.append(this.createTitleContent());
  618. // Close button
  619. $container.prepend(this.createCloseButton());
  620. return $container;
  621. },
  622. createTitleContent: function() {
  623. var $title = $('<div></div>');
  624. $title.addClass(this.getNamespace('title'));
  625. return $title;
  626. },
  627. createCloseButton: function() {
  628. var $container = $('<div></div>');
  629. $container.addClass(this.getNamespace('close-button'));
  630. var $icon = $('<button class="close">&times;</button>');
  631. $container.append($icon);
  632. $container.on('click', {dialog: this}, function(event) {
  633. event.data.dialog.close();
  634. });
  635. return $container;
  636. },
  637. createBodyContent: function() {
  638. var $container = $('<div></div>');
  639. $container.addClass(this.getNamespace('body'));
  640. // Message
  641. $container.append(this.createMessageContent());
  642. return $container;
  643. },
  644. createMessageContent: function() {
  645. var $message = $('<div></div>');
  646. $message.addClass(this.getNamespace('message'));
  647. return $message;
  648. },
  649. createFooterContent: function() {
  650. var $container = $('<div></div>');
  651. $container.addClass(this.getNamespace('footer'));
  652. return $container;
  653. },
  654. createFooterButtons: function() {
  655. var that = this;
  656. var $container = $('<div></div>');
  657. $container.addClass(this.getNamespace('footer-buttons'));
  658. this.indexedButtons = {};
  659. $.each(this.options.buttons, function(index, button) {
  660. if (!button.id) {
  661. button.id = BootstrapDialog.newGuid();
  662. }
  663. var $button = that.createButton(button);
  664. that.indexedButtons[button.id] = $button;
  665. $container.append($button);
  666. });
  667. return $container;
  668. },
  669. createButton: function(button) {
  670. var $button = $('<button class="btn"></button>');
  671. $button.prop('id', button.id);
  672. // Icon
  673. if (typeof button.icon !== 'undefined' && $.trim(button.icon) !== '') {
  674. $button.append(this.createButtonIcon(button.icon));
  675. }
  676. // Label
  677. if (typeof button.label !== 'undefined') {
  678. $button.append(button.label);
  679. }
  680. // Css class
  681. if (typeof button.cssClass !== 'undefined' && $.trim(button.cssClass) !== '') {
  682. $button.addClass(button.cssClass);
  683. } else {
  684. $button.addClass('btn-default');
  685. }
  686. // Hotkey
  687. if (typeof button.hotkey !== 'undefined') {
  688. this.registeredButtonHotkeys[button.hotkey] = $button;
  689. }
  690. // Button on click
  691. $button.on('click', {dialog: this, $button: $button, button: button}, function(event) {
  692. var dialog = event.data.dialog;
  693. var $button = event.data.$button;
  694. var button = event.data.button;
  695. if (typeof button.action === 'function') {
  696. button.action.call($button, dialog);
  697. }
  698. if (button.autospin) {
  699. $button.toggleSpin(true);
  700. }
  701. });
  702. // Dynamically add extra functions to $button
  703. this.enhanceButton($button);
  704. return $button;
  705. },
  706. /**
  707. * Dynamically add extra functions to $button
  708. *
  709. * Using '$this' to reference 'this' is just for better readability.
  710. *
  711. * @param {type} $button
  712. * @returns {_L13.BootstrapDialog.prototype}
  713. */
  714. enhanceButton: function($button) {
  715. $button.dialog = this;
  716. // Enable / Disable
  717. $button.toggleEnable = function(enable) {
  718. var $this = this;
  719. if (typeof enable !== 'undefined') {
  720. $this.prop("disabled", !enable).toggleClass('disabled', !enable);
  721. } else {
  722. $this.prop("disabled", !$this.prop("disabled"));
  723. }
  724. return $this;
  725. };
  726. $button.enable = function() {
  727. var $this = this;
  728. $this.toggleEnable(true);
  729. return $this;
  730. };
  731. $button.disable = function() {
  732. var $this = this;
  733. $this.toggleEnable(false);
  734. return $this;
  735. };
  736. // Icon spinning, helpful for indicating ajax loading status.
  737. $button.toggleSpin = function(spin) {
  738. var $this = this;
  739. var dialog = $this.dialog;
  740. var $icon = $this.find('.' + dialog.getNamespace('button-icon'));
  741. if (typeof spin === 'undefined') {
  742. spin = !($button.find('.icon-spin').length > 0);
  743. }
  744. if (spin) {
  745. $icon.hide();
  746. $button.prepend(dialog.createButtonIcon(dialog.getSpinicon()).addClass('icon-spin'));
  747. } else {
  748. $icon.show();
  749. $button.find('.icon-spin').remove();
  750. }
  751. return $this;
  752. };
  753. $button.spin = function() {
  754. var $this = this;
  755. $this.toggleSpin(true);
  756. return $this;
  757. };
  758. $button.stopSpin = function() {
  759. var $this = this;
  760. $this.toggleSpin(false);
  761. return $this;
  762. };
  763. return this;
  764. },
  765. createButtonIcon: function(icon) {
  766. var $icon = $('<span></span>');
  767. $icon.addClass(this.getNamespace('button-icon')).addClass(icon);
  768. return $icon;
  769. },
  770. /**
  771. * Invoke this only after the dialog is realized.
  772. *
  773. * @param {type} enable
  774. * @returns {undefined}
  775. */
  776. enableButtons: function(enable) {
  777. $.each(this.indexedButtons, function(id, $button) {
  778. $button.toggleEnable(enable);
  779. });
  780. return this;
  781. },
  782. /**
  783. * Invoke this only after the dialog is realized.
  784. *
  785. * @returns {undefined}
  786. */
  787. updateClosable: function() {
  788. if (this.isRealized()) {
  789. // Close button
  790. this.getModalHeader().find('.' + this.getNamespace('close-button')).toggle(this.isClosable());
  791. }
  792. return this;
  793. },
  794. /**
  795. * Set handler for modal event 'show.bs.modal'.
  796. * This is a setter!
  797. */
  798. onShow: function(onshow) {
  799. this.options.onshow = onshow;
  800. return this;
  801. },
  802. /**
  803. * Set handler for modal event 'shown.bs.modal'.
  804. * This is a setter!
  805. */
  806. onShown: function(onshown) {
  807. this.options.onshown = onshown;
  808. return this;
  809. },
  810. /**
  811. * Set handler for modal event 'hide.bs.modal'.
  812. * This is a setter!
  813. */
  814. onHide: function(onhide) {
  815. this.options.onhide = onhide;
  816. return this;
  817. },
  818. /**
  819. * Set handler for modal event 'hidden.bs.modal'.
  820. * This is a setter!
  821. */
  822. onHidden: function(onhidden) {
  823. this.options.onhidden = onhidden;
  824. return this;
  825. },
  826. isRealized: function() {
  827. return this.realized;
  828. },
  829. setRealized: function(realized) {
  830. this.realized = realized;
  831. return this;
  832. },
  833. isOpened: function() {
  834. return this.opened;
  835. },
  836. setOpened: function(opened) {
  837. this.opened = opened;
  838. return this;
  839. },
  840. handleModalEvents: function() {
  841. this.getModal().on('show.bs.modal', {dialog: this}, function(event) {
  842. var dialog = event.data.dialog;
  843. if (dialog.isModalEvent(event) && typeof dialog.options.onshow === 'function') {
  844. return dialog.options.onshow(dialog);
  845. }
  846. });
  847. this.getModal().on('shown.bs.modal', {dialog: this}, function(event) {
  848. var dialog = event.data.dialog;
  849. dialog.isModalEvent(event) && typeof dialog.options.onshown === 'function' && dialog.options.onshown(dialog);
  850. });
  851. this.getModal().on('hide.bs.modal', {dialog: this}, function(event) {
  852. var dialog = event.data.dialog;
  853. if (dialog.isModalEvent(event) && typeof dialog.options.onhide === 'function') {
  854. return dialog.options.onhide(dialog);
  855. }
  856. });
  857. this.getModal().on('hidden.bs.modal', {dialog: this}, function(event) {
  858. var dialog = event.data.dialog;
  859. dialog.isModalEvent(event) && typeof dialog.options.onhidden === 'function' && dialog.options.onhidden(dialog);
  860. dialog.isAutodestroy() && $(this).remove();
  861. BootstrapDialog.moveFocus();
  862. });
  863. // Backdrop, I did't find a way to change bs3 backdrop option after the dialog is popped up, so here's a new wheel.
  864. this.handleModalBackdropEvent();
  865. // ESC key support
  866. this.getModal().on('keyup', {dialog: this}, function(event) {
  867. event.which === 27 && event.data.dialog.isClosable() && event.data.dialog.canCloseByKeyboard() && event.data.dialog.close();
  868. });
  869. // Button hotkey
  870. this.getModal().on('keyup', {dialog: this}, function(event) {
  871. var dialog = event.data.dialog;
  872. if (typeof dialog.registeredButtonHotkeys[event.which] !== 'undefined') {
  873. var $button = $(dialog.registeredButtonHotkeys[event.which]);
  874. !$button.prop('disabled') && $button.focus().trigger('click');
  875. }
  876. });
  877. return this;
  878. },
  879. handleModalBackdropEvent: function() {
  880. this.getModal().on('click', {dialog: this}, function(event) {
  881. $(event.target).hasClass('modal-backdrop') && event.data.dialog.isClosable() && event.data.dialog.canCloseByBackdrop() && event.data.dialog.close();
  882. });
  883. return this;
  884. },
  885. isModalEvent: function(event) {
  886. return typeof event.namespace !== 'undefined' && event.namespace === 'bs.modal';
  887. },
  888. makeModalDraggable: function() {
  889. if (this.options.draggable) {
  890. this.getModalHeader().addClass(this.getNamespace('draggable')).on('mousedown', {dialog: this}, function(event) {
  891. var dialog = event.data.dialog;
  892. dialog.draggableData.isMouseDown = true;
  893. var dialogOffset = dialog.getModalDialog().offset();
  894. dialog.draggableData.mouseOffset = {
  895. top: event.clientY - dialogOffset.top,
  896. left: event.clientX - dialogOffset.left
  897. };
  898. });
  899. this.getModal().on('mouseup mouseleave', {dialog: this}, function(event) {
  900. event.data.dialog.draggableData.isMouseDown = false;
  901. });
  902. $('body').on('mousemove', {dialog: this}, function(event) {
  903. var dialog = event.data.dialog;
  904. if (!dialog.draggableData.isMouseDown) {
  905. return;
  906. }
  907. dialog.getModalDialog().offset({
  908. top: event.clientY - dialog.draggableData.mouseOffset.top,
  909. left: event.clientX - dialog.draggableData.mouseOffset.left
  910. });
  911. });
  912. }
  913. return this;
  914. },
  915. realize: function() {
  916. this.initModalStuff();
  917. this.getModal().addClass(BootstrapDialog.NAMESPACE)
  918. .addClass(this.getCssClass());
  919. this.updateSize();
  920. if (this.getDescription()) {
  921. this.getModal().attr('aria-describedby', this.getDescription());
  922. }
  923. this.getModalFooter().append(this.createFooterContent());
  924. this.getModalHeader().append(this.createHeaderContent());
  925. this.getModalBody().append(this.createBodyContent());
  926. this.getModal().data('bs.modal', new BootstrapDialogModal(this.getModal(), {
  927. backdrop: 'static',
  928. keyboard: false,
  929. show: false
  930. }));
  931. this.makeModalDraggable();
  932. this.handleModalEvents();
  933. this.setRealized(true);
  934. this.updateButtons();
  935. this.updateType();
  936. this.updateTitle();
  937. this.updateMessage();
  938. this.updateClosable();
  939. this.updateAnimate();
  940. this.updateSize();
  941. return this;
  942. },
  943. open: function() {
  944. !this.isRealized() && this.realize();
  945. this.getModal().modal('show');
  946. this.setOpened(true);
  947. return this;
  948. },
  949. close: function() {
  950. this.getModal().modal('hide');
  951. if (this.isAutodestroy()) {
  952. delete BootstrapDialog.dialogs[this.getId()];
  953. }
  954. this.setOpened(false);
  955. return this;
  956. }
  957. };
  958. // Add compatible methods.
  959. BootstrapDialog.prototype = $.extend(BootstrapDialog.prototype, BootstrapDialog.METHODS_TO_OVERRIDE[BootstrapDialogModal.getModalVersion()]);
  960. /**
  961. * RFC4122 version 4 compliant unique id creator.
  962. *
  963. * Added by https://github.com/tufanbarisyildirim/
  964. *
  965. * @returns {String}
  966. */
  967. BootstrapDialog.newGuid = function() {
  968. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  969. var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
  970. return v.toString(16);
  971. });
  972. };
  973. /* ================================================
  974. * For lazy people
  975. * ================================================ */
  976. /**
  977. * Shortcut function: show
  978. *
  979. * @param {type} options
  980. * @returns the created dialog instance
  981. */
  982. BootstrapDialog.show = function(options) {
  983. return new BootstrapDialog(options).open();
  984. };
  985. /**
  986. * Alert window
  987. *
  988. * @returns the created dialog instance
  989. */
  990. BootstrapDialog.alert = function() {
  991. var options = {};
  992. var defaultOptions = {
  993. type: BootstrapDialog.TYPE_PRIMARY,
  994. title: null,
  995. message: null,
  996. closable: true,
  997. buttonLabel: BootstrapDialog.DEFAULT_TEXTS.OK,
  998. callback: null
  999. };
  1000. if (typeof arguments[0] === 'object' && arguments[0].constructor === {}.constructor) {
  1001. options = $.extend(true, defaultOptions, arguments[0]);
  1002. } else {
  1003. options = $.extend(true, defaultOptions, {
  1004. message: arguments[0],
  1005. closable: false,
  1006. buttonLabel: BootstrapDialog.DEFAULT_TEXTS.OK,
  1007. callback: typeof arguments[1] !== 'undefined' ? arguments[1] : null
  1008. });
  1009. }
  1010. return new BootstrapDialog({
  1011. type: options.type,
  1012. title: options.title,
  1013. message: options.message,
  1014. closable: options.closable,
  1015. data: {
  1016. callback: options.callback
  1017. },
  1018. onhide: function(dialog) {
  1019. !dialog.getData('btnClicked') && dialog.isClosable() && typeof dialog.getData('callback') === 'function' && dialog.getData('callback')(false);
  1020. },
  1021. buttons: [{
  1022. label: options.buttonLabel,
  1023. action: function(dialog) {
  1024. dialog.setData('btnClicked', true);
  1025. typeof dialog.getData('callback') === 'function' && dialog.getData('callback')(true);
  1026. dialog.close();
  1027. }
  1028. }]
  1029. }).open();
  1030. };
  1031. /**
  1032. * Confirm window
  1033. *
  1034. * @param {type} message
  1035. * @param {type} callback
  1036. * @returns the created dialog instance
  1037. */
  1038. BootstrapDialog.confirm = function(message, callback) {
  1039. return new BootstrapDialog({
  1040. title: 'Confirmation',
  1041. message: message,
  1042. closable: false,
  1043. data: {
  1044. 'callback': callback
  1045. },
  1046. buttons: [{
  1047. label: BootstrapDialog.DEFAULT_TEXTS.CANCEL,
  1048. action: function(dialog) {
  1049. typeof dialog.getData('callback') === 'function' && dialog.getData('callback')(false);
  1050. dialog.close();
  1051. }
  1052. }, {
  1053. label: BootstrapDialog.DEFAULT_TEXTS.OK,
  1054. cssClass: 'btn-primary',
  1055. action: function(dialog) {
  1056. typeof dialog.getData('callback') === 'function' && dialog.getData('callback')(true);
  1057. dialog.close();
  1058. }
  1059. }]
  1060. }).open();
  1061. };
  1062. /**
  1063. * Warning window
  1064. *
  1065. * @param {type} message
  1066. * @returns the created dialog instance
  1067. */
  1068. BootstrapDialog.warning = function(message, callback) {
  1069. return new BootstrapDialog({
  1070. type: BootstrapDialog.TYPE_WARNING,
  1071. message: message
  1072. }).open();
  1073. };
  1074. /**
  1075. * Danger window
  1076. *
  1077. * @param {type} message
  1078. * @returns the created dialog instance
  1079. */
  1080. BootstrapDialog.danger = function(message, callback) {
  1081. return new BootstrapDialog({
  1082. type: BootstrapDialog.TYPE_DANGER,
  1083. message: message
  1084. }).open();
  1085. };
  1086. /**
  1087. * Success window
  1088. *
  1089. * @param {type} message
  1090. * @returns the created dialog instance
  1091. */
  1092. BootstrapDialog.success = function(message, callback) {
  1093. return new BootstrapDialog({
  1094. type: BootstrapDialog.TYPE_SUCCESS,
  1095. message: message
  1096. }).open();
  1097. };
  1098. return BootstrapDialog;
  1099. }));