├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bower.json ├── build └── angular-ui-notification.templates.js ├── demo ├── angular-csp.css ├── angular-ui-notification.min.css ├── angular-ui-notification.min.js ├── angular.min.js ├── bootstrap.min.css ├── custom_template.html ├── index.html ├── max_count.html ├── notification_config.html ├── notification_kill.html ├── notification_priority.html ├── notification_replace.html └── onclose.html ├── dist ├── angular-ui-notification.css └── angular-ui-notification.js ├── gulpfile.js ├── index.js ├── package.json ├── protractor_conf.js ├── src ├── angular-ui-notification.html ├── angular-ui-notification.js ├── angular-ui-notification.less └── build.less └── test └── e2e └── main.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | lib 27 | bower_components 28 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.2" 4 | 5 | before_script: 6 | - export DISPLAY=:99.0 7 | - sh -e /etc/init.d/xvfb start 8 | - npm install --quiet -g gulp 9 | - npm install --quiet -g http-server 10 | - npm install 11 | - npm run update-chromedriver 12 | - npm install --quiet -g bower 13 | - bower install 14 | - http-server ./demo/ & 15 | 16 | script: gulp 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Alexey Avramchik 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-ui-notification 2 | ======================= 3 | 4 | [![Dependency Status](https://david-dm.org/alexcrack/angular-ui-notification.png)](https://david-dm.org/alexcrack/angular-ui-notification) 5 | [![devDependency Status](https://david-dm.org/alexcrack/angular-ui-notification/dev-status.png)](https://david-dm.org/alexcrack/angular-ui-notification#info=devDependencies) 6 | [![Build Status](https://travis-ci.org/alexcrack/angular-ui-notification.svg?branch=master)](https://travis-ci.org/alexcrack/angular-ui-notification) 7 | [![Dependency Status](https://www.versioneye.com/user/projects/54f96af44f3108e7800000e4/badge.svg?style=flat)](https://www.versioneye.com/user/projects/54f96af44f3108e7800000e4) 8 | [![Code Climate](https://codeclimate.com/github/alexcrack/angular-ui-notification/badges/gpa.svg)](https://codeclimate.com/github/alexcrack/angular-ui-notification) 9 | 10 | Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animations 11 | 12 | ## Features 13 | * No dependencies except of angular.js. 14 | * CSS3 Animations. 15 | * Small size. 16 | * 5 message types. 17 | * Use HTML in your messages. 18 | * Configure options globally py the provider 19 | * Use custom options by the message 20 | * Use custom template 21 | 22 | ## Install 23 | 24 | To install the package using bower and save as a dependency use... 25 | ```bash 26 | bower install angular-ui-notification --save 27 | ``` 28 | 29 | To install via NPM: 30 | ```bash 31 | npm install angular-ui-notification --save 32 | ``` 33 | 34 | ## Usage 35 | [Heres a plunker demo](http://plnkr.co/edit/h08qQF2qlVE3arERpdfi?p=preview) 36 | 37 | 38 | In your html/template add 39 | ```html 40 | ... 41 | 42 | ... 43 | 44 | ... 45 | 46 | ``` 47 | 48 | In your application, declare dependency injection like so.. 49 | 50 | ```javascript 51 | angular.module('notificationTest', ['ui-notification']); 52 | ... 53 | ``` 54 | 55 | You can configure module by the provider 56 | ```javascript 57 | angular.module('notificationTest', ['ui-notification']) 58 | .config(function(NotificationProvider) { 59 | NotificationProvider.setOptions({ 60 | delay: 10000, 61 | startTop: 20, 62 | startRight: 10, 63 | verticalSpacing: 20, 64 | horizontalSpacing: 20, 65 | positionX: 'left', 66 | positionY: 'bottom' 67 | }); 68 | }); 69 | ... 70 | ``` 71 | 72 | 73 | And when you need to show notifications, inject service and call it! 74 | 75 | ```javascript 76 | angular.module('notificationTest').controller('notificationController', function($scope, Notification) { 77 | 78 | Notification.primary('Primary notification'); 79 | // or simply.. 80 | Notification('Primary notification'); 81 | 82 | // Other Options 83 | // Success 84 | Notification.success('Success notification'); 85 | 86 | // Message with custom type 87 | Notification({message: 'Warning notification'}, 'warning'); 88 | 89 | // With Title 90 | Notification({message: 'Primary notification', title: 'Primary notification'}); 91 | 92 | // Message with custom delay 93 | Notification.error({message: 'Error notification 1s', delay: 1000}); 94 | 95 | // Embed HTML within your message..... 96 | Notification.success({message: 'Success notification
Some other content
This is a link
', title: 'Html content'}); 97 | 98 | // Change position notification 99 | Notification.error({message: 'Error Bottom Right', positionY: 'bottom', positionX: 'right'}); 100 | 101 | // Replace message 102 | Notification.error({message: 'Error notification 1s', replaceMessage: true}); 103 | } 104 | ``` 105 | 106 | ## Service 107 | 108 | Module name: "ui-notification" 109 | 110 | Service: "Notification" 111 | 112 | Configuration provider: "NotificationProvider" 113 | 114 | 115 | ## Options 116 | 117 | Options can be passed to configuration provider globally or used in the current message. 118 | 119 | The options list: 120 | 121 | | Option | Possible values | Default value | Description | 122 | | ----------------- | ------------------------- | ------------------------------ | ------------------------------------------------------------------------ | 123 | | delay | Any integer value | 5000 | The time in ms the message is showing before start fading out | 124 | | startTop | Any integer value | 10 | Vertical padding between messages and vertical border of the browser | 125 | | startRight | Any integer value | 10 | Horizontal padding between messages and horizontal border of the browser | 126 | | verticalSpacing | Any integer value | 10 | Vertical spacing between messages | 127 | | horizontalSpacing | Any integer value | 10 | Horizontal spacing between messages | 128 | | positionX | "right", "left", "center" | "right" | Horizontal position of the message | 129 | | positionY | "top", "bottom" | "top" | Vertical position of the message | 130 | | replaceMessage | true, false | false | If true every next appearing message replace old messages | 131 | | templateUrl | Any string | "angular-ui-notification.html" | Custom template filename (URL) | 132 | | onClose | Any function | undefined | Callback to execute when a notification element is closed. Callback receives the element as its argument. | 133 | | closeOnClick | true, false | true | If true, messages are closed on click | 134 | | maxCount | Any integer | 0 | Show only [maxCount] last messages. Old messages will be killed. 0 - do not kill | 135 | | priority | Any integer | 10 | The highier the priority is, the higher the notification will be | 136 | 137 | Also you can pass the "scope" option. This is an angular scope option Notification scope will be inherited from. This option can be passed only in the methods. The default value is $rootScope 138 | 139 | ## Methods 140 | 141 | #### Notification service methods 142 | 143 | | Method name | Description | 144 | |----------------------------------------|-------------------------------------------------| 145 | | Notification(), Notification.primary() | Show the message with bootstrap's primary class | 146 | | Notification.info() | Show the message with bootstrap's info class | 147 | | Notification.success() | Show the message with bootstrap's success class | 148 | | Notification.warning() | Show the message with bootstrap's warn class | 149 | | Notification.error() | Show the message with bootstrap's danger class | 150 | | Notification.clearAll() | Remove all shown messages | 151 | 152 | #### Notification service options 153 | 154 | | Option | Possible values | Default value | Description | 155 | | -------------- | ------------------------------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------ | 156 | | title | *String* | `""` | Title to appear at the top of the notification | 157 | | message | *String* | `""` | Message to appear in the notification | 158 | | templateUrl | *String* | `"angular-ui-notification.html"` | URL of template to be used for notification | 159 | | delay | *Int* (?) | `5000` or configured global delay | Number of ms before notification fades out. If not an integer, notification will persist until killed. | 160 | | type | "primary", "info", "success", "warning", "error" | `"primary"` | Bootstrap flavoring | 161 | | positionY | "top", "bottom" | `"top"` | | 162 | | positionX | "right", "left", "center" | `"right" | | 163 | | replaceMessage | *Boolean* | `false` | If true this message will replace old(er) message(s) | 164 | | closeOnClick | true, false | true | If true, the message is closed on click | 165 | 166 | #### Returning value 167 | 168 | Every "show" method returns a promise that resolves a notification scope with these methods: 169 | 170 | | Method name | Description | 171 | |--------------------------------|------------------------------------------------------------------------------------------------------------------| 172 | | notificationScope.kill(isHard) | Remove the specific message
isHard - if false or omitted kill message with fadeout effect (default). If true - immediately remove the message| 173 | 174 | 175 | 176 | ## Custom Templates 177 | 178 | Custom template can be provided. 179 | 180 | ```html 181 |
182 |

183 |
184 |
185 | ``` 186 | Default existing scope values is "title" - the title of the message and "message". 187 | Also any custom scope's properties can be used. 188 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-notification", 3 | "version": "0.3.6", 4 | "homepage": "https://github.com/alexcrack/angular-ui-notification", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/jermorin/angular-ui-notification" 8 | }, 9 | "authors": [ 10 | "Alexey Avramchik" 11 | ], 12 | "description": "Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animating", 13 | "main": [ 14 | "dist/angular-ui-notification.css", 15 | "dist/angular-ui-notification.js" 16 | ], 17 | "keywords": [ 18 | "angular", 19 | "notification", 20 | "notify", 21 | "bootstrap" 22 | ], 23 | "license": "MIT", 24 | "ignore": [ 25 | "**/.*", 26 | "node_modules", 27 | "bower_components", 28 | "test", 29 | "tests", 30 | "demo", 31 | "build" 32 | ], 33 | "dependencies": { 34 | "angular": "^1.5.x" 35 | }, 36 | "devDependencies": { 37 | "bootstrap": "^3.3.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /build/angular-ui-notification.templates.js: -------------------------------------------------------------------------------- 1 | angular.module("ui-notification").run(["$templateCache", function($templateCache) {$templateCache.put("angular-ui-notification.html","

");}]); -------------------------------------------------------------------------------- /demo/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | 15 | .ng-animate-block-transitions { 16 | transition:0s all!important; 17 | -webkit-transition:0s all!important; 18 | } 19 | 20 | /* show the element during a show/hide animation when the 21 | * animation is ongoing, but the .ng-hide class is active */ 22 | .ng-hide-add-active, .ng-hide-remove { 23 | display: block!important; 24 | } 25 | -------------------------------------------------------------------------------- /demo/angular-ui-notification.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-ui-notification - Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animating 3 | * @author Alex_Crack 4 | * @version v0.3.6 5 | * @link https://github.com/alexcrack/angular-ui-notification 6 | * @license MIT 7 | */ 8 | .ui-notification{position:fixed;z-index:9999;width:300px;-webkit-transition:all ease .5s;-o-transition:all ease .5s;transition:all ease .5s;color:#fff;border-radius:0;background:#337ab7;box-shadow:5px 5px 10px rgba(0,0,0,.3)}.ui-notification.clickable{cursor:pointer}.ui-notification.clickable:hover{opacity:.7}.ui-notification.killed{-webkit-transition:opacity ease 1s;-o-transition:opacity ease 1s;transition:opacity ease 1s;opacity:0}.ui-notification>h3{font-size:14px;font-weight:700;display:block;margin:10px 10px 0;padding:0 0 5px;text-align:left;border-bottom:1px solid rgba(255,255,255,.3)}.ui-notification a{color:#fff}.ui-notification a:hover{text-decoration:underline}.ui-notification>.message{margin:10px}.ui-notification.warning{color:#fff;background:#f0ad4e}.ui-notification.error{color:#fff;background:#d9534f}.ui-notification.success{color:#fff;background:#5cb85c}.ui-notification.info{color:#fff;background:#5bc0de} -------------------------------------------------------------------------------- /demo/angular-ui-notification.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-ui-notification - Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animating 3 | * @author Alex_Crack 4 | * @version v0.3.6 5 | * @link https://github.com/alexcrack/angular-ui-notification 6 | * @license MIT 7 | */ 8 | angular.module("ui-notification",[]),angular.module("ui-notification").provider("Notification",function(){this.options={delay:5e3,startTop:10,startRight:10,verticalSpacing:10,horizontalSpacing:10,positionX:"right",positionY:"top",replaceMessage:!1,templateUrl:"angular-ui-notification.html",onClose:void 0,onClick:void 0,closeOnClick:!0,maxCount:0,container:"body",priority:10},this.setOptions=function(t){if(!angular.isObject(t))throw new Error("Options should be an object!");this.options=angular.extend({},this.options,t)},this.$get=["$timeout","$http","$compile","$templateCache","$rootScope","$injector","$sce","$q","$window",function(t,i,n,e,o,s,a,r,l){var c=this.options,p=c.startTop,u=c.startRight,d=c.verticalSpacing,f=c.horizontalSpacing,m=c.delay,g=[],h=!1,y=function(s,y){function C(i){function e(t){["-webkit-transition","-o-transition","transition"].forEach(function(i){C.css(i,t)})}var o=s.scope.$new();o.message=a.trustAsHtml(s.message),o.title=a.trustAsHtml(s.title),o.t=s.type.substr(0,1),o.delay=s.delay,o.onClose=s.onClose,o.onClick=s.onClick;var r=function(t,i){return t._priority-i._priority},m=function(t,i){return i._priority-t._priority},y=function(){var t=0,i=0,n=p,e=u,o=[];"top"===s.positionY?g.sort(r):"bottom"===s.positionY&&g.sort(m);for(var a=g.length-1;a>=0;a--){var l=g[a];if(s.replaceMessage&&awindow.innerHeight&&(C=p,i++,t=0);var k=n=C?0===t?C:C+d:p,v=e+i*(f+y);l.css(l._positionY,k+"px"),"center"===l._positionX?l.css("left",parseInt(window.innerWidth/2-y/2)+"px"):l.css(l._positionX,v+"px"),o[l._positionY+l._positionX]=k+h,c.maxCount>0&&g.length>c.maxCount&&0===a&&l.scope().kill(!0),t++}}},C=n(i)(o);C._positionY=s.positionY,C._positionX=s.positionX,C._priority=s.priority,C.addClass(s.type);var v=function(t){t=t.originalEvent||t,("click"===t.type||"opacity"===t.propertyName&&t.elapsedTime>=1)&&(o.onClose&&o.$apply(o.onClose(C)),"click"===t.type&&o.onClick&&o.$apply(o.onClick(C)),C.remove(),g.splice(g.indexOf(C),1),o.$destroy(),y())};s.closeOnClick&&(C.addClass("clickable"),C.bind("click",v)),C.bind("webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",v),angular.isNumber(s.delay)&&t(function(){C.addClass("killed")},s.delay),e("none"),angular.element(document.querySelector(s.container)).append(C);var _=-(parseInt(C[0].offsetHeight)+50);if(C.css(C._positionY,_+"px"),g.push(C),"center"==s.positionX){var w=parseInt(C[0].offsetWidth);C.css("left",parseInt(window.innerWidth/2-w/2)+"px")}t(function(){e("")}),o._templateElement=C,o.kill=function(i){i?(o.onClose&&o.$apply(o.onClose(o._templateElement)),g.splice(g.indexOf(o._templateElement),1),o._templateElement.remove(),o.$destroy(),t(y)):o._templateElement.addClass("killed")},t(y),h||(angular.element(l).bind("resize",function(i){t(y)}),h=!0),k.resolve(o)}var k=r.defer();"object"==typeof s&&null!==s||(s={message:s}),s.scope=s.scope?s.scope:o,s.template=s.templateUrl?s.templateUrl:c.templateUrl,s.delay=angular.isUndefined(s.delay)?m:s.delay,s.type=y||s.type||c.type||"",s.positionY=s.positionY?s.positionY:c.positionY,s.positionX=s.positionX?s.positionX:c.positionX,s.replaceMessage=s.replaceMessage?s.replaceMessage:c.replaceMessage,s.onClose=s.onClose?s.onClose:c.onClose,s.onClick=s.onClick?s.onClick:c.onClick,s.closeOnClick=null!==s.closeOnClick&&void 0!==s.closeOnClick?s.closeOnClick:c.closeOnClick,s.container=s.container?s.container:c.container,s.priority=s.priority?s.priority:c.priority;var v=e.get(s.template);return v?C(v):i.get(s.template,{cache:!0}).then(function(t){C(t.data)})["catch"](function(t){throw new Error("Template ("+s.template+") could not be loaded. "+t)}),k.promise};return y.primary=function(t){return this(t,"primary")},y.error=function(t){return this(t,"error")},y.success=function(t){return this(t,"success")},y.info=function(t){return this(t,"info")},y.warning=function(t){return this(t,"warning")},y.clearAll=function(){angular.forEach(g,function(t){t.addClass("killed")})},y}]}),angular.module("ui-notification").run(["$templateCache",function(t){t.put("angular-ui-notification.html",'

')}]); -------------------------------------------------------------------------------- /demo/angular.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.2.23 3 | (c) 2010-2014 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(Q,X,t){'use strict';function x(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.2.23/"+(b?b+"/":"")+a;for(c=1;c").append(b).html();try{return 3===b[0].nodeType?N(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+N(b)})}catch(d){return N(c)}}function dc(b){try{return decodeURIComponent(b)}catch(a){}}function ec(b){var a={},c,d;r((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,"%20").split("="),d=dc(c[0]),A(d)&&(b=A(c[1])?dc(c[1]):!0,ib.call(a,d)?H(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Cb(b){var a= 16 | [];r(b,function(b,d){H(b)?r(b,function(b){a.push(Ba(d,!0)+(!0===b?"":"="+Ba(b,!0)))}):a.push(Ba(d,!0)+(!0===b?"":"="+Ba(b,!0)))});return a.length?a.join("&"):""}function jb(b){return Ba(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Ba(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function Xc(b,a){function c(a){a&&d.push(a)}var d=[b],e,f,g=["ng:app","ng-app","x-ng-app", 17 | "data-ng-app"],k=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;r(g,function(a){g[a]=!0;c(X.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(r(b.querySelectorAll("."+a),c),r(b.querySelectorAll("."+a+"\\:"),c),r(b.querySelectorAll("["+a+"]"),c))});r(d,function(a){if(!e){var b=k.exec(" "+a.className+" ");b?(e=a,f=(b[2]||"").replace(/\s+/g,",")):r(a.attributes,function(b){!e&&g[b.name]&&(e=a,f=b.value)})}});e&&a(e,f?[f]:[])}function fc(b,a){var c=function(){b=u(b);if(b.injector()){var c=b[0]===X? 18 | "document":ha(b);throw Sa("btstrpd",c.replace(//,">"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");c=gc(a);c.invoke(["$rootScope","$rootElement","$compile","$injector","$animate",function(a,b,c,d,e){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(Q&&!d.test(Q.name))return c();Q.name=Q.name.replace(d,"");Ua.resumeBootstrap=function(b){r(b,function(b){a.push(b)});c()}}function kb(b,a){a= 19 | a||"_";return b.replace(Yc,function(b,d){return(d?a:"")+b.toLowerCase()})}function Db(b,a,c){if(!b)throw Sa("areq",a||"?",c||"required");return b}function Va(b,a,c){c&&H(b)&&(b=b[b.length-1]);Db(P(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function Ca(b,a){if("hasOwnProperty"===b)throw Sa("badname",a);}function hc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g "+e[1]+a.replace(me,"<$1>")+e[2];d.removeChild(d.firstChild);for(a=e[0];a--;)d=d.lastChild;a=0;for(e=d.childNodes.length;a=R?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Ka(b,a){var c=typeof b,d;"function"==c||"object"==c&&null!==b?"function"==typeof(d= 32 | b.$$hashKey)?d=b.$$hashKey():d===t&&(d=b.$$hashKey=(a||gb)()):d=b;return c+":"+d}function ab(b,a){if(a){var c=0;this.nextUid=function(){return++c}}r(b,this.put,this)}function sc(b){var a,c;"function"===typeof b?(a=b.$inject)||(a=[],b.length&&(c=b.toString().replace(pe,""),c=c.match(qe),r(c[1].split(re),function(b){b.replace(se,function(b,c,d){a.push(d)})})),b.$inject=a):H(b)?(c=b.length-1,Va(b[c],"fn"),a=b.slice(0,c)):Va(b,"fn",!0);return a}function gc(b){function a(a){return function(b,c){if(T(b))r(b, 33 | $b(a));else return a(b,c)}}function c(a,b){Ca(a,"service");if(P(b)||H(b))b=n.instantiate(b);if(!b.$get)throw bb("pget",a);return l[a+k]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[],c,d,f,k;r(a,function(a){if(!h.get(a)){h.put(a,!0);try{if(z(a))for(c=Xa(a),b=b.concat(e(c.requires)).concat(c._runBlocks),d=c._invokeQueue,f=0,k=d.length;f 4096 bytes)!"));else{if(m.cookie!==ea)for(ea=m.cookie, 41 | d=ea.split("; "),W={},f=0;fh&&this.remove(p.key),b},get:function(a){if(h").parent()[0])});var f=K(a,b,a,c,d,e);da(a,"ng-scope");return function(b,c,d,e){Db(b,"scope");var g=c?La.clone.call(a):a;r(d,function(a,b){g.data("$"+b+"Controller",a)});d=0;for(var m=g.length;d 52 | arguments.length&&(b=a,a=t);Ea&&(c=ea);return p(a,b,c)}var v,M,w,I,E,J,ea={},qb;v=c===f?d:ga(d,new Ob(u(f),d.$attr));M=v.$$element;if(K){var Na=/^\s*([@=&])(\??)\s*(\w*)\s*$/;J=e.$new(!0);!F||F!==K&&F!==K.$$originalDirective?M.data("$isolateScopeNoTemplate",J):M.data("$isolateScope",J);da(M,"ng-isolate-scope");r(K.scope,function(a,c){var d=a.match(Na)||[],f=d[3]||c,g="?"==d[2],d=d[1],m,l,n,p;J.$$isolateBindings[c]=d+f;switch(d){case "@":v.$observe(f,function(a){J[c]=a});v.$$observers[f].$$scope=e; 53 | v[f]&&(J[c]=b(v[f])(e));break;case "=":if(g&&!v[f])break;l=q(v[f]);p=l.literal?za:function(a,b){return a===b||a!==a&&b!==b};n=l.assign||function(){m=J[c]=l(e);throw ia("nonassign",v[f],K.name);};m=J[c]=l(e);J.$watch(function(){var a=l(e);p(a,J[c])||(p(a,m)?n(e,a=J[c]):J[c]=a);return m=a},null,l.literal);break;case "&":l=q(v[f]);J[c]=function(a){return l(e,a)};break;default:throw ia("iscp",K.name,c,a);}})}qb=p&&L;W&&r(W,function(a){var b={$scope:a===K||a.$$isolateScope?J:e,$element:M,$attrs:v,$transclude:qb}, 54 | c;E=a.controller;"@"==E&&(E=v[a.name]);c=s(E,b);ea[a.name]=c;Ea||M.data("$"+a.name+"Controller",c);a.controllerAs&&(b.$scope[a.controllerAs]=c)});g=0;for(w=m.length;gG.priority)break;if(U=G.scope)I=I||G,G.templateUrl||(N("new/isolated scope",K,G,x),T(U)&&(K=G));oa=G.name;!G.templateUrl&&G.controller&&(U=G.controller,W=W||{},N("'"+oa+"' controller",W[oa],G,x),W[oa]=G);if(U=G.transclude)D= 56 | !0,G.$$tlb||(N("transclusion",ca,G,x),ca=G),"element"==U?(Ea=!0,v=G.priority,U=x,x=d.$$element=u(X.createComment(" "+oa+": "+d[oa]+" ")),c=x[0],Na(f,Aa.call(U,0),c),S=w(U,e,v,g&&g.name,{nonTlbTranscludeDirective:ca})):(U=u(Kb(c)).contents(),x.empty(),S=w(U,e));if(G.template)if(B=!0,N("template",F,G,x),F=G,U=P(G.template)?G.template(x,d):G.template,U=Z(U),G.replace){g=G;U=Ib.test(U)?u(aa(U)):[];c=U[0];if(1!=U.length||1!==c.nodeType)throw ia("tplrt",oa,"");Na(f,x,c);pa={$attr:{}};U=ea(c,[],pa);var $= 57 | a.splice(Q+1,a.length-(Q+1));K&&pb(U);a=a.concat(U).concat($);A(d,pa);pa=a.length}else x.html(U);if(G.templateUrl)B=!0,N("template",F,G,x),F=G,G.replace&&(g=G),O=y(a.splice(Q,a.length-Q),x,d,f,D&&S,m,n,{controllerDirectives:W,newIsolateScopeDirective:K,templateDirective:F,nonTlbTranscludeDirective:ca}),pa=a.length;else if(G.compile)try{R=G.compile(x,d,S),P(R)?L(null,R,V,Y):R&&L(R.pre,R.post,V,Y)}catch(ba){l(ba,ha(x))}G.terminal&&(O.terminal=!0,v=Math.max(v,G.priority))}O.scope=I&&!0===I.scope;O.transcludeOnThisElement= 58 | D;O.templateOnThisElement=B;O.transclude=S;p.hasElementTranscludeDirective=Ea;return O}function pb(a){for(var b=0,c=a.length;bp.priority)&&-1!=p.restrict.indexOf(f)&&(q&&(p=bc(p,{$$start:q,$$end:n})),b.push(p),h=p)}catch(L){l(L)}}return h}function A(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;r(a,function(d,e){"$"!= 59 | e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,f){"class"==f?(da(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function y(a,b,c,d,e,f,g,m){var h=[],l,q,s=b[0],v=a.shift(),L=B({},v,{templateUrl:null,transclude:null,replace:null,$$originalDirective:v}),O=P(v.templateUrl)?v.templateUrl(b,c):v.templateUrl; 60 | b.empty();n.get(C.getTrustedResourceUrl(O),{cache:p}).success(function(n){var p,C;n=Z(n);if(v.replace){n=Ib.test(n)?u(aa(n)):[];p=n[0];if(1!=n.length||1!==p.nodeType)throw ia("tplrt",v.name,O);n={$attr:{}};Na(d,b,p);var w=ea(p,[],n);T(v.scope)&&pb(w);a=w.concat(a);A(c,n)}else p=s,b.html(n);a.unshift(L);l=F(a,p,c,e,b,v,f,g,m);r(d,function(a,c){a==p&&(d[c]=b[0])});for(q=K(b[0].childNodes,e);h.length;){n=h.shift();C=h.shift();var I=h.shift(),E=h.shift(),w=b[0];if(C!==s){var J=C.className;m.hasElementTranscludeDirective&& 61 | v.replace||(w=Kb(p));Na(I,u(C),w);da(u(w),J)}C=l.transcludeOnThisElement?W(n,l.transclude,E):E;l(q,n,w,d,C)}h=null}).error(function(a,b,c,d){throw ia("tpload",d.url);});return function(a,b,c,d,e){a=e;h?(h.push(b),h.push(c),h.push(d),h.push(a)):(l.transcludeOnThisElement&&(a=W(b,l.transclude,e)),l(q,b,c,d,a))}}function D(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.namea.status?d:n.reject(d)}var c={method:"get",transformRequest:e.transformRequest,transformResponse:e.transformResponse},d=function(a){var b=e.headers,c=B({},a.headers),d,f,b=B({},b.common,b[N(a.method)]); 71 | a:for(d in b){a=N(d);for(f in c)if(N(f)===a)continue a;c[d]=b[d]}(function(a){var b;r(a,function(c,d){P(c)&&(b=c(),null!=b?a[d]=b:delete a[d])})})(c);return c}(a);B(c,a);c.headers=d;c.method=Ia(c.method);var f=[function(a){d=a.headers;var c=xc(a.data,wc(d),a.transformRequest);D(c)&&r(d,function(a,b){"content-type"===N(b)&&delete d[b]});D(a.withCredentials)&&!D(e.withCredentials)&&(a.withCredentials=e.withCredentials);return s(a,c,d).then(b,b)},t],g=n.when(c);for(r(C,function(a){(a.request||a.requestError)&& 72 | f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var m=f.shift(),g=g.then(a,m)}g.success=function(a){g.then(function(b){a(b.data,b.status,b.headers,c)});return g};g.error=function(a){g.then(null,function(b){a(b.data,b.status,b.headers,c)});return g};return g}function s(c,f,g){function h(a,b,c,e){E&&(200<=a&&300>a?E.put(u,[a,b,vc(c),e]):E.remove(u));p(b,a,c,e);d.$$phase||d.$apply()}function p(a,b,d,e){b=Math.max(b,0);(200<= 73 | b&&300>b?C.resolve:C.reject)({data:a,status:b,headers:wc(d),config:c,statusText:e})}function s(){var a=Qa(q.pendingRequests,c);-1!==a&&q.pendingRequests.splice(a,1)}var C=n.defer(),r=C.promise,E,F,u=L(c.url,c.params);q.pendingRequests.push(c);r.then(s,s);!c.cache&&!e.cache||(!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method)||(E=T(c.cache)?c.cache:T(e.cache)?e.cache:v);if(E)if(F=E.get(u),A(F)){if(F&&P(F.then))return F.then(s,s),F;H(F)?p(F[1],F[0],ga(F[2]),F[3]):p(F,200,{},"OK")}else E.put(u,r);D(F)&& 74 | ((F=Pb(c.url)?b.cookies()[c.xsrfCookieName||e.xsrfCookieName]:t)&&(g[c.xsrfHeaderName||e.xsrfHeaderName]=F),a(c.method,u,f,h,g,c.timeout,c.withCredentials,c.responseType));return r}function L(a,b){if(!b)return a;var c=[];Tc(b,function(a,b){null===a||D(a)||(H(a)||(a=[a]),r(a,function(a){T(a)&&(sa(a)?a=a.toISOString():T(a)&&(a=ta(a)));c.push(Ba(b)+"="+Ba(a))}))});0=R&&(!b.match(/^(get|post|head|put|delete|options)$/i)|| 76 | !Q.XMLHttpRequest))return new Q.ActiveXObject("Microsoft.XMLHTTP");if(Q.XMLHttpRequest)return new Q.XMLHttpRequest;throw x("$httpBackend")("noxhr");}function Vd(){this.$get=["$browser","$window","$document",function(b,a,c){return we(b,ve,b.defer,a.angular.callbacks,c[0])}]}function we(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),g=null;f.type="text/javascript";f.src=a;f.async=!0;g=function(a){Za(f,"load",g);Za(f,"error",g);e.body.removeChild(f);f=null;var k=-1,s="unknown";a&&("load"!== 77 | a.type||d[b].called||(a={type:"error"}),s=a.type,k="error"===a.type?404:200);c&&c(k,s)};rb(f,"load",g);rb(f,"error",g);8>=R&&(f.onreadystatechange=function(){z(f.readyState)&&/loaded|complete/.test(f.readyState)&&(f.onreadystatechange=null,g({type:"load"}))});e.body.appendChild(f);return g}var g=-1;return function(e,m,h,l,n,p,q,s){function L(){C=g;I&&I();w&&w.abort()}function v(a,d,e,f,g){K&&c.cancel(K);I=w=null;0===d&&(d=e?200:"file"==ua(m).protocol?404:0);a(1223===d?204:d,e,f,g||"");b.$$completeOutstandingRequest(y)} 78 | var C;b.$$incOutstandingRequestCount();m=m||b.url();if("jsonp"==N(e)){var O="_"+(d.counter++).toString(36);d[O]=function(a){d[O].data=a;d[O].called=!0};var I=f(m.replace("JSON_CALLBACK","angular.callbacks."+O),O,function(a,b){v(l,a,d[O].data,"",b);d[O]=y})}else{var w=a(e);w.open(e,m,!0);r(n,function(a,b){A(a)&&w.setRequestHeader(b,a)});w.onreadystatechange=function(){if(w&&4==w.readyState){var a=null,b=null,c="";C!==g&&(a=w.getAllResponseHeaders(),b="response"in w?w.response:w.responseText);C===g&& 79 | 10>R||(c=w.statusText);v(l,C||w.status,b,a,c)}};q&&(w.withCredentials=!0);if(s)try{w.responseType=s}catch(da){if("json"!==s)throw da;}w.send(h||null)}if(0=k&&(n.resolve(q),l(p.$$intervalId),delete e[p.$$intervalId]);s||b.$apply()},g);e[p.$$intervalId]=n;return p}var e={};d.cancel= 82 | function(b){return b&&b.$$intervalId in e?(e[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),delete e[b.$$intervalId],!0):!1};return d}]}function bd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "), 83 | SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function Qb(b){b=b.split("/");for(var a=b.length;a--;)b[a]= 84 | jb(b[a]);return b.join("/")}function zc(b,a,c){b=ua(b,c);a.$$protocol=b.protocol;a.$$host=b.hostname;a.$$port=Z(b.port)||xe[b.protocol]||null}function Ac(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=ua(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=ec(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function qa(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function cb(b){var a= 85 | b.indexOf("#");return-1==a?b:b.substr(0,a)}function Rb(b){return b.substr(0,cb(b).lastIndexOf("/")+1)}function Bc(b,a){this.$$html5=!0;a=a||"";var c=Rb(b);zc(b,this,b);this.$$parse=function(a){var e=qa(c,a);if(!z(e))throw Sb("ipthprfx",a,c);Ac(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Cb(this.$$search),b=this.$$hash?"#"+jb(this.$$hash):"";this.$$url=Qb(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e; 86 | if((e=qa(b,d))!==t)return d=e,(e=qa(a,e))!==t?c+(qa("/",e)||e):b+d;if((e=qa(c,d))!==t)return c+e;if(c==d+"/")return c}}function Tb(b,a){var c=Rb(b);zc(b,this,b);this.$$parse=function(d){var e=qa(b,d)||qa(c,d),e="#"==e.charAt(0)?qa(a,e):this.$$html5?e:"";if(!z(e))throw Sb("ihshprfx",d,a);Ac(e,this,b);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Cb(this.$$search),e=this.$$hash? 87 | "#"+jb(this.$$hash):"";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(cb(b)==cb(a))return a}}function Ub(b,a){this.$$html5=!0;Tb.apply(this,arguments);var c=Rb(b);this.$$rewrite=function(d){var e;if(b==cb(d))return d;if(e=qa(c,d))return b+a+e;if(c===d+"/")return c};this.$$compose=function(){var c=Cb(this.$$search),e=this.$$hash?"#"+jb(this.$$hash):"";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function sb(b){return function(){return this[b]}} 88 | function Cc(b,a){return function(c){if(D(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Wd(){var b="",a=!1;this.hashPrefix=function(a){return A(a)?(b=a,this):b};this.html5Mode=function(b){return A(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,f){function g(a){c.$broadcast("$locationChangeSuccess",k.absUrl(),a)}var k,m,h=d.baseHref(),l=d.url(),n;a?(n=l.substring(0,l.indexOf("/",l.indexOf("//")+2))+(h||"/"),m=e.history?Bc:Ub):(n= 89 | cb(l),m=Tb);k=new m(n,"#"+b);k.$$parse(k.$$rewrite(l));var p=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var e=u(a.target);"a"!==N(e[0].nodeName);)if(e[0]===f[0]||!(e=e.parent())[0])return;var g=e.prop("href");T(g)&&"[object SVGAnimatedString]"===g.toString()&&(g=ua(g.animVal).href);if(!p.test(g)){if(m===Ub){var h=e.attr("href")||e.attr("xlink:href");if(h&&0>h.indexOf("://"))if(g="#"+b,"/"==h[0])g=n+g+h;else if("#"==h[0])g=n+g+(k.path()||"/")+h; 90 | else{var l=k.path().split("/"),h=h.split("/");2!==l.length||l[1]||(l.length=1);for(var q=0;qe?Dc(d[0],d[1],d[2],d[3],d[4],c,a):function(b,f){var g=0,k;do k=Dc(d[g++],d[g++],d[g++],d[g++],d[g++],c,a)(b,f),f=t,b=k;while(ga)for(b in h++,e)e.hasOwnProperty(b)&&!d.hasOwnProperty(b)&&(r--,delete e[b])}else e!==d&&(e=d,h++);return h},function(){n?(n=!1,b(d,d,c)):b(d,g,c);if(k)if(T(d))if(fb(d)){g=Array(d.length);for(var a=0;at&&(u=4-t,W[u]||(W[u]=[]),J=P(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):d.exp, 110 | J+="; newVal: "+ta(f)+"; oldVal: "+ta(g),W[u].push(J));else if(d===c){w=!1;break a}}catch(A){p.$$phase=null,e(A)}if(!(k=K.$$childHead||K!==this&&K.$$nextSibling))for(;K!==this&&!(k=K.$$nextSibling);)K=K.$parent}while(K=k);if((w||h.length)&&!t--)throw p.$$phase=null,a("infdig",b,ta(W));}while(w||h.length);for(p.$$phase=null;l.length;)try{l.shift()()}catch(x){e(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this!==p&&(r(this.$$listenerCount, 111 | Bb(null,l,this)),a.$$childHead==this&&(a.$$childHead=this.$$nextSibling),a.$$childTail==this&&(a.$$childTail=this.$$prevSibling),this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling),this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling),this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=null,this.$$listeners={},this.$$watchers=this.$$asyncQueue=this.$$postDigestQueue=[],this.$destroy=this.$digest=this.$apply=y,this.$on= 112 | this.$watch=function(){return y})}},$eval:function(a,b){return f(a)(this,b)},$evalAsync:function(a){p.$$phase||p.$$asyncQueue.length||g.defer(function(){p.$$asyncQueue.length&&p.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return m("$apply"),this.$eval(a)}catch(b){e(b)}finally{p.$$phase=null;try{p.$digest()}catch(c){throw e(c),c;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]= 113 | c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){c[Qa(c,b)]=null;l(e,1,a)}},$emit:function(a,b){var c=[],d,f=this,g=!1,k={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){k.defaultPrevented=!0},defaultPrevented:!1},h=[k].concat(Aa.call(arguments,1)),m,l;do{d=f.$$listeners[a]||c;k.currentScope=f;m=0;for(l=d.length;mc.msieDocumentMode)throw wa("iequirks");var e=ga(fa);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b}, 120 | e.valueOf=Ga);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,d(a,c))}};var f=e.parseAs,g=e.getTrusted,k=e.trustAs;r(fa,function(a,b){var c=N(b);e[Ya("parse_as_"+c)]=function(b){return f(a,b)};e[Ya("get_trusted_"+c)]=function(b){return g(a,b)};e[Ya("trust_as_"+c)]=function(b){return k(a,b)}});return e}]}function ce(){this.$get=["$window","$document",function(b,a){var c={},d=Z((/android (\d+)/.exec(N((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator|| 121 | {}).userAgent),f=a[0]||{},g=f.documentMode,k,m=/^(Moz|webkit|O|ms)(?=[A-Z])/,h=f.body&&f.body.style,l=!1,n=!1;if(h){for(var p in h)if(l=m.exec(p)){k=l[0];k=k.substr(0,1).toUpperCase()+k.substr(1);break}k||(k="WebkitOpacity"in h&&"webkit");l=!!("transition"in h||k+"Transition"in h);n=!!("animation"in h||k+"Animation"in h);!d||l&&n||(l=z(f.body.style.webkitTransition),n=z(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!g||7< 122 | g),hasEvent:function(a){if("input"==a&&9==R)return!1;if(D(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Wa(),vendorPrefix:k,transitions:l,animations:n,android:d,msie:R,msieDocumentMode:g}}]}function ee(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,k,m){var h=c.defer(),l=h.promise,n=A(m)&&!m;k=a.defer(function(){try{h.resolve(e())}catch(a){h.reject(a),d(a)}finally{delete f[l.$$timeoutId]}n||b.$apply()},k);l.$$timeoutId=k;f[k]=h; 123 | return l}var f={};e.cancel=function(b){return b&&b.$$timeoutId in f?(f[b.$$timeoutId].reject("canceled"),delete f[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function ua(b,a){var c=b;R&&(V.setAttribute("href",c),c=V.href);V.setAttribute("href",c);return{href:V.href,protocol:V.protocol?V.protocol.replace(/:$/,""):"",host:V.host,search:V.search?V.search.replace(/^\?/,""):"",hash:V.hash?V.hash.replace(/^#/,""):"",hostname:V.hostname,port:V.port,pathname:"/"===V.pathname.charAt(0)?V.pathname: 124 | "/"+V.pathname}}function Pb(b){b=z(b)?ua(b):b;return b.protocol===Hc.protocol&&b.host===Hc.host}function fe(){this.$get=$(Q)}function mc(b){function a(d,e){if(T(d)){var f={};r(d,function(b,c){f[c]=a(c,b)});return f}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Ic);a("date",Jc);a("filter",Ae);a("json",Be);a("limitTo",Ce);a("lowercase",De);a("number",Kc);a("orderBy",Lc);a("uppercase",Ee)}function Ae(){return function(b, 125 | a,c){if(!H(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;bb;b=Math.abs(b);var g=b+"",k="",m=[],h=!1;if(-1!==g.indexOf("e")){var l=g.match(/([\d\.]+)e(-?)(\d+)/);l&&"-"==l[2]&& 128 | l[3]>e+1?(g="0",b=0):(k=g,h=!0)}if(h)0b)&&(k=b.toFixed(e));else{g=(g.split(Nc)[1]||"").length;D(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);b=(""+b).split(Nc);g=b[0];b=b[1]||"";var l=0,n=a.lgSize,p=a.gSize;if(g.length>=n+p)for(l=g.length-n,h=0;hb&&(d="-",b=-b);for(b=""+b;b.length-c)e+=c;0===e&&-12==c&&(e=12);return Xb(e,a,d)}}function ub(b,a){return function(c,d){var e=c["get"+b](),f=Ia(a?"SHORT"+b:b);return d[f][e]}}function Jc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,k=b[8]?a.setUTCFullYear: 130 | a.setFullYear,m=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=Z(b[9]+b[10]),g=Z(b[9]+b[11]));k.call(a,Z(b[1]),Z(b[2])-1,Z(b[3]));f=Z(b[4]||0)-f;g=Z(b[5]||0)-g;k=Z(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));m.call(a,f,g,k,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var f="",g=[],k,m;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;z(c)&&(c=Fe.test(c)?Z(c):a(c));Ab(c)&&(c=new Date(c));if(!sa(c))return c; 131 | for(;e;)(m=Ge.exec(e))?(g=g.concat(Aa.call(m,1)),e=g.pop()):(g.push(e),e=null);r(g,function(a){k=He[a];f+=k?k(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return f}}function Be(){return function(b){return ta(b,!0)}}function Ce(){return function(b,a){if(!H(b)&&!z(b))return b;a=Infinity===Math.abs(Number(a))?Number(a):Z(a);if(z(b))return a?0<=a?b.slice(0,a):b.slice(a,b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0a||37<=a&&40>=a)||q()});if(e.hasEvent("paste"))a.on("paste cut",q)}a.on("change",n);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)};var s=c.ngPattern;s&&((e=s.match(/^\/(.*)\/([gim]*)$/))?(s=RegExp(e[1],e[2]),e=function(a){return ra(d, 138 | "pattern",d.$isEmpty(a)||s.test(a),a)}):e=function(c){var e=b.$eval(s);if(!e||!e.test)throw x("ngPattern")("noregexp",s,e,ha(a));return ra(d,"pattern",d.$isEmpty(c)||e.test(c),c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var r=Z(c.ngMinlength);e=function(a){return ra(d,"minlength",d.$isEmpty(a)||a.length>=r,a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var v=Z(c.ngMaxlength);e=function(a){return ra(d,"maxlength",d.$isEmpty(a)||a.length<=v,a)};d.$parsers.push(e); 139 | d.$formatters.push(e)}}function Yb(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;dR?function(b){b=b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?Ia(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var Wa=function(){if(A(Wa.isActive_))return Wa.isActive_;var b=!(!X.querySelector("[ng-csp]")&&!X.querySelector("[data-ng-csp]")); 143 | if(!b)try{new Function("")}catch(a){b=!0}return Wa.isActive_=b},Yc=/[A-Z]/g,ad={full:"1.2.23",major:1,minor:2,dot:23,codeName:"superficial-malady"};S.expando="ng339";var $a=S.cache={},ne=1,rb=Q.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},Za=Q.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)};S._data=function(b){return this.cache[b[this.expando]]||{}};var ie=/([\:\-\_]+(.))/g, 144 | je=/^moz([A-Z])/,Hb=x("jqLite"),ke=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Ib=/<|&#?\w+;/,le=/<([\w:]+)/,me=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ba={option:[1,'"],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ba.optgroup=ba.option;ba.tbody=ba.tfoot=ba.colgroup=ba.caption=ba.thead;ba.th= 145 | ba.td;var La=S.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===X.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),S(Q).on("load",a))},toString:function(){var b=[];r(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?u(this[b]):u(this[this.length+b])},length:0,push:Ke,sort:[].sort,splice:[].splice},ob={};r("multiple selected checked disabled readOnly required open".split(" "),function(b){ob[N(b)]=b});var rc={};r("input select option textarea button form details".split(" "), 146 | function(b){rc[Ia(b)]=!0});r({data:Mb,removeData:Lb},function(b,a){S[a]=b});r({data:Mb,inheritedData:nb,scope:function(b){return u.data(b,"$scope")||nb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return u.data(b,"$isolateScope")||u.data(b,"$isolateScopeNoTemplate")},controller:oc,injector:function(b){return nb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Nb,css:function(b,a,c){a=Ya(a);if(A(c))b.style[a]=c;else{var d;8>=R&&(d=b.currentStyle&&b.currentStyle[a], 147 | ""===d&&(d="auto"));d=d||b.style[a];8>=R&&(d=""===d?t:d);return d}},attr:function(b,a,c){var d=N(a);if(ob[d])if(A(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||y).specified?d:t;else if(A(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?t:b},prop:function(b,a,c){if(A(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];if(D(d))return e?b[e]:"";b[e]=d}var a=[];9>R?(a[1]= 148 | "innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(D(a)){if("SELECT"===Ma(b)&&b.multiple){var c=[];r(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(D(a))return b.innerHTML;for(var c=0,d=b.childNodes;c":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Pe={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Wb=function(a){this.options=a};Wb.prototype= 161 | {constructor:Wb,lex:function(a){this.text=a;this.index=0;this.ch=t;this.lastCh=":";for(this.tokens=[];this.index=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=A(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw ka("lexerr",a,c,this.text); 164 | },readNumber:function(){for(var a="",c=this.index;this.index","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(db.ZERO,a.fn,this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()): 175 | this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=Ec(d,this.options,this.text);return B(function(c,d,k){return e(k||a(c,d))},{assign:function(e,g,k){(k=a(e,k))||a.assign(e,k={});return tb(k,d,g,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return B(function(e,f){var g=a(e,f),k=d(e,f),m;ja(k,c.text);if(!g)return t;(g=Oa(g[k],c.text))&&(g.then&&c.options.unwrapPromises)&&(m=g,"$$v"in g||(m.$$v=t,m.then(function(a){m.$$v=a})),g= 176 | g.$$v);return g},{assign:function(e,f,g){var k=ja(d(e,g),c.text);(g=Oa(a(e,g),c.text))||a.assign(e,g={});return g[k]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this;return function(f,g){for(var k=[],m=c?c(f,g):f,h=0;ha.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Xb(Math[0=R&&(c.href||c.name||c.$set("href",""),a.append(X.createComment("IE fix")));if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){var f="[object SVGAnimatedString]"===ya.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}),Fb={};r(ob,function(a, 181 | c){if("multiple"!=a){var d=na("ng-"+c);Fb[d]=function(){return{priority:100,link:function(a,f,g){a.$watch(g[d],function(a){g.$set(c,!!a)})}}}}});r(["src","srcset","href"],function(a){var c=na("ng-"+a);Fb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,k=a;"href"===a&&"[object SVGAnimatedString]"===ya.call(e.prop("href"))&&(k="xlinkHref",f.$attr[k]="xlink:href",g=null);f.$observe(c,function(c){c?(f.$set(k,c),R&&g&&e.prop(g,f[k])):"href"===a&&f.$set(k,null)})}}}});var xb={$addControl:y, 182 | $removeControl:y,$setValidity:y,$setDirty:y,$setPristine:y};Oc.$inject=["$element","$attrs","$scope","$animate"];var Rc=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:Oc,compile:function(){return{pre:function(a,e,f,g){if(!f.action){var k=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};rb(e[0],"submit",k);e.on("$destroy",function(){c(function(){Za(e[0],"submit",k)},0,!1)})}var m=e.parent().controller("form"),h=f.name||f.ngForm;h&&tb(a, 183 | h,g,h);if(m)e.on("$destroy",function(){m.$removeControl(g);h&&tb(a,h,t,h);B(g,xb)})}}}}}]},ed=Rc(),rd=Rc(!0),Qe=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,Re=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,Se=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Sc={text:zb,number:function(a,c,d,e,f,g){zb(a,c,d,e,f,g);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||Se.test(a))return e.$setValidity("number",!0),""=== 184 | a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return t});Ie(e,"number",Te,null,e.$$validityState);e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=parseFloat(d.min);return ra(e,"min",e.$isEmpty(a)||a>=c,a)},e.$parsers.push(a),e.$formatters.push(a));d.max&&(a=function(a){var c=parseFloat(d.max);return ra(e,"max",e.$isEmpty(a)||a<=c,a)},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){return ra(e,"number",e.$isEmpty(a)||Ab(a),a)})}, 185 | url:function(a,c,d,e,f,g){zb(a,c,d,e,f,g);a=function(a){return ra(e,"url",e.$isEmpty(a)||Qe.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,f,g){zb(a,c,d,e,f,g);a=function(a){return ra(e,"email",e.$isEmpty(a)||Re.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){D(d.name)&&c.attr("name",gb());c.on("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value", 186 | e.$render)},checkbox:function(a,c,d,e){var f=d.ngTrueValue,g=d.ngFalseValue;z(f)||(f=!0);z(g)||(g=!1);c.on("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==f};e.$formatters.push(function(a){return a===f});e.$parsers.push(function(a){return a?f:g})},hidden:y,button:y,submit:y,reset:y,file:y},Te=["badInput"],jc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d, 187 | e,f,g){g&&(Sc[N(f.type)]||Sc.text)(d,e,f,g,c,a)}}}],wb="ng-valid",vb="ng-invalid",Pa="ng-pristine",yb="ng-dirty",Ue=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate",function(a,c,d,e,f,g){function k(a,c){c=c?"-"+kb(c,"-"):"";g.removeClass(e,(a?vb:wb)+c);g.addClass(e,(a?wb:vb)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var m=f(d.ngModel), 188 | h=m.assign;if(!h)throw x("ngModel")("nonassign",d.ngModel,ha(e));this.$render=y;this.$isEmpty=function(a){return D(a)||""===a||null===a||a!==a};var l=e.inheritedData("$formController")||xb,n=0,p=this.$error={};e.addClass(Pa);k(!0);this.$setValidity=function(a,c){p[a]!==!c&&(c?(p[a]&&n--,n||(k(!0),this.$valid=!0,this.$invalid=!1)):(k(!1),this.$invalid=!0,this.$valid=!1,n++),p[a]=!c,k(c,a),l.$setValidity(a,c,this))};this.$setPristine=function(){this.$dirty=!1;this.$pristine=!0;g.removeClass(e,yb);g.addClass(e, 189 | Pa)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,g.removeClass(e,Pa),g.addClass(e,yb),l.$setDirty());r(this.$parsers,function(a){d=a(d)});this.$modelValue!==d&&(this.$modelValue=d,h(a,d),r(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}}))};var q=this;a.$watch(function(){var c=m(a);if(q.$modelValue!==c){var d=q.$formatters,e=d.length;for(q.$modelValue=c;e--;)c=d[e](c);q.$viewValue!==c&&(q.$viewValue=c,q.$render())}return c})}],Gd= 190 | function(){return{require:["ngModel","^?form"],controller:Ue,link:function(a,c,d,e){var f=e[0],g=e[1]||xb;g.$addControl(f);a.$on("$destroy",function(){g.$removeControl(f)})}}},Id=$({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),kc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var f=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(f); 191 | e.$parsers.unshift(f);d.$observe("required",function(){f(e.$viewValue)})}}}},Hd=function(){return{require:"ngModel",link:function(a,c,d,e){var f=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!D(a)){var c=[];a&&r(a.split(f),function(a){a&&c.push(aa(a))});return c}});e.$formatters.push(function(a){return H(a)?a.join(", "):t});e.$isEmpty=function(a){return!a||!a.length}}}},Ve=/^(true|false|\d+)$/,Jd=function(){return{priority:100,compile:function(a,c){return Ve.test(c.ngValue)? 192 | function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},jd=xa({compile:function(a){a.addClass("ng-binding");return function(a,d,e){d.data("$binding",e.ngBind);a.$watch(e.ngBind,function(a){d.text(a==t?"":a)})}}}),ld=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],kd=["$sce","$parse",function(a,c){return{compile:function(d){d.addClass("ng-binding"); 193 | return function(d,f,g){f.data("$binding",g.ngBindHtml);var k=c(g.ngBindHtml);d.$watch(function(){return(k(d)||"").toString()},function(c){f.html(a.getTrustedHtml(k(d))||"")})}}}}],md=Yb("",!0),od=Yb("Odd",0),nd=Yb("Even",1),pd=xa({compile:function(a,c){c.$set("ngCloak",t);a.removeClass("ng-cloak")}}),qd=[function(){return{scope:!0,controller:"@",priority:500}}],lc={};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "), 194 | function(a){var c=na("ng-"+a);lc[c]=["$parse",function(d){return{compile:function(e,f){var g=d(f[c]);return function(c,d){d.on(N(a),function(a){c.$apply(function(){g(c,{$event:a})})})}}}}]});var td=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var k,m,h;c.$watch(e.ngIf,function(f){Ta(f)?m||(m=c.$new(),g(m,function(c){c[c.length++]=X.createComment(" end ngIf: "+e.ngIf+" ");k={clone:c};a.enter(c,d.parent(),d)})):(h&&(h.remove(), 195 | h=null),m&&(m.$destroy(),m=null),k&&(h=Eb(k.clone),a.leave(h,function(){h=null}),k=null))})}}}],ud=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,f){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:Ua.noop,compile:function(g,k){var m=k.ngInclude||k.src,h=k.onload||"",l=k.autoscroll;return function(g,k,q,r,L){var v=0,t,u,I,w=function(){u&&(u.remove(),u=null);t&&(t.$destroy(),t=null);I&&(e.leave(I,function(){u=null}),u=I,I=null)};g.$watch(f.parseAsResourceUrl(m), 196 | function(f){var m=function(){!A(l)||l&&!g.$eval(l)||d()},q=++v;f?(a.get(f,{cache:c}).success(function(a){if(q===v){var c=g.$new();r.template=a;a=L(c,function(a){w();e.enter(a,null,k,m)});t=c;I=a;t.$emit("$includeContentLoaded");g.$eval(h)}}).error(function(){q===v&&w()}),g.$emit("$includeContentRequested")):(w(),r.template=null)})}}}}],Kd=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){d.html(f.template);a(d.contents())(c)}}}],vd=xa({priority:450, 197 | compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),wd=xa({terminal:!0,priority:1E3}),xd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,f,g){var k=g.count,m=g.$attr.when&&f.attr(g.$attr.when),h=g.offset||0,l=e.$eval(m)||{},n={},p=c.startSymbol(),q=c.endSymbol(),s=/^when(Minus)?(.+)$/;r(g,function(a,c){s.test(c)&&(l[N(c.replace("when","").replace("Minus","-"))]=f.attr(g.$attr[c]))});r(l,function(a,e){n[e]=c(a.replace(d,p+k+"-"+h+q))});e.$watch(function(){var c= 198 | parseFloat(e.$eval(k));if(isNaN(c))return"";c in l||(c=a.pluralCat(c-h));return n[c](e,f,!0)},function(a){f.text(a)})}}}],yd=["$parse","$animate",function(a,c){var d=x("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,link:function(e,f,g,k,m){var h=g.ngRepeat,l=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),n,p,q,s,t,v,C={$id:Ka};if(!l)throw d("iexp",h);g=l[1];k=l[2];(l=l[3])?(n=a(l),p=function(a,c,d){v&&(C[v]=a);C[t]=c;C.$index=d;return n(e, 199 | C)}):(q=function(a,c){return Ka(c)},s=function(a){return a});l=g.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!l)throw d("iidexp",g);t=l[3]||l[1];v=l[2];var A={};e.$watchCollection(k,function(a){var g,k,l=f[0],n,C={},J,E,F,x,z,y,H=[];if(fb(a))z=a,n=p||q;else{n=p||s;z=[];for(F in a)a.hasOwnProperty(F)&&"$"!=F.charAt(0)&&z.push(F);z.sort()}J=z.length;k=H.length=z.length;for(g=0;gB;)t.pop().element.remove()}for(;z.length>M;)z.pop()[0].element.remove()}var h;if(!(h=s.match(d)))throw We("iexp",s,ha(f));var l=c(h[2]||h[1]),m=h[4]||h[6],n=h[5],p=c(h[3]||""),r=c(h[2]?h[1]:m),v=c(h[7]),u=h[8]?c(h[8]):null,z=[[{element:f,label:""}]];x&&(a(x)(e),x.removeClass("ng-scope"),x.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=v(e)||[],d={},h,l,p,s,w,x,y;if(q)for(l=[],s=0,x=z.length;s< 211 | x;s++)for(a=z[s],p=1,w=a.length;p@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}.ng-hide-add-active,.ng-hide-remove{display:block!important;}'); 215 | //# sourceMappingURL=angular.min.js.map 216 | -------------------------------------------------------------------------------- /demo/custom_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Notification demo (custom templates) 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Angular ui-notification demo (custom templates)

11 |
12 | 13 |
14 | 15 |
16 |

Custom templates

17 |
18 | 19 | 20 |
21 |
22 | 23 |
24 | 25 | 33 | 34 | 42 | 43 | 44 | 45 | 68 | 69 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Notification demo 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Angular ui-notification demo

11 |
12 | 13 |
14 |
15 |

Types of notifications

16 |
17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 |
26 |

Notification with title

27 |
28 | 29 |
30 |
31 | 32 |
33 |

Custom show delay

34 |
35 | 36 | 37 | 38 |
39 |
40 | 41 |
42 |

Using html tags within message and title

43 |
44 | 45 | 46 |
47 |
48 | 49 |
50 |

Change position notification

51 |
52 | 53 | 54 | 55 | 56 | 57 |
58 |
59 | 60 |
61 |

Custom template and scope

62 |
63 | 65 |
66 |
Clicks: {{nClicksLog.length}}
67 |
    68 |
  • {{$index + 1}}. {{l}}
  • 69 |
70 |
71 | 72 |
73 | 74 | 88 | 89 | 90 | 91 | 190 | 191 | -------------------------------------------------------------------------------- /demo/max_count.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Notification demo 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Angular ui-notification demo

11 |
12 | 13 |
14 |
15 |

Max count of notifications

16 |
17 | 18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 48 | 49 | -------------------------------------------------------------------------------- /demo/notification_config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Notification demo (custom configuration) 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Notification demo (custom configuration)

12 |
13 | 14 |
15 |
16 |

Types of notifications

17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 | 30 | 31 | 101 | 102 | -------------------------------------------------------------------------------- /demo/notification_kill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Notification demo (kill message) 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Angular ui-notification demo (kill message)

11 |
12 | 13 |
14 | 15 |
16 |

Kill message

17 |
18 | 19 | 20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 67 | 68 | -------------------------------------------------------------------------------- /demo/notification_priority.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Notification demo (custom configuration) 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Notification demo (custom configuration)

12 |
13 | 14 |
15 |
16 |

Types of notifications

17 |
18 | 19 | 20 | 21 |
22 |
23 |
24 | 25 | 26 | 27 | 69 | 70 | -------------------------------------------------------------------------------- /demo/notification_replace.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Notification demo (replace messages) 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Notification demo (replace messages)

12 |
13 | 14 |
15 |
16 |

Types of notifications

17 |
18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 |
26 | 27 | 28 | 29 | 69 | 70 | -------------------------------------------------------------------------------- /demo/onclose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Notification demo 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Angular ui-notification demo

11 |
12 | 13 |
14 |
15 |

Types of notifications

16 |
17 | 18 | 19 |
20 |
21 |
{{hasElementClosed}}
22 |
23 | 24 | 25 | 26 | 27 | 55 | 56 | -------------------------------------------------------------------------------- /dist/angular-ui-notification.css: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-ui-notification - Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animating 3 | * @author Alex_Crack 4 | * @version v0.3.6 5 | * @link https://github.com/alexcrack/angular-ui-notification 6 | * @license MIT 7 | */ 8 | .ui-notification 9 | { 10 | position: fixed; 11 | z-index: 9999; 12 | 13 | width: 300px; 14 | 15 | -webkit-transition: all ease .5s; 16 | -o-transition: all ease .5s; 17 | transition: all ease .5s; 18 | 19 | color: #fff; 20 | border-radius: 0; 21 | background: #337ab7; 22 | box-shadow: 5px 5px 10px rgba(0, 0, 0, .3); 23 | } 24 | .ui-notification.clickable 25 | { 26 | cursor: pointer; 27 | } 28 | .ui-notification.clickable:hover 29 | { 30 | opacity: .7; 31 | } 32 | .ui-notification.killed 33 | { 34 | -webkit-transition: opacity ease 1s; 35 | -o-transition: opacity ease 1s; 36 | transition: opacity ease 1s; 37 | 38 | opacity: 0; 39 | } 40 | .ui-notification > h3 41 | { 42 | font-size: 14px; 43 | font-weight: bold; 44 | 45 | display: block; 46 | 47 | margin: 10px 10px 0 10px; 48 | padding: 0 0 5px 0; 49 | 50 | text-align: left; 51 | 52 | border-bottom: 1px solid rgba(255, 255, 255, .3); 53 | } 54 | .ui-notification a 55 | { 56 | color: #fff; 57 | } 58 | .ui-notification a:hover 59 | { 60 | text-decoration: underline; 61 | } 62 | .ui-notification > .message 63 | { 64 | margin: 10px 10px 10px 10px; 65 | } 66 | .ui-notification.warning 67 | { 68 | color: #fff; 69 | background: #f0ad4e; 70 | } 71 | .ui-notification.error 72 | { 73 | color: #fff; 74 | background: #d9534f; 75 | } 76 | .ui-notification.success 77 | { 78 | color: #fff; 79 | background: #5cb85c; 80 | } 81 | .ui-notification.info 82 | { 83 | color: #fff; 84 | background: #5bc0de; 85 | } 86 | -------------------------------------------------------------------------------- /dist/angular-ui-notification.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-ui-notification - Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animating 3 | * @author Alex_Crack 4 | * @version v0.3.6 5 | * @link https://github.com/alexcrack/angular-ui-notification 6 | * @license MIT 7 | */ 8 | angular.module('ui-notification', []); 9 | 10 | angular.module('ui-notification').provider('Notification', function () { 11 | 12 | this.options = { 13 | delay: 5000, 14 | startTop: 10, 15 | startRight: 10, 16 | verticalSpacing: 10, 17 | horizontalSpacing: 10, 18 | positionX: 'right', 19 | positionY: 'top', 20 | replaceMessage: false, 21 | templateUrl: 'angular-ui-notification.html', 22 | onClose: undefined, 23 | onClick: undefined, 24 | closeOnClick: true, 25 | maxCount: 0, // 0 - Infinite 26 | container: 'body', 27 | priority: 10 28 | }; 29 | 30 | this.setOptions = function (options) { 31 | if (!angular.isObject(options)) throw new Error("Options should be an object!"); 32 | this.options = angular.extend({}, this.options, options); 33 | }; 34 | 35 | this.$get = ["$timeout", "$http", "$compile", "$templateCache", "$rootScope", "$injector", "$sce", "$q", "$window", function ($timeout, $http, $compile, $templateCache, $rootScope, $injector, $sce, $q, $window) { 36 | var options = this.options; 37 | 38 | var startTop = options.startTop; 39 | var startRight = options.startRight; 40 | var verticalSpacing = options.verticalSpacing; 41 | var horizontalSpacing = options.horizontalSpacing; 42 | var delay = options.delay; 43 | 44 | var messageElements = []; 45 | var isResizeBound = false; 46 | 47 | var notify = function (args, t) { 48 | var deferred = $q.defer(); 49 | 50 | if (typeof args !== 'object' || args === null) { 51 | args = {message: args}; 52 | } 53 | 54 | args.scope = args.scope ? args.scope : $rootScope; 55 | args.template = args.templateUrl ? args.templateUrl : options.templateUrl; 56 | args.delay = !angular.isUndefined(args.delay) ? args.delay : delay; 57 | args.type = t || args.type || options.type || ''; 58 | args.positionY = args.positionY ? args.positionY : options.positionY; 59 | args.positionX = args.positionX ? args.positionX : options.positionX; 60 | args.replaceMessage = args.replaceMessage ? args.replaceMessage : options.replaceMessage; 61 | args.onClose = args.onClose ? args.onClose : options.onClose; 62 | args.onClick = args.onClick ? args.onClick : options.onClick; 63 | args.closeOnClick = (args.closeOnClick !== null && args.closeOnClick !== undefined) ? args.closeOnClick : options.closeOnClick; 64 | args.container = args.container ? args.container : options.container; 65 | args.priority = args.priority ? args.priority : options.priority; 66 | 67 | var template = $templateCache.get(args.template); 68 | 69 | if (template) { 70 | processNotificationTemplate(template); 71 | } else { 72 | // load it via $http only if it isn't default template and template isn't exist in template cache 73 | // cache:true means cache it for later access. 74 | $http.get(args.template, {cache: true}) 75 | .then(function (response) { 76 | processNotificationTemplate(response.data); 77 | }) 78 | .catch(function (data) { 79 | throw new Error('Template (' + args.template + ') could not be loaded. ' + data); 80 | }); 81 | } 82 | 83 | 84 | function processNotificationTemplate(template) { 85 | 86 | var scope = args.scope.$new(); 87 | scope.message = $sce.trustAsHtml(args.message); 88 | scope.title = $sce.trustAsHtml(args.title); 89 | scope.t = args.type.substr(0, 1); 90 | scope.delay = args.delay; 91 | scope.onClose = args.onClose; 92 | scope.onClick = args.onClick; 93 | 94 | var priorityCompareTop = function (a, b) { 95 | return a._priority - b._priority; 96 | }; 97 | 98 | var priorityCompareBtm = function (a, b) { 99 | return b._priority - a._priority; 100 | }; 101 | 102 | var reposite = function () { 103 | var j = 0; 104 | var k = 0; 105 | var lastTop = startTop; 106 | var lastRight = startRight; 107 | var lastPosition = []; 108 | 109 | if (args.positionY === 'top') { 110 | messageElements.sort(priorityCompareTop); 111 | } else if (args.positionY === 'bottom') { 112 | messageElements.sort(priorityCompareBtm); 113 | } 114 | 115 | for (var i = messageElements.length - 1; i >= 0; i--) { 116 | var element = messageElements[i]; 117 | if (args.replaceMessage && i < messageElements.length - 1) { 118 | element.addClass('killed'); 119 | continue; 120 | } 121 | var elHeight = parseInt(element[0].offsetHeight); 122 | var elWidth = parseInt(element[0].offsetWidth); 123 | var position = lastPosition[element._positionY + element._positionX]; 124 | 125 | if ((top + elHeight) > window.innerHeight) { 126 | position = startTop; 127 | k++; 128 | j = 0; 129 | } 130 | 131 | var top = (lastTop = position ? (j === 0 ? position : position + verticalSpacing) : startTop); 132 | var right = lastRight + (k * (horizontalSpacing + elWidth)); 133 | 134 | element.css(element._positionY, top + 'px'); 135 | if (element._positionX === 'center') { 136 | element.css('left', parseInt(window.innerWidth / 2 - elWidth / 2) + 'px'); 137 | } else { 138 | element.css(element._positionX, right + 'px'); 139 | } 140 | 141 | lastPosition[element._positionY + element._positionX] = top + elHeight; 142 | 143 | if (options.maxCount > 0 && messageElements.length > options.maxCount && i === 0) { 144 | element.scope().kill(true); 145 | } 146 | 147 | j++; 148 | } 149 | }; 150 | 151 | var templateElement = $compile(template)(scope); 152 | templateElement._positionY = args.positionY; 153 | templateElement._positionX = args.positionX; 154 | templateElement._priority = args.priority; 155 | templateElement.addClass(args.type); 156 | 157 | var closeEvent = function (e) { 158 | e = e.originalEvent || e; 159 | if (e.type === 'click' || e.propertyName === 'opacity' && e.elapsedTime >= 1) { 160 | 161 | if (scope.onClose) { 162 | scope.$apply(scope.onClose(templateElement)); 163 | } 164 | 165 | if (e.type === 'click') 166 | if (scope.onClick) { 167 | scope.$apply(scope.onClick(templateElement)); 168 | } 169 | 170 | templateElement.remove(); 171 | messageElements.splice(messageElements.indexOf(templateElement), 1); 172 | scope.$destroy(); 173 | reposite(); 174 | } 175 | }; 176 | 177 | if (args.closeOnClick) { 178 | templateElement.addClass('clickable'); 179 | templateElement.bind('click', closeEvent); 180 | } 181 | 182 | templateElement.bind('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd', closeEvent); 183 | 184 | if (angular.isNumber(args.delay)) { 185 | $timeout(function () { 186 | templateElement.addClass('killed'); 187 | }, args.delay); 188 | } 189 | 190 | setCssTransitions('none'); 191 | 192 | angular.element(document.querySelector(args.container)).append(templateElement); 193 | var offset = -(parseInt(templateElement[0].offsetHeight) + 50); 194 | templateElement.css(templateElement._positionY, offset + "px"); 195 | messageElements.push(templateElement); 196 | 197 | if (args.positionX == 'center') { 198 | var elWidth = parseInt(templateElement[0].offsetWidth); 199 | templateElement.css('left', parseInt(window.innerWidth / 2 - elWidth / 2) + 'px'); 200 | } 201 | 202 | $timeout(function () { 203 | setCssTransitions(''); 204 | }); 205 | 206 | function setCssTransitions(value) { 207 | ['-webkit-transition', '-o-transition', 'transition'].forEach(function (prefix) { 208 | templateElement.css(prefix, value); 209 | }); 210 | } 211 | 212 | scope._templateElement = templateElement; 213 | 214 | scope.kill = function (isHard) { 215 | if (isHard) { 216 | if (scope.onClose) { 217 | scope.$apply(scope.onClose(scope._templateElement)); 218 | } 219 | 220 | messageElements.splice(messageElements.indexOf(scope._templateElement), 1); 221 | scope._templateElement.remove(); 222 | scope.$destroy(); 223 | $timeout(reposite); 224 | } else { 225 | scope._templateElement.addClass('killed'); 226 | } 227 | }; 228 | 229 | $timeout(reposite); 230 | 231 | if (!isResizeBound) { 232 | angular.element($window).bind('resize', function (e) { 233 | $timeout(reposite); 234 | }); 235 | isResizeBound = true; 236 | } 237 | 238 | deferred.resolve(scope); 239 | 240 | } 241 | 242 | return deferred.promise; 243 | }; 244 | 245 | notify.primary = function (args) { 246 | return this(args, 'primary'); 247 | }; 248 | notify.error = function (args) { 249 | return this(args, 'error'); 250 | }; 251 | notify.success = function (args) { 252 | return this(args, 'success'); 253 | }; 254 | notify.info = function (args) { 255 | return this(args, 'info'); 256 | }; 257 | notify.warning = function (args) { 258 | return this(args, 'warning'); 259 | }; 260 | 261 | notify.clearAll = function () { 262 | angular.forEach(messageElements, function (element) { 263 | element.addClass('killed'); 264 | }); 265 | }; 266 | 267 | return notify; 268 | }]; 269 | }); 270 | 271 | angular.module("ui-notification").run(["$templateCache", function($templateCache) {$templateCache.put("angular-ui-notification.html","

");}]); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var less = require('gulp-less'); 3 | var minifyCSS = require('gulp-minify-css'); 4 | var csscomb = require('gulp-csscomb'); 5 | var ngAnnotate = require('gulp-ng-annotate'); 6 | var uglify = require('gulp-uglify'); 7 | var jshint = require('gulp-jshint'); 8 | var rename = require('gulp-rename'); 9 | var header = require('gulp-header'); 10 | var templateCache = require('gulp-angular-templatecache'); 11 | var minifyHtml = require("gulp-minify-html"); 12 | var concat = require('gulp-concat'); 13 | var addsrc = require('gulp-add-src'); 14 | var order = require("gulp-order"); 15 | var protractor = require("gulp-protractor").protractor; 16 | 17 | var pkg = require('./package.json'); 18 | var banner = ['/**', 19 | ' * <%= pkg.name %> - <%= pkg.description %>', 20 | ' * @author <%= pkg.author %>', 21 | ' * @version v<%= pkg.version %>', 22 | ' * @link <%= pkg.homepage %>', 23 | ' * @license <%= pkg.license %>', 24 | ' */', 25 | ''].join('\n'); 26 | 27 | // ==== Styles 28 | gulp.task('styles', function() { 29 | gulp.src('src/build.less') 30 | .pipe(less({ 31 | strictMath: true 32 | })) 33 | .pipe(csscomb()) 34 | .pipe(header(banner, { pkg : pkg })) 35 | .pipe(rename({ 36 | basename: 'angular-ui-notification' 37 | })) 38 | .pipe(gulp.dest('dist')) 39 | .pipe(minifyCSS()) 40 | .pipe(rename({ 41 | suffix: '.min' 42 | })) 43 | .pipe(header(banner, { pkg : pkg })) 44 | .pipe(gulp.dest('dist')) 45 | .pipe(gulp.dest('demo')); 46 | }); 47 | 48 | // ====== Templates 49 | gulp.task('templates', function() { 50 | gulp.src(['*.html'], {cwd: 'src'}) 51 | .pipe(minifyHtml({ 52 | empty: true, 53 | spare: true, 54 | quotes: true 55 | })) 56 | .pipe(templateCache({ 57 | module: 'ui-notification' 58 | })) 59 | .pipe(rename('angular-ui-notification.templates.js')) 60 | .pipe(gulp.dest("build")); 61 | }); 62 | 63 | gulp.task('service', function() { 64 | gulp.src(['src/*.js']) 65 | .pipe(jshint()) 66 | .pipe(jshint.reporter('default')) 67 | .pipe(jshint.reporter('fail')) 68 | .pipe(ngAnnotate()) 69 | .pipe(addsrc('build/*.js')) 70 | .pipe(order([ 71 | 'src/*.js', 72 | 'build/angular-ui-notification.templates.js' 73 | ])) 74 | .pipe(concat('angular-ui-notification.js')) 75 | 76 | .pipe(header(banner, { pkg : pkg })) 77 | .pipe(gulp.dest('dist')) 78 | 79 | .pipe(uglify()) 80 | .pipe(rename({ 81 | suffix: '.min' 82 | })) 83 | .pipe(header(banner, { pkg : pkg })) 84 | .pipe(gulp.dest('dist')) 85 | .pipe(gulp.dest('demo')); 86 | }); 87 | 88 | // ====== 89 | gulp.task('e2eTest', function() { 90 | gulp.src(['./test/**/*_spec.js']) 91 | .pipe(protractor({ 92 | configFile: "protractor_conf.js" 93 | })) 94 | .on('error', function(e) {throw e}); 95 | }); 96 | 97 | gulp.task('tests', ['e2eTest']); 98 | gulp.task('build', ['templates', 'service', 'styles']); 99 | gulp.task('deploy', ['build', 'tests']); 100 | 101 | gulp.task('default', ['deploy'], function() {}); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by alex_crack on 20.11.15. 3 | */ 4 | require('./dist/angular-ui-notification.js'); 5 | module.exports = 'ui-notification'; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-notification", 3 | "version": "0.3.6", 4 | "description": "Angular.js service providing simple notifications using Bootstrap 3 styles with css transitions for animating", 5 | "main": "index.js", 6 | "scripts": { 7 | "update-chromedriver": "./node_modules/protractor/bin/webdriver-manager update", 8 | "test": "gulp tests" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/alexcrack/angular-ui-notification.git" 13 | }, 14 | "keywords": [ 15 | "angular", 16 | "notification", 17 | "notify", 18 | "bootstrap" 19 | ], 20 | "author": "Alex_Crack", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/alexcrack/angular-ui-notification/issues" 24 | }, 25 | "homepage": "https://github.com/alexcrack/angular-ui-notification", 26 | "devDependencies": { 27 | "chromedriver": "^2.14.1", 28 | "gulp": "^3.8.11", 29 | "gulp-add-src": "^0.2.0", 30 | "gulp-angular-templatecache": "^1.5.0", 31 | "gulp-concat": "^2.5.2", 32 | "gulp-csscomb": "^3.0.3", 33 | "gulp-header": "^1.2.2", 34 | "gulp-jshint": "^1.9.2", 35 | "gulp-less": "^3.0.1", 36 | "gulp-minify-css": "^0.5.1", 37 | "gulp-minify-html": "^1.0.0", 38 | "gulp-ng-annotate": "^0.5.2", 39 | "gulp-order": "^1.1.1", 40 | "gulp-protractor": "0.0.12", 41 | "gulp-rename": "^1.2.0", 42 | "gulp-uglify": "^1.1.0", 43 | "protractor": "^1.8.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /protractor_conf.js: -------------------------------------------------------------------------------- 1 | // An example configuration file. 2 | exports.config = { 3 | allScriptsTimeout: 99999, 4 | // Do not start a Selenium Standalone sever - only run this using chrome. 5 | //directConnect: true, 6 | //chromeDriver: './node_modules/protractor/selenium/chromedriver', 7 | 8 | seleniumArgs: ['-browserTimeout=60'], 9 | 10 | // Capabilities to be passed to the webdriver instance. 11 | capabilities: { 12 | 'browserName': 'firefox' 13 | }, 14 | 15 | // Spec patterns are relative to the current working directly when 16 | // protractor is called. 17 | specs: ['test/e2e/**/*.spec.js'], 18 | 19 | // Options to be passed to Jasmine-node. 20 | jasmineNodeOpts: { 21 | showColors: true, 22 | defaultTimeoutInterval: 30000, 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/angular-ui-notification.html: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 |
-------------------------------------------------------------------------------- /src/angular-ui-notification.js: -------------------------------------------------------------------------------- 1 | angular.module('ui-notification', []); 2 | 3 | angular.module('ui-notification').provider('Notification', function () { 4 | 5 | this.options = { 6 | delay: 5000, 7 | startTop: 10, 8 | startRight: 10, 9 | verticalSpacing: 10, 10 | horizontalSpacing: 10, 11 | positionX: 'right', 12 | positionY: 'top', 13 | replaceMessage: false, 14 | templateUrl: 'angular-ui-notification.html', 15 | onClose: undefined, 16 | onClick: undefined, 17 | closeOnClick: true, 18 | maxCount: 0, // 0 - Infinite 19 | container: 'body', 20 | priority: 10 21 | }; 22 | 23 | this.setOptions = function (options) { 24 | if (!angular.isObject(options)) throw new Error("Options should be an object!"); 25 | this.options = angular.extend({}, this.options, options); 26 | }; 27 | 28 | this.$get = function ($timeout, $http, $compile, $templateCache, $rootScope, $injector, $sce, $q, $window) { 29 | var options = this.options; 30 | 31 | var startTop = options.startTop; 32 | var startRight = options.startRight; 33 | var verticalSpacing = options.verticalSpacing; 34 | var horizontalSpacing = options.horizontalSpacing; 35 | var delay = options.delay; 36 | 37 | var messageElements = []; 38 | var isResizeBound = false; 39 | 40 | var notify = function (args, t) { 41 | var deferred = $q.defer(); 42 | 43 | if (typeof args !== 'object' || args === null) { 44 | args = {message: args}; 45 | } 46 | 47 | args.scope = args.scope ? args.scope : $rootScope; 48 | args.template = args.templateUrl ? args.templateUrl : options.templateUrl; 49 | args.delay = !angular.isUndefined(args.delay) ? args.delay : delay; 50 | args.type = t || args.type || options.type || ''; 51 | args.positionY = args.positionY ? args.positionY : options.positionY; 52 | args.positionX = args.positionX ? args.positionX : options.positionX; 53 | args.replaceMessage = args.replaceMessage ? args.replaceMessage : options.replaceMessage; 54 | args.onClose = args.onClose ? args.onClose : options.onClose; 55 | args.onClick = args.onClick ? args.onClick : options.onClick; 56 | args.closeOnClick = (args.closeOnClick !== null && args.closeOnClick !== undefined) ? args.closeOnClick : options.closeOnClick; 57 | args.container = args.container ? args.container : options.container; 58 | args.priority = args.priority ? args.priority : options.priority; 59 | 60 | var template = $templateCache.get(args.template); 61 | 62 | if (template) { 63 | processNotificationTemplate(template); 64 | } else { 65 | // load it via $http only if it isn't default template and template isn't exist in template cache 66 | // cache:true means cache it for later access. 67 | $http.get(args.template, {cache: true}) 68 | .then(function (response) { 69 | processNotificationTemplate(response.data); 70 | }) 71 | .catch(function (data) { 72 | throw new Error('Template (' + args.template + ') could not be loaded. ' + data); 73 | }); 74 | } 75 | 76 | 77 | function processNotificationTemplate(template) { 78 | 79 | var scope = args.scope.$new(); 80 | scope.message = $sce.trustAsHtml(args.message); 81 | scope.title = $sce.trustAsHtml(args.title); 82 | scope.t = args.type.substr(0, 1); 83 | scope.delay = args.delay; 84 | scope.onClose = args.onClose; 85 | scope.onClick = args.onClick; 86 | 87 | var priorityCompareTop = function (a, b) { 88 | return a._priority - b._priority; 89 | }; 90 | 91 | var priorityCompareBtm = function (a, b) { 92 | return b._priority - a._priority; 93 | }; 94 | 95 | var reposite = function () { 96 | var j = 0; 97 | var k = 0; 98 | var lastTop = startTop; 99 | var lastRight = startRight; 100 | var lastPosition = []; 101 | 102 | if (args.positionY === 'top') { 103 | messageElements.sort(priorityCompareTop); 104 | } else if (args.positionY === 'bottom') { 105 | messageElements.sort(priorityCompareBtm); 106 | } 107 | 108 | for (var i = messageElements.length - 1; i >= 0; i--) { 109 | var element = messageElements[i]; 110 | if (args.replaceMessage && i < messageElements.length - 1) { 111 | element.addClass('killed'); 112 | continue; 113 | } 114 | var elHeight = parseInt(element[0].offsetHeight); 115 | var elWidth = parseInt(element[0].offsetWidth); 116 | var position = lastPosition[element._positionY + element._positionX]; 117 | 118 | if ((top + elHeight) > window.innerHeight) { 119 | position = startTop; 120 | k++; 121 | j = 0; 122 | } 123 | 124 | var top = (lastTop = position ? (j === 0 ? position : position + verticalSpacing) : startTop); 125 | var right = lastRight + (k * (horizontalSpacing + elWidth)); 126 | 127 | element.css(element._positionY, top + 'px'); 128 | if (element._positionX === 'center') { 129 | element.css('left', parseInt(window.innerWidth / 2 - elWidth / 2) + 'px'); 130 | } else { 131 | element.css(element._positionX, right + 'px'); 132 | } 133 | 134 | lastPosition[element._positionY + element._positionX] = top + elHeight; 135 | 136 | if (options.maxCount > 0 && messageElements.length > options.maxCount && i === 0) { 137 | element.scope().kill(true); 138 | } 139 | 140 | j++; 141 | } 142 | }; 143 | 144 | var templateElement = $compile(template)(scope); 145 | templateElement._positionY = args.positionY; 146 | templateElement._positionX = args.positionX; 147 | templateElement._priority = args.priority; 148 | templateElement.addClass(args.type); 149 | 150 | var closeEvent = function (e) { 151 | e = e.originalEvent || e; 152 | if (e.type === 'click' || e.propertyName === 'opacity' && e.elapsedTime >= 1) { 153 | 154 | if (scope.onClose) { 155 | scope.$apply(scope.onClose(templateElement)); 156 | } 157 | 158 | if (e.type === 'click') 159 | if (scope.onClick) { 160 | scope.$apply(scope.onClick(templateElement)); 161 | } 162 | 163 | templateElement.remove(); 164 | messageElements.splice(messageElements.indexOf(templateElement), 1); 165 | scope.$destroy(); 166 | reposite(); 167 | } 168 | }; 169 | 170 | if (args.closeOnClick) { 171 | templateElement.addClass('clickable'); 172 | templateElement.bind('click', closeEvent); 173 | } 174 | 175 | templateElement.bind('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd', closeEvent); 176 | 177 | if (angular.isNumber(args.delay)) { 178 | $timeout(function () { 179 | templateElement.addClass('killed'); 180 | }, args.delay); 181 | } 182 | 183 | setCssTransitions('none'); 184 | 185 | angular.element(document.querySelector(args.container)).append(templateElement); 186 | var offset = -(parseInt(templateElement[0].offsetHeight) + 50); 187 | templateElement.css(templateElement._positionY, offset + "px"); 188 | messageElements.push(templateElement); 189 | 190 | if (args.positionX == 'center') { 191 | var elWidth = parseInt(templateElement[0].offsetWidth); 192 | templateElement.css('left', parseInt(window.innerWidth / 2 - elWidth / 2) + 'px'); 193 | } 194 | 195 | $timeout(function () { 196 | setCssTransitions(''); 197 | }); 198 | 199 | function setCssTransitions(value) { 200 | ['-webkit-transition', '-o-transition', 'transition'].forEach(function (prefix) { 201 | templateElement.css(prefix, value); 202 | }); 203 | } 204 | 205 | scope._templateElement = templateElement; 206 | 207 | scope.kill = function (isHard) { 208 | if (isHard) { 209 | if (scope.onClose) { 210 | scope.$apply(scope.onClose(scope._templateElement)); 211 | } 212 | 213 | messageElements.splice(messageElements.indexOf(scope._templateElement), 1); 214 | scope._templateElement.remove(); 215 | scope.$destroy(); 216 | $timeout(reposite); 217 | } else { 218 | scope._templateElement.addClass('killed'); 219 | } 220 | }; 221 | 222 | $timeout(reposite); 223 | 224 | if (!isResizeBound) { 225 | angular.element($window).bind('resize', function (e) { 226 | $timeout(reposite); 227 | }); 228 | isResizeBound = true; 229 | } 230 | 231 | deferred.resolve(scope); 232 | 233 | } 234 | 235 | return deferred.promise; 236 | }; 237 | 238 | notify.primary = function (args) { 239 | return this(args, 'primary'); 240 | }; 241 | notify.error = function (args) { 242 | return this(args, 'error'); 243 | }; 244 | notify.success = function (args) { 245 | return this(args, 'success'); 246 | }; 247 | notify.info = function (args) { 248 | return this(args, 'info'); 249 | }; 250 | notify.warning = function (args) { 251 | return this(args, 'warning'); 252 | }; 253 | 254 | notify.clearAll = function () { 255 | angular.forEach(messageElements, function (element) { 256 | element.addClass('killed'); 257 | }); 258 | }; 259 | 260 | return notify; 261 | }; 262 | }); 263 | -------------------------------------------------------------------------------- /src/angular-ui-notification.less: -------------------------------------------------------------------------------- 1 | @ui-notification-border-radius: 0px; 2 | 3 | .ui-notification { 4 | border-radius: @ui-notification-border-radius; 5 | position: fixed; 6 | z-index: 9999; 7 | box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.3); 8 | width: 300px; 9 | color: @btn-primary-color; 10 | background: @brand-primary; 11 | .transition(all ease 0.5s); 12 | &.clickable { 13 | cursor: pointer; 14 | 15 | &:hover { 16 | opacity: 0.7; 17 | } 18 | } 19 | &.killed { 20 | opacity: 0; 21 | .transition(opacity ease 1s); 22 | } 23 | & > h3 { 24 | display: block; 25 | margin: 10px 10px 0 10px; 26 | padding: 0 0 5px 0; 27 | text-align: left; 28 | font-size: @font-size-base; 29 | font-weight: bold; 30 | border-bottom: 1px solid fadeout(@btn-primary-color, 70%); 31 | } 32 | & a { 33 | color: @btn-primary-color; 34 | &:hover { 35 | text-decoration: underline; 36 | } 37 | } 38 | & > .message { 39 | margin: 10px 10px 10px 10px; 40 | } 41 | &.warning { 42 | color: @btn-warning-color; 43 | background: @brand-warning; 44 | } 45 | &.error { 46 | color: @btn-danger-color; 47 | background: @brand-danger; 48 | } 49 | &.success { 50 | color: @btn-success-color; 51 | background: @brand-success; 52 | } 53 | &.info { 54 | color: @btn-info-color; 55 | background: @brand-info; 56 | } 57 | } -------------------------------------------------------------------------------- /src/build.less: -------------------------------------------------------------------------------- 1 | @import "../bower_components/bootstrap/less/variables.less"; 2 | @import "../bower_components/bootstrap/less/mixins.less"; 3 | @import "angular-ui-notification.less"; 4 | -------------------------------------------------------------------------------- /test/e2e/main.spec.js: -------------------------------------------------------------------------------- 1 | describe('E2E: main page', function() { 2 | beforeEach(function() { 3 | browser.ignoreSynchronization = true; 4 | browser.driver.get('http://localhost:8080/index.html'); 5 | }); 6 | 7 | describe('Buttons exists', function() { 8 | it('should be 15', function() { 9 | expect(element.all(by.css('button.btn')).count()).toBe(17); 10 | }); 11 | }); 12 | 13 | describe('Show notifications', function() { 14 | it('should be shown notifications', function() { 15 | var buttons = element.all(by.css('button.btn')); 16 | buttons.each(function(button) { 17 | button.click(); 18 | }); 19 | 20 | var notifications = element.all(by.css('.ui-notification')); 21 | expect(notifications.count()).toBe(buttons.count()); 22 | }); 23 | 24 | }); 25 | 26 | describe('Custom template and scope', function() { 27 | beforeEach(function() { 28 | var notifications = element.all(by.css('.ui-notification')); 29 | notifications.each(function(notification) { 30 | notification.addClass('killed'); 31 | }); 32 | }); 33 | 34 | it('should be the message with custom template', function() { 35 | element(by.css('button.show-custom')).click(); 36 | expect(element.all(by.css('div.custom-template')).count()).toBe(1); 37 | }); 38 | 39 | it('should trigger click callback in current scope from notification', function(done) { 40 | element(by.css('button.show-custom')).click(); 41 | 42 | setTimeout(function() { 43 | element(by.css('a.close-notification')).click(); 44 | 45 | setTimeout(function() { 46 | var clicksLog = element.all(by.css('.elements-count li')); 47 | expect(clicksLog.count()).toBe(1); 48 | done(); 49 | }, 1000); 50 | 51 | }, 1000); 52 | }); 53 | }); 54 | 55 | }); 56 | 57 | describe("E2E: notification with configuration", function() { 58 | beforeEach(function() { 59 | browser.ignoreSynchronization = true; 60 | browser.driver.get('http://localhost:8080/notification_config.html'); 61 | }); 62 | 63 | describe('Show notifications', function() { 64 | it('should be shown notifications', function() { 65 | var buttons = element.all(by.css('button.btn')); 66 | buttons.each(function(button) { 67 | button.click(); 68 | }); 69 | 70 | var notifications = element.all(by.css('.ui-notification')); 71 | expect(notifications.count()).toBe(buttons.count()); 72 | }); 73 | 74 | }); 75 | }); 76 | 77 | describe("E2E: replace notifications", function() { 78 | beforeEach(function() { 79 | browser.ignoreSynchronization = true; 80 | browser.driver.get('http://localhost:8080/notification_replace.html'); 81 | }); 82 | 83 | describe('show all notifications', function() { 84 | it('should leave only one last message', function(done) { 85 | var buttons = element.all(by.css('button.btn')); 86 | buttons.each(function(button) { 87 | button.click(); 88 | }); 89 | 90 | setTimeout(function() { 91 | var notifications = element.all(by.css('.ui-notification')); 92 | expect(notifications.count()).toEqual(1); 93 | done(); 94 | }, 4000); 95 | }); 96 | }); 97 | }); 98 | 99 | describe("E2E: kill notification", function() { 100 | beforeEach(function() { 101 | browser.ignoreSynchronization = true; 102 | browser.driver.get('http://localhost:8080/notification_kill.html'); 103 | }); 104 | 105 | describe('Show and kill notification', function() { 106 | 107 | it('should kill notification with fadeout', function(done) { 108 | element(by.css('button.btn.kill-soft')).click(); 109 | 110 | setTimeout(function() { 111 | var notifications = element.all(by.css('.ui-notification')); 112 | expect(notifications.count()).toEqual(3); 113 | done(); 114 | }, 6000); 115 | }); 116 | 117 | it('should hard kill notification', function(done) { 118 | element(by.css('button.btn.kill-hard')).click(); 119 | 120 | setTimeout(function() { 121 | var notifications = element.all(by.css('.ui-notification')); 122 | expect(notifications.count()).toEqual(3); 123 | done(); 124 | }, 6000); 125 | }); 126 | 127 | }); 128 | }); 129 | 130 | describe("E2E: custom templates", function() { 131 | beforeEach(function() { 132 | browser.ignoreSynchronization = true; 133 | browser.driver.get('http://localhost:8080/custom_template.html'); 134 | }); 135 | 136 | describe('Show custom templated notifications', function() { 137 | 138 | it('should show 4 notifications (2 types of every template)', function() { 139 | var buttons = element.all(by.css('button.btn')); 140 | buttons.each(function(button) { 141 | button.click(); 142 | button.click(); 143 | }); 144 | 145 | expect(element.all(by.css('div.custom-template')).count()).toBe(2); 146 | expect(element.all(by.css('div.custom-template-overriden')).count()).toBe(2); 147 | }); 148 | 149 | }); 150 | }); 151 | 152 | 153 | ddescribe("E2E: call onClose callback", function() { 154 | beforeEach(function() { 155 | browser.ignoreSynchronization = true; 156 | browser.driver.get('http://localhost:8080/onclose.html'); 157 | }); 158 | 159 | // TODO: Fix this test. 160 | // describe('Change scope variable when notification hs closed', function() { 161 | // 162 | // it('should open and close one notification', function(done) { 163 | // element(by.css('button.btn-primary')).click(); 164 | // expect(element.all(by.css('.ui-notification')).count()).toBe(1); 165 | // 166 | // setTimeout(function() { 167 | // expect(element(by.css('#is-closed')).evaluate('hasElementClosed')).toEqual(true); 168 | // done(); 169 | // }, 10000); 170 | // }); 171 | // 172 | // }); 173 | 174 | describe('Do not close on click', function() { 175 | 176 | it('should leave message after clicking', function() { 177 | element(by.css('button.btn-danger')).click(); 178 | expect(element.all(by.css('.ui-notification.error')).count()).toBe(1); 179 | 180 | element(by.css('.ui-notification.error')).click(); 181 | expect(element.all(by.css('.ui-notification.error')).count()).toBe(1); 182 | }); 183 | 184 | }); 185 | }); 186 | 187 | describe("E2E: Max count", function() { 188 | beforeEach(function() { 189 | browser.ignoreSynchronization = true; 190 | browser.driver.get('http://localhost:8080/max_count.html'); 191 | }); 192 | 193 | describe('Click many times but messages less than max count', function() { 194 | 195 | it('should click 20 times but messages should be less than 5', function() { 196 | for (var i = 0; i < 20; i++) { 197 | // Click 20 times 198 | element(by.css('button.btn-primary')).click(); 199 | } 200 | 201 | expect(element.all(by.css('.ui-notification')).count()).toBe(5); 202 | }); 203 | 204 | }); 205 | }); --------------------------------------------------------------------------------