bootstrap-notify.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * Project: Bootstrap Notify = v3.1.3
  3. * Description: Turns standard Bootstrap alerts into "Growl-like" notifications.
  4. * Author: Mouse0270 aka Robert McIntosh
  5. * License: MIT License
  6. * Website: https://github.com/mouse0270/bootstrap-growl
  7. */
  8. (function (factory) {
  9. if (typeof define === 'function' && define.amd) {
  10. // AMD. Register as an anonymous module.
  11. define(['jquery'], factory);
  12. } else if (typeof exports === 'object') {
  13. // Node/CommonJS
  14. factory(require('jquery'));
  15. } else {
  16. // Browser globals
  17. factory(jQuery);
  18. }
  19. }(function ($) {
  20. // Create the defaults once
  21. var defaults = {
  22. element: 'body',
  23. position: null,
  24. type: "info",
  25. allow_dismiss: true,
  26. newest_on_top: false,
  27. showProgressbar: false,
  28. placement: {
  29. from: "top",
  30. align: "right"
  31. },
  32. offset: 20,
  33. spacing: 10,
  34. z_index: 1031,
  35. delay: 5000,
  36. timer: 1000,
  37. url_target: '_blank',
  38. mouse_over: null,
  39. animate: {
  40. enter: 'animated fadeInDown',
  41. exit: 'animated fadeOutUp'
  42. },
  43. onShow: null,
  44. onShown: null,
  45. onClose: null,
  46. onClosed: null,
  47. icon_type: 'class',
  48. template: '<div data-notify="container" class="col-xs-11 col-sm-4 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="close" data-notify="dismiss">&times;</button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>'
  49. };
  50. String.format = function() {
  51. var str = arguments[0];
  52. for (var i = 1; i < arguments.length; i++) {
  53. str = str.replace(RegExp("\\{" + (i - 1) + "\\}", "gm"), arguments[i]);
  54. }
  55. return str;
  56. };
  57. function Notify ( element, content, options ) {
  58. // Setup Content of Notify
  59. var content = {
  60. content: {
  61. message: typeof content == 'object' ? content.message : content,
  62. title: content.title ? content.title : '',
  63. icon: content.icon ? content.icon : '',
  64. url: content.url ? content.url : '#',
  65. target: content.target ? content.target : '-'
  66. }
  67. };
  68. options = $.extend(true, {}, content, options);
  69. this.settings = $.extend(true, {}, defaults, options);
  70. this._defaults = defaults;
  71. if (this.settings.content.target == "-") {
  72. this.settings.content.target = this.settings.url_target;
  73. }
  74. this.animations = {
  75. start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart',
  76. end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend'
  77. }
  78. if (typeof this.settings.offset == 'number') {
  79. this.settings.offset = {
  80. x: this.settings.offset,
  81. y: this.settings.offset
  82. };
  83. }
  84. this.init();
  85. };
  86. $.extend(Notify.prototype, {
  87. init: function () {
  88. var self = this;
  89. this.buildNotify();
  90. if (this.settings.content.icon) {
  91. this.setIcon();
  92. }
  93. if (this.settings.content.url != "#") {
  94. this.styleURL();
  95. }
  96. this.styleDismiss();
  97. this.placement();
  98. this.bind();
  99. this.notify = {
  100. $ele: this.$ele,
  101. update: function(command, update) {
  102. var commands = {};
  103. if (typeof command == "string") {
  104. commands[command] = update;
  105. }else{
  106. commands = command;
  107. }
  108. for (var command in commands) {
  109. switch (command) {
  110. case "type":
  111. this.$ele.removeClass('alert-' + self.settings.type);
  112. this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type);
  113. self.settings.type = commands[command];
  114. this.$ele.addClass('alert-' + commands[command]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[command]);
  115. break;
  116. case "icon":
  117. var $icon = this.$ele.find('[data-notify="icon"]');
  118. if (self.settings.icon_type.toLowerCase() == 'class') {
  119. $icon.removeClass(self.settings.content.icon).addClass(commands[command]);
  120. }else{
  121. if (!$icon.is('img')) {
  122. $icon.find('img');
  123. }
  124. $icon.attr('src', commands[command]);
  125. }
  126. break;
  127. case "progress":
  128. var newDelay = self.settings.delay - (self.settings.delay * (commands[command] / 100));
  129. this.$ele.data('notify-delay', newDelay);
  130. this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[command]).css('width', commands[command] + '%');
  131. break;
  132. case "url":
  133. this.$ele.find('[data-notify="url"]').attr('href', commands[command]);
  134. break;
  135. case "target":
  136. this.$ele.find('[data-notify="url"]').attr('target', commands[command]);
  137. break;
  138. default:
  139. this.$ele.find('[data-notify="' + command +'"]').html(commands[command]);
  140. };
  141. }
  142. var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y);
  143. self.reposition(posX);
  144. },
  145. close: function() {
  146. self.close();
  147. }
  148. };
  149. },
  150. buildNotify: function () {
  151. var content = this.settings.content;
  152. this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target));
  153. this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align);
  154. if (!this.settings.allow_dismiss) {
  155. this.$ele.find('[data-notify="dismiss"]').css('display', 'none');
  156. }
  157. if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) {
  158. this.$ele.find('[data-notify="progressbar"]').remove();
  159. }
  160. },
  161. setIcon: function() {
  162. if (this.settings.icon_type.toLowerCase() == 'class') {
  163. this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon);
  164. }else{
  165. if (this.$ele.find('[data-notify="icon"]').is('img')) {
  166. this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon);
  167. }else{
  168. this.$ele.find('[data-notify="icon"]').append('<img src="'+this.settings.content.icon+'" alt="Notify Icon" />');
  169. }
  170. }
  171. },
  172. styleDismiss: function() {
  173. this.$ele.find('[data-notify="dismiss"]').css({
  174. position: 'absolute',
  175. right: '10px',
  176. top: '5px',
  177. zIndex: this.settings.z_index + 2
  178. });
  179. },
  180. styleURL: function() {
  181. this.$ele.find('[data-notify="url"]').css({
  182. backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)',
  183. height: '100%',
  184. left: '0px',
  185. position: 'absolute',
  186. top: '0px',
  187. width: '100%',
  188. zIndex: this.settings.z_index + 1
  189. });
  190. },
  191. placement: function() {
  192. var self = this,
  193. offsetAmt = this.settings.offset.y,
  194. css = {
  195. display: 'inline-block',
  196. margin: '0px auto',
  197. position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'),
  198. transition: 'all .5s ease-in-out',
  199. zIndex: this.settings.z_index
  200. },
  201. hasAnimation = false,
  202. settings = this.settings;
  203. $('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function() {
  204. return offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing));
  205. });
  206. if (this.settings.newest_on_top == true) {
  207. offsetAmt = this.settings.offset.y;
  208. }
  209. css[this.settings.placement.from] = offsetAmt+'px';
  210. switch (this.settings.placement.align) {
  211. case "left":
  212. case "right":
  213. css[this.settings.placement.align] = this.settings.offset.x+'px';
  214. break;
  215. case "center":
  216. css.left = 0;
  217. css.right = 0;
  218. break;
  219. }
  220. this.$ele.css(css).addClass(this.settings.animate.enter);
  221. $.each(Array('webkit-', 'moz-', 'o-', 'ms-', ''), function(index, prefix) {
  222. self.$ele[0].style[prefix+'AnimationIterationCount'] = 1;
  223. });
  224. $(this.settings.element).append(this.$ele);
  225. if (this.settings.newest_on_top == true) {
  226. offsetAmt = (parseInt(offsetAmt)+parseInt(this.settings.spacing)) + this.$ele.outerHeight();
  227. this.reposition(offsetAmt);
  228. }
  229. if ($.isFunction(self.settings.onShow)) {
  230. self.settings.onShow.call(this.$ele);
  231. }
  232. this.$ele.one(this.animations.start, function(event) {
  233. hasAnimation = true;
  234. }).one(this.animations.end, function(event) {
  235. if ($.isFunction(self.settings.onShown)) {
  236. self.settings.onShown.call(this);
  237. }
  238. });
  239. setTimeout(function() {
  240. if (!hasAnimation) {
  241. if ($.isFunction(self.settings.onShown)) {
  242. self.settings.onShown.call(this);
  243. }
  244. }
  245. }, 600);
  246. },
  247. bind: function() {
  248. var self = this;
  249. this.$ele.find('[data-notify="dismiss"]').on('click', function() {
  250. self.close();
  251. })
  252. this.$ele.mouseover(function(e) {
  253. $(this).data('data-hover', "true");
  254. }).mouseout(function(e) {
  255. $(this).data('data-hover', "false");
  256. });
  257. this.$ele.data('data-hover', "false");
  258. if (this.settings.delay > 0) {
  259. self.$ele.data('notify-delay', self.settings.delay);
  260. var timer = setInterval(function() {
  261. var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer;
  262. if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over == "pause") || self.settings.mouse_over != "pause") {
  263. var percent = ((self.settings.delay - delay) / self.settings.delay) * 100;
  264. self.$ele.data('notify-delay', delay);
  265. self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%');
  266. }
  267. if (delay <= -(self.settings.timer)) {
  268. clearInterval(timer);
  269. self.close();
  270. }
  271. }, self.settings.timer);
  272. }
  273. },
  274. close: function() {
  275. var self = this,
  276. $successors = null,
  277. posX = parseInt(this.$ele.css(this.settings.placement.from)),
  278. hasAnimation = false;
  279. this.$ele.data('closing', 'true').addClass(this.settings.animate.exit);
  280. self.reposition(posX);
  281. if ($.isFunction(self.settings.onClose)) {
  282. self.settings.onClose.call(this.$ele);
  283. }
  284. this.$ele.one(this.animations.start, function(event) {
  285. hasAnimation = true;
  286. }).one(this.animations.end, function(event) {
  287. $(this).remove();
  288. if ($.isFunction(self.settings.onClosed)) {
  289. self.settings.onClosed.call(this);
  290. }
  291. });
  292. setTimeout(function() {
  293. if (!hasAnimation) {
  294. self.$ele.remove();
  295. if (self.settings.onClosed) {
  296. self.settings.onClosed(self.$ele);
  297. }
  298. }
  299. }, 600);
  300. },
  301. reposition: function(posX) {
  302. var self = this,
  303. notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])',
  304. $elements = this.$ele.nextAll(notifies);
  305. if (this.settings.newest_on_top == true) {
  306. $elements = this.$ele.prevAll(notifies);
  307. }
  308. $elements.each(function() {
  309. $(this).css(self.settings.placement.from, posX);
  310. posX = (parseInt(posX)+parseInt(self.settings.spacing)) + $(this).outerHeight();
  311. });
  312. }
  313. });
  314. $.notify = function ( content, options ) {
  315. var plugin = new Notify( this, content, options );
  316. return plugin.notify;
  317. };
  318. $.notifyDefaults = function( options ) {
  319. defaults = $.extend(true, {}, defaults, options);
  320. return defaults;
  321. };
  322. $.notifyClose = function( command ) {
  323. if (typeof command === "undefined" || command == "all") {
  324. $('[data-notify]').find('[data-notify="dismiss"]').trigger('click');
  325. }else{
  326. $('[data-notify-position="'+command+'"]').find('[data-notify="dismiss"]').trigger('click');
  327. }
  328. };
  329. }));