toaster.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. 'use strict';
  2. /*
  3. * AngularJS Toaster
  4. * Version: 0.4.8
  5. *
  6. * Copyright 2013 Jiri Kavulak.
  7. * All Rights Reserved.
  8. * Use, reproduction, distribution, and modification of this code is subject to the terms and
  9. * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
  10. *
  11. * Author: Jiri Kavulak
  12. * Related to project of John Papa and Hans Fjällemark
  13. */
  14. angular.module('toaster', ['ngAnimate'])
  15. .service('toaster', ['$rootScope', function ($rootScope) {
  16. this.pop = function (type, title, body, timeout, bodyOutputType, clickHandler) {
  17. this.toast = {
  18. type: type,
  19. title: title,
  20. body: body,
  21. timeout: timeout,
  22. bodyOutputType: bodyOutputType,
  23. clickHandler: clickHandler
  24. };
  25. $rootScope.$broadcast('toaster-newToast');
  26. };
  27. this.clear = function () {
  28. $rootScope.$broadcast('toaster-clearToasts');
  29. };
  30. }])
  31. .constant('toasterConfig', {
  32. 'limit': 0, // limits max number of toasts
  33. 'tap-to-dismiss': true,
  34. 'close-button': false,
  35. 'newest-on-top': true,
  36. //'fade-in': 1000, // done in css
  37. //'on-fade-in': undefined, // not implemented
  38. //'fade-out': 1000, // done in css
  39. // 'on-fade-out': undefined, // not implemented
  40. //'extended-time-out': 1000, // not implemented
  41. 'time-out': 5000, // Set timeOut and extendedTimeout to 0 to make it sticky
  42. 'icon-classes': {
  43. error: 'toast-error',
  44. info: 'toast-info',
  45. wait: 'toast-wait',
  46. success: 'toast-success',
  47. warning: 'toast-warning'
  48. },
  49. 'body-output-type': '', // Options: '', 'trustedHtml', 'template'
  50. 'body-template': 'toasterBodyTmpl.html',
  51. 'icon-class': 'toast-info',
  52. 'position-class': 'toast-top-right',
  53. 'title-class': 'toast-title',
  54. 'message-class': 'toast-message'
  55. })
  56. .directive('toasterContainer', ['$compile', '$timeout', '$sce', 'toasterConfig', 'toaster',
  57. function ($compile, $timeout, $sce, toasterConfig, toaster) {
  58. return {
  59. replace: true,
  60. restrict: 'EA',
  61. scope: true, // creates an internal scope for this directive
  62. link: function (scope, elm, attrs) {
  63. var id = 0,
  64. mergedConfig;
  65. mergedConfig = angular.extend({}, toasterConfig, scope.$eval(attrs.toasterOptions));
  66. scope.config = {
  67. position: mergedConfig['position-class'],
  68. title: mergedConfig['title-class'],
  69. message: mergedConfig['message-class'],
  70. tap: mergedConfig['tap-to-dismiss'],
  71. closeButton: mergedConfig['close-button']
  72. };
  73. scope.configureTimer = function configureTimer(toast) {
  74. var timeout = typeof (toast.timeout) == "number" ? toast.timeout : mergedConfig['time-out'];
  75. if (timeout > 0)
  76. setTimeout(toast, timeout);
  77. };
  78. function addToast(toast) {
  79. toast.type = mergedConfig['icon-classes'][toast.type];
  80. if (!toast.type)
  81. toast.type = mergedConfig['icon-class'];
  82. id++;
  83. angular.extend(toast, { id: id });
  84. // Set the toast.bodyOutputType to the default if it isn't set
  85. toast.bodyOutputType = toast.bodyOutputType || mergedConfig['body-output-type'];
  86. switch (toast.bodyOutputType) {
  87. case 'trustedHtml':
  88. toast.html = $sce.trustAsHtml(toast.body);
  89. break;
  90. case 'template':
  91. toast.bodyTemplate = toast.body || mergedConfig['body-template'];
  92. break;
  93. }
  94. scope.configureTimer(toast);
  95. if (mergedConfig['newest-on-top'] === true) {
  96. scope.toasters.unshift(toast);
  97. if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
  98. scope.toasters.pop();
  99. }
  100. } else {
  101. scope.toasters.push(toast);
  102. if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
  103. scope.toasters.shift();
  104. }
  105. }
  106. }
  107. function setTimeout(toast, time) {
  108. toast.timeout = $timeout(function () {
  109. scope.removeToast(toast.id);
  110. }, time);
  111. }
  112. scope.toasters = [];
  113. scope.$on('toaster-newToast', function () {
  114. addToast(toaster.toast);
  115. });
  116. scope.$on('toaster-clearToasts', function () {
  117. scope.toasters.splice(0, scope.toasters.length);
  118. });
  119. },
  120. controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
  121. $scope.stopTimer = function (toast) {
  122. if (toast.timeout) {
  123. $timeout.cancel(toast.timeout);
  124. toast.timeout = null;
  125. }
  126. };
  127. $scope.restartTimer = function (toast) {
  128. if (!toast.timeout)
  129. $scope.configureTimer(toast);
  130. };
  131. $scope.removeToast = function (id) {
  132. var i = 0;
  133. for (i; i < $scope.toasters.length; i++) {
  134. if ($scope.toasters[i].id === id)
  135. break;
  136. }
  137. $scope.toasters.splice(i, 1);
  138. };
  139. $scope.click = function (toaster) {
  140. if ($scope.config.tap === true) {
  141. if (toaster.clickHandler && angular.isFunction($scope.$parent.$eval(toaster.clickHandler))) {
  142. var result = $scope.$parent.$eval(toaster.clickHandler)(toaster);
  143. if (result === true)
  144. $scope.removeToast(toaster.id);
  145. } else {
  146. if (angular.isString(toaster.clickHandler))
  147. console.log("TOAST-NOTE: Your click handler is not inside a parent scope of toaster-container.");
  148. $scope.removeToast(toaster.id);
  149. }
  150. }
  151. };
  152. }],
  153. template:
  154. '<div id="toast-container" ng-class="config.position">' +
  155. '<div ng-repeat="toaster in toasters" class="toast" ng-class="toaster.type" ng-click="click(toaster)" ng-mouseover="stopTimer(toaster)" ng-mouseout="restartTimer(toaster)">' +
  156. '<button class="toast-close-button" ng-show="config.closeButton">&times;</button>' +
  157. '<div ng-class="config.title">{{toaster.title}}</div>' +
  158. '<div ng-class="config.message" ng-switch on="toaster.bodyOutputType">' +
  159. '<div ng-switch-when="trustedHtml" ng-bind-html="toaster.html"></div>' +
  160. '<div ng-switch-when="template"><div ng-include="toaster.bodyTemplate"></div></div>' +
  161. '<div ng-switch-default >{{toaster.body}}</div>' +
  162. '</div>' +
  163. '</div>' +
  164. '</div>'
  165. };
  166. }]);