├── .gitignore ├── .idea ├── .name ├── angular-inform.iml ├── encodings.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── jsLibraryMappings.xml ├── libraries │ └── angular_inform_node_modules.xml ├── misc.xml ├── modules.xml ├── runConfigurations │ ├── gulp_dist.xml │ ├── gulp_kitchensink.xml │ └── gulp_test_watch.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml └── webResources.xml ├── .npmignore ├── LICENSE ├── README.md ├── bower.json ├── build-config.js ├── dist ├── README.md ├── angular-inform.css ├── angular-inform.css.map ├── angular-inform.js ├── angular-inform.js.map ├── angular-inform.min.css ├── angular-inform.min.js └── angular-inform.min.js.map ├── gulp-tasks ├── bower-task.js ├── index-task.js ├── lib │ ├── gulp-autoprefixer-map.js │ ├── gulp-bower-power.js │ ├── gulp-csswring.js │ ├── gulp-mini-filter.js │ ├── gulp-modify-content.js │ ├── gulp-rename-filename.js │ ├── gulp-wrap-src.js │ └── ng-xml.js ├── module-tasks │ ├── clean-task.js │ ├── copy-task.js │ ├── scripts-task.js │ ├── styles-task.js │ ├── svg-task.js │ └── templates-task.js ├── modules-task.js └── visual-studio.targets ├── gulpfile.js ├── karma.conf.js ├── package.json └── src ├── angular-inform ├── angular-inform.css ├── angular-inform.js ├── controller.js ├── directive.js ├── directive.ng.html ├── exception │ ├── exception.js │ ├── exception.test.js │ ├── http.js │ └── http.test.js ├── provider.js └── provider.test.js ├── angular-showdown └── angular-showdown.js ├── app ├── app.css ├── app.js ├── assets │ └── favicon.ico ├── demo │ ├── demo.css │ ├── demo.html │ └── demo.js └── readme │ └── readme.html └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/workspace.xml 2 | .idea/tasks.xml 3 | .DS_Store 4 | node_modules/* 5 | bower_components/* 6 | dest/* 7 | 8 | projectFilesBackup -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | angular-inform -------------------------------------------------------------------------------- /.idea/angular-inform.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 37 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/libraries/angular_inform_node_modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations/gulp_dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations/gulp_kitchensink.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations/gulp_test_watch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/webResources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/workspace.xml 2 | .idea/tasks.xml 3 | .DS_Store 4 | node_modules/* 5 | bower_components/* 6 | dest/* 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 McNull 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [angular-inform](https://github.com/McNull/angular-inform) 2 | A small growl-like easy-to-use message toaster for angular. 3 | 4 | #### Dependencies 5 | Besides AngularJS (~1.2.4), none. 6 | 7 | #### Browser Support 8 | 9 | Tested under _Internet Explorer 8+_, _Chrome 34+_, _FireFox 28+_ and _Safari 7+_. The styling should work on any _decent_ mobile device. 10 | 11 | #### Demo 12 | 13 | Either visit [this Plunker](http://plnkr.co/edit/x0sJj8) page or clone this repository locally and run a http-server from the `public` folder. 14 | 15 | Installation 16 | ============ 17 | 18 | The latest version can be installed via _bower_ from the command line. 19 | 20 | $ bower --save install angular-inform 21 | 22 | Include both the _JavaScript_ and _CSS_ in your _HTML_. 23 | 24 | ``` 25 | 26 | 27 | 28 | ``` 29 | 30 | Make your main angular module dependent of the `inform` module. 31 | 32 | angular.module('myApp', ['inform']); 33 | 34 | Insert the `inform` directive somewhere in your _HTML_. 35 | 36 | ``` 37 |
38 | ``` 39 | 40 | ### Optional Installation Steps 41 | 42 | Provide the element the `inform-fixed` class if you want the messages to float at the upper left position. 43 | 44 | ``` 45 |
46 | ``` 47 | 48 | Provide the element the `inform-fixes` and `inform-center` to display the messages in the center of the screen. 49 | 50 | ``` 51 |
52 | ``` 53 | 54 | Provide the element the `inform-shadow` to give the messages a light box-shadow. 55 | 56 | ``` 57 |
58 | ``` 59 | 60 | #### Unhandled Exceptions 61 | 62 | Make your main angular module dependent of the `inform-exception` module if you want _unhandled exceptions_ to be displayed in the notification list. 63 | 64 | angular.module('myApp', ['inform', 'inform-exception']); 65 | 66 | Make your main angular module dependent of the `inform-http-exception` module if you want _http exceptions_ to be displayed in the notification list. 67 | 68 | angular.module('myApp', ['inform', 'inform-http-exception']); 69 | 70 | #### Animations 71 | 72 | Install `angular-animate` and provide the element the `inform-animate` class if you want to have CSS3 animations. 73 | 74 | ``` 75 | # Install angular-animate 76 | $ bower install angular-animate 77 | 78 | // Enable ngAnimate in your module 79 | angular.module('myApp', ['inform', 'ngAnimate']); 80 | 81 | 82 |
83 | ``` 84 | 85 | Usage 86 | ===== 87 | 88 | The module exposes an injectable service by the name `inform`, which allows you to add notifications. 89 | 90 | ``` 91 | angular.module('myApp') 92 | .controller('MyController', function($scope, inform) { 93 | inform.add('Hello!'); 94 | }); 95 | ``` 96 | 97 | Service Methods 98 | =============== 99 | 100 | #### Add 101 | The `add` methods appends new messages to the list of notifications. The first argument is the message to display and the _optional_ second argument is an object containing some options. 102 | 103 | inform.add('The content of the message', { 104 | ttl: 2000, type: 'warning' 105 | }); 106 | 107 | **Option properties** 108 | 109 | The default property values are used if no options are provided. See _configuration_ how to change these default values. 110 | 111 | { 112 | /* 113 | The time to live for the message in milliseconds. 114 | Default value is 5000. Specify <0 to make the message sticky. 115 | */ 116 | ttl: 5000, 117 | 118 | /* 119 | The type of message to enable styling. 120 | Values can be any of the following: 121 | 122 | - 'default' 123 | - 'primary' 124 | - 'success' 125 | - 'info' 126 | - 'warning' 127 | - 'danger' 128 | - or any other custom type. 129 | 130 | Default value is 'default' 131 | */ 132 | type: 'danger' 133 | } 134 | 135 | **Flood protection** 136 | 137 | To prevent the list from flooding, the method will search the current notification list for a match on content and type. If an earlier message is found, than the _TTL_ of that message will be reset and a track count is increased. 138 | 139 | **Return value** 140 | 141 | The method will either return a newly created message object or any match found. 142 | 143 | #### Remove 144 | 145 | Any earlier returned message object can be manually removed from the notification list if needed. This will also cancel any pending TTL timeouts. 146 | 147 | var myMsg = inform.add('My Message'); 148 | inform.remove(myMsg); 149 | 150 | #### Clear 151 | 152 | This method (no arguments) will simply clear the list of messages. 153 | 154 | #### Messages 155 | 156 | Returns the array of messages maintained by the service. 157 | 158 | Service Configuration 159 | ===================== 160 | 161 | The defaults of the service can be manipulated in the config phase of your module by injecting the `informProvider`. 162 | 163 | angular.module('myApp') 164 | .config(function(informProvider) { 165 | 166 | var myDefaults = { 167 | /* default time to live for each notification */ 168 | ttl: 1234, 169 | /* default type of notification */ 170 | type: 'danger' 171 | }; 172 | 173 | informProvider.defaults(myDefaults); 174 | 175 | }); -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-inform", 3 | "description": "A small growl-like easy-to-use message toaster for angular.", 4 | "keywords": [ "angular", "growl", "message", "message-list", "notification", "notification-list", "toaster", "toastr", "popup", "alert", "alert-list" ], 5 | "version": "0.0.18", 6 | "authors": [ 7 | "Null McNull " 8 | ], 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "gulp-tasks", 15 | "src", 16 | "dest", 17 | "/build-config.js", 18 | "/gulpfile.js", 19 | "/package.json", 20 | "/README.md", 21 | "/karma.conf.js" 22 | ], 23 | "dependencies": { 24 | "angular": "~1.2.16" 25 | }, 26 | "main": [ 27 | "dist/angular-inform.css", 28 | "dist/angular-inform.js" 29 | ], 30 | "devDependencies": { 31 | "angular-mocks": "~1.2.26", 32 | "angular-route": "~1.2.26", 33 | "angular-animate": "~1.2.26", 34 | "bootstrap-css": "~3.1.1", 35 | "jquery": "1.*", 36 | "modernizr": "~2.8.3", 37 | "respondJS": "~1.4.2", 38 | "showdown": "~0.3.1" 39 | }, 40 | "overrides": { 41 | "respond": { 42 | "main": "dest/respond.min.js" 43 | }, 44 | "showdown": { 45 | "main": "compressed/showdown.js" 46 | }, 47 | "angular-mocks": { 48 | "ignore": true 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /build-config.js: -------------------------------------------------------------------------------- 1 | // - - - - - 8-< - - - - - - - - - - - - - - 2 | 3 | var _ = require('lodash'); 4 | 5 | // - - - - - 8-< - - - - - - - - - - - - - - 6 | 7 | var pkg = require('./package.json'); 8 | pkg.year = pkg.year || new Date().getFullYear(); 9 | 10 | // - - - - - 8-< - - - - - - - - - - - - - - 11 | 12 | var config = { 13 | 14 | /* 'production' || 'development' */ 15 | env: process.env.NODE_ENV, 16 | 17 | folders: { 18 | dest: 'dest/', 19 | src: 'src/' 20 | }, 21 | 22 | modules: [ 23 | { 24 | name: 'angular-inform', 25 | alias: 'inform' 26 | }, 27 | { 28 | name: 'angular-showdown', 29 | alias: 'showdown' 30 | }, 31 | { 32 | name: 'app' 33 | } 34 | ], 35 | 36 | bower: { 37 | includeDev: true 38 | }, 39 | 40 | header: _.template([ 41 | '/*!', 42 | ' <%= pkg.name %> v<%= pkg.version %>', 43 | ' (c) <%= pkg.year %> <%= pkg.author %> <%= pkg.homepage %>', 44 | ' License: <%= pkg.license %>', 45 | '*/\n' 46 | ].join('\n'), { pkg: pkg }) 47 | 48 | }; 49 | 50 | // - - - - - 8-< - - - - - - - - - - - - - - 51 | 52 | module.exports = config; -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | # [angular-inform](https://github.com/McNull/angular-inform) 2 | A small growl-like easy-to-use message toaster for angular. 3 | 4 | #### Dependencies 5 | Besides AngularJS (~1.2.4), none. 6 | 7 | #### Browser Support 8 | 9 | Tested under _Internet Explorer 8+_, _Chrome 34+_, _FireFox 28+_ and _Safari 7+_. The styling should work on any _decent_ mobile device. 10 | 11 | #### Demo 12 | 13 | Either visit [this Plunker](http://plnkr.co/edit/x0sJj8) page or clone this repository locally and run a http-server from the `public` folder. 14 | 15 | Installation 16 | ============ 17 | 18 | The latest version can be installed via _bower_ from the command line. 19 | 20 | $ bower --save install angular-inform 21 | 22 | Include both the _JavaScript_ and _CSS_ in your _HTML_. 23 | 24 | ``` 25 | 26 | 27 | 28 | ``` 29 | 30 | Make your main angular module dependent of the `inform` module. 31 | 32 | angular.module('myApp', ['inform']); 33 | 34 | Insert the `inform` directive somewhere in your _HTML_. 35 | 36 | ``` 37 |
38 | ``` 39 | 40 | ### Optional Installation Steps 41 | 42 | Provide the element the `inform-fixed` class if you want the messages to float at the upper left position. 43 | 44 | ``` 45 |
46 | ``` 47 | 48 | Provide the element the `inform-fixes` and `inform-center` to display the messages in the center of the screen. 49 | 50 | ``` 51 |
52 | ``` 53 | 54 | Provide the element the `inform-shadow` to give the messages a light box-shadow. 55 | 56 | ``` 57 |
58 | ``` 59 | 60 | #### Unhandled Exceptions 61 | 62 | Make your main angular module dependent of the `inform-exception` module if you want _unhandled exceptions_ to be displayed in the notification list. 63 | 64 | angular.module('myApp', ['inform', 'inform-exception']); 65 | 66 | Make your main angular module dependent of the `inform-http-exception` module if you want _http exceptions_ to be displayed in the notification list. 67 | 68 | angular.module('myApp', ['inform', 'inform-http-exception']); 69 | 70 | #### Animations 71 | 72 | Install `angular-animate` and provide the element the `inform-animate` class if you want to have CSS3 animations. 73 | 74 | ``` 75 | # Install angular-animate 76 | $ bower install angular-animate 77 | 78 | // Enable ngAnimate in your module 79 | angular.module('myApp', ['inform', 'ngAnimate']); 80 | 81 | 82 |
83 | ``` 84 | 85 | Usage 86 | ===== 87 | 88 | The module exposes an injectable service by the name `inform`, which allows you to add notifications. 89 | 90 | ``` 91 | angular.module('myApp') 92 | .controller('MyController', function($scope, inform) { 93 | inform.add('Hello!'); 94 | }); 95 | ``` 96 | 97 | Service Methods 98 | =============== 99 | 100 | #### Add 101 | The `add` methods appends new messages to the list of notifications. The first argument is the message to display and the _optional_ second argument is an object containing some options. 102 | 103 | inform.add('The content of the message', { 104 | ttl: 2000, type: 'warning' 105 | }); 106 | 107 | **Option properties** 108 | 109 | The default property values are used if no options are provided. See _configuration_ how to change these default values. 110 | 111 | { 112 | /* 113 | The time to live for the message in milliseconds. 114 | Default value is 5000. Specify <0 to make the message sticky. 115 | */ 116 | ttl: 5000, 117 | 118 | /* 119 | The type of message to enable styling. 120 | Values can be any of the following: 121 | 122 | - 'default' 123 | - 'primary' 124 | - 'success' 125 | - 'info' 126 | - 'warning' 127 | - 'danger' 128 | - or any other custom type. 129 | 130 | Default value is 'default' 131 | */ 132 | type: 'danger' 133 | } 134 | 135 | **Flood protection** 136 | 137 | To prevent the list from flooding, the method will search the current notification list for a match on content and type. If an earlier message is found, than the _TTL_ of that message will be reset and a track count is increased. 138 | 139 | **Return value** 140 | 141 | The method will either return a newly created message object or any match found. 142 | 143 | #### Remove 144 | 145 | Any earlier returned message object can be manually removed from the notification list if needed. This will also cancel any pending TTL timeouts. 146 | 147 | var myMsg = inform.add('My Message'); 148 | inform.remove(myMsg); 149 | 150 | #### Clear 151 | 152 | This method (no arguments) will simply clear the list of messages. 153 | 154 | #### Messages 155 | 156 | Returns the array of messages maintained by the service. 157 | 158 | Service Configuration 159 | ===================== 160 | 161 | The defaults of the service can be manipulated in the config phase of your module by injecting the `informProvider`. 162 | 163 | angular.module('myApp') 164 | .config(function(informProvider) { 165 | 166 | var myDefaults = { 167 | /* default time to live for each notification */ 168 | ttl: 1234, 169 | /* default type of notification */ 170 | type: 'danger' 171 | }; 172 | 173 | informProvider.defaults(myDefaults); 174 | 175 | }); -------------------------------------------------------------------------------- /dist/angular-inform.css: -------------------------------------------------------------------------------- 1 | /*! 2 | angular-inform v0.0.18 3 | (c) 2014 (null) McNull https://github.com/McNull/angular-inform 4 | License: MIT 5 | */ 6 | 7 | 8 | 9 | .inform-fixed { 10 | position: fixed; 11 | top: 20px; 12 | left: 20px; 13 | right: 20px; 14 | z-index: 1002; 15 | } 16 | 17 | .inform-fixed .inform-message { 18 | width: auto; 19 | } 20 | 21 | @media screen and (min-width: 480px) { 22 | .inform-fixed { 23 | left: auto; 24 | width: 440px; 25 | } 26 | 27 | .inform-fixed.inform-center { 28 | right: 0; 29 | left: 0; 30 | margin-left: auto; 31 | margin-right: auto; 32 | } 33 | } 34 | 35 | .inform-message { 36 | overflow: hidden; 37 | } 38 | 39 | .inform-message > button.close { 40 | margin-left: 15px; 41 | } 42 | 43 | .inform-shadow .inform-message { 44 | box-shadow: 0px 5px 20px 0px rgba(0, 0, 0, 0.25); 45 | } 46 | 47 | .inform-message.alert-primary { 48 | color: #FFF; 49 | background-color: #428BCA; 50 | } 51 | 52 | .inform-message.alert-default { 53 | background-color: #F5F5F5; 54 | border: 1px solid #CCC; 55 | color: #333; 56 | } 57 | 58 | 59 | .inform-message-wrap { 60 | /*overflow: hidden;*/ 61 | } 62 | .inform-animate .inform-message-wrap.ng-enter { 63 | -webkit-animation: 1.0s inform-message-slide-in; 64 | animation: 1.0s inform-message-slide-in; 65 | } 66 | 67 | .inform-animate .inform-message-wrap.ng-leave { 68 | -webkit-animation: 1.0s inform-message-fade-out; 69 | animation: 1.0s inform-message-fade-out; 70 | } 71 | 72 | .inform-animate .inform-badge.ng-enter { 73 | -webkit-animation: inform-badge-scale 0.5s; 74 | animation: inform-badge-scale 0.5s; 75 | } 76 | 77 | @-webkit-keyframes inform-badge-scale { 78 | 0% { 79 | opacity: 0; 80 | -webkit-transform: scale(0); 81 | transform: scale(0); 82 | } 83 | 25% { 84 | opacity: 1; 85 | -webkit-transform: scale(1.5); 86 | transform: scale(1.5); 87 | } 88 | 100% { 89 | opacity: 1; 90 | -webkit-transform: scale(1); 91 | transform: scale(1); 92 | } 93 | } 94 | 95 | @keyframes inform-badge-scale { 96 | 0% { 97 | opacity: 0; 98 | -webkit-transform: scale(0); 99 | transform: scale(0); 100 | } 101 | 25% { 102 | opacity: 1; 103 | -webkit-transform: scale(1.5); 104 | transform: scale(1.5); 105 | } 106 | 100% { 107 | opacity: 1; 108 | -webkit-transform: scale(1); 109 | transform: scale(1); 110 | } 111 | } 112 | 113 | @-webkit-keyframes inform-message-slide-in { 114 | 0% { 115 | opacity: 0; 116 | -webkit-transform: translateX(100%); 117 | transform: translateX(100%); 118 | max-height: 0px; 119 | } 120 | 50% { 121 | opacity: 0; 122 | -webkit-transform: translateX(100%); 123 | transform: translateX(100%); 124 | max-height: 1000px; 125 | } 126 | 100% { 127 | opacity: 1; 128 | -webkit-transform: translateX(0%); 129 | transform: translateX(0%); 130 | max-height: 1000px; 131 | } 132 | } 133 | 134 | @keyframes inform-message-slide-in { 135 | 0% { 136 | opacity: 0; 137 | -webkit-transform: translateX(100%); 138 | transform: translateX(100%); 139 | max-height: 0px; 140 | } 141 | 50% { 142 | opacity: 0; 143 | -webkit-transform: translateX(100%); 144 | transform: translateX(100%); 145 | max-height: 1000px; 146 | } 147 | 100% { 148 | opacity: 1; 149 | -webkit-transform: translateX(0%); 150 | transform: translateX(0%); 151 | max-height: 1000px; 152 | } 153 | } 154 | 155 | @-webkit-keyframes inform-message-fade-out { 156 | 0% { 157 | opacity: 1; 158 | max-height: 1000px; 159 | } 160 | 50% { 161 | opacity: 0; 162 | max-height: 1000px; 163 | } 164 | 100% { 165 | opacity: 0; 166 | max-height: 0px; 167 | } 168 | } 169 | 170 | @keyframes inform-message-fade-out { 171 | 0% { 172 | opacity: 1; 173 | max-height: 1000px; 174 | } 175 | 50% { 176 | opacity: 0; 177 | max-height: 1000px; 178 | } 179 | 100% { 180 | opacity: 0; 181 | max-height: 0px; 182 | } 183 | } 184 | /*# sourceMappingURL=angular-inform.css.map */ -------------------------------------------------------------------------------- /dist/angular-inform.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["angular-inform-header.css","angular-inform.css"],"names":[],"mappings":"AAAA;;;;GAIA;;;;ACFA;EACA,iBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,eAAA;EACA;;AAEA;EACA,aAAA;EACA;;AAEA;EACA;IACA,YAAA;IACA,cAAA;IACA;;EAEA;IACA,UAAA;IACA,SAAA;IACA,mBAAA;IACA,oBAAA;IACA;EACA;;AAEA;EACA,kBAAA;EACA;;AAEA;EACA,mBAAA;EACA;;AAEA;EACA,kDAAA;EACA;;AAEA;EACA,aAAA;EACA,2BAAA;EACA;;AAEA;EACA,2BAAA;EACA,wBAAA;EACA,aAAA;EACA;;;AAGA;EACA,sBAAA;EACA;AACA;EACA,iDAAA;UAAA,yCAAA;EACA;;AAEA;EACA,iDAAA;UAAA,yCAAA;EACA;;AAEA;EACA,4CAAA;UAAA,oCAAA;EACA;;AAEA;EACA;IACA,YAAA;IACA,6BAAA;YAAA,qBAAA;IACA;EACA;IACA,YAAA;IACA,+BAAA;YAAA,uBAAA;IACA;EACA;IACA,YAAA;IACA,6BAAA;YAAA,qBAAA;IACA;EACA;;AAbA;EACA;IACA,YAAA;IACA,6BAAA;YAAA,qBAAA;IACA;EACA;IACA,YAAA;IACA,+BAAA;YAAA,uBAAA;IACA;EACA;IACA,YAAA;IACA,6BAAA;YAAA,qBAAA;IACA;EACA;;AAEA;EACA;IACA,YAAA;IACA,qCAAA;YAAA,6BAAA;IACA,iBAAA;IACA;EACA;IACA,YAAA;IACA,qCAAA;YAAA,6BAAA;IACA,oBAAA;IACA;EACA;IACA,YAAA;IACA,mCAAA;YAAA,2BAAA;IACA,oBAAA;IACA;EACA;;AAhBA;EACA;IACA,YAAA;IACA,qCAAA;YAAA,6BAAA;IACA,iBAAA;IACA;EACA;IACA,YAAA;IACA,qCAAA;YAAA,6BAAA;IACA,oBAAA;IACA;EACA;IACA,YAAA;IACA,mCAAA;YAAA,2BAAA;IACA,oBAAA;IACA;EACA;;AAEA;EACA;IACA,YAAA;IACA,oBAAA;IACA;EACA;IACA,YAAA;IACA,oBAAA;IACA;EACA;IACA,YAAA;IACA,iBAAA;IACA;EACA;;AAbA;EACA;IACA,YAAA;IACA,oBAAA;IACA;EACA;IACA,YAAA;IACA,oBAAA;IACA;EACA;IACA,YAAA;IACA,iBAAA;IACA;EACA","file":"angular-inform.css","sourcesContent":["/*!\n angular-inform v0.0.18\n (c) 2014 (null) McNull https://github.com/McNull/angular-inform\n License: MIT\n*/\n","\n\n.inform-fixed {\n position: fixed;\n top: 20px;\n left: 20px;\n right: 20px;\n z-index: 1002;\n}\n\n.inform-fixed .inform-message {\n width: auto;\n}\n\n@media screen and (min-width: 480px) {\n .inform-fixed {\n left: auto;\n width: 440px;\n }\n\n .inform-fixed.inform-center {\n right: 0;\n left: 0;\n margin-left: auto;\n margin-right: auto;\n }\n}\n\n.inform-message {\n overflow: hidden;\n}\n\n.inform-message > button.close {\n margin-left: 15px;\n}\n\n.inform-shadow .inform-message {\n box-shadow: 0px 5px 20px 0px rgba(0, 0, 0, 0.25);\n}\n\n.inform-message.alert-primary {\n color: #FFF;\n background-color: #428BCA;\n}\n\n.inform-message.alert-default {\n background-color: #F5F5F5;\n border: 1px solid #CCC;\n color: #333;\n}\n\n\n.inform-message-wrap {\n /*overflow: hidden;*/\n}\n.inform-animate .inform-message-wrap.ng-enter {\n animation: 1.0s inform-message-slide-in;\n}\n\n.inform-animate .inform-message-wrap.ng-leave {\n animation: 1.0s inform-message-fade-out;\n}\n\n.inform-animate .inform-badge.ng-enter {\n animation: inform-badge-scale 0.5s;\n}\n\n@keyframes inform-badge-scale {\n 0% {\n opacity: 0;\n transform: scale(0);\n }\n 25% {\n opacity: 1;\n transform: scale(1.5);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@keyframes inform-message-slide-in {\n 0% {\n opacity: 0;\n transform: translateX(100%);\n max-height: 0px;\n }\n 50% {\n opacity: 0;\n transform: translateX(100%);\n max-height: 1000px;\n }\n 100% {\n opacity: 1;\n transform: translateX(0%);\n max-height: 1000px;\n }\n}\n\n@keyframes inform-message-fade-out {\n 0% {\n opacity: 1;\n max-height: 1000px;\n }\n 50% {\n opacity: 0;\n max-height: 1000px;\n }\n 100% {\n opacity: 0;\n max-height: 0px;\n }\n}"],"sourceRoot":"../src/angular-inform"} -------------------------------------------------------------------------------- /dist/angular-inform.js: -------------------------------------------------------------------------------- 1 | /*! 2 | angular-inform v0.0.18 3 | (c) 2014 (null) McNull https://github.com/McNull/angular-inform 4 | License: MIT 5 | */ 6 | (function(angular) { 7 | 8 | var inform = angular.module('inform', []); 9 | 10 | 11 | inform.controller('InformCtrl', ["$scope", "inform", function($scope, inform) { 12 | 13 | $scope.messages = inform.messages(); 14 | $scope.remove = inform.remove; 15 | $scope.cancelTimeout = inform.cancelTimeout; 16 | $scope.setTimeout = inform.setTimeout; 17 | 18 | }]); 19 | 20 | inform.directive('inform', function () { 21 | return { 22 | restrict: 'AE', 23 | templateUrl: 'angular-inform/directive.ng.html', 24 | controller: 'InformCtrl' 25 | }; 26 | }); 27 | inform.provider('inform', function () { 28 | 29 | var provider = this; 30 | 31 | this._defaults = { 32 | type: 'default', 33 | ttl: 5000 34 | }; 35 | 36 | this.defaults = function (options) { 37 | provider._defaults = angular.extend(provider._defaults, options || {}); 38 | return provider._defaults; 39 | }; 40 | 41 | 42 | this.$get = ['$timeout', '$sce', function ($timeout, $sce) { 43 | 44 | var _messages = []; 45 | 46 | function _indexOf(predicate) { 47 | var i = _messages.length; 48 | 49 | while (i--) { 50 | if (predicate(_messages[i])) { 51 | return i; 52 | } 53 | } 54 | 55 | return -1; 56 | } 57 | 58 | function cancelTimeout(msg) { 59 | if (msg.timeout) { 60 | $timeout.cancel(msg.timeout); 61 | delete msg.timeout; 62 | } 63 | } 64 | 65 | function setTimeout(msg) { 66 | 67 | cancelTimeout(msg); 68 | 69 | if (msg.ttl > 0) { 70 | msg.timeout = $timeout(function () { 71 | remove(msg); 72 | }, msg.ttl); 73 | } 74 | } 75 | 76 | function add(content, options) { 77 | 78 | var msg = angular.extend({}, provider._defaults, options); 79 | 80 | if(!angular.isString(content)) { 81 | content = '
' + JSON.stringify(content, null, '  ') + '
'; 82 | msg.html = true; 83 | } 84 | 85 | var idx = _indexOf(function (x) { 86 | return x.content.toString() === content && x.type == msg.type; 87 | }); 88 | 89 | if (idx >= 0) { 90 | 91 | msg = _messages[idx]; 92 | msg.count += 1; 93 | 94 | } else { 95 | 96 | msg.content = content; 97 | 98 | if(msg.html) { 99 | msg.content = $sce.trustAsHtml(content); 100 | } 101 | 102 | msg.tickCount = +new Date(); 103 | msg.count = 1; 104 | 105 | _messages.push(msg); 106 | } 107 | 108 | setTimeout(msg); 109 | 110 | return msg; 111 | } 112 | 113 | function remove(msg) { 114 | 115 | var idx = _indexOf(function (x) { 116 | return x === msg; 117 | }); 118 | 119 | if (idx >= 0) { 120 | _messages.splice(idx, 1); 121 | cancelTimeout(msg); 122 | } 123 | } 124 | 125 | function clear() { 126 | _messages.length = 0; 127 | } 128 | 129 | return { 130 | messages: function () { 131 | return _messages; 132 | }, 133 | add: add, 134 | remove: remove, 135 | clear: clear, 136 | cancelTimeout: cancelTimeout, 137 | setTimeout: setTimeout 138 | }; 139 | }]; 140 | 141 | }); 142 | 143 | angular.module('inform-exception', ['inform']) 144 | .config(["$provide", function($provide) { 145 | $provide.decorator('$exceptionHandler', ['$delegate', '$injector',function($delegate, $injector) { 146 | 147 | var inform; 148 | 149 | return function(exception, cause) { 150 | try { 151 | inform = inform || $injector.get('inform'); 152 | inform.add(exception.toString(), { type: 'danger', ttl: 0 }); 153 | } catch(ex) { 154 | console.log('$exceptionHandler', ex); 155 | } 156 | $delegate(exception, cause); 157 | }; 158 | }]); 159 | }]); 160 | 161 | angular.module('inform-http-exception', ['inform']) 162 | 163 | .factory('informHttpInterceptor', ["$q", "inform", function ($q, inform) { 164 | 165 | function interceptor(rejection) { 166 | try { 167 | var msg = 'Network error (' + rejection.status + '): ' + rejection.statusText; 168 | inform.add(msg, { type: 'danger', ttl: 0}); 169 | } catch(ex) { 170 | console.log('$httpProvider', ex); 171 | } 172 | 173 | return $q.reject(rejection); 174 | } 175 | 176 | return { 177 | requestError: interceptor, 178 | responseError: interceptor 179 | }; 180 | 181 | }]) 182 | 183 | .config(["$httpProvider", function ($httpProvider) { 184 | $httpProvider.interceptors.push('informHttpInterceptor'); 185 | }]); 186 | 187 | // Automatically generated. 188 | // This file is already embedded in your main javascript output, there's no need to include this file 189 | // manually in the index.html. This file is only here for your debugging pleasures. 190 | angular.module('inform').run(['$templateCache', function($templateCache){ 191 | $templateCache.put('angular-inform/directive.ng.html', '
1\">{{ msg.count }}
'); 192 | }]); 193 | })(angular); 194 | //# sourceMappingURL=angular-inform.js.map -------------------------------------------------------------------------------- /dist/angular-inform.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["angular-inform-header.js","angular-inform.js","controller.js","directive.js","provider.js","exception/exception.js","exception/http.js","angular-inform-templates.js","angular-inform-footer.js"],"names":[],"mappings":"AAAA;GACA;GACA;GACA;AACA;AACA;;ACLA;;;ACAA,mBAAA,UAAA,GAAA,qBAAA;;EAEA;EACA;EACA;EACA;;AAEA,CAAA,CAAA;;ACPA,kBAAA,MAAA;EACA;IACA;IACA;IACA,aAAA,UAAA;EACA;AACA;ACNA,iBAAA,MAAA;;EAEA;;EAEA;IACA;IACA;EACA;;EAEA;IACA;IACA;EACA;;;EAGA;;IAEA;;IAEA;MACA;;MAEA;QACA;UACA;QACA;MACA;;MAEA;IACA;;IAEA;MACA;QACA;QACA;MACA;IACA;;IAEA;;MAEA;;MAEA;QACA;UACA;QACA;MACA;IACA;;IAEA;;MAEA;;MAEA;QACA;QACA;MACA;;MAEA;QACA;MACA;;MAEA;;QAEA;QACA;;MAEA;;QAEA;;QAEA;UACA;QACA;;QAEA;QACA;;QAEA;MACA;;MAEA;;MAEA;IACA;;IAEA;;MAEA;QACA;MACA;;MAEA;QACA;QACA;MACA;IACA;;IAEA;MACA;IACA;;IAEA;MACA;QACA;MACA;MACA;MACA;MACA;MACA;MACA;IACA;EACA;;AAEA;;ACjHA;EACA,QAAA,aAAA;IACA,oBAAA,iBAAA;;MAEA;;MAEA;QACA;UACA;UACA;QACA;UACA;QACA;QACA;MACA;IACA;EACA,CAAA,CAAA;;ACjBA;;EAEA,UAAA,qBAAA,GAAA,iBAAA;;IAEA;MACA;QACA;QACA;MACA;QACA;MACA;;MAEA;IACA;;IAEA;MACA;MACA;IACA;;EAEA,CAAA,CAAA;;EAEA,QAAA,kBAAA;IACA,iCAAA,qBAAA;EACA,CAAA,CAAA;;ACxBA;AACA;AACA;AACA;EACA;AACA;ACLA","file":"angular-inform.js","sourcesContent":["/*!\n angular-inform v0.0.18\n (c) 2014 (null) McNull https://github.com/McNull/angular-inform\n License: MIT\n*/\n(function(angular) {\n","var inform = angular.module('inform', []);\n\n","inform.controller('InformCtrl', function($scope, inform) {\n\n $scope.messages = inform.messages();\n $scope.remove = inform.remove;\n $scope.cancelTimeout = inform.cancelTimeout;\n $scope.setTimeout = inform.setTimeout;\n\n});\n","inform.directive('inform', function () {\n return {\n restrict: 'AE',\n templateUrl: 'angular-inform/directive.ng.html',\n controller: 'InformCtrl'\n };\n});","inform.provider('inform', function () {\n\n var provider = this;\n\n this._defaults = {\n type: 'default',\n ttl: 5000\n };\n\n this.defaults = function (options) {\n provider._defaults = angular.extend(provider._defaults, options || {});\n return provider._defaults;\n };\n\n\n this.$get = ['$timeout', '$sce', function ($timeout, $sce) {\n\n var _messages = [];\n\n function _indexOf(predicate) {\n var i = _messages.length;\n\n while (i--) {\n if (predicate(_messages[i])) {\n return i;\n }\n }\n\n return -1;\n }\n\n function cancelTimeout(msg) {\n if (msg.timeout) {\n $timeout.cancel(msg.timeout);\n delete msg.timeout;\n }\n }\n\n function setTimeout(msg) {\n\n cancelTimeout(msg);\n\n if (msg.ttl > 0) {\n msg.timeout = $timeout(function () {\n remove(msg);\n }, msg.ttl);\n }\n }\n\n function add(content, options) {\n\n var msg = angular.extend({}, provider._defaults, options);\n\n if(!angular.isString(content)) {\n content = '
' + JSON.stringify(content, null, '  ') + '
';\n msg.html = true;\n }\n\n var idx = _indexOf(function (x) {\n return x.content.toString() === content && x.type == msg.type;\n });\n\n if (idx >= 0) {\n\n msg = _messages[idx];\n msg.count += 1;\n\n } else {\n\n msg.content = content;\n\n if(msg.html) {\n msg.content = $sce.trustAsHtml(content);\n }\n\n msg.tickCount = +new Date();\n msg.count = 1;\n\n _messages.push(msg);\n }\n\n setTimeout(msg);\n\n return msg;\n }\n\n function remove(msg) {\n\n var idx = _indexOf(function (x) {\n return x === msg;\n });\n\n if (idx >= 0) {\n _messages.splice(idx, 1);\n cancelTimeout(msg);\n }\n }\n\n function clear() {\n _messages.length = 0;\n }\n\n return {\n messages: function () {\n return _messages;\n },\n add: add,\n remove: remove,\n clear: clear,\n cancelTimeout: cancelTimeout,\n setTimeout: setTimeout\n };\n }];\n\n});","\nangular.module('inform-exception', ['inform'])\n .config(function($provide) {\n $provide.decorator('$exceptionHandler', ['$delegate', '$injector',function($delegate, $injector) {\n\n var inform;\n\n return function(exception, cause) {\n try {\n inform = inform || $injector.get('inform');\n inform.add(exception.toString(), { type: 'danger', ttl: 0 });\n } catch(ex) {\n console.log('$exceptionHandler', ex);\n }\n $delegate(exception, cause);\n };\n }]);\n });\n","angular.module('inform-http-exception', ['inform'])\n\n .factory('informHttpInterceptor', function ($q, inform) {\n\n function interceptor(rejection) {\n try {\n var msg = 'Network error (' + rejection.status + '): ' + rejection.statusText;\n inform.add(msg, { type: 'danger', ttl: 0});\n } catch(ex) {\n console.log('$httpProvider', ex);\n }\n\n return $q.reject(rejection);\n }\n\n return {\n requestError: interceptor,\n responseError: interceptor\n };\n\n })\n\n .config(function ($httpProvider) {\n $httpProvider.interceptors.push('informHttpInterceptor');\n });\n","// Automatically generated.\n// This file is already embedded in your main javascript output, there's no need to include this file\n// manually in the index.html. This file is only here for your debugging pleasures.\nangular.module('inform').run(['$templateCache', function($templateCache){\n $templateCache.put('angular-inform/directive.ng.html', '
1\\\">{{ msg.count }}
');\n}]);","})(angular);"],"sourceRoot":"../src/angular-inform"} -------------------------------------------------------------------------------- /dist/angular-inform.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | angular-inform v0.0.18 3 | (c) 2014 (null) McNull https://github.com/McNull/angular-inform 4 | License: MIT 5 | */.inform-fixed{position:fixed;top:20px;left:20px;right:20px;z-index:1002}.inform-fixed .inform-message{width:auto}@media screen and (min-width:480px){.inform-fixed{left:auto;width:440px}.inform-fixed.inform-center{right:0;left:0;margin-left:auto;margin-right:auto}}.inform-message{overflow:hidden}.inform-message>button.close{margin-left:15px}.inform-shadow .inform-message{box-shadow:0 5px 20px 0 rgba(0,0,0,.25)}.inform-message.alert-primary{color:#FFF;background-color:#428BCA}.inform-message.alert-default{background-color:#F5F5F5;border:1px solid #CCC;color:#333}.inform-animate .inform-message-wrap.ng-enter{-webkit-animation:1s inform-message-slide-in;animation:1s inform-message-slide-in}.inform-animate .inform-message-wrap.ng-leave{-webkit-animation:1s inform-message-fade-out;animation:1s inform-message-fade-out}.inform-animate .inform-badge.ng-enter{-webkit-animation:inform-badge-scale .5s;animation:inform-badge-scale .5s}@-webkit-keyframes inform-badge-scale{0%{opacity:0;-webkit-transform:scale(0);transform:scale(0)}25%{opacity:1;-webkit-transform:scale(1.5);transform:scale(1.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes inform-badge-scale{0%{opacity:0;-webkit-transform:scale(0);transform:scale(0)}25%{opacity:1;-webkit-transform:scale(1.5);transform:scale(1.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes inform-message-slide-in{0%{opacity:0;-webkit-transform:translateX(100%);transform:translateX(100%);max-height:0}50%{opacity:0;-webkit-transform:translateX(100%);transform:translateX(100%);max-height:1000px}100%{opacity:1;-webkit-transform:translateX(0%);transform:translateX(0%);max-height:1000px}}@keyframes inform-message-slide-in{0%{opacity:0;-webkit-transform:translateX(100%);transform:translateX(100%);max-height:0}50%{opacity:0;-webkit-transform:translateX(100%);transform:translateX(100%);max-height:1000px}100%{opacity:1;-webkit-transform:translateX(0%);transform:translateX(0%);max-height:1000px}}@-webkit-keyframes inform-message-fade-out{0%{opacity:1;max-height:1000px}50%{opacity:0;max-height:1000px}100%{opacity:0;max-height:0}}@keyframes inform-message-fade-out{0%{opacity:1;max-height:1000px}50%{opacity:0;max-height:1000px}100%{opacity:0;max-height:0}} -------------------------------------------------------------------------------- /dist/angular-inform.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | angular-inform v0.0.18 3 | (c) 2014 (null) McNull https://github.com/McNull/angular-inform 4 | License: MIT 5 | */ 6 | !function(t){var e=t.module("inform",[]);e.controller("InformCtrl",["$scope","inform",function(t,e){t.messages=e.messages(),t.remove=e.remove,t.cancelTimeout=e.cancelTimeout,t.setTimeout=e.setTimeout}]),e.directive("inform",function(){return{restrict:"AE",templateUrl:"angular-inform/directive.ng.html",controller:"InformCtrl"}}),e.provider("inform",function(){var e=this;this._defaults={type:"default",ttl:5e3},this.defaults=function(n){return e._defaults=t.extend(e._defaults,n||{}),e._defaults},this.$get=["$timeout","$sce",function(n,r){function o(t){for(var e=m.length;e--;)if(t(m[e]))return e;return-1}function i(t){t.timeout&&(n.cancel(t.timeout),delete t.timeout)}function s(t){i(t),t.ttl>0&&(t.timeout=n(function(){u(t)},t.ttl))}function c(n,i){var c=t.extend({},e._defaults,i);t.isString(n)||(n="
"+JSON.stringify(n,null,"  ")+"
",c.html=!0);var u=o(function(t){return t.content.toString()===n&&t.type==c.type});return u>=0?(c=m[u],c.count+=1):(c.content=n,c.html&&(c.content=r.trustAsHtml(n)),c.tickCount=+new Date,c.count=1,m.push(c)),s(c),c}function u(t){var e=o(function(e){return e===t});e>=0&&(m.splice(e,1),i(t))}function a(){m.length=0}var m=[];return{messages:function(){return m},add:c,remove:u,clear:a,cancelTimeout:i,setTimeout:s}}]}),t.module("inform-exception",["inform"]).config(["$provide",function(t){t.decorator("$exceptionHandler",["$delegate","$injector",function(t,e){var n;return function(r,o){try{n=n||e.get("inform"),n.add(r.toString(),{type:"danger",ttl:0})}catch(i){console.log("$exceptionHandler",i)}t(r,o)}}])}]),t.module("inform-http-exception",["inform"]).factory("informHttpInterceptor",["$q","inform",function(t,e){function n(n){try{var r="Network error ("+n.status+"): "+n.statusText;e.add(r,{type:"danger",ttl:0})}catch(o){console.log("$httpProvider",o)}return t.reject(n)}return{requestError:n,responseError:n}}]).config(["$httpProvider",function(t){t.interceptors.push("informHttpInterceptor")}]),t.module("inform").run(["$templateCache",function(t){t.put("angular-inform/directive.ng.html",'
')}])}(angular); 7 | //# sourceMappingURL=angular-inform.min.js.map -------------------------------------------------------------------------------- /dist/angular-inform.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["angular-inform.js"],"names":["angular","inform","module","controller","$scope","messages","remove","cancelTimeout","setTimeout","directive","restrict","templateUrl","provider","this","_defaults","type","ttl","defaults","options","extend","$get","$timeout","$sce","_indexOf","predicate","i","_messages","length","msg","timeout","cancel","add","content","isString","JSON","stringify","html","idx","x","toString","count","trustAsHtml","tickCount","Date","push","splice","clear","config","$provide","decorator","$delegate","$injector","exception","cause","get","ex","console","log","factory","$q","interceptor","rejection","status","statusText","reject","requestError","responseError","$httpProvider","interceptors","run","$templateCache","put"],"mappings":";;;;;CAKA,SAAUA,GAEV,GAAIC,GAASD,EAAQE,OAAO,YAG5BD,GAAOE,WAAW,cAAe,SAAU,SAAU,SAASC,EAAQH,GAEpEG,EAAOC,SAAWJ,EAAOI,WACzBD,EAAOE,OAASL,EAAOK,OACvBF,EAAOG,cAAgBN,EAAOM,cAC9BH,EAAOI,WAAaP,EAAOO,cAI7BP,EAAOQ,UAAU,SAAU,WACzB,OACEC,SAAU,KACVC,YAAa,mCACbR,WAAY,gBAGhBF,EAAOW,SAAS,SAAU,WAExB,GAAIA,GAAWC,IAEfA,MAAKC,WACHC,KAAM,UACNC,IAAK,KAGPH,KAAKI,SAAW,SAAUC,GAExB,MADAN,GAASE,UAAYd,EAAQmB,OAAOP,EAASE,UAAWI,OACjDN,EAASE,WAIlBD,KAAKO,MAAQ,WAAY,OAAQ,SAAUC,EAAUC,GAInD,QAASC,GAASC,GAGhB,IAFA,GAAIC,GAAIC,EAAUC,OAEXF,KACL,GAAID,EAAUE,EAAUD,IACtB,MAAOA,EAIX,OAAO,GAGT,QAASlB,GAAcqB,GACjBA,EAAIC,UACNR,EAASS,OAAOF,EAAIC,eACbD,GAAIC,SAIf,QAASrB,GAAWoB,GAElBrB,EAAcqB,GAEVA,EAAIZ,IAAM,IACZY,EAAIC,QAAUR,EAAS,WACrBf,EAAOsB,IACNA,EAAIZ,MAIX,QAASe,GAAIC,EAASd,GAEpB,GAAIU,GAAM5B,EAAQmB,UAAWP,EAASE,UAAWI,EAE7ClB,GAAQiC,SAASD,KACnBA,EAAU,cAAgBE,KAAKC,UAAUH,EAAS,KAAM,MAAQ,gBAChEJ,EAAIQ,MAAO,EAGb,IAAIC,GAAMd,EAAS,SAAUe,GAC3B,MAAOA,GAAEN,QAAQO,aAAeP,GAAWM,EAAEvB,MAAQa,EAAIb,MAwB3D,OArBIsB,IAAO,GAETT,EAAMF,EAAUW,GAChBT,EAAIY,OAAS,IAIbZ,EAAII,QAAUA,EAEXJ,EAAIQ,OACLR,EAAII,QAAUV,EAAKmB,YAAYT,IAGjCJ,EAAIc,WAAa,GAAIC,MACrBf,EAAIY,MAAQ,EAEZd,EAAUkB,KAAKhB,IAGjBpB,EAAWoB,GAEJA,EAGT,QAAStB,GAAOsB,GAEd,GAAIS,GAAMd,EAAS,SAAUe,GAC3B,MAAOA,KAAMV,GAGXS,IAAO,IACTX,EAAUmB,OAAOR,EAAK,GACtB9B,EAAcqB,IAIlB,QAASkB,KACPpB,EAAUC,OAAS,EAlFrB,GAAID,KAqFJ,QACErB,SAAU,WACR,MAAOqB,IAETK,IAAKA,EACLzB,OAAQA,EACRwC,MAAOA,EACPvC,cAAeA,EACfC,WAAYA,OAMlBR,EAAQE,OAAO,oBAAqB,WACjC6C,QAAQ,WAAY,SAASC,GAC5BA,EAASC,UAAU,qBAAsB,YAAa,YAAY,SAASC,EAAWC,GAEpF,GAAIlD,EAEJ,OAAO,UAASmD,EAAWC,GACzB,IACEpD,EAASA,GAAUkD,EAAUG,IAAI,UACjCrD,EAAO8B,IAAIqB,EAAUb,YAAcxB,KAAM,SAAUC,IAAK,IACxD,MAAMuC,GACNC,QAAQC,IAAI,oBAAqBF,GAEnCL,EAAUE,EAAWC,UAK7BrD,EAAQE,OAAO,yBAA0B,WAEtCwD,QAAQ,yBAA0B,KAAM,SAAU,SAAUC,EAAI1D,GAE/D,QAAS2D,GAAYC,GACnB,IACE,GAAIjC,GAAM,kBAAoBiC,EAAUC,OAAS,MAAQD,EAAUE,UACnE9D,GAAO8B,IAAIH,GAAOb,KAAM,SAAUC,IAAK,IACvC,MAAMuC,GACNC,QAAQC,IAAI,gBAAiBF,GAG/B,MAAOI,GAAGK,OAAOH,GAGnB,OACEI,aAAcL,EACdM,cAAeN,MAKlBb,QAAQ,gBAAiB,SAAUoB,GAClCA,EAAcC,aAAaxB,KAAK,4BAMpC5C,EAAQE,OAAO,UAAUmE,KAAK,iBAAkB,SAASC,GACvDA,EAAeC,IAAI,mCAAoC,+lBAEtDvE","file":"angular-inform.min.js","sourceRoot":"./"} -------------------------------------------------------------------------------- /gulp-tasks/bower-task.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 4 | 5 | var path = require('path'); 6 | var clean = require('gulp-clean'); 7 | 8 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 9 | 10 | var bower = require('./lib/gulp-bower-power.js'); 11 | var config = require('../build-config.js'); 12 | 13 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 14 | 15 | module.exports = function(gulp) { 16 | gulp.task('bower-clean', function () { 17 | 18 | return gulp.src(path.join(config.folders.dest, 'vendor'), { read: false }) 19 | .pipe(clean({ force: true })); 20 | 21 | }); 22 | 23 | gulp.task('bower', ['bower-clean'], function () { 24 | 25 | return bower.src(gulp, { env: config.env, includeDev: config.bower && config.bower.includeDev }) 26 | .pipe(gulp.dest(path.join(config.folders.dest, 'vendor'))); 27 | 28 | }); 29 | }; 30 | 31 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 32 | -------------------------------------------------------------------------------- /gulp-tasks/index-task.js: -------------------------------------------------------------------------------- 1 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 2 | 3 | var config = require('../build-config.js'); 4 | var modify = require('./lib/gulp-modify-content.js'); 5 | var pkg = require('../package.json'); 6 | var _ = require('lodash'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | 10 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 11 | 12 | module.exports = function (gulp) { 13 | 14 | gulp.task('index', ['modules'], function () { 15 | 16 | return gulp.src('index.html', { 17 | cwd: config.folders.src 18 | }).pipe(modify(function (content) { 19 | 20 | var css = [], js = []; 21 | 22 | var ext = config.env === 'production' ? '.min' : ''; 23 | 24 | _.forEach(config.modules, function (module) { 25 | 26 | var moduleBase = module.name + '/' + module.name + ext; 27 | var moduleCss = moduleBase + '.css'; 28 | var moduleJs = moduleBase + '.js'; 29 | 30 | if(fs.existsSync(path.join(config.folders.dest, moduleCss))) { 31 | css.push(module.name + '/' + module.name + ext + '.css'); 32 | } 33 | 34 | if(fs.existsSync(path.join(config.folders.dest, moduleJs))) { 35 | js.push(module.name + '/' + module.name + ext + '.js'); 36 | } 37 | }); 38 | 39 | return _.template(content, { 40 | pkg: pkg, css: css, js: js 41 | }); 42 | 43 | })).pipe(gulp.dest(config.folders.dest)); 44 | 45 | }); 46 | }; 47 | 48 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 49 | -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-autoprefixer-map.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | Hack to enable source maps in with gulp-sourcemaps and autoprefixer. 5 | 6 | Created by null on 12/08/14. 7 | 8 | npm --save-dev install through2 9 | npm --save-dev install autoprefixer 10 | npm --save-dev install vinyl-sourcemaps-apply 11 | 12 | var prefixer = require('./gulp-autoprefixer-map.js'); 13 | 14 | gulp.task('css', [], function() { 15 | .pipe(sourcemaps.init()) 16 | .pipe(prefixer()) 17 | .pipe(sourcemaps.write('.')) 18 | }); 19 | 20 | */ 21 | 22 | var through = require('through2'); 23 | var prefix = require('autoprefixer'); 24 | //var applySourceMap = require('vinyl-sourcemaps-apply'); 25 | 26 | module.exports = function(browsers, options) { 27 | 28 | options = options || {}; 29 | 30 | return through.obj(function(file, enc, cb) { 31 | 32 | if(file.isStream()) { 33 | throw new Error('Streams not supported'); 34 | } 35 | 36 | if(!file.isNull()) { 37 | 38 | if(file.sourceMap) { 39 | options.map = { 40 | prev: file.sourceMap.mappings ? file.sourceMap : undefined, 41 | annotation: false, 42 | sourcesContent: true 43 | }; 44 | options.to = options.from = file.relative; 45 | } 46 | 47 | var contents = file.contents.toString(); 48 | 49 | var result = prefix.apply(this, browsers).process(contents, options); 50 | contents = result.css; 51 | 52 | file.contents = new Buffer(contents); 53 | 54 | if(file.sourceMap) { 55 | var map = JSON.parse(result.map.toString()); 56 | 57 | map.sources = file.sourceMap.sources; 58 | map.file = file.sourceMap.file; 59 | 60 | file.sourceMap = map; 61 | 62 | //applySourceMap(file, map); 63 | } 64 | } 65 | 66 | this.push(file); 67 | cb(); 68 | 69 | }); 70 | 71 | }; 72 | -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-bower-power.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | 6 | var mainBowerFiles = require('main-bower-files'); 7 | 8 | module.exports = { 9 | src: src 10 | }; 11 | 12 | function src(gulp, options) { 13 | 14 | options = options || {}; 15 | 16 | // Grab globs from main-bower-files module. 17 | // This sorts out the dependencies. 18 | 19 | var globs = mainBowerFiles(options); 20 | 21 | for(var i = 0; i < globs.length; i++) { 22 | 23 | var glob = globs[i]; 24 | 25 | var ext = path.extname(glob); 26 | 27 | // Check if the extension is JS or css 28 | if(ext === '.js' || ext === '.css') { 29 | 30 | if(glob.indexOf('.min') == -1) { 31 | // Add the minified version file name to the list 32 | var dirname = path.dirname(glob); 33 | var basename = path.basename(glob); 34 | 35 | var minified = basename.split('.'); 36 | minified.splice(minified.length - 1, 0, 'min'); 37 | minified = minified.join('.'); 38 | minified = path.join(dirname, minified); 39 | 40 | i += 1; 41 | globs.splice(i, 0, minified); 42 | 43 | // Add the source map file name for the minified version 44 | 45 | i += 1; 46 | globs.splice(i, 0, minified + '.map'); 47 | } 48 | 49 | // Add the source map file name for the unminified version 50 | 51 | i += 1; 52 | globs.splice(i, 0, glob + '.map'); 53 | 54 | } 55 | } 56 | 57 | return gulp.src(globs, options); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-csswring.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var through = require('through2'); 4 | var csswring = require('csswring'); 5 | var applySourceMap = require('vinyl-sourcemaps-apply'); 6 | 7 | module.exports = function(options) { 8 | 9 | options = options || {}; 10 | 11 | return through.obj(function(file, enc, cb) { 12 | 13 | if(file.isStream()) { 14 | throw new Error('Streams are not supported.'); 15 | } 16 | 17 | if (!file.isNull()) { 18 | 19 | if(file.sourceMap) { 20 | options.map = { 21 | prev: file.sourceMap.mappings ? file.sourceMap : undefined, 22 | annotation: false, 23 | sourcesContent: true 24 | }; 25 | options.from = 26 | options.to = file.relative; 27 | } 28 | 29 | var contents = file.contents.toString(); 30 | 31 | var result = csswring().wring(contents, options); 32 | 33 | file.contents = new Buffer(result.css); 34 | 35 | if(file.sourceMap) { 36 | var map = JSON.parse(result.map.toString()); 37 | 38 | map.sources = file.sourceMap.sources; 39 | map.file = file.sourceMap.file; 40 | 41 | // applySourceMap(file, map); 42 | file.sourceMap = map; 43 | } 44 | } 45 | 46 | this.push(file); 47 | cb(); 48 | 49 | }); 50 | }; -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-mini-filter.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var through = require('through2'); 4 | 5 | module.exports = function filter(fn) { 6 | return through.obj(function(file, enc, cb) { 7 | 8 | if(fn(file)) { 9 | this.push(file); 10 | } 11 | 12 | cb(); 13 | }); 14 | }; -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-modify-content.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var through = require('through2'); 4 | 5 | module.exports = function modify(modifiers) { 6 | 7 | return through.obj(function(file, encoding, done) { 8 | 9 | var stream = this; 10 | 11 | function applyModifiers(content) { 12 | (typeof modifiers === 'function' ? [modifiers] : modifiers).forEach(function(modifier) { 13 | content = modifier(content, file); 14 | }); 15 | return content; 16 | } 17 | 18 | function write(data) { 19 | file.contents = new Buffer(data); 20 | stream.push(file); 21 | return done(); 22 | } 23 | 24 | if (file.isBuffer()) { 25 | return write(applyModifiers(String(file.contents))); 26 | } else if (file.isStream()) { 27 | var buffer = ''; 28 | file.contents.on('data', function(chunk) { 29 | buffer += chunk; 30 | }); 31 | file.contents.on('end', function() { 32 | write(applyModifiers(String(buffer))); 33 | }); 34 | } 35 | }); 36 | }; -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-rename-filename.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | var through = require('through2'); 4 | 5 | function rename(filename) { 6 | return through.obj(function(file, enc, cb) { 7 | 8 | file.path = path.join(file.base, filename); 9 | 10 | if(file.sourceMap) { 11 | file.sourceMap.file = file.relative; 12 | } 13 | 14 | this.push(file); 15 | cb(); 16 | }); 17 | } 18 | 19 | module.exports = rename; -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-wrap-src.js: -------------------------------------------------------------------------------- 1 | var File = require('vinyl'); 2 | var through = require('through2'); 3 | 4 | // options = { 5 | // header: { 6 | // contents: '/* my header */', 7 | // path: 'my-virtual-header.js' 8 | // } || '/* my header */, 9 | // footer: { 10 | // contents: '/* my footer */', 11 | // path: 'my-virtual-footer.js' 12 | // } || '/* my footer */ 13 | // }; 14 | 15 | module.exports = function (options) { 16 | 17 | var pushedHeader = false; 18 | 19 | function pushHeader(file, enc, cb) { 20 | 21 | if (!pushedHeader) { 22 | pushedHeader = true; 23 | 24 | if (typeof(options.header) == 'string') { 25 | options.header = { 26 | contents: options.header 27 | }; 28 | } 29 | 30 | if (options.header.contents === undefined) { 31 | throw new Error('No header content provided.'); 32 | } 33 | 34 | var headerFile = new File({ 35 | path: options.header.path || 'header', 36 | contents: new Buffer(options.header.contents) 37 | }); 38 | 39 | this.push(headerFile); 40 | } 41 | 42 | this.push(file); 43 | cb(); 44 | 45 | } 46 | 47 | function pass(file, enc, cb) { 48 | this.push(file); 49 | cb(); 50 | } 51 | 52 | function pushFooter(cb) { 53 | 54 | if (typeof(options.footer) == 'string') { 55 | options.footer = { 56 | contents: options.footer 57 | }; 58 | } 59 | 60 | if (options.header.contents === undefined) { 61 | throw new Error('No header content provided.'); 62 | } 63 | 64 | var footerFile = new File({ 65 | path: options.footer.path || 'footer', 66 | contents: new Buffer(options.footer.contents) 67 | }); 68 | 69 | this.push(footerFile); 70 | 71 | cb(); 72 | } 73 | 74 | return through.obj( 75 | options.header ? pushHeader : pass, 76 | options.footer ? pushFooter : undefined 77 | ); 78 | }; -------------------------------------------------------------------------------- /gulp-tasks/lib/ng-xml.js: -------------------------------------------------------------------------------- 1 | // Optimised version of https://github.com/fraserxu/gulp-html2js 2 | 3 | var path = require('path'); 4 | var through = require('through2'); 5 | var File = require('vinyl'); 6 | 7 | var bsRegexp = new RegExp('\\\\', 'g'); 8 | var quoteRegexp = new RegExp('\\\"', 'g'); 9 | var nlReplace = '\\n\'' + ' +\n \''; 10 | 11 | function escape(content) { 12 | return content 13 | .replace(bsRegexp, '\\\\') 14 | .replace(quoteRegexp, '\\\"') 15 | .replace(/'/g, '\\\'') 16 | .replace(/\r?\n/g, nlReplace); 17 | } 18 | 19 | function templateCache(content, key) { 20 | return '$templateCache.put(\'' + key + '\', \'' + escape(content) + '\');' 21 | } 22 | 23 | function ensureSlashPath(p) { 24 | if (path.sep !== '/') { 25 | p = p.replace(/\\/g, '/') 26 | } 27 | return p; 28 | } 29 | 30 | /* 31 | options.base: base filepath of the template. This value is used as cache key. 32 | options.moduleName: the name of the module where to inject the templates. 33 | options.filename: [optional] target filename of the result. 34 | */ 35 | 36 | module.exports = function (options) { 37 | 38 | var buffer = []; 39 | 40 | function transform(file, encoding, callback) { 41 | 42 | var key = ensureSlashPath(path.relative(options.base, file.path)); 43 | var contents = templateCache(file.contents.toString(), key); 44 | 45 | buffer.push(' ' + contents); 46 | callback(); 47 | 48 | } 49 | 50 | function flush(callback) { 51 | 52 | var contents = 'angular.module(\'' + options.moduleName + '\').run([\'$templateCache\', function($templateCache){\n' + 53 | buffer.join('\n') + '\n}]);'; 54 | 55 | 56 | var file = new File({ 57 | path: options.filename || options.moduleName + '-templates.js', 58 | contents: new Buffer(contents) 59 | }); 60 | 61 | this.push(file); 62 | 63 | return callback(); 64 | } 65 | 66 | return through.obj(transform, flush); 67 | }; 68 | 69 | -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/clean-task.js: -------------------------------------------------------------------------------- 1 | 2 | var clean = require('gulp-clean'); 3 | 4 | module.exports = function(gulp, module) { 5 | 6 | module.task('clean', function () { 7 | 8 | return gulp.src(module.folders.dest, { read: false }) 9 | .pipe(clean({ force: true })); 10 | 11 | }); 12 | 13 | 14 | }; -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/copy-task.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | var filter = require('../lib/gulp-mini-filter.js'); 4 | 5 | module.exports = function(gulp, module) { 6 | 7 | module.task('copy', ['scripts', 'styles', 'svg'], function () { 8 | 9 | var glob = [ 10 | path.join(module.folders.src, '/**/*'), 11 | '!**/*.test.js', 12 | '!**/*.ignore.*' 13 | ]; 14 | 15 | module.touched.forEach(function (filename) { 16 | glob.push('!' + filename); 17 | }); 18 | 19 | return gulp.src(glob) 20 | .pipe(filter(function(file) { 21 | // Only copy files -- don't copy empty directories 22 | return file.stat.isFile(); 23 | })) 24 | .pipe(gulp.dest(module.folders.dest)); 25 | 26 | }); 27 | 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/scripts-task.js: -------------------------------------------------------------------------------- 1 | var sourcemaps = require('gulp-sourcemaps'); 2 | var concat = require('gulp-concat'); 3 | var ngAnnotate = require('gulp-ng-annotate'); 4 | var uglify = require('gulp-uglify'); 5 | var path = require('path'); 6 | 7 | var wrap = require('../lib/gulp-wrap-src.js'); 8 | var config = require('../../build-config.js'); 9 | var rename = require('../lib/gulp-rename-filename.js'); 10 | var filter = require('../lib/gulp-mini-filter.js'); 11 | 12 | module.exports = function (gulp, module) { 13 | 14 | module.task('scripts', ['clean', 'templates'], function () { 15 | 16 | // At the moment of writing, generating and consuming source maps isn't optimal. Compile tools merge previous 17 | // maps incorrectly, browsers aren't 100% certain about breakpoint locations and are unable to unmangle 18 | // argument names. The most stable seems to be to uglify and map in two stages: 19 | 20 | // 1. concat all js in one file and persist to the filesystem 21 | // 2. uglify the previous file and point point the content of the source map to the original file. 22 | 23 | var jsGlob = [ 24 | path.join(module.folders.src, module.name + '.js'), 25 | path.join(module.folders.src, '**/*.js'), 26 | path.join(module.folders.dest, module.name + '-templates.js'), 27 | '!**/*.test.js', 28 | '!**/*.ignore.js' 29 | ]; 30 | 31 | return gulp.src(jsGlob) 32 | .pipe(module.touch()) 33 | .pipe(wrap({ 34 | header: { 35 | path: module.name + '-header.js', 36 | contents: config.header + '(function(angular) {\n' 37 | }, 38 | footer: { 39 | path: module.name + '-footer.js', 40 | contents: '})(angular);' 41 | } 42 | })) 43 | .pipe(sourcemaps.init()) 44 | .pipe(concat(module.name + '.js')) 45 | .pipe(ngAnnotate()) 46 | .pipe(sourcemaps.write('.', { sourceRoot: '../src/' + module.name })) 47 | .pipe(gulp.dest(module.folders.dest)) 48 | 49 | // Create the minified version 50 | 51 | .pipe(sourcemaps.init()) 52 | .pipe(filter(function(file) { 53 | 54 | // Filter out the previous map file. 55 | return path.extname(file.path) != '.map'; 56 | 57 | })) 58 | .pipe(rename(module.name + '.min.js')) 59 | .pipe(uglify({ preserveComments: 'some' })) 60 | .pipe(sourcemaps.write('.', { includeContent: false, sourceRoot: './' })) 61 | .pipe(gulp.dest(module.folders.dest)); 62 | } 63 | ); 64 | }; 65 | 66 | -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/styles-task.js: -------------------------------------------------------------------------------- 1 | var sourcemaps = require('gulp-sourcemaps'); 2 | var concat = require('gulp-concat'); 3 | var minifyCss = require('gulp-minify-css'); 4 | var path = require('path'); 5 | 6 | var config = require('../../build-config.js'); 7 | 8 | var prefixer = require('../lib/gulp-autoprefixer-map.js'); 9 | var rename = require('../lib/gulp-rename-filename.js'); 10 | var filter = require('../lib/gulp-mini-filter.js'); 11 | var wrapper = require('../lib/gulp-wrap-src'); 12 | 13 | module.exports = function (gulp, module) { 14 | 15 | module.task('styles', 'clean', function () { 16 | 17 | var cssGlob = [ 18 | path.join(module.folders.src, module.name + '.css'), 19 | path.join(module.folders.src, '**/*.css'), 20 | '!**/*.ignore.css' 21 | ]; 22 | 23 | // Gulp plugins with support for sourcemaps with css are minimal and buggy. 24 | // I couldn't get any minifier to output a valid sourcemap in all situations. 25 | // The task here generates two stylesheets. 26 | // 27 | // 1. unminified + concat + autoprefixed + sourcemaps 28 | // 2. minified 29 | // 30 | // The minified version is based on the output generated by the 1st but does 31 | // not have a sourcemap associated with it. 32 | 33 | return gulp.src(cssGlob) 34 | 35 | .pipe(wrapper({ 36 | header: { 37 | path: module.name + '-header.css', 38 | contents: config.header 39 | } 40 | })) 41 | 42 | // Generate the unminified stylesheet 43 | 44 | .pipe(module.touch()) 45 | .pipe(sourcemaps.init()) 46 | .pipe(concat(module.name + '.css')) 47 | .pipe(prefixer(['last 2 versions', '> 1%', 'ie 8'])) 48 | .pipe(sourcemaps.write('.', { sourceRoot: '../src/' + module.name })) 49 | .pipe(gulp.dest(module.folders.dest)) 50 | 51 | // Generate the minified stylesheet 52 | 53 | .pipe(filter(function (file) { 54 | 55 | // delete the previous generated sourcemap so we don't trigger 56 | // sourcemap merging in csswring. 57 | 58 | delete file.sourceMap; 59 | 60 | // Filter out the previous map file. 61 | 62 | return path.extname(file.path) != '.map'; 63 | 64 | })) 65 | .pipe(minifyCss({ keepSpecialComments: 1 })) 66 | .pipe(rename(module.name + '.min.css')) 67 | .pipe(gulp.dest(module.folders.dest)); 68 | 69 | }); 70 | 71 | }; 72 | -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/svg-task.js: -------------------------------------------------------------------------------- 1 | 2 | var svgmin = require('gulp-svgmin'); 3 | var path = require('path'); 4 | 5 | module.exports = function(gulp, module) { 6 | module.task('svg', 'clean', function () { 7 | 8 | var glob = [ 9 | path.join(module.folders.src, '/**/*.svg'), 10 | '!**/*.ng.svg', 11 | '!**/*.ignore.svg' 12 | ]; 13 | 14 | return gulp.src(glob) 15 | .pipe(module.touch()) 16 | .pipe(svgmin()) 17 | .pipe(gulp.dest(module.folders.dest)); 18 | 19 | }); 20 | 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/templates-task.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * Created by null on 11/08/14. 4 | */ 5 | 6 | var minifyHtml = require('gulp-minify-html'); 7 | var svgmin = require('gulp-svgmin'); 8 | var merge = require('merge-stream'); 9 | 10 | var path = require('path'); 11 | 12 | var ngXml = require('../lib/ng-xml.js'); 13 | var modify = require('../lib/gulp-modify-content.js'); 14 | var config = require('../../build-config.js'); 15 | 16 | module.exports = function(gulp, module) { 17 | 18 | module.task('templates', 'clean', function () { 19 | 20 | var ngHtmlGlob = [ 21 | path.join(module.folders.src, '**/*.ng.html'), 22 | '!**/*.ignore.ng.html' 23 | ]; 24 | 25 | var ngHtmlStream = gulp.src(ngHtmlGlob) 26 | .pipe(module.touch()) 27 | .pipe(minifyHtml({ 28 | empty: true, 29 | spare: true, 30 | quotes: true 31 | })); 32 | 33 | var ngSvgGlob = [ 34 | path.join(module.folders.src, '/**/*.ng.svg'), 35 | '!**/*.ignore.ng.svg' 36 | ]; 37 | 38 | var ngSvgStream = gulp.src(ngSvgGlob) 39 | .pipe(module.touch()) 40 | .pipe(svgmin()); 41 | 42 | return merge(ngHtmlStream, ngSvgStream) 43 | .pipe(ngXml({ 44 | moduleName: module.alias || module.name, 45 | base: config.folders.src, 46 | filename: module.name + '-templates.js' 47 | })) 48 | .pipe(modify(function(contents) { 49 | return [ 50 | '// Automatically generated.', 51 | '// This file is already embedded in your main javascript output, there\'s no need to include this file', 52 | '// manually in the index.html. This file is only here for your debugging pleasures.', 53 | '' 54 | ].join('\n') + contents; 55 | })) 56 | .pipe(gulp.dest(module.folders.dest)); 57 | 58 | }); 59 | 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /gulp-tasks/modules-task.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var through = require('through2'); 3 | var config = require('../build-config.js'); 4 | 5 | module.exports = function (gulp) { 6 | 7 | var moduleTasks = []; 8 | 9 | for (var i = 0; i < config.modules.length; i++) (function (module) { 10 | 11 | moduleTasks.push(module.name); 12 | 13 | module.folders = { 14 | src: path.join(config.folders.src, module.name), 15 | dest: path.join(config.folders.dest, module.name) 16 | }; 17 | 18 | // We'll track the tasks that are created for this module so that we can one gulp task that is dependent on this list. 19 | 20 | module.tasks = []; 21 | module.task = moduleTask; 22 | 23 | // Filenames of every file we'll process in some way is kept in this array. Everything not touched is copied to the 24 | // destination directory at the end (copy-task). 25 | 26 | module.touched = []; 27 | module.touch = moduleTouchFile; 28 | 29 | // Register all tasks for the current module. 30 | 31 | require('./module-tasks/clean-task.js')(gulp, module); 32 | require('./module-tasks/styles-task.js')(gulp, module); 33 | require('./module-tasks/templates-task.js')(gulp, module); 34 | require('./module-tasks/scripts-task.js')(gulp, module); 35 | require('./module-tasks/svg-task.js')(gulp, module); 36 | require('./module-tasks/copy-task.js')(gulp, module); 37 | 38 | // Register a task for this module that is dependent on all sub tasks. 39 | 40 | gulp.task(module.name, module.tasks); 41 | 42 | })(config.modules[i]); 43 | 44 | // Register the main task that is dependent on all module tasks 45 | 46 | gulp.task('modules', moduleTasks); 47 | 48 | // - - - - - - - 8-< - - - - - - - - - - - - - - - - - - - - 49 | 50 | function moduleTask(taskname, depsOrFn, fn) { 51 | 52 | var module = this; 53 | 54 | if (typeof(depsOrFn) == 'string') { 55 | depsOrFn = [depsOrFn]; 56 | } 57 | 58 | if (Array.isArray(depsOrFn)) { 59 | 60 | var map = []; 61 | 62 | depsOrFn.forEach(function (dep) { 63 | map.push(module.name + '-' + dep); 64 | }); 65 | 66 | depsOrFn = map; 67 | 68 | } 69 | 70 | var fulltaskname = module.name + '-' + taskname; 71 | gulp.task(fulltaskname, depsOrFn, fn); 72 | module.tasks.push(fulltaskname); 73 | 74 | } 75 | 76 | // - - - - - - - 8-< - - - - - - - - - - - - - - - - - - - - 77 | 78 | function moduleTouchFile() { 79 | 80 | var module = this; 81 | 82 | return through.obj(function (file, enc, callback) { 83 | module.touched.push(file.path); 84 | this.push(file); 85 | return callback(); 86 | }); 87 | 88 | } 89 | 90 | // - - - - - - - 8-< - - - - - - - - - - - - - - - - - - - - 91 | }; 92 | 93 | -------------------------------------------------------------------------------- /gulp-tasks/visual-studio.targets: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | release 9 | debug 10 | c:\program files (x86)\nodejs\node.exe 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 4 | 5 | var config = require('./build-config.js'); 6 | 7 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 8 | 9 | var path = require('path'); 10 | var gulp = require('gulp'); 11 | var clean = require('gulp-clean'); 12 | var karma = require('gulp-karma'); 13 | 14 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 15 | 16 | require('./gulp-tasks/bower-task.js')(gulp); 17 | require('./gulp-tasks/modules-task.js')(gulp); 18 | require('./gulp-tasks/index-task.js')(gulp); 19 | 20 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 21 | // Builds the whole kitchensink including example website 22 | 23 | gulp.task('kitchensink', [ 'modules', 'bower', 'index', 'sandbox' ], function () { 24 | 25 | var path = require('path'); 26 | 27 | return gulp.src('README.md') 28 | .pipe(gulp.dest(path.join(config.folders.dest, 'app'))); 29 | 30 | }); 31 | 32 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 33 | 34 | gulp.task('sandbox-clean', [ 'modules', 'bower' ], function () { 35 | return gulp.src(path.join(config.folders.dest, 'sandbox')) 36 | .pipe(clean()); 37 | }); 38 | 39 | gulp.task('sandbox', [ 'sandbox-clean', 'modules', 'bower' ], function () { 40 | return gulp.src(path.join(config.folders.src, 'sandbox/**/*')) 41 | .pipe(gulp.dest(path.join(config.folders.dest, 'sandbox'))); 42 | }); 43 | // 44 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 45 | 46 | gulp.task('test-run', ['angular-inform'], function () { 47 | 48 | return gulp.src([ 49 | 'bower_components/angular/angular.js', 50 | 'bower_components/angular-mocks/angular-mocks.js', 51 | path.join(config.folders.dest, 'angular-inform/angular-inform.min.js'), 52 | path.join(config.folders.src, 'angular-inform/**/*.test.js') 53 | ]) 54 | .pipe(karma({ 55 | configFile: 'karma.conf.js', 56 | action: 'run' 57 | })).on('error', function (err) { 58 | throw err; 59 | }); 60 | 61 | }); 62 | 63 | gulp.task('test-watch', ['angular-inform'], function () { 64 | 65 | gulp.src([ 66 | 'bower_components/angular/angular.js', 67 | 'bower_components/angular-mocks/angular-mocks.js', 68 | path.join(config.folders.src, 'angular-inform/angular-inform.js'), 69 | path.join(config.folders.dest, 'angular-inform/angular-inform-templates.js'), 70 | path.join(config.folders.src, 'angular-inform/**/*.js') 71 | ]) 72 | .pipe(karma({ 73 | configFile: 'karma.conf.js', 74 | action: 'watch' 75 | })); 76 | 77 | }); 78 | 79 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 80 | 81 | gulp.task('dist-clean', ['angular-inform-clean'], function () { 82 | 83 | return gulp.src('dist/**/*', { read: false }).pipe(clean()); 84 | 85 | }); 86 | 87 | gulp.task('dist', ['dist-clean', 'angular-inform', 'test-run'], function () { 88 | 89 | var destGlob = path.join(config.folders.dest, 'angular-inform/**/*'); 90 | return gulp.src([ destGlob, 'README.md', '!**/angular-inform-templates.js' ]) 91 | .pipe(gulp.dest('dist')); 92 | 93 | }); 94 | 95 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 96 | 97 | gulp.task('default', [ 'dist' ]); 98 | 99 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Aug 14 2014 11:05:59 GMT+0200 (CEST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | ], 19 | 20 | 21 | // list of files to exclude 22 | exclude: [ 23 | ], 24 | 25 | 26 | // preprocess matching files before serving them to the browser 27 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 28 | preprocessors: { 29 | }, 30 | 31 | 32 | // test results reporter to use 33 | // possible values: 'dots', 'progress' 34 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 35 | reporters: ['dots', 'growl'], 36 | 37 | 38 | // web server port 39 | port: 9876, 40 | 41 | 42 | // enable / disable colors in the output (reporters and logs) 43 | colors: true, 44 | 45 | 46 | // level of logging 47 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 48 | logLevel: config.LOG_INFO, 49 | 50 | 51 | // enable / disable watching file and executing tests whenever any file changes 52 | autoWatch: false, 53 | 54 | 55 | // start these browsers 56 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 57 | browsers: ['PhantomJS'], 58 | 59 | 60 | // Continuous Integration mode 61 | // if true, Karma captures browsers, runs the tests and exits 62 | singleRun: false 63 | }); 64 | }; 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-inform", 3 | "version": "0.0.18", 4 | "description": "A growl-like easy-to-use message toaster for angular.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "homepage": "https://github.com/McNull/angular-inform", 10 | "author": "(null) McNull", 11 | "license": "MIT", 12 | "dependencies": {}, 13 | "devDependencies": { 14 | "gulp-clean": "~0.3.1", 15 | "main-bower-files": "~1.0.2", 16 | "gulp": "~3.8.7", 17 | "through2": "~0.5.1", 18 | "gulp-concat": "~2.3.4", 19 | "gulp-minify-css": "~0.3.7", 20 | "gulp-sourcemaps": "~1.1.1", 21 | "lodash": "~2.4.1", 22 | "gulp-uglify": "~0.3.1", 23 | "gulp-minify-html": "~0.1.4", 24 | "vinyl": "~0.3.2", 25 | "merge-stream": "~0.1.5", 26 | "gulp-svgmin": "~0.4.6", 27 | "gulp-ng-annotate": "~0.3.0", 28 | "autoprefixer": "~2.2.0", 29 | "karma": "~0.12.23", 30 | "gulp-karma": "0.0.4", 31 | "karma-growl-reporter": "~0.1.1", 32 | "karma-jasmine": "~0.1.5", 33 | "karma-phantomjs-launcher": "~0.1.4" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/angular-inform/angular-inform.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .inform-fixed { 4 | position: fixed; 5 | top: 20px; 6 | left: 20px; 7 | right: 20px; 8 | z-index: 1002; 9 | } 10 | 11 | .inform-fixed .inform-message { 12 | width: auto; 13 | } 14 | 15 | @media screen and (min-width: 480px) { 16 | .inform-fixed { 17 | left: auto; 18 | width: 440px; 19 | } 20 | 21 | .inform-fixed.inform-center { 22 | right: 0; 23 | left: 0; 24 | margin-left: auto; 25 | margin-right: auto; 26 | } 27 | } 28 | 29 | .inform-message { 30 | overflow: hidden; 31 | } 32 | 33 | .inform-message > button.close { 34 | margin-left: 15px; 35 | } 36 | 37 | .inform-shadow .inform-message { 38 | box-shadow: 0px 5px 20px 0px rgba(0, 0, 0, 0.25); 39 | } 40 | 41 | .inform-message.alert-primary { 42 | color: #FFF; 43 | background-color: #428BCA; 44 | } 45 | 46 | .inform-message.alert-default { 47 | background-color: #F5F5F5; 48 | border: 1px solid #CCC; 49 | color: #333; 50 | } 51 | 52 | 53 | .inform-message-wrap { 54 | /*overflow: hidden;*/ 55 | } 56 | .inform-animate .inform-message-wrap.ng-enter { 57 | animation: 1.0s inform-message-slide-in; 58 | } 59 | 60 | .inform-animate .inform-message-wrap.ng-leave { 61 | animation: 1.0s inform-message-fade-out; 62 | } 63 | 64 | .inform-animate .inform-badge.ng-enter { 65 | animation: inform-badge-scale 0.5s; 66 | } 67 | 68 | @keyframes inform-badge-scale { 69 | 0% { 70 | opacity: 0; 71 | transform: scale(0); 72 | } 73 | 25% { 74 | opacity: 1; 75 | transform: scale(1.5); 76 | } 77 | 100% { 78 | opacity: 1; 79 | transform: scale(1); 80 | } 81 | } 82 | 83 | @keyframes inform-message-slide-in { 84 | 0% { 85 | opacity: 0; 86 | transform: translateX(100%); 87 | max-height: 0px; 88 | } 89 | 50% { 90 | opacity: 0; 91 | transform: translateX(100%); 92 | max-height: 1000px; 93 | } 94 | 100% { 95 | opacity: 1; 96 | transform: translateX(0%); 97 | max-height: 1000px; 98 | } 99 | } 100 | 101 | @keyframes inform-message-fade-out { 102 | 0% { 103 | opacity: 1; 104 | max-height: 1000px; 105 | } 106 | 50% { 107 | opacity: 0; 108 | max-height: 1000px; 109 | } 110 | 100% { 111 | opacity: 0; 112 | max-height: 0px; 113 | } 114 | } -------------------------------------------------------------------------------- /src/angular-inform/angular-inform.js: -------------------------------------------------------------------------------- 1 | var inform = angular.module('inform', []); 2 | 3 | -------------------------------------------------------------------------------- /src/angular-inform/controller.js: -------------------------------------------------------------------------------- 1 | inform.controller('InformCtrl', function($scope, inform) { 2 | 3 | $scope.messages = inform.messages(); 4 | $scope.remove = inform.remove; 5 | $scope.cancelTimeout = inform.cancelTimeout; 6 | $scope.setTimeout = inform.setTimeout; 7 | 8 | }); 9 | -------------------------------------------------------------------------------- /src/angular-inform/directive.js: -------------------------------------------------------------------------------- 1 | inform.directive('inform', function () { 2 | return { 3 | restrict: 'AE', 4 | templateUrl: 'angular-inform/directive.ng.html', 5 | controller: 'InformCtrl' 6 | }; 7 | }); -------------------------------------------------------------------------------- /src/angular-inform/directive.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 11 |
12 |
-------------------------------------------------------------------------------- /src/angular-inform/exception/exception.js: -------------------------------------------------------------------------------- 1 | 2 | angular.module('inform-exception', ['inform']) 3 | .config(function($provide) { 4 | $provide.decorator('$exceptionHandler', ['$delegate', '$injector',function($delegate, $injector) { 5 | 6 | var inform; 7 | 8 | return function(exception, cause) { 9 | try { 10 | inform = inform || $injector.get('inform'); 11 | inform.add(exception.toString(), { type: 'danger', ttl: 0 }); 12 | } catch(ex) { 13 | console.log('$exceptionHandler', ex); 14 | } 15 | $delegate(exception, cause); 16 | }; 17 | }]); 18 | }); 19 | -------------------------------------------------------------------------------- /src/angular-inform/exception/exception.test.js: -------------------------------------------------------------------------------- 1 | describe('angular-inform', function () { 2 | 3 | describe('exception', function() { 4 | 5 | var inform, $exceptionHandler; 6 | 7 | beforeEach(function() { 8 | 9 | module('inform-exception'); 10 | 11 | inject(function(_inform_, _$exceptionHandler_) { 12 | 13 | inform = _inform_; 14 | $exceptionHandler = _$exceptionHandler_; 15 | 16 | }); 17 | 18 | }); 19 | 20 | it('should add an error message if a unhandled exception has occured', function() { 21 | 22 | expect(inform.messages().length).toBe(0); 23 | 24 | var error = new Error('This is a baddass error!'); 25 | 26 | try { 27 | $exceptionHandler(error); 28 | } 29 | catch(e) { 30 | if(e !== error) { 31 | throw e; 32 | } 33 | } 34 | finally { 35 | expect(inform.messages().length).toBe(1); 36 | expect(inform.messages()[0].content).toBe(error.toString()); 37 | } 38 | }); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /src/angular-inform/exception/http.js: -------------------------------------------------------------------------------- 1 | angular.module('inform-http-exception', ['inform']) 2 | 3 | .factory('informHttpInterceptor', function ($q, inform) { 4 | 5 | function interceptor(rejection) { 6 | try { 7 | var msg = 'Network error (' + rejection.status + '): ' + rejection.statusText; 8 | inform.add(msg, { type: 'danger', ttl: 0}); 9 | } catch(ex) { 10 | console.log('$httpProvider', ex); 11 | } 12 | 13 | return $q.reject(rejection); 14 | } 15 | 16 | return { 17 | requestError: interceptor, 18 | responseError: interceptor 19 | }; 20 | 21 | }) 22 | 23 | .config(function ($httpProvider) { 24 | $httpProvider.interceptors.push('informHttpInterceptor'); 25 | }); 26 | -------------------------------------------------------------------------------- /src/angular-inform/exception/http.test.js: -------------------------------------------------------------------------------- 1 | describe('angular-inform', function () { 2 | 3 | describe('informHttpInterceptor', function() { 4 | 5 | var inform, informHttpInterceptor; 6 | 7 | beforeEach(function() { 8 | 9 | module('inform-http-exception'); 10 | 11 | inject(function(_inform_, _informHttpInterceptor_) { 12 | 13 | inform = _inform_; 14 | informHttpInterceptor = _informHttpInterceptor_; 15 | 16 | }); 17 | 18 | }); 19 | 20 | it('should have the same methods for response and request', function() { 21 | // Just to safeguard the tests ... if this fails we'll need to test both functions. 22 | expect(informHttpInterceptor.responseError).toBe(informHttpInterceptor.requestError); 23 | }); 24 | 25 | it('should add an error message if a http exception has occured', function() { 26 | 27 | expect(inform.messages().length).toBe(0); 28 | 29 | var rejection = { 30 | status: 123, 31 | statusText: 'This is bad!' 32 | }; 33 | 34 | try { 35 | informHttpInterceptor.responseError(rejection); 36 | } 37 | catch(e) { 38 | if(e !== error) { 39 | throw e; 40 | } 41 | } 42 | finally { 43 | expect(inform.messages().length).toBe(1); 44 | var content = inform.messages()[0].content; 45 | expect(content.indexOf(rejection.status.toString())).not.toBe(-1); 46 | expect(content.indexOf(rejection.statusText.toString())).not.toBe(-1); 47 | } 48 | }); 49 | }); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /src/angular-inform/provider.js: -------------------------------------------------------------------------------- 1 | inform.provider('inform', function () { 2 | 3 | var provider = this; 4 | 5 | this._defaults = { 6 | type: 'default', 7 | ttl: 5000 8 | }; 9 | 10 | this.defaults = function (options) { 11 | provider._defaults = angular.extend(provider._defaults, options || {}); 12 | return provider._defaults; 13 | }; 14 | 15 | 16 | this.$get = ['$timeout', '$sce', function ($timeout, $sce) { 17 | 18 | var _messages = []; 19 | 20 | function _indexOf(predicate) { 21 | var i = _messages.length; 22 | 23 | while (i--) { 24 | if (predicate(_messages[i])) { 25 | return i; 26 | } 27 | } 28 | 29 | return -1; 30 | } 31 | 32 | function cancelTimeout(msg) { 33 | if (msg.timeout) { 34 | $timeout.cancel(msg.timeout); 35 | delete msg.timeout; 36 | } 37 | } 38 | 39 | function setTimeout(msg) { 40 | 41 | cancelTimeout(msg); 42 | 43 | if (msg.ttl > 0) { 44 | msg.timeout = $timeout(function () { 45 | remove(msg); 46 | }, msg.ttl); 47 | } 48 | } 49 | 50 | function add(content, options) { 51 | 52 | var msg = angular.extend({}, provider._defaults, options); 53 | 54 | if(!angular.isString(content)) { 55 | content = '
' + JSON.stringify(content, null, '  ') + '
'; 56 | msg.html = true; 57 | } 58 | 59 | var idx = _indexOf(function (x) { 60 | return x.content.toString() === content && x.type == msg.type; 61 | }); 62 | 63 | if (idx >= 0) { 64 | 65 | msg = _messages[idx]; 66 | msg.count += 1; 67 | 68 | } else { 69 | 70 | msg.content = content; 71 | 72 | if(msg.html) { 73 | msg.content = $sce.trustAsHtml(content); 74 | } 75 | 76 | msg.tickCount = +new Date(); 77 | msg.count = 1; 78 | 79 | _messages.push(msg); 80 | } 81 | 82 | setTimeout(msg); 83 | 84 | return msg; 85 | } 86 | 87 | function remove(msg) { 88 | 89 | var idx = _indexOf(function (x) { 90 | return x === msg; 91 | }); 92 | 93 | if (idx >= 0) { 94 | _messages.splice(idx, 1); 95 | cancelTimeout(msg); 96 | } 97 | } 98 | 99 | function clear() { 100 | _messages.length = 0; 101 | } 102 | 103 | return { 104 | messages: function () { 105 | return _messages; 106 | }, 107 | add: add, 108 | remove: remove, 109 | clear: clear, 110 | cancelTimeout: cancelTimeout, 111 | setTimeout: setTimeout 112 | }; 113 | }]; 114 | 115 | }); -------------------------------------------------------------------------------- /src/angular-inform/provider.test.js: -------------------------------------------------------------------------------- 1 | describe('angular-inform', function () { 2 | 3 | 4 | describe('informProvider', function () { 5 | 6 | // http://stackoverflow.com/questions/14771810/how-to-test-angularjs-custom-provider 7 | 8 | var provider; 9 | 10 | beforeEach(function () { 11 | 12 | angular.module('i.am.so.fake', []).config(function (informProvider) { 13 | provider = informProvider; 14 | }); 15 | 16 | module('inform', 'i.am.so.fake'); 17 | 18 | inject(function () { 19 | }); 20 | 21 | }); 22 | 23 | it('should resolve the informProvider', function () { 24 | expect(provider).not.toBeUndefined(); 25 | }); 26 | 27 | it('should return the default options', function () { 28 | 29 | var defaults = provider.defaults(); 30 | 31 | expect(defaults).not.toBeUndefined(); 32 | 33 | }); 34 | 35 | it('should extend the default options', function () { 36 | 37 | expect(provider.defaults().type).not.toBe('pannekoek'); // u nvr knw 38 | 39 | var defaults = provider.defaults({ 40 | type: 'pannekoek' 41 | }); 42 | 43 | expect(defaults.type).toBe('pannekoek'); 44 | }); 45 | 46 | }); 47 | 48 | describe('informService', function () { 49 | 50 | var service, $timeout; 51 | 52 | beforeEach(function () { 53 | module('inform'); 54 | 55 | //angular.copy(origDefaultOptions, defaultOptions); 56 | 57 | inject(function (inform, _$timeout_) { 58 | service = inform; 59 | $timeout = _$timeout_; 60 | }); 61 | }); 62 | 63 | it('should resolve the inform service', function () { 64 | expect(service).not.toBeUndefined(); 65 | }); 66 | 67 | it('should add a message', function () { 68 | 69 | expect(service.messages().length).toBe(0); 70 | 71 | service.add('simple message'); 72 | 73 | expect(service.messages().length).toBe(1); 74 | expect(service.messages()[0].content).toBe('simple message'); 75 | }); 76 | 77 | it('should add a message using the provided type', function () { 78 | service.add('simple message', { type: 'pannekoek' }); 79 | expect(service.messages()[0].type).toBe('pannekoek'); 80 | }); 81 | 82 | it('should remove the specified message', function () { 83 | expect(service.messages().length).toBe(0); 84 | var msg = service.add('kill me'); 85 | expect(service.messages().length).toBe(1); 86 | service.remove(msg); 87 | expect(service.messages().length).toBe(0); 88 | }); 89 | 90 | it('should remove the message after a timeout', function () { 91 | 92 | expect(service.messages().length).toBe(0); 93 | service.add('timeout me'); 94 | expect(service.messages().length).toBe(1); 95 | 96 | $timeout.flush(); 97 | 98 | expect(service.messages().length).toBe(0); 99 | 100 | }); 101 | 102 | it('should cancel a pending timeout when removing a msg', function () { 103 | 104 | var msg = service.add('timeout me'); 105 | expect(msg.timeout).toBeDefined(); 106 | 107 | service.remove(msg); 108 | 109 | expect(msg.timeout).toBeFalsy(); 110 | 111 | }); 112 | 113 | it('should clear all messages', function () { 114 | 115 | var c = 5; 116 | 117 | while (c--) { 118 | service.add('message ' + c); 119 | } 120 | 121 | service.clear(); 122 | 123 | expect(service.messages().length).toBe(0); 124 | 125 | }); 126 | 127 | it('should not display duplicate messages', function () { 128 | 129 | var c = 5; 130 | 131 | while (c--) { 132 | service.add('duplicate message'); 133 | } 134 | 135 | expect(service.messages().length).toBe(1); 136 | }); 137 | 138 | it('should allow duplicate messages of different kinds', function () { 139 | 140 | service.add('duplicate message', { type: 'success' }); 141 | service.add('duplicate message', { type: 'info' }); 142 | 143 | expect(service.messages().length).toBe(2); 144 | }); 145 | 146 | it('should return the same msg object for duplicates', function () { 147 | 148 | var msg1 = service.add('duplicate message'); 149 | var msg2 = service.add('duplicate message'); 150 | 151 | expect(msg1).toBe(msg2); 152 | 153 | }); 154 | 155 | it('should increase occurence count for duplicates', function () { 156 | 157 | var duplicate = service.add('duplicate message'); 158 | service.add('duplicate message'); 159 | service.add('duplicate message'); 160 | 161 | expect(duplicate.count).toBe(3); 162 | 163 | }); 164 | 165 | }); 166 | 167 | 168 | }); 169 | -------------------------------------------------------------------------------- /src/angular-showdown/angular-showdown.js: -------------------------------------------------------------------------------- 1 | angular.module('showdown', []); 2 | 3 | angular.module('showdown').directive('showdown', function(showdown, showdownConfig) { 4 | 5 | return { 6 | restrict: 'EA', 7 | link: function($scope, $element, $attrs) { 8 | 9 | var options = angular.copy(showdownConfig); 10 | 11 | options.outline = $attrs.showdownSrc ? $attrs.showdownSrc == 'true' : options.outline; 12 | options.src = $attrs.showdownSrc; 13 | 14 | $scope.$watch($attrs.showdownModel, function(value) { 15 | options.markdown = value; 16 | showdown.updateElement($element, options); 17 | }); 18 | } 19 | }; 20 | }); 21 | 22 | angular.module('showdown').provider('showdownConfig', function() { 23 | var config = { 24 | extensions: [], 25 | css: { 26 | loading: 'showdown-loading', 27 | error: 'showdown-error' 28 | }, 29 | outline: true 30 | }; 31 | this.$get = function() { 32 | return config; 33 | }; 34 | }); 35 | 36 | angular.module('showdown').factory('showdown', function($http) { 37 | 38 | var _converter; 39 | 40 | function getConverter(options) { 41 | 42 | // options: { extensions: ['twitter', mine] } 43 | // TODO: Locate (and create) converter based on extensions 44 | 45 | _converter = _converter || new Showdown.converter(options); 46 | 47 | return _converter; 48 | } 49 | 50 | function makeHtml(options) { 51 | 52 | var converter = getConverter(options); 53 | 54 | return converter.makeHtml(options.markdown || options.defaultMarkdown); 55 | } 56 | 57 | function outline(text) { 58 | if (text) { 59 | 60 | // trim leading empty lines 61 | 62 | text = text.replace(/^\s*\n/, ''); 63 | 64 | // grab the first ident on the first line 65 | 66 | var m = text.match(/^[ \t]+/); 67 | if (m && m.length) { 68 | 69 | // build a pattern to strip out the located ident from all lines 70 | 71 | var p = '^[ \t]{' + m[0].length + '}'; 72 | var r = new RegExp(p, 'gm'); 73 | text = text.replace(r, ''); 74 | } 75 | } 76 | 77 | return text; 78 | } 79 | 80 | function updateElement($element, options) { 81 | 82 | if (options.defaultMarkdown === undefined) { 83 | 84 | var t = $element.text(); 85 | 86 | if (options.outline) { 87 | t = outline(t); 88 | } 89 | 90 | options.defaultMarkdown = t; 91 | } 92 | 93 | if (options.src) { 94 | 95 | $element.addClass(options.css.loading); 96 | $element.removeClass(options.css.error); 97 | options.markdown = ''; 98 | 99 | $http.get(options.src).then(function(response) { 100 | options.markdown = response.data; 101 | $element.removeClass(options.css.error); 102 | })['catch'](function() { 103 | $element.addClass(options.css.error); 104 | })['finally'](function() { 105 | $element.removeClass(options.css.loading); 106 | options.src = undefined; 107 | updateElement($element, options); 108 | }); 109 | } else { 110 | var html = makeHtml(options); 111 | $element.html(html); 112 | } 113 | 114 | } 115 | 116 | return { 117 | makeHtml: makeHtml, 118 | updateElement: updateElement, 119 | outline: outline 120 | }; 121 | }); 122 | -------------------------------------------------------------------------------- /src/app/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | /*padding-top: 59px;*/ 3 | padding-bottom: 100px; 4 | overflow: hidden; 5 | } 6 | 7 | .container { 8 | max-width: 900px; 9 | min-width: 320px; 10 | } 11 | 12 | html { 13 | overflow-y: scroll; 14 | } -------------------------------------------------------------------------------- /src/app/app.js: -------------------------------------------------------------------------------- 1 | 2 | var app = angular.module('app', ['ngRoute', 'inform', 'inform-exception', 'inform-http-exception', 'showdown', 'ngAnimate']); 3 | 4 | app.config(function($routeProvider) { 5 | 6 | $routeProvider.when('/readme', { 7 | templateUrl: 'app/readme/readme.html' 8 | }); 9 | 10 | $routeProvider.when('/demo', { 11 | templateUrl: 'app/demo/demo.html' 12 | }); 13 | 14 | $routeProvider.otherwise({ 15 | redirectTo: '/demo' 16 | }); 17 | 18 | }); 19 | 20 | app.controller('MainCtrl', function($scope, $location) { 21 | 22 | // Keep track of the active url to highlite the nav-pill 23 | 24 | $scope.$watch(function() { 25 | return $location.path(); 26 | }, function(value) { 27 | $scope.activeUrl = value; 28 | }); 29 | 30 | }); -------------------------------------------------------------------------------- /src/app/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McNull/angular-inform/68e839ad6f9eef3be438cc7b864f4e4346fb00a0/src/app/assets/favicon.ico -------------------------------------------------------------------------------- /src/app/demo/demo.css: -------------------------------------------------------------------------------- 1 | .messages-form { 2 | margin-bottom: 15px; 3 | } -------------------------------------------------------------------------------- /src/app/demo/demo.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |

Javascript

6 | 7 |
8 |
9 |
10 | 11 |
12 | 13 | 14 | 17 | 18 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 | 31 |
32 | 33 |
34 | 35 | 43 |
44 | 45 | 46 |
47 | 48 |
49 | 50 |
51 | 52 | 53 |
54 | 55 |
56 | 57 |
{{ jsStatement() }}
58 | 59 |

Element

60 | 61 |
62 |
63 | 66 |
67 | 68 |
69 | 72 |
73 | 74 |
75 | 78 |
79 | 80 |
81 | 84 |
85 | 86 |
87 | 88 |
{{ htmlMarkup() }}
89 |
angular.module('myModule', ['inform', 'ngAnimate']);
90 | 91 |

Messages

92 | 93 |
95 | 96 |

Unhandled Exceptions

97 |
angular.module('myModule', ['inform', 'inform-exception']);
98 | 99 | 100 |

HTTP Exceptions

101 |
angular.module('myModule', ['inform', 'inform-http-exception']);
102 | 103 |
104 | 105 | 106 |
-------------------------------------------------------------------------------- /src/app/demo/demo.js: -------------------------------------------------------------------------------- 1 | app.controller('DemoController', function($scope, inform, $http) { 2 | 3 | if(inform.messages().length === 0) { 4 | 5 | //inform.add('
{{ msg | json }}
', { ttl: -1, type: 'success', html: true }); 6 | // 7 | // 8 | //inform.add('Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab amet, blanditiis debitis dolorem doloremque et facilis harum in iusto laborum minima molestiae nemo nisi non possimus, quisquam rerum tempore voluptates.', { ttl: -1, type: 'info' }); 9 | //inform.add('Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda atque, earum est id illum laboriosam maxime praesentium quisquam! Blanditiis cum deleniti eum impedit quasi. Eius eligendi eveniet ipsum natus quis.', { ttl: -1, type: 'warning' }); 10 | //inform.add('Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda deserunt dicta esse, ipsam molestiae nobis provident qui quidem quis repudiandae totam veniam voluptas voluptatum? A adipisci aut cumque earum pariatur?', { ttl: -1, type: 'danger' }); 11 | // 12 | //inform.add('

HTML content

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

Someone famous
', { ttl: -1, html: true }); 13 | //inform.add('

HTML content

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

Someone famous
', { ttl: -1, html: true }); 14 | } 15 | 16 | $scope.inform = inform; 17 | 18 | $scope.msg = { 19 | content: 'The message content', 20 | options: { 21 | ttl: 5000, 22 | type: 'default' 23 | } 24 | }; 25 | 26 | $scope.htmlMarkup = function() { 27 | 28 | var classes = []; 29 | 30 | if($scope.element.animations) { 31 | classes.push('inform-animate'); 32 | } 33 | 34 | if($scope.element.fixed) { 35 | classes.push('inform-fixed'); 36 | } 37 | 38 | if($scope.element.shadows) { 39 | classes.push('inform-shadow'); 40 | } 41 | 42 | if($scope.element.center) { 43 | classes.push('inform-center'); 44 | } 45 | 46 | var markup = '
3 |
4 |
-------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <% _.forEach(css, function(include) { %> 13 | 14 | <% 15 | }); %> 16 | 17 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 |

Angular Inform

30 |

A small growl-like easy-to-use message toaster for angular.

31 | 32 | 43 | 44 |
45 |
46 |
47 | 48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | <% _.forEach(js, function(include) { %> 56 | 57 | <% 58 | }); %> 59 | 60 | 61 | --------------------------------------------------------------------------------