From 630a0e949b398289210140a9b1e6d68b96243284 Mon Sep 17 00:00:00 2001 From: Stabzs Date: Fri, 5 Feb 2016 18:43:17 -0700 Subject: [PATCH 1/2] Added onShow callback Per https://github.com/jirikavi/AngularJS-Toaster/issues/189, added an onShow callback to match onHide callback. Added test coverage. Updated README with new documentation. Removed unnecessary inclusions in package.json --- README.md | 30 +++++++++++++++++++++++------- bower.json | 2 +- package.json | 6 +----- test/toasterContainerSpec.js | 30 ++++++++++++++++++++++++++++++ toaster.js | 7 ++++++- toaster.min.js | 4 ++-- 6 files changed, 63 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 02e03df..035254c 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ AngularJS-Toaster **AngularJS Toaster** is an AngularJS port of the **toastr** non-blocking notification jQuery library. It requires AngularJS v1.2.6 or higher and angular-animate for the CSS3 transformations. [![Build Status](https://travis-ci.org/jirikavi/AngularJS-Toaster.svg)](https://travis-ci.org/jirikavi/AngularJS-Toaster) -[![Coverage Status](https://coveralls.io/repos/jirikavi/AngularJS-Toaster/badge.svg?branch=master&service=github&bust=2)](https://coveralls.io/github/jirikavi/AngularJS-Toaster?branch=master) +[![Coverage Status](https://coveralls.io/repos/jirikavi/AngularJS-Toaster/badge.svg?branch=master&service=github&busted=1)](https://coveralls.io/github/jirikavi/AngularJS-Toaster?branch=master) -### Current Version 1.0.0 +### Current Version 1.0.1 ## Demo - Simple demo is at http://plnkr.co/edit/HKTC1a @@ -28,10 +28,10 @@ npm install --save angularjs-toaster * Link scripts: ```html - + - + ``` * Add toaster container directive: @@ -191,17 +191,33 @@ All four options can be configured either globally for all toasts or individuall }); ``` +### On Show Callback +An onShow callback function can be attached to each toast instance. The callback will be invoked upon toast add. + +```js +toaster.pop({ + title: 'A toast', + body: 'with an onShow callback', + onShowCallback: function () { + toaster.pop({ + title: 'A toast', + body: 'invoked as an onShow callback' + }); + } +}); +``` + ### On Hide Callback -A callback function can be attached to each toast instance. The callback will be invoked upon toast removal. This can be used to chain toast calls. +An onHide callback function can be attached to each toast instance. The callback will be invoked upon toast removal. This can be used to chain toast calls. ```js toaster.pop({ title: 'A toast', - body: 'with a callback', + body: 'with an onHide callback', onHideCallback: function () { toaster.pop({ title: 'A toast', - body: 'invoked as a callback' + body: 'invoked as an onHide callback' }); } }); diff --git a/bower.json b/bower.json index 63c9d45..196d5d6 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "AngularJS-Toaster", - "version": "1.0.0", + "version": "1.0.1", "main": [ "toaster.js", "toaster.css" diff --git a/package.json b/package.json index 0f5ecb3..7cbba6c 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,7 @@ { "name": "angularjs-toaster", - "version": "1.0.0", + "version": "1.0.1", "description": "AngularJS Toaster is a customized version of toastr non-blocking notification javascript library", - "scripts": { - "test": "gulp test" - }, "author": "Jiri Kavulak", "license": "MIT", "repository": { @@ -16,7 +13,6 @@ "angular": ">1.2.6", "angular-animate": "~1.2.8", "angular-mocks": "^1.4.7", - "gulp": "^3.9.0", "jasmine-core": "^2.3.4", "karma": "^0.13.14", "karma-chrome-launcher": "^0.2.1", diff --git a/test/toasterContainerSpec.js b/test/toasterContainerSpec.js index 1ae2814..40353a5 100644 --- a/test/toasterContainerSpec.js +++ b/test/toasterContainerSpec.js @@ -382,6 +382,36 @@ describe('toasterContainer', function () { expect(scope.toasters[0].body).toBe('second'); expect(scope.toasters[1].body).toBe('third'); }); + + it('should invoke onShowCallback if it exists when toast is added', function () { + compileContainer(); + var mock = { + callback : function () { } + }; + + spyOn(mock, 'callback'); + + toaster.pop({ type: 'info', body: 'toast 1', onShowCallback: mock.callback }); + + rootScope.$digest(); + + expect(mock.callback).toHaveBeenCalled(); + }); + + it('should not invoke onShowCallback if it does not exist when toast is added', function () { + compileContainer(); + var mock = { + callback : function () { } + }; + + spyOn(mock, 'callback'); + + toaster.pop({ type: 'info', body: 'toast 1' }); + + rootScope.$digest(); + + expect(mock.callback).not.toHaveBeenCalled(); + }); }); diff --git a/toaster.js b/toaster.js index 006b33d..4b20570 100644 --- a/toaster.js +++ b/toaster.js @@ -4,7 +4,7 @@ /* * AngularJS Toaster - * Version: 1.0.0 + * Version: 1.0.1 * * Copyright 2013-2016 Jiri Kavulak. * All Rights Reserved. @@ -72,6 +72,7 @@ showCloseButton: params.showCloseButton, closeHtml: params.closeHtml, uid: params.toastId, + onShowCallback: params.onShowCallback, onHideCallback: params.onHideCallback, directiveData: params.directiveData }; @@ -353,6 +354,10 @@ scope.toasters.shift(); } } + + if (angular.isFunction(toast.onShowCallback)) { + toast.onShowCallback(); + } } scope.removeToast = function (id) { diff --git a/toaster.min.js b/toaster.min.js index 79b7ebf..b805d79 100644 --- a/toaster.min.js +++ b/toaster.min.js @@ -1,6 +1,6 @@ /* * AngularJS Toaster - * Version: 1.0.0 + * Version: 1.0.1 * * Copyright 2013-2016 Jiri Kavulak. * All Rights Reserved. @@ -10,4 +10,4 @@ * Author: Jiri Kavulak * Related to project of John Papa, Hans Fjällemark and Nguyễn Thiện Hùng (thienhung1989) */ -!function(t,e){"use strict";angular.module("toaster",[]).constant("toasterConfig",{limit:0,"tap-to-dismiss":!0,"close-button":!1,"close-html":'',"newest-on-top":!0,"time-out":5e3,"icon-classes":{error:"toast-error",info:"toast-info",wait:"toast-wait",success:"toast-success",warning:"toast-warning"},"body-output-type":"","body-template":"toasterBodyTmpl.html","icon-class":"toast-info","position-class":"toast-top-right","title-class":"toast-title","message-class":"toast-message","prevent-duplicates":!1,"mouseover-timer-stop":!0}).service("toaster",["$rootScope","toasterConfig",function(t,e){function o(t){return function(e,o,s,i,n,a,r,c,l){angular.isString(e)?this.pop(t,e,o,s,i,n,a,r,c,l):this.pop(angular.extend(e,{type:t}))}}this.pop=function(e,o,s,i,n,a,r,c,l,u){if(angular.isObject(e)){var d=e;this.toast={type:d.type,title:d.title,body:d.body,timeout:d.timeout,bodyOutputType:d.bodyOutputType,clickHandler:d.clickHandler,showCloseButton:d.showCloseButton,closeHtml:d.closeHtml,uid:d.toastId,onHideCallback:d.onHideCallback,directiveData:d.directiveData},l=d.toastId,r=d.toasterId}else this.toast={type:e,title:o,body:s,timeout:i,bodyOutputType:n,clickHandler:a,showCloseButton:c,uid:l,onHideCallback:u};t.$emit("toaster-newToast",r,l)},this.clear=function(e,o){t.$emit("toaster-clearToasts",e,o)};for(var s in e["icon-classes"])this[s]=o(s)}]).factory("toasterEventRegistry",["$rootScope",function(t){var e,o=null,s=null,i=[],n=[];return e={setup:function(){o||(o=t.$on("toaster-newToast",function(t,e,o){for(var s=0,n=i.length;n>s;s++)i[s](t,e,o)})),s||(s=t.$on("toaster-clearToasts",function(t,e,o){for(var s=0,i=n.length;i>s;s++)n[s](t,e,o)}))},subscribeToNewToastEvent:function(t){i.push(t)},subscribeToClearToastsEvent:function(t){n.push(t)},unsubscribeToNewToastEvent:function(t){var e=i.indexOf(t);e>=0&&i.splice(e,1),0===i.length&&(o(),o=null)},unsubscribeToClearToastsEvent:function(t){var e=n.indexOf(t);e>=0&&n.splice(e,1),0===n.length&&(s(),s=null)}},{setup:e.setup,subscribeToNewToastEvent:e.subscribeToNewToastEvent,subscribeToClearToastsEvent:e.subscribeToClearToastsEvent,unsubscribeToNewToastEvent:e.unsubscribeToNewToastEvent,unsubscribeToClearToastsEvent:e.unsubscribeToClearToastsEvent}}]).directive("directiveTemplate",["$compile","$injector",function(t,e){return{restrict:"A",scope:{directiveName:"@directiveName",directiveData:"@directiveData"},replace:!0,link:function(o,s,i){o.$watch("directiveName",function(n){if(angular.isUndefined(n)||n.length<=0)throw new Error("A valid directive name must be provided via the toast body argument when using bodyOutputType: directive");var a=e.has(i.$normalize(n)+"Directive");if(!a)throw new Error(n+" could not be found.");o.directiveData&&(o.directiveData=angular.fromJson(o.directiveData));var r=t("
")(o);s.append(r)})}}}]).directive("toasterContainer",["$parse","$rootScope","$interval","$sce","toasterConfig","toaster","toasterEventRegistry",function(t,e,o,s,i,n,a){return{replace:!0,restrict:"EA",scope:!0,link:function(e,r,c){function l(t,s){t.timeoutPromise=o(function(){e.removeToast(t.id)},s,1)}function u(o,i){if(o.type=v["icon-classes"][o.type],o.type||(o.type=v["icon-class"]),v["prevent-duplicates"]===!0)if(p(i)){if(e.toasters.length>0&&e.toasters[e.toasters.length-1].body===o.body)return}else{var n,a;for(n=0,a=e.toasters.length;a>n;n++)e.toasters[n].uid===i&&(d(n),n--,a=e.toasters.length)}o.id=++f,p(i)||(o.uid=i);var r=v["close-button"];if("boolean"==typeof o.showCloseButton);else if("boolean"==typeof r)o.showCloseButton=r;else if("object"==typeof r){var c=r[o.type];"undefined"!=typeof c&&null!==c&&(o.showCloseButton=c)}else o.showCloseButton=!1;switch(o.showCloseButton&&(o.closeHtml=s.trustAsHtml(o.closeHtml||e.config.closeHtml)),o.bodyOutputType=o.bodyOutputType||v["body-output-type"],o.bodyOutputType){case"trustedHtml":o.html=s.trustAsHtml(o.body);break;case"template":o.bodyTemplate=o.body||v["body-template"];break;case"templateWithData":var l=t(o.body||v["body-template"]),u=l(e);o.bodyTemplate=u.template,o.data=u.data;break;case"directive":o.html=o.body}e.configureTimer(o),v["newest-on-top"]===!0?(e.toasters.unshift(o),v.limit>0&&e.toasters.length>v.limit&&e.toasters.pop()):(e.toasters.push(o),v.limit>0&&e.toasters.length>v.limit&&e.toasters.shift())}function d(t){var s=e.toasters[t];s.timeoutPromise&&o.cancel(s.timeoutPromise),e.toasters.splice(t,1),angular.isFunction(s.onHideCallback)&&s.onHideCallback()}function m(t){for(var o=e.toasters.length-1;o>=0;o--)p(t)?d(o):e.toasters[o].uid==t&&d(o)}function p(t){return angular.isUndefined(t)||null===t}var v,f=0;v=angular.extend({},i,e.$eval(c.toasterOptions)),e.config={toasterId:v["toaster-id"],position:v["position-class"],title:v["title-class"],message:v["message-class"],tap:v["tap-to-dismiss"],closeButton:v["close-button"],closeHtml:v["close-html"],animation:v["animation-class"],mouseoverTimer:v["mouseover-timer-stop"]},e.$on("$destroy",function(){a.unsubscribeToNewToastEvent(e._onNewToast),a.unsubscribeToClearToastsEvent(e._onClearToasts)}),e.configureTimer=function(t){var e=angular.isNumber(t.timeout)?t.timeout:v["time-out"];"object"==typeof e&&(e=e[t.type]),e>0&&l(t,e)},e.removeToast=function(t){var o,s;for(o=0,s=e.toasters.length;s>o;o++)if(e.toasters[o].id===t){d(o);break}},e.toasters=[],e._onNewToast=function(t,o,s){(p(e.config.toasterId)&&p(o)||!p(e.config.toasterId)&&!p(o)&&e.config.toasterId==o)&&u(n.toast,s)},e._onClearToasts=function(t,o,s){("*"==o||p(e.config.toasterId)&&p(o)||!p(e.config.toasterId)&&!p(o)&&e.config.toasterId==o)&&m(s)},a.setup(),a.subscribeToNewToastEvent(e._onNewToast),a.subscribeToClearToastsEvent(e._onClearToasts)},controller:["$scope","$element","$attrs",function(t,e,s){t.stopTimer=function(e){t.config.mouseoverTimer===!0&&e.timeoutPromise&&(o.cancel(e.timeoutPromise),e.timeoutPromise=null)},t.restartTimer=function(e){t.config.mouseoverTimer===!0?e.timeoutPromise||t.configureTimer(e):null===e.timeoutPromise&&t.removeToast(e.id)},t.click=function(e,o){if(t.config.tap===!0||e.showCloseButton===!0&&o===!0){var s=!0;e.clickHandler&&(angular.isFunction(e.clickHandler)?s=e.clickHandler(e,o):angular.isFunction(t.$parent.$eval(e.clickHandler))?s=t.$parent.$eval(e.clickHandler)(e,o):console.log("TOAST-NOTE: Your click handler is not inside a parent scope of toaster-container.")),s&&t.removeToast(e.id)}}}],template:'
{{toaster.title}}
{{toaster.body}}
'}}])}(window,document); \ No newline at end of file +!function(t,e){"use strict";angular.module("toaster",[]).constant("toasterConfig",{limit:0,"tap-to-dismiss":!0,"close-button":!1,"close-html":'',"newest-on-top":!0,"time-out":5e3,"icon-classes":{error:"toast-error",info:"toast-info",wait:"toast-wait",success:"toast-success",warning:"toast-warning"},"body-output-type":"","body-template":"toasterBodyTmpl.html","icon-class":"toast-info","position-class":"toast-top-right","title-class":"toast-title","message-class":"toast-message","prevent-duplicates":!1,"mouseover-timer-stop":!0}).service("toaster",["$rootScope","toasterConfig",function(t,e){function o(t){return function(e,o,s,i,a,n,r,c,l){angular.isString(e)?this.pop(t,e,o,s,i,a,n,r,c,l):this.pop(angular.extend(e,{type:t}))}}this.pop=function(e,o,s,i,a,n,r,c,l,u){if(angular.isObject(e)){var d=e;this.toast={type:d.type,title:d.title,body:d.body,timeout:d.timeout,bodyOutputType:d.bodyOutputType,clickHandler:d.clickHandler,showCloseButton:d.showCloseButton,closeHtml:d.closeHtml,uid:d.toastId,onShowCallback:d.onShowCallback,onHideCallback:d.onHideCallback,directiveData:d.directiveData},l=d.toastId,r=d.toasterId}else this.toast={type:e,title:o,body:s,timeout:i,bodyOutputType:a,clickHandler:n,showCloseButton:c,uid:l,onHideCallback:u};t.$emit("toaster-newToast",r,l)},this.clear=function(e,o){t.$emit("toaster-clearToasts",e,o)};for(var s in e["icon-classes"])this[s]=o(s)}]).factory("toasterEventRegistry",["$rootScope",function(t){var e,o=null,s=null,i=[],a=[];return e={setup:function(){o||(o=t.$on("toaster-newToast",function(t,e,o){for(var s=0,a=i.length;a>s;s++)i[s](t,e,o)})),s||(s=t.$on("toaster-clearToasts",function(t,e,o){for(var s=0,i=a.length;i>s;s++)a[s](t,e,o)}))},subscribeToNewToastEvent:function(t){i.push(t)},subscribeToClearToastsEvent:function(t){a.push(t)},unsubscribeToNewToastEvent:function(t){var e=i.indexOf(t);e>=0&&i.splice(e,1),0===i.length&&(o(),o=null)},unsubscribeToClearToastsEvent:function(t){var e=a.indexOf(t);e>=0&&a.splice(e,1),0===a.length&&(s(),s=null)}},{setup:e.setup,subscribeToNewToastEvent:e.subscribeToNewToastEvent,subscribeToClearToastsEvent:e.subscribeToClearToastsEvent,unsubscribeToNewToastEvent:e.unsubscribeToNewToastEvent,unsubscribeToClearToastsEvent:e.unsubscribeToClearToastsEvent}}]).directive("directiveTemplate",["$compile","$injector",function(t,e){return{restrict:"A",scope:{directiveName:"@directiveName",directiveData:"@directiveData"},replace:!0,link:function(o,s,i){o.$watch("directiveName",function(a){if(angular.isUndefined(a)||a.length<=0)throw new Error("A valid directive name must be provided via the toast body argument when using bodyOutputType: directive");var n=e.has(i.$normalize(a)+"Directive");if(!n)throw new Error(a+" could not be found.");o.directiveData&&(o.directiveData=angular.fromJson(o.directiveData));var r=t("
")(o);s.append(r)})}}}]).directive("toasterContainer",["$parse","$rootScope","$interval","$sce","toasterConfig","toaster","toasterEventRegistry",function(t,e,o,s,i,a,n){return{replace:!0,restrict:"EA",scope:!0,link:function(e,r,c){function l(t,s){t.timeoutPromise=o(function(){e.removeToast(t.id)},s,1)}function u(o,i){if(o.type=v["icon-classes"][o.type],o.type||(o.type=v["icon-class"]),v["prevent-duplicates"]===!0)if(p(i)){if(e.toasters.length>0&&e.toasters[e.toasters.length-1].body===o.body)return}else{var a,n;for(a=0,n=e.toasters.length;n>a;a++)e.toasters[a].uid===i&&(d(a),a--,n=e.toasters.length)}o.id=++f,p(i)||(o.uid=i);var r=v["close-button"];if("boolean"==typeof o.showCloseButton);else if("boolean"==typeof r)o.showCloseButton=r;else if("object"==typeof r){var c=r[o.type];"undefined"!=typeof c&&null!==c&&(o.showCloseButton=c)}else o.showCloseButton=!1;switch(o.showCloseButton&&(o.closeHtml=s.trustAsHtml(o.closeHtml||e.config.closeHtml)),o.bodyOutputType=o.bodyOutputType||v["body-output-type"],o.bodyOutputType){case"trustedHtml":o.html=s.trustAsHtml(o.body);break;case"template":o.bodyTemplate=o.body||v["body-template"];break;case"templateWithData":var l=t(o.body||v["body-template"]),u=l(e);o.bodyTemplate=u.template,o.data=u.data;break;case"directive":o.html=o.body}e.configureTimer(o),v["newest-on-top"]===!0?(e.toasters.unshift(o),v.limit>0&&e.toasters.length>v.limit&&e.toasters.pop()):(e.toasters.push(o),v.limit>0&&e.toasters.length>v.limit&&e.toasters.shift()),angular.isFunction(o.onShowCallback)&&o.onShowCallback()}function d(t){var s=e.toasters[t];s.timeoutPromise&&o.cancel(s.timeoutPromise),e.toasters.splice(t,1),angular.isFunction(s.onHideCallback)&&s.onHideCallback()}function m(t){for(var o=e.toasters.length-1;o>=0;o--)p(t)?d(o):e.toasters[o].uid==t&&d(o)}function p(t){return angular.isUndefined(t)||null===t}var v,f=0;v=angular.extend({},i,e.$eval(c.toasterOptions)),e.config={toasterId:v["toaster-id"],position:v["position-class"],title:v["title-class"],message:v["message-class"],tap:v["tap-to-dismiss"],closeButton:v["close-button"],closeHtml:v["close-html"],animation:v["animation-class"],mouseoverTimer:v["mouseover-timer-stop"]},e.$on("$destroy",function(){n.unsubscribeToNewToastEvent(e._onNewToast),n.unsubscribeToClearToastsEvent(e._onClearToasts)}),e.configureTimer=function(t){var e=angular.isNumber(t.timeout)?t.timeout:v["time-out"];"object"==typeof e&&(e=e[t.type]),e>0&&l(t,e)},e.removeToast=function(t){var o,s;for(o=0,s=e.toasters.length;s>o;o++)if(e.toasters[o].id===t){d(o);break}},e.toasters=[],e._onNewToast=function(t,o,s){(p(e.config.toasterId)&&p(o)||!p(e.config.toasterId)&&!p(o)&&e.config.toasterId==o)&&u(a.toast,s)},e._onClearToasts=function(t,o,s){("*"==o||p(e.config.toasterId)&&p(o)||!p(e.config.toasterId)&&!p(o)&&e.config.toasterId==o)&&m(s)},n.setup(),n.subscribeToNewToastEvent(e._onNewToast),n.subscribeToClearToastsEvent(e._onClearToasts)},controller:["$scope","$element","$attrs",function(t,e,s){t.stopTimer=function(e){t.config.mouseoverTimer===!0&&e.timeoutPromise&&(o.cancel(e.timeoutPromise),e.timeoutPromise=null)},t.restartTimer=function(e){t.config.mouseoverTimer===!0?e.timeoutPromise||t.configureTimer(e):null===e.timeoutPromise&&t.removeToast(e.id)},t.click=function(e,o){if(t.config.tap===!0||e.showCloseButton===!0&&o===!0){var s=!0;e.clickHandler&&(angular.isFunction(e.clickHandler)?s=e.clickHandler(e,o):angular.isFunction(t.$parent.$eval(e.clickHandler))?s=t.$parent.$eval(e.clickHandler)(e,o):console.log("TOAST-NOTE: Your click handler is not inside a parent scope of toaster-container.")),s&&t.removeToast(e.id)}}}],template:'
{{toaster.title}}
{{toaster.body}}
'}}])}(window,document); \ No newline at end of file From 274c799e36963b9d28944a45ea2f730316a84c0a Mon Sep 17 00:00:00 2001 From: Stabzs Date: Fri, 5 Feb 2016 18:45:21 -0700 Subject: [PATCH 2/2] Remove gulp install command from travis build Remove gulp install command from travis build --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c05573a..9310783 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ before_install: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start before_script: - - npm install gulp - npm install karma - npm install karma-jasmine - npm install karma-chrome-launcher