├── src
├── growl.js
├── images
│ ├── error-red.png
│ ├── info-blue.png
│ ├── info-white.png
│ ├── error-white.png
│ ├── success-green.png
│ ├── success-white.png
│ ├── warning-white.png
│ └── warning-yellow.png
├── growlDirective.js
├── growlMessageService.js
├── growlFactory.js
└── growl.css
├── doc
└── screenshot.jpg
├── .gitignore
├── test
├── growlMessageServiceTests.js
├── growlDirectiveTests.js
└── growlFactoryTests.js
├── .jshintrc
├── demo
├── app.js
└── index.html
├── bower.json
├── package.json
├── LICENSE
├── karma.conf.js
├── gruntfile.js
├── CHANGELOG.md
└── README.md
/src/growl.js:
--------------------------------------------------------------------------------
1 | angular.module('angular-growl', []);
--------------------------------------------------------------------------------
/doc/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/doc/screenshot.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | components
4 | bower_components
5 | .idea
6 | *.iml
7 | nbproject
8 |
--------------------------------------------------------------------------------
/src/images/error-red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/src/images/error-red.png
--------------------------------------------------------------------------------
/src/images/info-blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/src/images/info-blue.png
--------------------------------------------------------------------------------
/src/images/info-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/src/images/info-white.png
--------------------------------------------------------------------------------
/src/images/error-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/src/images/error-white.png
--------------------------------------------------------------------------------
/src/images/success-green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/src/images/success-green.png
--------------------------------------------------------------------------------
/src/images/success-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/src/images/success-white.png
--------------------------------------------------------------------------------
/src/images/warning-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/src/images/warning-white.png
--------------------------------------------------------------------------------
/src/images/warning-yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JanStevens/angular-growl-2/HEAD/src/images/warning-yellow.png
--------------------------------------------------------------------------------
/test/growlMessageServiceTests.js:
--------------------------------------------------------------------------------
1 | describe("growlMessageService Spec", function() {
2 | "use strict";
3 |
4 | var growlMessages;
5 |
6 | beforeEach(module('angular-growl'));
7 | beforeEach(inject(['growlMessages', function (gm) {
8 | growlMessages = gm;
9 | }]));
10 |
11 | /// TESTS
12 | it('Should be defined', function () {
13 | expect(growlMessages).toBeDefined();
14 | });
15 | });
--------------------------------------------------------------------------------
/test/growlDirectiveTests.js:
--------------------------------------------------------------------------------
1 | describe("growlDirective Spec", function() {
2 | "use strict";
3 |
4 | var $compile,
5 | $rootScope;
6 |
7 | beforeEach(module('angular-growl'));
8 |
9 | beforeEach(inject(function (_$compile_, _$rootScope_) {
10 | $compile = _$compile_;
11 | $rootScope = _$rootScope_;
12 | }));
13 |
14 | it('Replaces the element with the appropriate content', function () {
15 |
16 | var growlElement = $compile('
=1.2.1"
32 | },
33 | "devDependencies": {
34 | "angular-mocks": ">=1.2.1"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-growl-v2",
3 | "version": "0.7.9",
4 | "description": "growl like notifications for angularJS projects, using bootstrap alert classes",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/JanStevens/angular-growl-2"
8 | },
9 | "main": "./build/angular-growl.min.js",
10 | "author": {
11 | "name": "Jan Stevens"
12 | },
13 | "license": "MIT",
14 | "dependencies": {
15 | "angular": ">=1.2.1"
16 | },
17 | "devDependencies": {
18 | "grunt": "~0.4.1",
19 | "grunt-bump": "0.0.2",
20 | "grunt-contrib-clean": "~0.5.0",
21 | "grunt-contrib-concat": "^0.3.0",
22 | "grunt-contrib-copy": "^0.4.1",
23 | "grunt-contrib-cssmin": "^0.6.2",
24 | "grunt-contrib-jshint": "^0.6.5",
25 | "grunt-contrib-uglify": "^0.2.7",
26 | "grunt-contrib-watch": "^0.5.3",
27 | "grunt-karma": "^0.7.3",
28 | "grunt-ngmin": "0.0.2",
29 | "grunt-push-release": "^0.1.9",
30 | "karma": "^0.12.16",
31 | "karma-jasmine": "^0.1.5",
32 | "karma-phantomjs-launcher": "^0.1.4",
33 | "matchdep": "^0.1.2"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Marco Rinck
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Mon Sep 16 2013 12:55:41 GMT+0200 (W. Europe Daylight Time)
3 |
4 | module.exports = function (config) {
5 | config.set({
6 |
7 | // base path, that will be used to resolve files and exclude
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | frameworks: ['jasmine'],
13 |
14 |
15 | // list of files / patterns to load in the browser
16 | files: [
17 | 'bower_components/angular/angular.js',
18 | 'bower_components/angular-mocks/angular-mocks.js',
19 | 'src/**/*.js',
20 | 'test/**/*.js'
21 | ],
22 |
23 |
24 | // list of files to exclude
25 | exclude: [
26 |
27 | ],
28 |
29 |
30 | // test results reporter to use
31 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
32 | reporters: ['progress'],
33 |
34 |
35 | // web server port
36 | port: 9876,
37 |
38 |
39 | // enable / disable colors in the output (reporters and logs)
40 | colors: true,
41 |
42 |
43 | // level of logging
44 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
45 | logLevel: config.LOG_INFO,
46 |
47 |
48 | // enable / disable watching file and executing tests whenever any file changes
49 | autoWatch: false,
50 |
51 |
52 | // Start these browsers, currently available:
53 | // - Chrome
54 | // - ChromeCanary
55 | // - Firefox
56 | // - Opera
57 | // - Safari (only Mac)
58 | // - PhantomJS
59 | // - IE (only Windows)
60 | browsers: ['PhantomJS'],
61 |
62 |
63 | // If browser does not capture in given timeout [ms], kill it
64 | captureTimeout: 60000,
65 |
66 |
67 | // Continuous Integration mode
68 | // if true, it capture browsers, run tests and exit
69 | singleRun: true
70 | });
71 | };
72 |
--------------------------------------------------------------------------------
/test/growlFactoryTests.js:
--------------------------------------------------------------------------------
1 | describe("growlFactory Spec", function() {
2 | "use strict";
3 |
4 | var growlFactory,
5 | growlMessages;
6 |
7 | beforeEach(module('angular-growl'));
8 | beforeEach(inject(['growl', 'growlMessages', function (gf, gm) {
9 | growlFactory = gf;
10 | growlMessages = gm;
11 | }]));
12 |
13 | /// TESTS
14 | it('Should be defined', function () {
15 | expect(growlFactory).toBeDefined();
16 | expect(growlMessages).toBeDefined();
17 | });
18 |
19 | it('Should set proper values on default message types', function () {
20 | var builtinTypes = [
21 | 'info',
22 | 'error',
23 | 'warning',
24 | 'success'
25 | ];
26 |
27 | var severity;
28 | var sampleText = 'text';
29 | for (var i = 0; i < builtinTypes.length; i++) {
30 |
31 | severity = builtinTypes[i];
32 | expect(growlFactory[severity]).toBeDefined();
33 | var msg = growlFactory.general(sampleText, null, severity);
34 |
35 | expect(msg).toBeDefined();
36 | expect(msg.text.toString()).toEqual(sampleText);
37 | expect(msg.referenceId).toEqual(0);
38 | expect(msg.position).toEqual('top-right');
39 | expect(msg.severity).toEqual(severity);
40 | }
41 | });
42 |
43 | it('Should add and remove 1 message', function () {
44 | var msg = growlFactory.info('text');
45 |
46 | expect(growlMessages.getAllMessages().length).toEqual(1);
47 | msg.destroy();
48 | expect(growlMessages.getAllMessages().length).toEqual(0);
49 | });
50 |
51 | it('Should be able to destroy all messages', function () {
52 | var messageCount = 10;
53 | for (var i = 0; i < messageCount; i++) {
54 | growlFactory.info('Test ' + i);
55 | }
56 |
57 | expect(growlMessages.getAllMessages().length).toEqual(messageCount);
58 |
59 | growlMessages.destroyAllMessages();
60 | expect(growlMessages.getAllMessages().length).toEqual(0);
61 |
62 | });
63 | });
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Growl Sample
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | "use strict";
3 |
4 | grunt.initConfig({
5 |
6 | pkg: grunt.file.readJSON('bower.json'),
7 |
8 | language: grunt.option('lang') || 'en',
9 |
10 | meta: {
11 | banner: '/**\n * <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
12 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
13 | ' * <%= pkg.homepage %>\n' +
14 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;' +
15 | ' Licensed <%= pkg.license %>\n */\n'
16 | },
17 |
18 | build_dir: 'build',
19 |
20 | lib_files: {
21 |
22 | core: [
23 | 'src/growl.js',
24 | 'src/growlDirective.js',
25 | 'src/growlFactory.js',
26 | 'src/growlMessageService.js'
27 | ],
28 | css: [
29 | 'src/growl.css'
30 | ],
31 | test: ['test/**/*.js']
32 | },
33 |
34 | watch: {
35 |
36 | scripts: {
37 | files: ['gruntfile.js', '<%= lib_files.core %>', '<%= lib_files.test %>'],
38 | tasks: ['jshint:all', 'karma:unit']
39 | },
40 |
41 | livereload: {
42 | options: {
43 | livereload: true
44 | },
45 | files: ['src/**/*.*'],
46 | tasks: ['jshint', 'karma:unit']
47 | }
48 | },
49 |
50 | jshint: {
51 | options: {
52 | jshintrc: '.jshintrc'
53 | },
54 |
55 | all: ['gruntfile.js', '<%= lib_files.core %>', '<%= lib_files.test %>'],
56 |
57 | core: {
58 | files: {
59 | src: ['<%= lib_files.core %>']
60 | }
61 | },
62 |
63 | test: {
64 | files: {
65 | src: ['<%= lib_files.test %>']
66 | }
67 | }
68 | },
69 |
70 | concat: {
71 | banner: {
72 | options: {
73 | banner: '<%= meta.banner %>'
74 | },
75 | src: '<%= concat.core.dest %>',
76 | dest: '<%= concat.core.dest %>',
77 | },
78 |
79 | core: {
80 | src: ['<%= lib_files.core %>'],
81 | dest: '<%= build_dir %>/angular-growl.js'
82 | },
83 |
84 | css: {
85 | options: {
86 | banner: '<%= meta.banner %>'
87 | },
88 | src: ['<%= lib_files.css %>'],
89 | dest: '<%= build_dir %>/angular-growl.css'
90 | }
91 | },
92 |
93 | cssmin: {
94 | core: {
95 | files: {
96 | 'build/angular-growl.min.css': '<%= lib_files.css %>'
97 | },
98 | options: {
99 | 'banner': '<%= meta.banner %>',
100 | 'report': 'gzip'
101 | }
102 | }
103 | },
104 |
105 | uglify: {
106 | core: {
107 | files: {
108 | '<%= build_dir %>/angular-growl.min.js': '<%= concat.core.dest %>'
109 | },
110 | options: {
111 | banner: '<%= meta.banner %>',
112 | report: 'gzip'
113 | }
114 | }
115 | },
116 |
117 | karma: {
118 | unit: {
119 | configFile: 'karma.conf.js',
120 | singleRun: true
121 | }
122 | },
123 |
124 | ngmin: {
125 | core: {
126 | src: '<%= concat.core.dest %>',
127 | dest: '<%= concat.core.dest %>'
128 | }
129 | },
130 | push: {
131 | options: {
132 | files: ['package.json', 'bower.json'],
133 | add: true,
134 | addFiles: ['.'], // '.' for all files except ingored files in .gitignore
135 | commit: true,
136 | commitMessage: 'Release v%VERSION%',
137 | commitFiles: ['package.json', 'bower.json', 'build/angular-growl.js', 'build/angular-growl.min.js', 'build/angular-growl.min.css', 'README.md'], // '-a' for all files
138 | createTag: true,
139 | tagName: 'v%VERSION%',
140 | tagMessage: 'Version %VERSION%',
141 | push: true,
142 | pushTo: 'origin',
143 | npm: true,
144 | npmTag: 'Release v%VERSION%',
145 | gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d' // options to use with '$ git describe'
146 | }
147 | }
148 | });
149 |
150 |
151 | grunt.registerTask('default', ['jshint:all', 'karma']);
152 | grunt.registerTask('test', ['karma']);
153 |
154 | grunt.registerTask('build', [
155 | 'jshint:all',
156 | 'karma',
157 | 'build:core'
158 | ]);
159 |
160 | grunt.registerTask('build:core', [
161 | 'concat:core',
162 | 'concat:css',
163 | 'ngmin:core',
164 | 'concat:banner',
165 | 'uglify:core',
166 | 'cssmin:core'
167 | ]);
168 |
169 | // For development purpose.
170 | grunt.registerTask('dev', ['jshint', 'karma:unit', 'watch:livereload']);
171 |
172 | require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
173 | };
174 |
--------------------------------------------------------------------------------
/src/growlDirective.js:
--------------------------------------------------------------------------------
1 | angular.module("angular-growl").directive("growl", [
2 | function () {
3 | "use strict";
4 |
5 | return {
6 | restrict: 'A',
7 | templateUrl: 'templates/growl/growl.html',
8 | replace: false,
9 | scope: {
10 | reference: '@',
11 | inline: '=',
12 | limitMessages: '='
13 | },
14 | controller: ['$scope', '$interval', 'growl', 'growlMessages',
15 | function ($scope, $interval, growl, growlMessages) {
16 | $scope.referenceId = $scope.reference || 0;
17 |
18 | growlMessages.initDirective($scope.referenceId, $scope.limitMessages);
19 | $scope.growlMessages = growlMessages;
20 | $scope.inlineMessage = angular.isDefined($scope.inline) ? $scope.inline : growl.inlineMessages();
21 |
22 | $scope.$watch('limitMessages', function (limitMessages) {
23 | var directive = growlMessages.directives[$scope.referenceId];
24 | if (!angular.isUndefined(limitMessages) && !angular.isUndefined(directive)) {
25 | directive.limitMessages = limitMessages;
26 | }
27 | });
28 |
29 | //Cancels all promises within message upon deleting message or stop deleting.
30 | $scope.stopTimeoutClose = function (message) {
31 | if (!message.clickToClose) {
32 | angular.forEach(message.promises, function (promise) {
33 | $interval.cancel(promise);
34 | });
35 | if(message.ttl == undefined || message.ttl == -1 || message.close) {
36 | if (typeof (message.onclick) === 'function') {
37 | message.onclick();
38 | }
39 | growlMessages.deleteMessage(message);
40 | } else {
41 | message.close = true;
42 | }
43 | }
44 | };
45 |
46 | $scope.alertClasses = function (message) {
47 | return {
48 | 'alert-success': message.severity === "success",
49 | 'alert-error': message.severity === "error", //bootstrap 2.3
50 | 'alert-danger': message.severity === "error", //bootstrap 3
51 | 'alert-info': message.severity === "info",
52 | 'alert-warning': message.severity === "warning", //bootstrap 3, no effect in bs 2.3
53 | 'icon': message.disableIcons === false,
54 | 'alert-dismissable': !message.disableCloseButton
55 | };
56 | };
57 |
58 | $scope.showCountDown = function (message) {
59 | return !message.disableCountDown && message.ttl > 0;
60 | };
61 |
62 | $scope.wrapperClasses = function () {
63 | var classes = {};
64 | classes['growl-fixed'] = !$scope.inlineMessage;
65 | classes[growl.position()] = true;
66 | return classes;
67 | };
68 |
69 | $scope.computeTitle = function (message) {
70 | var ret = {
71 | 'success': 'Success',
72 | 'error': 'Error',
73 | 'info': 'Information',
74 | 'warn': 'Warning'
75 | };
76 | return ret[message.severity];
77 | };
78 | }
79 | ]
80 | };
81 | }
82 | ]);
83 |
84 | angular.module("angular-growl").run(['$templateCache', function ($templateCache) {
85 | "use strict";
86 | if ($templateCache.get('templates/growl/growl.html') === undefined) {
87 | $templateCache.put("templates/growl/growl.html",
88 | '
' +
89 | '
' +
90 | '
' +
91 | '
' +
92 | '
' +
93 | '
' +
94 | '
' +
95 | '
'
96 | );
97 | }
98 | }]);
99 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ##Changelog
2 | **0.8.0** - TBE TODO
3 | * Code clean up
4 | * Testing
5 | * Latest angular support
6 | * [Finding a solution for this issue](https://github.com/JanStevens/angular-growl-2/issues/54)
7 |
8 | **0.7.5** - 17 Jun 2015
9 | * Fixed #76 Middle vertical alignment (@fmenezes) [pull #88](https://github.com/JanStevens/angular-growl-2/pull/88)
10 | * Added base test suite for karma
11 | * Fixed potential issue described in [issue #93](https://github.com/JanStevens/angular-growl-2/issues/93)
12 |
13 | **0.7.4** - 26 May 2015
14 | * Feature request: Return the configuration object on config methods (@disoney) [pull #73](https://github.com/JanStevens/angular-growl-2/pull/73)
15 | * On responseError data is null, and an error is raised. (@AlexTM84) [pull #87](https://github.com/JanStevens/angular-growl-2/pull/87)
16 | * Fix empty title and text (@asabirov) [pull #86](https://github.com/JanStevens/angular-growl-2/pull/86)
17 |
18 | **0.7.3** - 05 Jan 2015
19 | * Fixes issue 62 where the service was called before the directive was initialized
20 |
21 | **0.7.2** - 20 Nov 2014
22 | * Possibility to toggle the translation of messages (@schoes) [pull #55](https://github.com/JanStevens/angular-growl-2/pull/55)
23 | * Check if the response is undefined (Offline Connections) (@brunoporto) [pull #50](https://github.com/JanStevens/angular-growl-2/pull/50)
24 | * Prevent NPEs when working with server-side messages (@madhead) [pull #45](https://github.com/JanStevens/angular-growl-2/pull/45)
25 | * Added a general method for setting the Growl type based on a server response (@madhead) [pull #41](https://github.com/JanStevens/angular-growl-2/pull/41)
26 | * Split Growl directive in a growl factory added a way to programatically close messages and a setText to update the message text (@chasemgray) [pull #38](https://github.com/JanStevens/angular-growl-2/pull/38)
27 |
28 | **0.7.0** - 10 Aug 2014
29 | * Added new documentation website with examples instead of this readme.
30 | * Growl Containers are now responsive for mobile devices (@tlvince) [pull #17](https://github.com/JanStevens/angular-growl-2/pull/17)
31 | * Add option to reverse order of messages (@MilosMosovsky) [pull #18](https://github.com/JanStevens/angular-growl-2/pull/18)
32 | * Add option to set the message limit of a growl container (@MilosMosovsky) [pull #21](https://github.com/JanStevens/angular-growl-2/pull/21)
33 | * Add new feature to stop the TTL when clicked and remove the message manually when clicked again (@willjk) [pull #27](https://github.com/JanStevens/angular-growl-2/pull/27)
34 | * Fix for issue #22 (@soumya92) [pull #23](https://github.com/JanStevens/angular-growl-2/pull/23)
35 | * Fix for angular 1.3 http interceptor API changes (@vik-singh) [pull #20](https://github.com/JanStevens/angular-growl-2/pull/20) & [pull #29](https://github.com/JanStevens/angular-growl-2/pull/29)
36 | * Fix only add template to cache if it doesn't exist already (@Anaphase) [pull #31](https://github.com/JanStevens/angular-growl-2/pull/31)
37 |
38 | **0.6.1** - 25 May 2014
39 | * Fixes edge case where message test is not a string
40 | * Fixes style issue where close button was floating outside the alert
41 | * Fixes issue [#12](https://github.com/JanStevens/angular-growl-2/issues/12), [#15](https://github.com/JanStevens/angular-growl-2/issues/15), [#16](https://github.com/JanStevens/angular-growl-2/issues/16)
42 |
43 | **0.6.0** - 16 Apr 2014
44 | * [CHANGE] remove enableHtml, `$sce.trustAsHtml` is always run on the message text
45 | * Possible to set global possition for non-inline growl messages (thanks @pauloprea)
46 | * Template can now easily be replace or styled with CSS
47 | * Include icons for the different notifications, can be disabled globally or per notification
48 | * Server side messages can now interpolate variables into the message ([original pull request](https://github.com/marcorinck/angular-growl/pull/19))
49 |
50 |
51 | **0.5.3** - 19 Mar 2014
52 | * Fixed bug where globalInlineMessage option would not work globally
53 |
54 | **0.5.2** - 19 Mar 2014
55 | * Added an option to show notifications inline instead of growl like behaviour (very handy for forms)
56 | * Added a referenceId field so different inline growl directives can be targeted
57 | * Converted tabs to spaces
58 | * Updated the demo site to show the new changes
59 |
60 | **0.5.0** - 18 Mar 2014
61 | * Manually merged some pull requests from the original branch
62 | * Fixed bower.json file to include itself and the css file
63 | * [BREAK] changed the function names to add growl notifications to be a shorter (success, info, warning, error VS addSuccessMessage, addInfoMessage...)
64 |
65 | **0.4.0** - 19th Nov 2013
66 |
67 | * updated dependency to angularJS 1.2.x, angular-growl does not work with 1.0.x anymore (BREAKING CHANGE)
68 | * new option: only display unique messages, which is the new default, disable to allow same message more than once (BREAKING CHANGE)
69 | * new option: allow html tags in messages, default is off you need to
70 |
71 | **0.3.1** - 1st Oct 2013
72 |
73 | * bugfix: translating of messages works again
74 | * change: also set alert css classes introduced by bootstrap 3
75 |
76 | **0.3.0** - 26th Sept 2013
77 |
78 | * adding css animations support via ngAnimate (for angularJS >= 1.2)
79 | * ability to configure server message keys
80 |
81 | **0.2.0** - 22nd Sept 2013
82 |
83 | * reworking, bugfixing and documenting handling of server sent messages/notifications
84 | * externalizing css styles of growl class
85 | * provide minified versions of js and css files in build folder
86 |
87 | **0.1.3** - 20th Sept 2013
88 |
89 | * introducing ttl config option, fixes #2
--------------------------------------------------------------------------------
/src/growlMessageService.js:
--------------------------------------------------------------------------------
1 | angular.module("angular-growl").service("growlMessages", ['$sce', '$interval', function ($sce, $interval) {
2 | "use strict";
3 |
4 | var self = this;
5 | this.directives = {};
6 | var preloadDirectives = {};
7 |
8 | /**
9 | * Allows for preloading a directive before the directives
10 | * controller is initialized
11 | * @param referenceId
12 | * @returns {*}
13 | */
14 | function preLoad(referenceId) {
15 | var directive;
16 | if (preloadDirectives[referenceId]) {
17 | directive = preloadDirectives[referenceId];
18 | } else {
19 | directive = preloadDirectives[referenceId] = {
20 | messages: []
21 | };
22 | }
23 | return directive;
24 | }
25 |
26 | function directiveForRefId(referenceId) {
27 | var refId = referenceId || 0;
28 | return (self.directives[refId] || preloadDirectives[refId]);
29 | }
30 |
31 | /**
32 | * Initialize a directive
33 | * We look at the preloaded directive and use this else we
34 | * create a new blank object
35 | * @param referenceId
36 | * @param limitMessages
37 | */
38 | this.initDirective = function (referenceId, limitMessages) {
39 | // If we already have a directive preloaded use this version
40 | // so our growl notifications are shown.
41 | if (preloadDirectives[referenceId]) {
42 | this.directives[referenceId] = preloadDirectives[referenceId];
43 | this.directives[referenceId].limitMessages = limitMessages;
44 | } else {
45 | this.directives[referenceId] = {
46 | messages: [],
47 | limitMessages: limitMessages
48 | };
49 | }
50 | return this.directives[referenceId];
51 | };
52 |
53 | this.getAllMessages = function (referenceId) {
54 | referenceId = referenceId || 0;
55 | var messages;
56 | if (directiveForRefId(referenceId)) {
57 | messages = directiveForRefId(referenceId).messages;
58 | } else {
59 | messages = [];
60 | }
61 | return messages;
62 | };
63 |
64 | this.destroyAllMessages = function (referenceId) {
65 | var messages = this.getAllMessages(referenceId);
66 | for (var i = messages.length - 1; i >= 0; i--) {
67 | messages[i].destroy();
68 | }
69 |
70 | var directive = directiveForRefId(referenceId);
71 | if (directive) {
72 | directive.messages = [];
73 | }
74 | };
75 |
76 | this.addMessage = function (message) {
77 | var directive, messages, found, msgText;
78 |
79 | // If we dont found our directive preload it!
80 | if (this.directives[message.referenceId]) {
81 | directive = this.directives[message.referenceId];
82 | } else {
83 | directive = preLoad(message.referenceId);
84 | }
85 |
86 | messages = directive.messages;
87 |
88 | if (this.onlyUnique) {
89 | angular.forEach(messages, function (msg) {
90 | msgText = $sce.getTrustedHtml(msg.text);
91 | if (message.text === msgText && message.severity === msg.severity && message.title === msg.title) {
92 | found = true;
93 | }
94 | });
95 |
96 | if (found) {
97 | return;
98 | }
99 | }
100 |
101 | message.text = $sce.trustAsHtml(String(message.text));
102 |
103 | /**If message closes on timeout, add's promises array for
104 | timeouts to stop close. Also sets message.closeoutTimer to ttl / 1000
105 | **/
106 | if (message.ttl && message.ttl !== -1) {
107 | message.countdown = message.ttl / 1000;
108 | message.promises = [];
109 | message.close = false;
110 | message.countdownFunction = function () {
111 | if (message.countdown > 1) {
112 | message.countdown--;
113 | message.promises.push($interval(message.countdownFunction, 1000, 1,1));
114 | } else {
115 | message.countdown--;
116 | }
117 | };
118 | }
119 |
120 | /** Limit the amount of messages in the container **/
121 | if (angular.isDefined(directive.limitMessages)) {
122 | var diff = messages.length - (directive.limitMessages - 1);
123 | if (diff > 0) {
124 | messages.splice(directive.limitMessages - 1, diff);
125 | }
126 | }
127 |
128 | /** abillity to reverse order (newest first ) **/
129 | if (this.reverseOrder) {
130 | messages.unshift(message);
131 | } else {
132 | messages.push(message);
133 | }
134 |
135 | if (typeof (message.onopen) === 'function') {
136 | message.onopen();
137 | }
138 |
139 | if (message.ttl && message.ttl !== -1) {
140 | //adds message timeout to promises and starts messages countdown function.
141 | var self = this;
142 | message.promises.push($interval(angular.bind(this, function () {
143 | self.deleteMessage(message);
144 | }), message.ttl, 1,1));
145 | message.promises.push($interval(message.countdownFunction, 1000, 1,1));
146 | }
147 |
148 | return message;
149 | };
150 |
151 | this.deleteMessage = function (message) {
152 | var messages = this.getAllMessages(message.referenceId),
153 | index = -1;
154 |
155 | for (var i in messages) {
156 | if (messages.hasOwnProperty(i)) {
157 | index = messages[i] === message ? i : index;
158 | }
159 | }
160 |
161 | if (index > -1) {
162 | messages[index].close = true;
163 | messages.splice(index, 1);
164 | }
165 |
166 | if (typeof (message.onclose) === 'function') {
167 | message.onclose();
168 | }
169 | };
170 | }]);
171 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #angular-growl-2
2 | Growl like notifications for angularJS projects, using bootstrap alert classes, originally developed by Marco Rinck and Jan Stevens
3 |
4 | ##Features
5 |
6 | 
7 |
8 | * growl like notifications like in MacOS X
9 | * using standard bootstrap classes (alert, alert-info, alert-error, alert-success)
10 | * global or per message configuration of a timeout when message will be automatically closed
11 | * automatic translation of messages if [angular-translate](https://github.com/PascalPrecht/angular-translate) filter is
12 | present, you only have to provide keys as messages, angular-translate will translate them
13 | * pre-defined $http-Interceptor to automatically handle $http responses for server-sent messages
14 | * automatic CSS animations when adding/closing notifications (only when using >= angularJS 1.2)
15 | * < 1 kB after GZIP
16 |
17 | ##Changelog
18 |
19 | **0.4.0** - 19th Nov 2013
20 |
21 | * updated dependency to angularJS 1.2.x, angular-growl does not work with 1.0.x anymore (BREAKING CHANGE)
22 | * new option: only display unique messages, which is the new default, disable to allow same message more than once (BREAKING CHANGE)
23 | * new option: allow html tags in messages, default is off, allows to use HTML tags in messages
24 |
25 | **0.3.1** - 1st Oct 2013
26 |
27 | * bugfix: translating of messages works again
28 | * change: also set alert css classes introduced by bootstrap 3
29 |
30 | **0.3.0** - 26th Sept 2013
31 |
32 | * adding css animations support via ngAnimate (for angularJS >= 1.2)
33 | * ability to configure server message keys
34 |
35 | ##Installation
36 |
37 | You can install angular-growl-v2 with bower:
38 |
39 | > bower install angular-growl-v2
40 |
41 | Alternatively you can download the files in the [build folder](build/) manually and include them in your project.
42 |
43 | ````html
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | ````
54 |
55 | As angular-growl is based on its own angularJS module, you have to alter your dependency list when creating your application
56 | module:
57 |
58 | ````javascript
59 | var app = angular.module('myApp', ['angular-growl']);
60 | ````
61 |
62 | Finally, you have to include the directive somewhere in your HTML like this:
63 |
64 | ````html
65 |
66 |
67 |
68 | ````
69 |
70 | ##Usage
71 |
72 | Just let angular inject the growl Factory into your code and call the 4 functions that the factory provides accordingly:
73 |
74 | ````javascript
75 | app.controller("demoCtrl", ['$scope', 'growl', function($scope, growl) {
76 | $scope.addSpecialWarnMessage = function() {
77 | growl.warning("This adds a warn message");
78 | growl.info("This adds a info message");
79 | growl.success("This adds a success message");
80 | growl.error("This adds a error message");
81 | }
82 | }]);
83 | ````
84 |
85 | The title must be set as a configuration parameter:
86 |
87 | ````javascript
88 | app.controller("demoCtrl", ['$scope', 'growl', function($scope, growl) {
89 | $scope.addSpecialWarnMessage = function() {
90 | growl.warning("This adds a warn message", {title: 'Warning!'});
91 | growl.info("This adds a info message", {title: 'Random Information'});
92 | growl.success("This adds a success message"); //no title here
93 | growl.error("This adds a error message", {title: 'ALERT WE GOT ERROR'});
94 | }
95 | }]);
96 | ````
97 |
98 | If [angular-translate](https://github.com/PascalPrecht/angular-translate) is present, its filter is automatically called for translating of messages, so you have to provide
99 | only the key:
100 |
101 | ````javascript
102 | app.controller("demoCtrl", ['$scope', 'growl', function($scope, growl) {
103 | $scope.addSpecialWarnMessage = function() {
104 | growl.success("SAVE_SUCCESS_MESSAGE");
105 | growl.error("VALIDATION_ERROR");
106 | }
107 | }]);
108 | ````
109 |
110 | ## Configuration/Documentation/Info
111 | For the configuration options, documentation and live examples visit the github pages:
112 |
113 | ## [http://janstevens.github.io/angular-growl-2/](http://janstevens.github.io/angular-growl-2/)
114 |
115 | Live demo's can be found on the following codepen collection:
116 |
117 | ## [Codepen Collection](http://codepen.io/collection/Jhcpi/)
118 |
119 | This stops messages from closing when mouse is over:
120 |
121 | ````javascript
122 | app.config(['growlProvider', function (growlProvider) {
123 | growlProvider.globalPauseOnMouseOver(true);
124 | }]);
125 | ````
126 |
127 | ## Contributions
128 | * Fork the project
129 | * Change/Fix/Add the stuff you want
130 | * Clone the codepens that have effect on your changes or if you add new features create a codepen that show them
131 | * Create a PR
132 | * Don't forget to add your name to the Thanks section!
133 |
134 | # Thanks
135 | Thanks Marco Rinck for the original code, the following people have contributed to this project:
136 |
137 | * [orangeskins](https://github.com/orangeskins)
138 | * [adamalbrecht](https://github.com/adamalbrecht)
139 | * [m0ppers](https://github.com/m0ppers)
140 | * [lbehnke](https://github.com/lbehnke)
141 | * [rorymadden](https://github.com/rorymadden)
142 | * [pauloprea](https://github.com/pauloprea)
143 | * [tlvince](https://github.com/tlvince)
144 | * [vik-singh](https://github.com/vik-singh)
145 | * [Anaphase](https://github.com/Anaphase)
146 | * [soumya92](https://github.com/soumya92)
147 | * [willjk](https://github.com/willjk)
148 | * [wardvijf](https://github.com/wardvijf)
149 | * [naveenpeterj](https://github.com/naveenpeterj)
150 |
151 | # License
152 | Copyright (C) 2015 Marco Rinck
153 |
154 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
155 |
156 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
157 |
158 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
159 |
--------------------------------------------------------------------------------
/src/growlFactory.js:
--------------------------------------------------------------------------------
1 | angular.module("angular-growl").provider("growl", function () {
2 | "use strict";
3 |
4 | var _ttl = {success: null, error: null, warning: null, info: null},
5 | _messagesKey = 'messages',
6 | _messageTextKey = 'text',
7 | _messageTitleKey = 'title',
8 | _messageSeverityKey = 'severity',
9 | _messageTTLKey = 'ttl',
10 | _onlyUniqueMessages = true,
11 | _messageVariableKey = 'variables',
12 | _messageReferenceIdKey = 'referenceId',
13 | _referenceId = 0,
14 | _inline = false,
15 | _position = 'top-right',
16 | _disableCloseButton = false,
17 | _disableIcons = false,
18 | _pauseOnMouseOver = false,
19 | _reverseOrder = false,
20 | _disableCountDown = false,
21 | _translateMessages = true;
22 |
23 | /**
24 | * set a global timeout (time to live) after which messages will be automatically closed
25 | *
26 | * @param ttl in seconds
27 | */
28 | this.globalTimeToLive = function (ttl) {
29 | if (typeof ttl === 'object') {
30 | for (var k in ttl) {
31 | if (ttl.hasOwnProperty(k)) {
32 | _ttl[k] = ttl[k];
33 | }
34 | }
35 | } else {
36 | for (var severity in _ttl) {
37 | if (_ttl.hasOwnProperty(severity)) {
38 | _ttl[severity] = ttl;
39 | }
40 | }
41 | }
42 | return this;
43 | };
44 |
45 | /**
46 | * set whether the messages should be translated (default:true) when the translator is available
47 | *
48 | * @param {bool} translateMessages true to to translate all messages
49 | */
50 | this.globalTranslateMessages = function (translateMessages) {
51 | _translateMessages = translateMessages;
52 | return this;
53 | };
54 |
55 | /**
56 | * set whether the close button should be displayed (default) or hidden
57 | *
58 | * @param {bool} disableCloseButton true to hide close button on all messages
59 | */
60 | this.globalDisableCloseButton = function (disableCloseButton) {
61 | _disableCloseButton = disableCloseButton;
62 | return this;
63 | };
64 |
65 | /**
66 | * set whether the icons will be shown in the message
67 | *
68 | * @param {bool} messageIcons
69 | */
70 | this.globalDisableIcons = function (disableIcons) {
71 | _disableIcons = disableIcons;
72 | return this;
73 | };
74 |
75 | /**
76 | * set whether message is paused on mouse-over
77 | *
78 | * @param {bool} pauseOnMouseOver
79 | */
80 | this.globalPauseOnMouseOver = function (pauseOnMouseOver) {
81 | _pauseOnMouseOver = pauseOnMouseOver;
82 | return this;
83 | };
84 |
85 | /**
86 | * set whether message ordering is reversed
87 | *
88 | * @param {bool} reverseOrder
89 | */
90 | this.globalReversedOrder = function (reverseOrder) {
91 | _reverseOrder = reverseOrder;
92 | return this;
93 | };
94 |
95 | /**
96 | * set whether to show the count down
97 | *
98 | * @param {bool} reverseOrder
99 | */
100 | this.globalDisableCountDown = function (countDown) {
101 | _disableCountDown = countDown;
102 | return this;
103 | };
104 |
105 | /**
106 | * set the key in server sent messages the serverMessagesInterecptor is looking for variables to inject in the message
107 | *
108 | * @param {string} messageVariableKey default: variables
109 | */
110 | this.messageVariableKey = function (messageVariableKey) {
111 | _messageVariableKey = messageVariableKey;
112 | return this;
113 | };
114 |
115 | /**
116 | * set wheter the notficiation is displayed inline our in growl like fasion
117 | *
118 | * @param {bool} inline true to show only inline notifications
119 | */
120 | this.globalInlineMessages = function (inline) {
121 | _inline = inline;
122 | return this;
123 | };
124 |
125 | /**
126 | * set position
127 | *
128 | * @param {string} messageVariableKey default: top-right
129 | */
130 | this.globalPosition = function (position) {
131 | _position = position;
132 | return this;
133 | };
134 | /**
135 | * sets the key in $http response the serverMessagesInterecptor is looking for server-sent messages, value of key
136 | * needs to be an array of objects
137 | *
138 | * @param {string} messagesKey default: messages
139 | */
140 | this.messagesKey = function (messagesKey) {
141 | _messagesKey = messagesKey;
142 | return this;
143 | };
144 |
145 | /**
146 | * sets the key in server sent messages the serverMessagesInterecptor is looking for text of message
147 | *
148 | * @param {string} messageTextKey default: text
149 | */
150 | this.messageTextKey = function (messageTextKey) {
151 | _messageTextKey = messageTextKey;
152 | return this;
153 | };
154 |
155 | /**
156 | * sets the key in server sent messages the serverMessagesInterecptor is looking for title of message
157 | *
158 | * @param {string} messageTextKey default: text
159 | */
160 | this.messageTitleKey = function (messageTitleKey) {
161 | _messageTitleKey = messageTitleKey;
162 | return this;
163 | };
164 |
165 | /**
166 | * sets the key in server sent messages the serverMessagesInterecptor is looking for severity of message
167 | *
168 | * @param {string} messageSeverityKey default: severity
169 | */
170 | this.messageSeverityKey = function (messageSeverityKey) {
171 | _messageSeverityKey = messageSeverityKey;
172 | return this;
173 | };
174 |
175 | /**
176 | * sets the key in server sent messages the serverMessagesInterecptor is looking for ttl of message
177 | *
178 | * @param {string} messageTTLKey default: ttl
179 | */
180 | this.messageTTLKey = function (messageTTLKey) {
181 | _messageTTLKey = messageTTLKey;
182 | return this;
183 | };
184 |
185 | /**
186 | * sets the key in server sent messages the serverMessagesInterecptor is looking for referenceId of message
187 | *
188 | * @param {string} messageReferenceIdKey default: referenceId
189 | */
190 | this.messageReferenceIdKey = function (messageReferenceIdKey) {
191 | _messageReferenceIdKey = messageReferenceIdKey;
192 | return this;
193 | };
194 |
195 |
196 | this.onlyUniqueMessages = function (onlyUniqueMessages) {
197 | _onlyUniqueMessages = onlyUniqueMessages;
198 | return this;
199 | };
200 |
201 | /**
202 | * $http interceptor that can be added to array of $http interceptors during config phase of application
203 | * via $httpProvider.responseInterceptors.push(...)
204 | *
205 | */
206 | this.serverMessagesInterceptor = ['$q', 'growl', function ($q, growl) {
207 | function checkResponse (response) {
208 | if (response !== undefined && response.data && response.data[_messagesKey] && response.data[_messagesKey].length > 0) {
209 | growl.addServerMessages(response.data[_messagesKey]);
210 | }
211 | }
212 |
213 | return {
214 | 'response': function (response) {
215 | checkResponse(response);
216 | return response;
217 | },
218 | 'responseError': function (rejection) {
219 | checkResponse(rejection);
220 | return $q.reject(rejection);
221 | }
222 | };
223 | }];
224 |
225 | this.$get = ["$rootScope", "$interpolate", "$sce", "$filter", "$interval", "growlMessages", function ($rootScope, $interpolate, $sce, $filter, $interval, growlMessages) {
226 | var translate;
227 |
228 | growlMessages.onlyUnique = _onlyUniqueMessages;
229 | growlMessages.reverseOrder = _reverseOrder;
230 |
231 | try {
232 | translate = $filter("translate");
233 | } catch (e) {
234 | //
235 | }
236 |
237 | function broadcastMessage (message) {
238 | if (translate && message.translateMessage) {
239 | message.text = translate(message.text, message.variables) || message.text;
240 | message.title = translate(message.title) || message.title;
241 | } else {
242 | var polation = $interpolate(message.text);
243 | message.text = polation(message.variables);
244 | }
245 | var addedMessage = growlMessages.addMessage(message);
246 | $rootScope.$broadcast("growlMessage", message);
247 | $interval(function () {
248 | }, 0, 1);
249 | return addedMessage;
250 | }
251 |
252 | function sendMessage (text, config, severity) {
253 | var _config = config || {}, message;
254 |
255 | message = {
256 | text: text,
257 | title: _config.title,
258 | severity: severity,
259 | ttl: _config.ttl || _ttl[severity],
260 | variables: _config.variables || {},
261 | disableCloseButton: _config.disableCloseButton === undefined ? _disableCloseButton : _config.disableCloseButton,
262 | disableIcons: _config.disableIcons === undefined ? _disableIcons : _config.disableIcons,
263 | pauseOnMouseOver: _config.pauseOnMouseOver === undefined ? _pauseOnMouseOver : _config.pauseOnMouseOver,
264 | disableCountDown: _config.disableCountDown === undefined ? _disableCountDown : _config.disableCountDown,
265 | position: _config.position || _position,
266 | referenceId: _config.referenceId || _referenceId,
267 | translateMessage: _config.translateMessage === undefined ? _translateMessages : _config.translateMessage,
268 | destroy: function () {
269 | growlMessages.deleteMessage(message);
270 | },
271 | setText: function (newText) {
272 | message.text = $sce.trustAsHtml(String(newText));
273 | },
274 | onclose: _config.onclose,
275 | onopen: _config.onopen
276 | };
277 |
278 | return broadcastMessage(message);
279 | }
280 |
281 | /**
282 | * add one warning message with bootstrap class: alert
283 | *
284 | * @param {string} text
285 | * @param {{ttl: number}} config
286 | */
287 | function warning (text, config) {
288 | return sendMessage(text, config, "warning");
289 | }
290 |
291 | /**
292 | * add one error message with bootstrap classes: alert, alert-error
293 | *
294 | * @param {string} text
295 | * @param {{ttl: number}} config
296 | */
297 | function error (text, config) {
298 | return sendMessage(text, config, "error");
299 | }
300 |
301 | /**
302 | * add one info message with bootstrap classes: alert, alert-info
303 | *
304 | * @param {string} text
305 | * @param {{ttl: number}} config
306 | */
307 | function info (text, config) {
308 | return sendMessage(text, config, "info");
309 | }
310 |
311 | /**
312 | * add one success message with bootstrap classes: alert, alert-success
313 | *
314 | * @param {string} text
315 | * @param {{ttl: number}} config
316 | */
317 | function success (text, config) {
318 | return sendMessage(text, config, "success");
319 | }
320 |
321 | /**
322 | * add one message with specified severity
323 | *
324 | * @param {string} text
325 | * @param {{ttl: number}} config
326 | * @param {string} severity
327 | */
328 | function general (text, config, severity) {
329 | severity = (severity || "error").toLowerCase();
330 | return sendMessage(text, config, severity);
331 | }
332 |
333 | /**
334 | * add a indefinite number of messages that a backend server may have sent as a validation result
335 | *
336 | * @param {Array.