You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

416 lines
14 KiB

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