bootstrap-notify.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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.placement();
  97. this.bind();
  98. this.notify = {
  99. $ele: this.$ele,
  100. update: function(command, update) {
  101. var commands = {};
  102. if (typeof command == "string") {
  103. commands[command] = update;
  104. }else{
  105. commands = command;
  106. }
  107. for (var command in commands) {
  108. switch (command) {
  109. case "type":
  110. this.$ele.removeClass('alert-' + self.settings.type);
  111. this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type);
  112. self.settings.type = commands[command];
  113. this.$ele.addClass('alert-' + commands[command]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[command]);
  114. break;
  115. case "icon":
  116. var $icon = this.$ele.find('[data-notify="icon"]');
  117. if (self.settings.icon_type.toLowerCase() == 'class') {
  118. $icon.removeClass(self.settings.content.icon).addClass(commands[command]);
  119. }else{
  120. if (!$icon.is('img')) {
  121. $icon.find('img');
  122. }
  123. $icon.attr('src', commands[command]);
  124. }
  125. break;
  126. case "progress":
  127. var newDelay = self.settings.delay - (self.settings.delay * (commands[command] / 100));
  128. this.$ele.data('notify-delay', newDelay);
  129. this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[command]).css('width', commands[command] + '%');
  130. break;
  131. case "url":
  132. this.$ele.find('[data-notify="url"]').attr('href', commands[command]);
  133. break;
  134. case "target":
  135. this.$ele.find('[data-notify="url"]').attr('target', commands[command]);
  136. break;
  137. default:
  138. this.$ele.find('[data-notify="' + command +'"]').html(commands[command]);
  139. };
  140. }
  141. var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y);
  142. self.reposition(posX);
  143. },
  144. close: function() {
  145. self.close();
  146. }
  147. };
  148. },
  149. buildNotify: function () {
  150. var content = this.settings.content;
  151. this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target));
  152. this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align);
  153. if (!this.settings.allow_dismiss) {
  154. this.$ele.find('[data-notify="dismiss"]').css('display', 'none');
  155. }
  156. if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) {
  157. this.$ele.find('[data-notify="progressbar"]').remove();
  158. }
  159. },
  160. setIcon: function() {
  161. if (this.settings.icon_type.toLowerCase() == 'class') {
  162. this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon);
  163. }else{
  164. if (this.$ele.find('[data-notify="icon"]').is('img')) {
  165. this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon);
  166. }else{
  167. this.$ele.find('[data-notify="icon"]').append('<img src="'+this.settings.content.icon+'" alt="Notify Icon" />');
  168. }
  169. }
  170. },
  171. styleURL: function() {
  172. this.$ele.find('[data-notify="url"]').css({
  173. backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)',
  174. height: '100%',
  175. left: '0px',
  176. position: 'absolute',
  177. top: '0px',
  178. width: '100%',
  179. zIndex: this.settings.z_index + 1
  180. });
  181. this.$ele.find('[data-notify="dismiss"]').css({
  182. position: 'absolute',
  183. right: '10px',
  184. top: '5px',
  185. zIndex: this.settings.z_index + 2
  186. });
  187. },
  188. placement: function() {
  189. var self = this,
  190. offsetAmt = this.settings.offset.y,
  191. css = {
  192. display: 'inline-block',
  193. margin: '0px auto',
  194. position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'),
  195. transition: 'all .5s ease-in-out',
  196. zIndex: this.settings.z_index
  197. },
  198. hasAnimation = false,
  199. settings = this.settings;
  200. $('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function() {
  201. return offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing));
  202. });
  203. if (this.settings.newest_on_top == true) {
  204. offsetAmt = this.settings.offset.y;
  205. }
  206. css[this.settings.placement.from] = offsetAmt+'px';
  207. switch (this.settings.placement.align) {
  208. case "left":
  209. case "right":
  210. css[this.settings.placement.align] = this.settings.offset.x+'px';
  211. break;
  212. case "center":
  213. css.left = 0;
  214. css.right = 0;
  215. break;
  216. }
  217. this.$ele.css(css).addClass(this.settings.animate.enter);
  218. $.each(Array('webkit', 'moz', 'o', 'ms', ''), function(index, prefix) {
  219. self.$ele[0].style[prefix+'AnimationIterationCount'] = 1;
  220. });
  221. $(this.settings.element).append(this.$ele);
  222. if (this.settings.newest_on_top == true) {
  223. offsetAmt = (parseInt(offsetAmt)+parseInt(this.settings.spacing)) + this.$ele.outerHeight();
  224. this.reposition(offsetAmt);
  225. }
  226. if ($.isFunction(self.settings.onShow)) {
  227. self.settings.onShow.call(this.$ele);
  228. }
  229. this.$ele.one(this.animations.start, function(event) {
  230. hasAnimation = true;
  231. }).one(this.animations.end, function(event) {
  232. if ($.isFunction(self.settings.onShown)) {
  233. self.settings.onShown.call(this);
  234. }
  235. });
  236. setTimeout(function() {
  237. if (!hasAnimation) {
  238. if ($.isFunction(self.settings.onShown)) {
  239. self.settings.onShown.call(this);
  240. }
  241. }
  242. }, 600);
  243. },
  244. bind: function() {
  245. var self = this;
  246. this.$ele.find('[data-notify="dismiss"]').on('click', function() {
  247. self.close();
  248. })
  249. this.$ele.mouseover(function(e) {
  250. $(this).data('data-hover', "true");
  251. }).mouseout(function(e) {
  252. $(this).data('data-hover', "false");
  253. });
  254. this.$ele.data('data-hover', "false");
  255. if (this.settings.delay > 0) {
  256. self.$ele.data('notify-delay', self.settings.delay);
  257. var timer = setInterval(function() {
  258. var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer;
  259. if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over == "pause") || self.settings.mouse_over != "pause") {
  260. var percent = ((self.settings.delay - delay) / self.settings.delay) * 100;
  261. self.$ele.data('notify-delay', delay);
  262. self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%');
  263. }
  264. if (delay <= -(self.settings.timer)) {
  265. clearInterval(timer);
  266. self.close();
  267. }
  268. }, self.settings.timer);
  269. }
  270. },
  271. close: function() {
  272. var self = this,
  273. $successors = null,
  274. posX = parseInt(this.$ele.css(this.settings.placement.from)),
  275. hasAnimation = false;
  276. this.$ele.data('closing', 'true').addClass(this.settings.animate.exit);
  277. self.reposition(posX);
  278. if ($.isFunction(self.settings.onClose)) {
  279. self.settings.onClose.call(this.$ele);
  280. }
  281. this.$ele.one(this.animations.start, function(event) {
  282. hasAnimation = true;
  283. }).one(this.animations.end, function(event) {
  284. $(this).remove();
  285. if ($.isFunction(self.settings.onClosed)) {
  286. self.settings.onClosed.call(this);
  287. }
  288. });
  289. setTimeout(function() {
  290. if (!hasAnimation) {
  291. self.$ele.remove();
  292. if (self.settings.onClosed) {
  293. self.settings.onClosed(self.$ele);
  294. }
  295. }
  296. }, 600);
  297. },
  298. reposition: function(posX) {
  299. var self = this,
  300. notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])',
  301. $elements = this.$ele.nextAll(notifies);
  302. if (this.settings.newest_on_top == true) {
  303. $elements = this.$ele.prevAll(notifies);
  304. }
  305. $elements.each(function() {
  306. $(this).css(self.settings.placement.from, posX);
  307. posX = (parseInt(posX)+parseInt(self.settings.spacing)) + $(this).outerHeight();
  308. });
  309. }
  310. });
  311. $.notify = function ( content, options ) {
  312. var plugin = new Notify( this, content, options );
  313. return plugin.notify;
  314. };
  315. $.notifyDefaults = function( options ) {
  316. defaults = $.extend(true, {}, defaults, options);
  317. return defaults;
  318. };
  319. $.notifyClose = function( command ) {
  320. if (typeof command === "undefined" || command == "all") {
  321. $('[data-notify]').find('[data-notify="dismiss"]').trigger('click');
  322. }else{
  323. $('[data-notify-position="'+command+'"]').find('[data-notify="dismiss"]').trigger('click');
  324. }
  325. };
  326. }));