toastr.js 16 KB

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