No Description

bootstrap-notify.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /*
  2. Creative Tim Modifications
  3. Lines: 238, 239 was changed from top: 5px to top: 50% and we added margin-top: -13px. In this way the close button will be aligned vertically
  4. Line:222 - modified when the icon is set, we add the class "alert-with-icon", so there will be enough space for the icon.
  5. */
  6. /*
  7. * Project: Bootstrap Notify = v3.1.5
  8. * Description: Turns standard Bootstrap alerts into "Growl-like" notifications.
  9. * Author: Mouse0270 aka Robert McIntosh
  10. * License: MIT License
  11. * Website: https://github.com/mouse0270/bootstrap-growl
  12. */
  13. /* global define:false, require: false, jQuery:false */
  14. (function(factory) {
  15. if (typeof define === 'function' && define.amd) {
  16. // AMD. Register as an anonymous module.
  17. define(['jquery'], factory);
  18. } else if (typeof exports === 'object') {
  19. // Node/CommonJS
  20. factory(require('jquery'));
  21. } else {
  22. // Browser globals
  23. factory(jQuery);
  24. }
  25. }(function($) {
  26. // Create the defaults once
  27. var defaults = {
  28. element: 'body',
  29. position: null,
  30. type: "info",
  31. allow_dismiss: true,
  32. allow_duplicates: true,
  33. newest_on_top: false,
  34. showProgressbar: false,
  35. placement: {
  36. from: "top",
  37. align: "right"
  38. },
  39. offset: 20,
  40. spacing: 10,
  41. z_index: 1060,
  42. delay: 5000,
  43. timer: 1000,
  44. url_target: '_blank',
  45. mouse_over: null,
  46. animate: {
  47. enter: 'animated fadeInDown',
  48. exit: 'animated fadeOutUp'
  49. },
  50. onShow: null,
  51. onShown: null,
  52. onClose: null,
  53. onClosed: null,
  54. onClick: null,
  55. icon_type: 'class',
  56. 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"><i class="tim-icons icon-simple-remove"></i></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>'
  57. };
  58. String.format = function() {
  59. var args = arguments;
  60. var str = arguments[0];
  61. return str.replace(/(\{\{\d\}\}|\{\d\})/g, function(str) {
  62. if (str.substring(0, 2) === "{{") return str;
  63. var num = parseInt(str.match(/\d/)[0]);
  64. return args[num + 1];
  65. });
  66. };
  67. function isDuplicateNotification(notification) {
  68. var isDupe = false;
  69. $('[data-notify="container"]').each(function(i, el) {
  70. var $el = $(el);
  71. var title = $el.find('[data-notify="title"]').html().trim();
  72. var message = $el.find('[data-notify="message"]').html().trim();
  73. // The input string might be different than the actual parsed HTML string!
  74. // (<br> vs <br /> for example)
  75. // So we have to force-parse this as HTML here!
  76. var isSameTitle = title === $("<div>" + notification.settings.content.title + "</div>").html().trim();
  77. var isSameMsg = message === $("<div>" + notification.settings.content.message + "</div>").html().trim();
  78. var isSameType = $el.hasClass('alert-' + notification.settings.type);
  79. if (isSameTitle && isSameMsg && isSameType) {
  80. //we found the dupe. Set the var and stop checking.
  81. isDupe = true;
  82. }
  83. return !isDupe;
  84. });
  85. return isDupe;
  86. }
  87. function Notify(element, content, options) {
  88. // Setup Content of Notify
  89. var contentObj = {
  90. content: {
  91. message: typeof content === 'object' ? content.message : content,
  92. title: content.title ? content.title : '',
  93. icon: content.icon ? content.icon : '',
  94. url: content.url ? content.url : '#',
  95. target: content.target ? content.target : '-'
  96. }
  97. };
  98. options = $.extend(true, {}, contentObj, options);
  99. this.settings = $.extend(true, {}, defaults, options);
  100. this._defaults = defaults;
  101. if (this.settings.content.target === "-") {
  102. this.settings.content.target = this.settings.url_target;
  103. }
  104. this.animations = {
  105. start: 'webkitAnimationStart oanimationstart MSAnimationStart animationstart',
  106. end: 'webkitAnimationEnd oanimationend MSAnimationEnd animationend'
  107. };
  108. if (typeof this.settings.offset === 'number') {
  109. this.settings.offset = {
  110. x: this.settings.offset,
  111. y: this.settings.offset
  112. };
  113. }
  114. //if duplicate messages are not allowed, then only continue if this new message is not a duplicate of one that it already showing
  115. if (this.settings.allow_duplicates || (!this.settings.allow_duplicates && !isDuplicateNotification(this))) {
  116. this.init();
  117. }
  118. }
  119. $.extend(Notify.prototype, {
  120. init: function() {
  121. var self = this;
  122. this.buildNotify();
  123. if (this.settings.content.icon) {
  124. this.setIcon();
  125. }
  126. if (this.settings.content.url != "#") {
  127. this.styleURL();
  128. }
  129. this.styleDismiss();
  130. this.placement();
  131. this.bind();
  132. this.notify = {
  133. $ele: this.$ele,
  134. update: function(command, update) {
  135. var commands = {};
  136. if (typeof command === "string") {
  137. commands[command] = update;
  138. } else {
  139. commands = command;
  140. }
  141. for (var cmd in commands) {
  142. switch (cmd) {
  143. case "type":
  144. this.$ele.removeClass('alert-' + self.settings.type);
  145. this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass('progress-bar-' + self.settings.type);
  146. self.settings.type = commands[cmd];
  147. this.$ele.addClass('alert-' + commands[cmd]).find('[data-notify="progressbar"] > .progress-bar').addClass('progress-bar-' + commands[cmd]);
  148. break;
  149. case "icon":
  150. var $icon = this.$ele.find('[data-notify="icon"]');
  151. if (self.settings.icon_type.toLowerCase() === 'class') {
  152. $icon.removeClass(self.settings.content.icon).addClass(commands[cmd]);
  153. } else {
  154. if (!$icon.is('img')) {
  155. $icon.find('img');
  156. }
  157. $icon.attr('src', commands[cmd]);
  158. }
  159. self.settings.content.icon = commands[command];
  160. break;
  161. case "progress":
  162. var newDelay = self.settings.delay - (self.settings.delay * (commands[cmd] / 100));
  163. this.$ele.data('notify-delay', newDelay);
  164. this.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', commands[cmd]).css('width', commands[cmd] + '%');
  165. break;
  166. case "url":
  167. this.$ele.find('[data-notify="url"]').attr('href', commands[cmd]);
  168. break;
  169. case "target":
  170. this.$ele.find('[data-notify="url"]').attr('target', commands[cmd]);
  171. break;
  172. default:
  173. this.$ele.find('[data-notify="' + cmd + '"]').html(commands[cmd]);
  174. }
  175. }
  176. var posX = this.$ele.outerHeight() + parseInt(self.settings.spacing) + parseInt(self.settings.offset.y);
  177. self.reposition(posX);
  178. },
  179. close: function() {
  180. self.close();
  181. }
  182. };
  183. },
  184. buildNotify: function() {
  185. var content = this.settings.content;
  186. this.$ele = $(String.format(this.settings.template, this.settings.type, content.title, content.message, content.url, content.target));
  187. this.$ele.attr('data-notify-position', this.settings.placement.from + '-' + this.settings.placement.align);
  188. if (!this.settings.allow_dismiss) {
  189. this.$ele.find('[data-notify="dismiss"]').css('display', 'none');
  190. }
  191. if ((this.settings.delay <= 0 && !this.settings.showProgressbar) || !this.settings.showProgressbar) {
  192. this.$ele.find('[data-notify="progressbar"]').remove();
  193. }
  194. },
  195. setIcon: function() {
  196. this.$ele.addClass('alert-with-icon');
  197. if (this.settings.icon_type.toLowerCase() === 'class') {
  198. this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon);
  199. } else {
  200. if (this.$ele.find('[data-notify="icon"]').is('img')) {
  201. this.$ele.find('[data-notify="icon"]').attr('src', this.settings.content.icon);
  202. } else {
  203. this.$ele.find('[data-notify="icon"]').append('<img src="' + this.settings.content.icon + '" alt="Notify Icon" />');
  204. }
  205. }
  206. },
  207. styleDismiss: function() {
  208. this.$ele.find('[data-notify="dismiss"]').css({
  209. position: 'absolute',
  210. right: '10px',
  211. top: '50%',
  212. marginTop: '-13px',
  213. zIndex: this.settings.z_index + 2
  214. });
  215. },
  216. styleURL: function() {
  217. this.$ele.find('[data-notify="url"]').css({
  218. backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)',
  219. height: '100%',
  220. left: 0,
  221. position: 'absolute',
  222. top: 0,
  223. width: '100%',
  224. zIndex: this.settings.z_index + 1
  225. });
  226. },
  227. placement: function() {
  228. var self = this,
  229. offsetAmt = this.settings.offset.y,
  230. css = {
  231. display: 'inline-block',
  232. margin: '0px auto',
  233. position: this.settings.position ? this.settings.position : (this.settings.element === 'body' ? 'fixed' : 'absolute'),
  234. transition: 'all .5s ease-in-out',
  235. zIndex: this.settings.z_index
  236. },
  237. hasAnimation = false,
  238. settings = this.settings;
  239. $('[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])').each(function() {
  240. offsetAmt = Math.max(offsetAmt, parseInt($(this).css(settings.placement.from)) + parseInt($(this).outerHeight()) + parseInt(settings.spacing));
  241. });
  242. if (this.settings.newest_on_top === true) {
  243. offsetAmt = this.settings.offset.y;
  244. }
  245. css[this.settings.placement.from] = offsetAmt + 'px';
  246. switch (this.settings.placement.align) {
  247. case "left":
  248. case "right":
  249. css[this.settings.placement.align] = this.settings.offset.x + 'px';
  250. break;
  251. case "center":
  252. css.left = 0;
  253. css.right = 0;
  254. break;
  255. }
  256. this.$ele.css(css).addClass(this.settings.animate.enter);
  257. $.each(Array('webkit-', 'moz-', 'o-', 'ms-', ''), function(index, prefix) {
  258. self.$ele[0].style[prefix + 'AnimationIterationCount'] = 1;
  259. });
  260. $(this.settings.element).append(this.$ele);
  261. if (this.settings.newest_on_top === true) {
  262. offsetAmt = (parseInt(offsetAmt) + parseInt(this.settings.spacing)) + this.$ele.outerHeight();
  263. this.reposition(offsetAmt);
  264. }
  265. if ($.isFunction(self.settings.onShow)) {
  266. self.settings.onShow.call(this.$ele);
  267. }
  268. this.$ele.one(this.animations.start, function() {
  269. hasAnimation = true;
  270. }).one(this.animations.end, function() {
  271. self.$ele.removeClass(self.settings.animate.enter);
  272. if ($.isFunction(self.settings.onShown)) {
  273. self.settings.onShown.call(this);
  274. }
  275. });
  276. setTimeout(function() {
  277. if (!hasAnimation) {
  278. if ($.isFunction(self.settings.onShown)) {
  279. self.settings.onShown.call(this);
  280. }
  281. }
  282. }, 600);
  283. },
  284. bind: function() {
  285. var self = this;
  286. this.$ele.find('[data-notify="dismiss"]').on('click', function() {
  287. self.close();
  288. });
  289. if ($.isFunction(self.settings.onClick)) {
  290. this.$ele.on('click', function(event) {
  291. if (event.target != self.$ele.find('[data-notify="dismiss"]')[0]) {
  292. self.settings.onClick.call(this, event);
  293. }
  294. });
  295. }
  296. this.$ele.mouseover(function() {
  297. $(this).data('data-hover', "true");
  298. }).mouseout(function() {
  299. $(this).data('data-hover', "false");
  300. });
  301. this.$ele.data('data-hover', "false");
  302. if (this.settings.delay > 0) {
  303. self.$ele.data('notify-delay', self.settings.delay);
  304. var timer = setInterval(function() {
  305. var delay = parseInt(self.$ele.data('notify-delay')) - self.settings.timer;
  306. if ((self.$ele.data('data-hover') === 'false' && self.settings.mouse_over === "pause") || self.settings.mouse_over != "pause") {
  307. var percent = ((self.settings.delay - delay) / self.settings.delay) * 100;
  308. self.$ele.data('notify-delay', delay);
  309. self.$ele.find('[data-notify="progressbar"] > div').attr('aria-valuenow', percent).css('width', percent + '%');
  310. }
  311. if (delay <= -(self.settings.timer)) {
  312. clearInterval(timer);
  313. self.close();
  314. }
  315. }, self.settings.timer);
  316. }
  317. },
  318. close: function() {
  319. var self = this,
  320. posX = parseInt(this.$ele.css(this.settings.placement.from)),
  321. hasAnimation = false;
  322. this.$ele.attr('data-closing', 'true').addClass(this.settings.animate.exit);
  323. self.reposition(posX);
  324. if ($.isFunction(self.settings.onClose)) {
  325. self.settings.onClose.call(this.$ele);
  326. }
  327. this.$ele.one(this.animations.start, function() {
  328. hasAnimation = true;
  329. }).one(this.animations.end, function() {
  330. $(this).remove();
  331. if ($.isFunction(self.settings.onClosed)) {
  332. self.settings.onClosed.call(this);
  333. }
  334. });
  335. setTimeout(function() {
  336. if (!hasAnimation) {
  337. self.$ele.remove();
  338. if (self.settings.onClosed) {
  339. self.settings.onClosed(self.$ele);
  340. }
  341. }
  342. }, 600);
  343. },
  344. reposition: function(posX) {
  345. var self = this,
  346. notifies = '[data-notify-position="' + this.settings.placement.from + '-' + this.settings.placement.align + '"]:not([data-closing="true"])',
  347. $elements = this.$ele.nextAll(notifies);
  348. if (this.settings.newest_on_top === true) {
  349. $elements = this.$ele.prevAll(notifies);
  350. }
  351. $elements.each(function() {
  352. $(this).css(self.settings.placement.from, posX);
  353. posX = (parseInt(posX) + parseInt(self.settings.spacing)) + $(this).outerHeight();
  354. });
  355. }
  356. });
  357. $.notify = function(content, options) {
  358. var plugin = new Notify(this, content, options);
  359. return plugin.notify;
  360. };
  361. $.notifyDefaults = function(options) {
  362. defaults = $.extend(true, {}, defaults, options);
  363. return defaults;
  364. };
  365. $.notifyClose = function(selector) {
  366. if (typeof selector === "undefined" || selector === "all") {
  367. $('[data-notify]').find('[data-notify="dismiss"]').trigger('click');
  368. } else if (selector === 'success' || selector === 'info' || selector === 'warning' || selector === 'danger') {
  369. $('.alert-' + selector + '[data-notify]').find('[data-notify="dismiss"]').trigger('click');
  370. } else if (selector) {
  371. $(selector + '[data-notify]').find('[data-notify="dismiss"]').trigger('click');
  372. } else {
  373. $('[data-notify-position="' + selector + '"]').find('[data-notify="dismiss"]').trigger('click');
  374. }
  375. };
  376. $.notifyCloseExcept = function(selector) {
  377. if (selector === 'success' || selector === 'info' || selector === 'warning' || selector === 'danger') {
  378. $('[data-notify]').not('.alert-' + selector).find('[data-notify="dismiss"]').trigger('click');
  379. } else {
  380. $('[data-notify]').not(selector).find('[data-notify="dismiss"]').trigger('click');
  381. }
  382. };
  383. }));