toastr.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. /*
  2. * Toastr
  3. * Copyright 2012-2015
  4. * Authors: John Papa, Hans Fjällemark, and Tim Ferrell.
  5. * All Rights Reserved.
  6. * Use, reproduction, distribution, and modification of this code is subject to the terms and
  7. * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
  8. *
  9. * ARIA Support: Greta Krafsig
  10. *
  11. * Project: https://github.com/CodeSeven/toastr
  12. */
  13. /* global define */
  14. (function (define) {
  15. define(['jquery'], function ($) {
  16. return (function () {
  17. var $container;
  18. var listener;
  19. var toastId = 0;
  20. var toastType = {
  21. error: 'error',
  22. info: 'info',
  23. success: 'success',
  24. warning: 'warning'
  25. };
  26. var toastr = {
  27. clear: clear,
  28. remove: remove,
  29. error: error,
  30. getContainer: getContainer,
  31. info: info,
  32. options: {},
  33. subscribe: subscribe,
  34. success: success,
  35. version: '2.1.3',
  36. warning: warning
  37. };
  38. var previousToast;
  39. return toastr;
  40. ////////////////
  41. function error(message, title, optionsOverride) {
  42. return notify({
  43. type: toastType.error,
  44. iconClass: getOptions().iconClasses.error,
  45. message: message,
  46. optionsOverride: optionsOverride,
  47. title: title
  48. });
  49. }
  50. function getContainer(options, create) {
  51. if (!options) { options = getOptions(); }
  52. $container = $('#' + options.containerId);
  53. if ($container.length) {
  54. return $container;
  55. }
  56. if (create) {
  57. $container = createContainer(options);
  58. }
  59. return $container;
  60. }
  61. function info(message, title, optionsOverride) {
  62. return notify({
  63. type: toastType.info,
  64. iconClass: getOptions().iconClasses.info,
  65. message: message,
  66. optionsOverride: optionsOverride,
  67. title: title
  68. });
  69. }
  70. function subscribe(callback) {
  71. listener = callback;
  72. }
  73. function success(message, title, optionsOverride) {
  74. return notify({
  75. type: toastType.success,
  76. iconClass: getOptions().iconClasses.success,
  77. message: message,
  78. optionsOverride: optionsOverride,
  79. title: title
  80. });
  81. }
  82. function warning(message, title, optionsOverride) {
  83. return notify({
  84. type: toastType.warning,
  85. iconClass: getOptions().iconClasses.warning,
  86. message: message,
  87. optionsOverride: optionsOverride,
  88. title: title
  89. });
  90. }
  91. function clear($toastElement, clearOptions) {
  92. var options = getOptions();
  93. if (!$container) { getContainer(options); }
  94. if (!clearToast($toastElement, options, clearOptions)) {
  95. clearContainer(options);
  96. }
  97. }
  98. function remove($toastElement) {
  99. var options = getOptions();
  100. if (!$container) { getContainer(options); }
  101. if ($toastElement && $(':focus', $toastElement).length === 0) {
  102. removeToast($toastElement);
  103. return;
  104. }
  105. if ($container.children().length) {
  106. $container.remove();
  107. }
  108. }
  109. // internal functions
  110. function clearContainer (options) {
  111. var toastsToClear = $container.children();
  112. for (var i = toastsToClear.length - 1; i >= 0; i--) {
  113. clearToast($(toastsToClear[i]), options);
  114. }
  115. }
  116. function clearToast ($toastElement, options, clearOptions) {
  117. var force = clearOptions && clearOptions.force ? clearOptions.force : false;
  118. if ($toastElement && (force || $(':focus', $toastElement).length === 0)) {
  119. $toastElement[options.hideMethod]({
  120. duration: options.hideDuration,
  121. easing: options.hideEasing,
  122. complete: function () { removeToast($toastElement); }
  123. });
  124. return true;
  125. }
  126. return false;
  127. }
  128. function createContainer(options) {
  129. $container = $('<div/>')
  130. .attr('id', options.containerId)
  131. .addClass(options.positionClass);
  132. $container.appendTo($(options.target));
  133. return $container;
  134. }
  135. function getDefaults() {
  136. return {
  137. tapToDismiss: true,
  138. toastClass: 'toast',
  139. containerId: 'toast-container',
  140. debug: false,
  141. showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
  142. showDuration: 300,
  143. showEasing: 'swing', //swing and linear are built into jQuery
  144. onShown: undefined,
  145. hideMethod: 'fadeOut',
  146. hideDuration: 1000,
  147. hideEasing: 'swing',
  148. onHidden: undefined,
  149. closeMethod: false,
  150. closeDuration: false,
  151. closeEasing: false,
  152. closeOnHover: true,
  153. extendedTimeOut: 1000,
  154. iconClasses: {
  155. error: 'toast-error',
  156. info: 'toast-info',
  157. success: 'toast-success',
  158. warning: 'toast-warning'
  159. },
  160. iconClass: 'toast-info',
  161. positionClass: 'toast-top-right',
  162. timeOut: 5000, // Set timeOut and extendedTimeOut to 0 to make it sticky
  163. titleClass: 'toast-title',
  164. messageClass: 'toast-message',
  165. escapeHtml: false,
  166. target: 'body',
  167. closeHtml: '<button type="button">&times;</button>',
  168. closeClass: 'toast-close-button',
  169. newestOnTop: true,
  170. preventDuplicates: false,
  171. progressBar: false,
  172. progressClass: 'toast-progress',
  173. rtl: false
  174. };
  175. }
  176. function publish(args) {
  177. if (!listener) { return; }
  178. listener(args);
  179. }
  180. function notify(map) {
  181. var options = getOptions();
  182. var iconClass = map.iconClass || options.iconClass;
  183. if (typeof (map.optionsOverride) !== 'undefined') {
  184. options = $.extend(options, map.optionsOverride);
  185. iconClass = map.optionsOverride.iconClass || iconClass;
  186. }
  187. if (shouldExit(options, map)) { return; }
  188. toastId++;
  189. $container = getContainer(options, true);
  190. var intervalId = null;
  191. var $toastElement = $('<div/>');
  192. var $titleElement = $('<div/>');
  193. var $messageElement = $('<div/>');
  194. var $progressElement = $('<div/>');
  195. var $closeElement = $(options.closeHtml);
  196. var progressBar = {
  197. intervalId: null,
  198. hideEta: null,
  199. maxHideTime: null
  200. };
  201. var response = {
  202. toastId: toastId,
  203. state: 'visible',
  204. startTime: new Date(),
  205. options: options,
  206. map: map
  207. };
  208. personalizeToast();
  209. displayToast();
  210. handleEvents();
  211. publish(response);
  212. if (options.debug && console) {
  213. console.log(response);
  214. }
  215. return $toastElement;
  216. function escapeHtml(source) {
  217. if (source == null) {
  218. source = '';
  219. }
  220. return source
  221. .replace(/&/g, '&amp;')
  222. .replace(/"/g, '&quot;')
  223. .replace(/'/g, '&#39;')
  224. .replace(/</g, '&lt;')
  225. .replace(/>/g, '&gt;');
  226. }
  227. function personalizeToast() {
  228. setIcon();
  229. setTitle();
  230. setMessage();
  231. setCloseButton();
  232. setProgressBar();
  233. setRTL();
  234. setSequence();
  235. setAria();
  236. }
  237. function setAria() {
  238. var ariaValue = '';
  239. switch (map.iconClass) {
  240. case 'toast-success':
  241. case 'toast-info':
  242. ariaValue = 'polite';
  243. break;
  244. default:
  245. ariaValue = 'assertive';
  246. }
  247. $toastElement.attr('aria-live', ariaValue);
  248. }
  249. function handleEvents() {
  250. if (options.closeOnHover) {
  251. $toastElement.hover(stickAround, delayedHideToast);
  252. }
  253. if (!options.onclick && options.tapToDismiss) {
  254. $toastElement.click(hideToast);
  255. }
  256. if (options.closeButton && $closeElement) {
  257. $closeElement.click(function (event) {
  258. if (event.stopPropagation) {
  259. event.stopPropagation();
  260. } else if (event.cancelBubble !== undefined && event.cancelBubble !== true) {
  261. event.cancelBubble = true;
  262. }
  263. if (options.onCloseClick) {
  264. options.onCloseClick(event);
  265. }
  266. hideToast(true);
  267. });
  268. }
  269. if (options.onclick) {
  270. $toastElement.click(function (event) {
  271. options.onclick(event);
  272. hideToast();
  273. });
  274. }
  275. }
  276. function displayToast() {
  277. $toastElement.hide();
  278. $toastElement[options.showMethod](
  279. {duration: options.showDuration, easing: options.showEasing, complete: options.onShown}
  280. );
  281. if (options.timeOut > 0) {
  282. intervalId = setTimeout(hideToast, options.timeOut);
  283. progressBar.maxHideTime = parseFloat(options.timeOut);
  284. progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime;
  285. if (options.progressBar) {
  286. progressBar.intervalId = setInterval(updateProgress, 10);
  287. }
  288. }
  289. }
  290. function setIcon() {
  291. if (map.iconClass) {
  292. $toastElement.addClass(options.toastClass).addClass(iconClass);
  293. }
  294. }
  295. function setSequence() {
  296. if (options.newestOnTop) {
  297. $container.prepend($toastElement);
  298. } else {
  299. $container.append($toastElement);
  300. }
  301. }
  302. function setTitle() {
  303. if (map.title) {
  304. var suffix = map.title;
  305. if (options.escapeHtml) {
  306. suffix = escapeHtml(map.title);
  307. }
  308. $titleElement.append(suffix).addClass(options.titleClass);
  309. $toastElement.append($titleElement);
  310. }
  311. }
  312. function setMessage() {
  313. if (map.message) {
  314. var suffix = map.message;
  315. if (options.escapeHtml) {
  316. suffix = escapeHtml(map.message);
  317. }
  318. $messageElement.append(suffix).addClass(options.messageClass);
  319. $toastElement.append($messageElement);
  320. }
  321. }
  322. function setCloseButton() {
  323. if (options.closeButton) {
  324. $closeElement.addClass(options.closeClass).attr('role', 'button');
  325. $toastElement.prepend($closeElement);
  326. }
  327. }
  328. function setProgressBar() {
  329. if (options.progressBar) {
  330. $progressElement.addClass(options.progressClass);
  331. $toastElement.prepend($progressElement);
  332. }
  333. }
  334. function setRTL() {
  335. if (options.rtl) {
  336. $toastElement.addClass('rtl');
  337. }
  338. }
  339. function shouldExit(options, map) {
  340. if (options.preventDuplicates) {
  341. if (map.message === previousToast) {
  342. return true;
  343. } else {
  344. previousToast = map.message;
  345. }
  346. }
  347. return false;
  348. }
  349. function hideToast(override) {
  350. var method = override && options.closeMethod !== false ? options.closeMethod : options.hideMethod;
  351. var duration = override && options.closeDuration !== false ?
  352. options.closeDuration : options.hideDuration;
  353. var easing = override && options.closeEasing !== false ? options.closeEasing : options.hideEasing;
  354. if ($(':focus', $toastElement).length && !override) {
  355. return;
  356. }
  357. clearTimeout(progressBar.intervalId);
  358. return $toastElement[method]({
  359. duration: duration,
  360. easing: easing,
  361. complete: function () {
  362. removeToast($toastElement);
  363. clearTimeout(intervalId);
  364. if (options.onHidden && response.state !== 'hidden') {
  365. options.onHidden();
  366. }
  367. response.state = 'hidden';
  368. response.endTime = new Date();
  369. publish(response);
  370. }
  371. });
  372. }
  373. function delayedHideToast() {
  374. if (options.timeOut > 0 || options.extendedTimeOut > 0) {
  375. intervalId = setTimeout(hideToast, options.extendedTimeOut);
  376. progressBar.maxHideTime = parseFloat(options.extendedTimeOut);
  377. progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime;
  378. }
  379. }
  380. function stickAround() {
  381. clearTimeout(intervalId);
  382. progressBar.hideEta = 0;
  383. $toastElement.stop(true, true)[options.showMethod](
  384. {duration: options.showDuration, easing: options.showEasing}
  385. );
  386. }
  387. function updateProgress() {
  388. var percentage = ((progressBar.hideEta - (new Date().getTime())) / progressBar.maxHideTime) * 100;
  389. $progressElement.width(percentage + '%');
  390. }
  391. }
  392. function getOptions() {
  393. return $.extend({}, getDefaults(), toastr.options);
  394. }
  395. function removeToast($toastElement) {
  396. if (!$container) { $container = getContainer(); }
  397. if ($toastElement.is(':visible')) {
  398. return;
  399. }
  400. $toastElement.remove();
  401. $toastElement = null;
  402. if ($container.children().length === 0) {
  403. $container.remove();
  404. previousToast = undefined;
  405. }
  406. }
  407. })();
  408. });
  409. }(typeof define === 'function' && define.amd ? define : function (deps, factory) {
  410. if (typeof module !== 'undefined' && module.exports) { //Node
  411. module.exports = factory(require('jquery'));
  412. } else {
  413. window.toastr = factory(window.jQuery);
  414. }
  415. }));