http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/README.md
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/README.md 
b/ui/bower_components/AngularJS-Toaster/README.md
new file mode 100644
index 0000000..513eb03
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/README.md
@@ -0,0 +1,305 @@
+AngularJS-Toaster
+=================
+
+**AngularJS Toaster** is an AngularJS port of the **toastr** non-blocking 
notification jQuery library. It requires AngularJS v1.2.6 or higher and 
angular-animate for the CSS3 transformations.
+
+[![Build 
Status](https://travis-ci.org/jirikavi/AngularJS-Toaster.svg)](https://travis-ci.org/jirikavi/AngularJS-Toaster)
+[![Coverage 
Status](https://coveralls.io/repos/jirikavi/AngularJS-Toaster/badge.svg?branch=master&service=github&busting=3)](https://coveralls.io/github/jirikavi/AngularJS-Toaster?branch=master)
+
+### Current Version 1.2.0
+
+## Angular Compatibility
+AngularJS-Toaster requires AngularJS v1.2.6 or higher and specifically targets 
AngularJS, not Angular 2, although it could be used via ngUpgrade.  
+If you are looking for the Angular 2 port of AngularJS-Toaster, it is located 
[here](https://github.com/Stabzs/Angular2-Toaster).
+
+## Demo
+- Simple demo is at http://plnkr.co/edit/HKTC1a
+- Older versions are http://plnkr.co/edit/1poa9A or 
http://plnkr.co/edit/4qpHwp or http://plnkr.co/edit/lzYaZt (with version 0.4.5)
+- Older version with Angular 1.2.0 is placed at http://plnkr.co/edit/mejR4h
+- Older version with Angular 1.2.0-rc.2 is placed at 
http://plnkr.co/edit/iaC2NY
+- Older version with Angular 1.1.5 is placed at http://plnkr.co/mVR4P4
+
+## Getting started
+
+Optionally: to install with bower, use:
+```
+bower install --save angularjs-toaster
+```
+or with npm :
+```
+npm install --save angularjs-toaster
+```
+* Link scripts:
+
+```html
+<link 
href="https://cdnjs.cloudflare.com/ajax/libs/angularjs-toaster/1.1.0/toaster.min.css";
 rel="stylesheet" />
+<script 
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"; 
></script>
+<script src="https://code.angularjs.org/1.2.0/angular-animate.min.js"; 
></script>
+<script 
src="https://cdnjs.cloudflare.com/ajax/libs/angularjs-toaster/1.1.0/toaster.min.js";></script>
+```
+
+* Add toaster container directive: 
+
+```html
+<toaster-container></toaster-container>
+```
+
+* Prepare the call of toaster method:
+
+```js
+// Display an info toast with no title
+angular.module('main', ['toaster', 'ngAnimate'])
+       .controller('myController', function($scope, toaster) {
+           $scope.pop = function(){
+               toaster.pop('success', "title", "text");
+           };
+       });
+```
+
+* Call controller method on button click:
+
+```html
+<div ng-controller="myController">
+    <button ng-click="pop()">Show a Toaster</button>
+</div>
+```
+
+### Close Button
+
+The Close Button's visibility can be configured at three different levels:
+
+* Globally in the config for all toast types:
+```html
+<toaster-container toaster-options="{'close-button': 
true}"></toaster-container>
+```
+
+* Per info-class type:
+By passing the close-button configuration as an object instead of a boolean, 
you can specify the global behavior an info-class type should have.
+```html
+<toaster-container toaster-options="
+    {'close-button':{ 'toast-warning': true, 'toast-error': false } }">
+</toaster-container>
+```
+If a type is not defined and specified, the default behavior for that type is 
false.
+
+* Per toast constructed via toaster.pop('success', "title", "text"):
+```html
+toaster.pop({
+                type: 'error',
+                title: 'Title text',
+                body: 'Body text',
+                showCloseButton: true
+            });
+```
+This option is given the most weight and will override the global 
configurations for that toast.  However, it will not persist to other toasts of 
that type and does not alter or pollute the global configuration.
+
+### Close Html
+
+The close button html can be overridden either globally or per toast call.
+
+ - Globally:
+
+    ```html
+    <toaster-container 
toaster-options="{'close-html':'<button>Close</button>', 
+        'showCloseButton':true}"></toaster-container>
+    ```
+ - Per toast:
+
+    ```js
+    toaster.pop({
+            type: 'error',
+            title: 'Title text',
+            body: 'Body text',
+            showCloseButton: true,
+            closeHtml: '<button>Close</button>'
+    });
+    ```
+
+
+### Body Output Type
+The rendering of the body content is configurable at both the Global level, 
which applies to all toasts, and the individual toast level when passed as an 
argument to the toast.
+
+There are four types of body renderings: trustedHtml', 'template', 
'templateWithData', 'directive'.
+
+ - trustedHtml:  When using this configuration, the toast will parse the body 
content using 
+       `$sce.trustAsHtml(toast.body)`.
+       If the html can be successfully parsed, it will be bound to the toast 
via `ng-bind-html`.  If it cannot be parsed as "trustable" html, an exception 
will be thrown.     
+
+ - template:  Will use the `toast.body` if passed as an argument, else it will 
fallback to the template bound to the `'body-template': 'toasterBodyTmpl.html'` 
configuration option.
+ 
+ - templateWithData: 
+        - Will use the `toast.body` if passed as an argument, else it will 
fallback to the template bound to the `'body-template': 'toasterBodyTmpl.html'` 
configuration option.
+        - Assigns any data associated with the template to the toast.
+
+ - directive 
+        - Will use the `toast.body` argument to represent the name of a 
directive that you want to render as the toast's body, else it will fallback to 
the template bound to the `'body-template': 'toasterBodyTmpl.html'` 
configuration option.
+    The directive name being passed to the `body` argument should appear as it 
exists in the markup, 
+     not camelCased as it would appear in the directive declaration 
(`cool-directive-name` instead of `coolDirectiveName`). The directive must be 
usable as an attribute.
+    
+      ```js
+    // The toast pop call, passing in a directive name to be rendered
+    toaster.pop({
+                   type: 'info',
+                   body: 'bind-unsafe-html',
+                   bodyOutputType: 'directive'
+       });
+      ```
+    
+      ```js
+    // The directive that will be dynamically rendered
+    .directive('bindUnsafeHtml', [function () {
+            return {
+                template: "<span style='color:orange'>Orange directive 
text!</span>"
+            };
+    }])
+    ```
+     - Will use the `toast.directiveData` argument to accept data that will be 
bound to the directive's scope. The directive cannot use isolateScope and will
+     throw an exception if isolateScope is detected.  All data must be passed 
via the directiveData argument.
+    
+        ```js
+      // The toast pop call, passing in a directive name to be rendered
+      toaster.pop({
+              type: 'info',
+              body: 'bind-name',
+              bodyOutputType: 'directive',
+              directiveData: { name: 'Bob' }
+      });
+        ```
+        
+        ```js
+      // The directive that will be dynamically rendered
+      .directive('bindName', [function () {
+            return {
+                template: "<span style='color:orange'>Hi 
{{directiveData.name}}!</span>"
+            };
+      }])
+        ```
+        
+    There are additional documented use cases in these 
[tests](test/directiveTemplateSpec.js).
+    
+All four options can be configured either globally for all toasts or 
individually per toast.pop() call.  If the `body-output-type` option is 
configured on the toast, it will take precedence over the global configuration 
for that toast instance.
+
+ - Globally:
+ 
+    ```html
+    <toaster-container toaster-options="{'body-output-type': 
'template'}"></toaster-container>
+    ```
+ 
+ - Per toast:
+ 
+    ```js
+    toaster.pop({
+            type: 'error',
+            title: 'Title text',
+            body: 'Body text',
+            bodyOutputType: 'trustedHtml'
+    });
+    ```
+
+### On Show Callback
+An onShow callback function can be attached to each toast instance.  The 
callback will be invoked upon toast add.
+
+```js
+toaster.pop({
+            title: 'A toast',
+                   body: 'with an onShow callback',
+                       onShowCallback: function () { 
+                           toaster.pop({
+                               title: 'A toast',
+                                   body: 'invoked as an onShow callback'
+                               });
+                       }
+});
+```
+
+### On Hide Callback
+An onHide callback function can be attached to each toast instance.  The 
callback will be invoked upon toast removal.  This can be used to chain toast 
calls.
+
+```js
+toaster.pop({
+            title: 'A toast',
+                   body: 'with an onHide callback',
+                       onHideCallback: function () { 
+                           toaster.pop({
+                               title: 'A toast',
+                                   body: 'invoked as an onHide callback'
+                               });
+                       }
+});
+```
+
+### Multiple Toaster Containers
+If desired, you can include multiple `<toaster-container></toaster-container>` 
+elements in your DOM.  The library will register an event handler for every 
instance 
+of the container that it identifies.  By default, when there are multiple 
registered 
+containers, each container will receive a toast notification and display it 
when a toast 
+is popped.  
+
+To target a specific container, you need to register that container with a 
unique `toaster-id`.
+
+```html
+<toaster-container toaster-options="{'toaster-id': 1, 
+    'animation-class': 'toast-top-left'}"></toaster-container>
+<toaster-container toaster-options="{'toaster-id': 2}"></toaster-container>
+```
+
+This gives you the ability to specifically target a unique container rather 
than broadcasting 
+new toast events to any containers that are currently registered.
+
+```js
+vm.popContainerOne = function () {
+    toaster.pop({ type: 'info', body: 'One', toasterId: 1 });
+}
+      
+vm.popContainerTwo = function () {
+    toaster.pop({ type: 'info', body: 'Two', toasterId: 2 });
+}
+```
+
+[This plnkr](http://plnkr.co/edit/4ICtcrpTSoAB9Vo5bRvN?p=preview) demonstrates 
this behavior 
+and it is documented in these [tests](test/toasterContainerSpec.js#L430).
+
+
+### Other Options
+
+```html
+// Change display position
+<toaster-container toaster-options="{'position-class': 
'toast-top-full-width'}"></toaster-container>
+```
+
+### Animations
+Unlike toastr, this library relies on ngAnimate and CSS3 transformations for 
optional animations.  To include and use animations, add a reference to 
angular-animate.min.js (as described in Getting started - Link scripts) and add 
ngAnimate as a dependency alongside toaster. 
+
+```js
+// Inject ngAnimate to enable animations
+angular.module('main', ['toaster', 'ngAnimate']);
+```
+If you do not want to use animations, you can safely remove the 
angular-animate.min.js reference as well as the injection of ngAnimate.  Toasts 
will be displayed without animations.
+
+
+### Common Issues
+- Toaster always shows up as "info"
+    - Your `<toaster-container></toaster-container` might be placed inside of 
your routing directive.
+    - You have multiple `<toaster-container></toaster-container` elements 
without unique `toaster-id` configuration arguments.
+- [$sce:itype] Attempted to trust a non-string value in a content requiring a 
string 
+    - You have not specified: `bodyOutputType: 'trustedHtml'` when passing 
html as a body argument.
+- My toasts do not show up when I pop them, but after I perform another action.
+    - You are calling `toaster.pop()` outside of AngularJS scope and a digest 
cycle is not being triggered.
+    Wrap your `toaster.pop()` call in `$timeout` to force a digest cycle.
+    ```js
+     $timeout(function () {
+        toaster.pop();
+     }, 0);
+    ```
+               
+## Author
+**Jiri Kavulak**
+
+## Credits
+Inspired by http://codeseven.github.io/toastr/demo.html.
+
+## Copyright
+Copyright © 2013-2016 [Jiri Kavulak](https://twitter.com/jirikavi).
+
+## License 
+AngularJS-Toaster is under MIT license - 
http://www.opensource.org/licenses/mit-license.php
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/bower.json
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/bower.json 
b/ui/bower_components/AngularJS-Toaster/bower.json
new file mode 100644
index 0000000..c435b2d
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/bower.json
@@ -0,0 +1,17 @@
+{
+  "name": "AngularJS-Toaster",
+  "version": "1.2.0",
+  "main": [
+    "toaster.js",
+    "toaster.css"
+  ],
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "components"
+  ],
+  "dependencies": {
+    "angular": ">1.2.6",
+    "angular-animate": ">1.2.8"
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/index.js
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/index.js 
b/ui/bower_components/AngularJS-Toaster/index.js
new file mode 100644
index 0000000..5434417
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/index.js
@@ -0,0 +1,2 @@
+require("./toaster.js");
+module.exports = "toaster";

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/karma.conf.js
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/karma.conf.js 
b/ui/bower_components/AngularJS-Toaster/karma.conf.js
new file mode 100644
index 0000000..4acfe56
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/karma.conf.js
@@ -0,0 +1,73 @@
+// Karma configuration
+// Generated on Wed Oct 21 2015 12:37:04 GMT-0600 (Mountain Daylight Time)
+
+module.exports = function (config) {
+  config.set({
+
+    // base path that will be used to resolve all patterns (eg. files, exclude)
+    basePath: '',
+
+
+    // frameworks to use
+    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
+    frameworks: ['jasmine'],
+
+
+    // list of files / patterns to load in the browser
+    files: [
+      'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js',
+      
'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-mocks.js',
+      'toaster.js',
+      'test/**/*Spec.js'
+    ],
+
+
+    // list of files to exclude
+    exclude: [
+    ],
+
+
+    // preprocess matching files before serving them to the browser
+    // available preprocessors: 
https://npmjs.org/browse/keyword/karma-preprocessor
+    preprocessors: {
+    },
+
+
+    // test results reporter to use
+    // possible values: 'dots', 'progress'
+    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
+    reporters: ['progress'],
+
+
+    // web server port
+    port: 9876,
+
+
+    // enable / disable colors in the output (reporters and logs)
+    colors: true,
+
+
+    // level of logging
+    // possible values: config.LOG_DISABLE || config.LOG_ERROR || 
config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+    logLevel: config.LOG_INFO,
+
+
+    // enable / disable watching file and executing tests whenever any file 
changes
+    autoWatch: true,
+
+
+    // start these browsers
+    // available browser launchers: 
https://npmjs.org/browse/keyword/karma-launcher
+    browsers: ['Chrome'],
+
+    plugins: [
+      'karma-chrome-launcher',
+      'karma-coverage',
+      'karma-jasmine'
+    ],
+
+    // Continuous Integration mode
+    // if true, Karma captures browsers, runs the tests and exits
+    singleRun: false,
+  });
+};

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/karma.coverage.js
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/karma.coverage.js 
b/ui/bower_components/AngularJS-Toaster/karma.coverage.js
new file mode 100644
index 0000000..ef76a3c
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/karma.coverage.js
@@ -0,0 +1,38 @@
+module.exports = function(config) {
+
+       require("./karma.conf")(config);
+
+       config.autoWatch = false;
+
+       config.preprocessors = {
+               'toaster.js': ['coverage']
+       };
+       
+       config.coverageReporter = {
+      dir: 'coverage/',
+         reporters: [
+                 { type: 'html', subdir: 'html-report' }
+         ]
+    };
+
+       config.singleRun = true;
+
+       config.reporters.push('coverage');
+       config.plugins.push('karma-coverage');
+
+       config.customLaunchers = {
+        Chrome_travis_ci: {
+            base: 'Chrome',
+            flags: ['--no-sandbox']
+        }
+    };
+       
+       if (process.env.TRAVIS) {
+       config.browsers = ['Chrome_travis_ci'];
+               config.coverageReporter.reporters.push({
+                       type: 'lcov', subdir: 'lcov-report'
+               });
+       }
+
+       config.set(config);
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/package.json
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/package.json 
b/ui/bower_components/AngularJS-Toaster/package.json
new file mode 100644
index 0000000..e13a770
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/package.json
@@ -0,0 +1,36 @@
+{
+  "name": "angularjs-toaster",
+  "version": "1.2.0",
+  "description": "AngularJS Toaster is a customized version of toastr 
non-blocking notification javascript library",
+  "author": "Jiri Kavulak",
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jirikavi/AngularJS-Toaster.git";
+  },
+  "dependencies": {},
+  "devDependencies": {
+    "angular": ">1.2.6",
+    "angular-animate": "~1.2.8",
+    "angular-mocks": "^1.4.7",
+    "jasmine-core": "^2.3.4",
+    "karma": "^0.13.21",
+    "karma-chrome-launcher": "^0.2.2",
+    "karma-coverage": "^0.5.3",
+    "karma-jasmine": "^0.3.7",
+    "coveralls": "^2.11.6"
+  },
+  "jspm": {
+    "main": "toaster",
+    "dependencies": {
+      "css": "jspm:css@*"
+    },
+    "shim": {
+      "toaster": {
+        "deps": [
+          "./toaster.css!"
+        ]
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/test/directiveTemplateSpec.js
----------------------------------------------------------------------
diff --git 
a/ui/bower_components/AngularJS-Toaster/test/directiveTemplateSpec.js 
b/ui/bower_components/AngularJS-Toaster/test/directiveTemplateSpec.js
new file mode 100644
index 0000000..b638dca
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/test/directiveTemplateSpec.js
@@ -0,0 +1,213 @@
+/* global describe global it global beforeEach global angular global inject 
global expect */
+
+'use strict';
+
+describe('directiveTemplate', function () {
+       var toaster, scope, $compile;
+
+       beforeEach(function () {
+        createDirectives(); 
+        
+               // load dependencies
+               module('testApp');
+               module('toaster');
+               
+               // inject the toaster service
+        inject(function (_toaster_, _$rootScope_, _$compile_) {
+                       toaster = _toaster_;
+                       scope = _$rootScope_;
+                       $compile = _$compile_;
+               });
+       });
+
+       it('should load and render the referenced directive template text', 
function () {
+               var container = compileContainer();
+               pop({ type: 'info', body: 'bind-template-only', bodyOutputType: 
'directive' });
+
+               expect(container[0].innerText).toBe('here is some great new 
text! It was brought in via directive!');
+       });
+
+       it('should bind directiveData to the directive template', function () {
+               var container = compileContainer();
+               pop({ type: 'info', body: 'bind-template-with-data', 
bodyOutputType: 'directive', directiveData: { name: 'Bob' } });
+
+               expect(container[0].innerText).toBe('Hello Bob');
+       });
+       
+       it('should parse type string directiveData to an object', function () {
+               var container = compileContainer();
+               pop({ type: 'info', body: 'bind-template-with-data', 
bodyOutputType: 'directive', directiveData: '{ "name": "Bob" }' });
+
+               expect(container[0].innerText).toBe('Hello Bob');
+       });
+       
+       it('should render type number directiveData', function () {
+               var container = compileContainer();
+               pop({ type: 'info', body: 'bind-template-with-numeric-data', 
bodyOutputType: 'directive', directiveData: 2 });
+
+               expect(container[0].innerText).toBe('1 + 1 = 2');
+       });
+
+       it('should bind Attribute-restricted templates', function () {
+               var container = compileContainer();
+               pop({ type: 'info', body: 'bind-template-only', bodyOutputType: 
'directive', directiveData: { name: 'Bob' } });
+
+               expect(container[0].innerText).toBe('here is some great new 
text! It was brought in via directive!');
+       });
+
+       it('should bind unrestricted templates', function () {
+               var container = compileContainer();
+               pop({ type: 'info', body: 'unrestricted-template', 
bodyOutputType: 'directive' });
+
+               expect(container[0].innerText).toBe('Unrestricted Template');
+       });
+
+       it('should not bind Element-only-restricted templates', function () {
+               var hasError = false;
+               var container = compileContainer();
+        
+        try {
+                 pop({ type: 'info', body: 'element-template', bodyOutputType: 
'directive' });
+        } catch(e) {
+            var message = 'Directives must be usable as attributes. ' + 
+                'Add "A" to the restrict option (or remove the option 
entirely). Occurred for directive element-template.';
+            
+            expect(e.message).toBe(message);
+            hasError = true;
+        }
+
+        expect(hasError).toBe(true);
+       });
+
+       it('should not bind Class-only-restricted templates', function () {
+        var hasError = false;
+               var container = compileContainer();
+        
+        try {
+                 pop({ type: 'info', body: 'class-template', bodyOutputType: 
'directive' });
+        } catch(e) {
+            var message = 'Directives must be usable as attributes. ' + 
+                'Add "A" to the restrict option (or remove the option 
entirely). Occurred for directive class-template.';
+            
+            expect(e.message).toBe(message);
+            hasError = true;
+        }
+
+        expect(hasError).toBe(true);
+       });
+
+       it('should throw an error if directiveName argument is not passed via 
body', function () {
+               var container = compileContainer();
+               var hasError = false;
+               
+               expect(container[0].innerText).toBe('');
+               
+               try {
+                       pop({ type: 'info', bodyOutputType: 'directive' });
+               } catch (e) {
+                       expect(e.message).toBe('A valid directive name must be 
provided via the toast body argument when using bodyOutputType: directive');
+                       hasError = true;
+               }
+               
+               expect(container[0].innerText).toBe('');
+               expect(hasError).toBe(true);
+       });
+       
+       it('should throw an error if directiveName argument is an empty 
string', function () {
+               var container = compileContainer();
+               var hasError = false;
+               
+               expect(container[0].innerText).toBe('');
+               
+               try {
+                       pop({ type: 'info', body: '', bodyOutputType: 
'directive' });
+               } catch (e) {
+                       expect(e.message).toBe('A valid directive name must be 
provided via the toast body argument when using bodyOutputType: directive');
+                       hasError = true;
+               }
+               
+               expect(container[0].innerText).toBe('');
+               expect(hasError).toBe(true);
+       });
+
+       it('should throw an error if the directive could not be found', 
function () {
+               var hasError = false;
+
+               compileContainer();
+
+               try {
+                       pop({ type: 'info', body: 'non-existent-directive', 
bodyOutputType: 'directive' });
+               } catch (e) {
+            var message = 'non-existent-directive could not be found. ' + 
+                'The name should appear as it exists in the markup,' + 
+                ' not camelCased as it would appear in the directive 
declaration,' +
+                ' e.g. directive-name not directiveName.'
+            
+                       expect(e.message).toBe(message);
+                       hasError = true;
+               }
+
+               expect(hasError).toBe(true);
+       });
+    
+    it('should throw an error if the directive uses isolate scope', function 
() {
+        var hasError = false;
+               compileContainer();
+        
+        try {
+            pop({ type: 'info', body: 'isolate-scope', bodyOutputType: 
'directive' });
+        } catch (e) {
+            var message = 'Cannot use a directive with an isolated scope.' +
+                ' The scope must be either true or falsy (e.g. 
false/null/undefined). Occurred for directive isolate-scope.';
+            
+            expect(e.message).toBe(message)
+            hasError = true;
+        }
+        
+        expect(hasError).toBe(true);
+    })
+
+
+       function compileContainer() {
+               var element = 
angular.element('<toaster-container></toaster-container>');
+               $compile(element)(scope);
+               scope.$digest();
+
+               return element;
+       }
+
+       function pop(params) {
+               toaster.pop(params);
+               
+               // force new toast to be rendered
+               scope.$digest();
+       }
+
+       function createDirectives() {
+               angular.module('testApp', [])
+                       .directive('bindTemplateOnly', function () {
+                               return {
+                                       restrict: 'A',
+                                       template: 'here is some great new text! 
<span style="color:orange">It was brought in via directive!</span>'
+                               }
+                       })
+                       .directive('bindTemplateWithData', function () {
+                               return { template: 'Hello 
{{directiveData.name}}' }
+                       })
+                       .directive('bindTemplateWithNumericData', function () {
+                               return { template: '1 + 1 = {{directiveData}}' }
+                       })
+                       .directive('elementTemplate', function () {
+                               return { restrict: 'E', template: 'Element 
Template' }
+                       })
+                       .directive('classTemplate', function () {
+                               return { restrict: 'C', template: 'Class 
Template' }
+                       })
+                       .directive('unrestrictedTemplate', function () {
+                               return { template: 'Unrestricted Template' }
+                       })
+            .directive('isolateScope', function () {
+                return { template: 'isolate scope template', scope: {}}
+            }); 
+       }
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/test/toasterContainerControllerSpec.js
----------------------------------------------------------------------
diff --git 
a/ui/bower_components/AngularJS-Toaster/test/toasterContainerControllerSpec.js 
b/ui/bower_components/AngularJS-Toaster/test/toasterContainerControllerSpec.js
new file mode 100644
index 0000000..7054098
--- /dev/null
+++ 
b/ui/bower_components/AngularJS-Toaster/test/toasterContainerControllerSpec.js
@@ -0,0 +1,358 @@
+/* global describe global it global beforeEach global angular global jasmine 
global inject global expect global spyOn */
+
+'use strict';
+
+var rootScope, toaster, $compile;
+
+describe('toasterContainer controller', function () {
+       beforeEach(function () {
+               module('toaster');
+               
+               // inject the toaster service
+        inject(function (_toaster_, _$rootScope_, _$compile_) {
+                       toaster = _toaster_;
+                       rootScope = _$rootScope_;
+                       $compile = _$compile_;
+               });
+       });
+
+       it('should stop timer if config.mouseoverTimer is true', function () {
+               var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'mouseover-timer-stop\': true }"></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+
+               expect(scope.config.mouseoverTimer).toBe(true);
+
+               toaster.pop({ type: 'info' });
+
+               rootScope.$digest();
+
+               expect(scope.toasters[0].timeoutPromise).toBeDefined();
+
+               scope.stopTimer(scope.toasters[0]);
+
+               rootScope.$digest();
+
+               expect(scope.toasters[0].timeoutPromise).toBe(null);
+       });
+
+       it('should do nothing if config.mouseoverTimer is true and stopTimer is 
called again', function () {
+               var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'mouseover-timer-stop\': true }"></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+
+               expect(scope.config.mouseoverTimer).toBe(true);
+
+               toaster.pop({ type: 'info' });
+
+               rootScope.$digest();
+
+               scope.stopTimer(scope.toasters[0]);
+               rootScope.$digest();
+
+               expect(scope.toasters[0].timeoutPromise).toBe(null);
+
+               scope.stopTimer(scope.toasters[0]);
+               rootScope.$digest();
+
+               expect(scope.toasters[0].timeoutPromise).toBe(null);
+       });
+
+       it('should not stop timer if config.mouseoverTimer is false', function 
() {
+               var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'mouseover-timer-stop\': false }"></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+
+               expect(scope.config.mouseoverTimer).toBe(false);
+
+               toaster.pop({ type: 'info' });
+
+               rootScope.$digest();
+
+               expect(scope.toasters[0].timeoutPromise).toBeDefined();
+
+               scope.stopTimer(scope.toasters[0]);
+
+               rootScope.$digest();
+
+               expect(scope.toasters[0].timeoutPromise).toBeDefined();
+       });
+
+       it('should restart timer if config.mouseoverTimer is true and 
timeoutPromise is falsy', function () {
+               var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'mouseover-timer-stop\': true }"></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+
+               toaster.pop({ type: 'info' });
+               rootScope.$digest();
+
+               expect(scope.toasters[0].timeoutPromise).toBeDefined();
+
+               scope.stopTimer(scope.toasters[0]);
+               expect(scope.toasters[0].timeoutPromise).toBe(null);
+
+               scope.restartTimer(scope.toasters[0]);
+               expect(scope.toasters[0].timeoutPromise).toBeDefined();
+       });
+
+       it('should not restart timer if config.mouseoverTimer is true and 
timeoutPromise is truthy', function () {
+               var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'mouseover-timer-stop\': true }"></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+
+               toaster.pop({ type: 'info' });
+               rootScope.$digest();
+
+               expect(scope.toasters[0].timeoutPromise).toBeDefined();
+
+               spyOn(scope, 'configureTimer').and.callThrough();
+
+               scope.restartTimer(scope.toasters[0]);
+               expect(scope.toasters[0].timeoutPromise).toBeDefined();
+               expect(scope.configureTimer).not.toHaveBeenCalled();
+       });
+
+       it('should not restart timer and should remove toast if 
config.mouseoverTimer is not true and timeoutPromise is null', function () {
+               var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'mouseover-timer-stop\': 2 }"></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+
+               toaster.pop({ type: 'info' });
+               rootScope.$digest();
+
+               expect(scope.config.mouseoverTimer).toBe(2);
+
+               scope.toasters[0].timeoutPromise = null;
+
+               spyOn(scope, 'configureTimer').and.callThrough();
+               spyOn(scope, 'removeToast').and.callThrough();
+
+               scope.restartTimer(scope.toasters[0]);
+
+               expect(scope.configureTimer).not.toHaveBeenCalled();
+               expect(scope.removeToast).toHaveBeenCalled();
+               expect(scope.toasters.length).toBe(0)
+       });
+
+       it('should not restart timer or remove toast if config.mouseoverTimer 
is not true and timeoutPromise is not null', function () {
+               var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'mouseover-timer-stop\': 2 }"></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+
+               toaster.pop({ type: 'info' });
+               rootScope.$digest();
+
+               expect(scope.config.mouseoverTimer).toBe(2);
+
+               spyOn(scope, 'configureTimer').and.callThrough();
+               spyOn(scope, 'removeToast').and.callThrough();
+
+               scope.restartTimer(scope.toasters[0]);
+
+               expect(scope.configureTimer).not.toHaveBeenCalled();
+               expect(scope.removeToast).not.toHaveBeenCalled();
+               expect(scope.toasters.length).toBe(1)
+       });
+
+       describe('click', function () {
+               it('should do nothing if config.tap is not true and 
toast.showCloseButton is not true', function () {
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': false, \'close-button\': false }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       
+                       toaster.pop({ type: 'info' });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0]);
+                       
+                       expect(scope.toasters.length).toBe(1);
+                       expect(scope.removeToast).not.toHaveBeenCalled();
+               });
+               
+               it('should do nothing if config.tap is not true and 
toast.showCloseButton is true', function () {
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': false, \'close-button\': true }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       
+                       toaster.pop({ type: 'info' });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0]);
+                       
+                       expect(scope.toasters.length).toBe(1);
+                       expect(scope.removeToast).not.toHaveBeenCalled();
+               });
+               
+               it('should do nothing if config.tap is not true and 
isCloseButton is not true', function () {
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': false, \'close-button\': true }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       
+                       toaster.pop({ type: 'info' });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0], false);
+                       
+                       expect(scope.toasters.length).toBe(1);
+                       expect(scope.removeToast).not.toHaveBeenCalled();
+               });
+               
+               it('should remove toast if config.tap is true', function () {
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': true, \'close-button\': true }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       
+                       toaster.pop({ type: 'info' });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0]);
+                       
+                       expect(scope.toasters.length).toBe(0);
+                       expect(scope.removeToast).toHaveBeenCalled();
+               });
+               
+               it('should remove toast if config.tap is true and the click 
handler function returns true', function () {
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': true, \'close-button\': true }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       
+                       toaster.pop({ type: 'info', clickHandler: function 
(toast, isCloseButton) { return true; } });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0]);
+                       
+                       expect(scope.toasters.length).toBe(0);
+                       expect(scope.removeToast).toHaveBeenCalled();
+               });
+               
+               it('should not remove toast if config.tap is true and the click 
handler function does not return true', function () {
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': true, \'close-button\': true }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       
+                       toaster.pop({ type: 'info', clickHandler: function 
(toast, isCloseButton) { } });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0]);
+                       
+                       expect(scope.toasters.length).toBe(1);
+                       expect(scope.removeToast).not.toHaveBeenCalled();
+               });
+               
+               it('should remove toast if config.tap is true and the click 
handler exists on the parent returning true', function () {
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': true, \'close-button\': true }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       scope.$parent.clickHandler = function () { return true; 
};
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       
+                       toaster.pop({ type: 'info', clickHandler: 
'clickHandler' });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0]);
+                       
+                       expect(scope.toasters.length).toBe(0);
+                       expect(scope.removeToast).toHaveBeenCalled();
+               });
+               
+               it('should not remove toast if config.tap is true and the click 
handler exists on the parent not returning true', function () {
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': true, \'close-button\': true }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       scope.$parent.clickHandler = function () { };
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       
+                       toaster.pop({ type: 'info', clickHandler: 
'clickHandler' });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0]);
+                       
+                       expect(scope.toasters.length).toBe(1);
+                       expect(scope.removeToast).not.toHaveBeenCalled();
+               });
+               
+               it('should remove toast if config.tap is true and the click 
handler does not exist on the parent', function () {
+                       // TODO: this functionality seems counter-intuitive.  
+                       // Need to identify use cases to see if this is 
actually correct.
+                       
+                       var container = angular.element(
+                       '<toaster-container toaster-options="{ 
\'tap-to-dismiss\': true, \'close-button\': true }"></toaster-container>');
+
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+                       
+                       spyOn(scope, 'removeToast').and.callThrough();
+                       console.log = jasmine.createSpy("log");
+                       
+                       toaster.pop({ type: 'info', clickHandler: 
'clickHandler' });
+                       rootScope.$digest();
+                       
+                       scope.click(scope.toasters[0]);
+                       
+                       expect(scope.toasters.length).toBe(0);
+                       expect(scope.removeToast).toHaveBeenCalled();
+                       
+               expect(console.log).toHaveBeenCalledWith("TOAST-NOTE: Your 
click handler is not inside a parent scope of toaster-container.");
+               });
+       });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/test/toasterContainerSpec.js
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/test/toasterContainerSpec.js 
b/ui/bower_components/AngularJS-Toaster/test/toasterContainerSpec.js
new file mode 100644
index 0000000..40353a5
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/test/toasterContainerSpec.js
@@ -0,0 +1,742 @@
+/* global describe global it global beforeEach global angular global jasmine 
global inject global expect global spyOn */
+
+'use strict';
+
+var rootScope, toaster, $compile;
+
+describe('toasterContainer', function () {
+       beforeEach(function () {
+               module('toaster');
+               
+               // inject the toaster service
+        inject(function (_toaster_, _$rootScope_, _$compile_) {
+                       toaster = _toaster_;
+                       rootScope = _$rootScope_;
+                       $compile = _$compile_;
+               });
+       });
+
+       it('should pop a toast via individual parameters', function () {
+               var container = compileContainer();
+               var scope = container.scope();
+               
+               toaster.pop('info', 'test', 'test');
+               
+               expect(scope.toasters.length).toBe(1);
+       });
+
+       it('should unsubscribe events on $destroy if handlers exist', function 
() {
+               var toasterEventRegistry;
+
+               inject(function (_toasterEventRegistry_) {
+                       toasterEventRegistry = _toasterEventRegistry_;
+               });
+
+               var container = compileContainer();
+               var scope = container.scope();
+
+               spyOn(toasterEventRegistry, 
'unsubscribeToNewToastEvent').and.callThrough();
+               spyOn(toasterEventRegistry, 
'unsubscribeToClearToastsEvent').and.callThrough();
+
+               scope.$destroy();
+
+               
expect(toasterEventRegistry.unsubscribeToNewToastEvent).toHaveBeenCalled();
+               
expect(toasterEventRegistry.unsubscribeToClearToastsEvent).toHaveBeenCalled();
+       });
+       
+       
+       describe('addToast', function () {
+               it('should default to icon-class config value if toast.type not 
found in icon-classes', function () {
+                       var toasterConfig;
+                       
+                       inject(function (_toasterConfig_) {
+                               toasterConfig = _toasterConfig_;
+                       });
+       
+                       compileContainer();
+                       
+                       expect(toasterConfig['icon-class']).toBe('toast-info'); 
+                       
+                       toaster.pop({ type: 'invalid' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(toaster.toast.type).toBe('toast-info');
+               });
+       
+               it('should allow subsequent duplicates if prevent-duplicates is 
not set', function () {
+                       var container = compileContainer();
+                       var scope = container.scope();
+                                       
+                       expect(scope.toasters.length).toBe(0);
+                                       
+                       toaster.pop({ type: 'info', title: 'title', body: 
'body' });
+                       toaster.pop({ type: 'info', title: 'title', body: 
'body' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(2);
+               });
+       
+               it('should not allow subsequent duplicates if 
prevent-duplicates is true without toastId param', function () {
+                       var container = angular.element(
+                               '<toaster-container 
toaster-options="{\'prevent-duplicates\': true}"></toaster-container>');
+       
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                                       
+                       var scope = container.scope();
+                                       
+                       expect(scope.toasters.length).toBe(0);
+                                       
+                       toaster.pop({ type: 'info', title: 'title', body: 
'body' });
+                       toaster.pop({ type: 'info', title: 'title', body: 
'body' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(1);
+               });
+               
+               it('should allow subsequent duplicates if prevent-duplicates is 
true with unique toastId params', function () {
+                       var container = angular.element(
+                               '<toaster-container 
toaster-options="{\'prevent-duplicates\': true}"></toaster-container>');
+       
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                                       
+                       var scope = container.scope();
+                                       
+                       expect(scope.toasters.length).toBe(0);
+                                       
+                       toaster.pop({ type: 'info', title: 'title', body: 
'body', toastId: 1 });
+                       toaster.pop({ type: 'info', title: 'title', body: 
'body', toastId: 2 });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(2);
+               });
+       
+               it('should not allow subsequent duplicates if 
prevent-duplicates is true with identical toastId params', function () {
+                       var container = angular.element(
+                               '<toaster-container 
toaster-options="{\'prevent-duplicates\': true}"></toaster-container>');
+       
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                                       
+                       var scope = container.scope();
+                                       
+                       expect(scope.toasters.length).toBe(0);
+                                       
+                       toaster.pop({ type: 'info', title: 'title', body: 
'body', toastId: 1 });
+                       toaster.pop({ type: 'info', title: 'title', body: 
'body', toastId: 1 });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(1);
+               });
+       
+               it('should not render the close button if showCloseButton is 
false', function () {
+                       var container = compileContainer();
+       
+                       toaster.pop({ type: 'info', body: 'With a close button' 
});
+       
+                       rootScope.$digest();
+       
+                       expect(container.find('button')[0]).toBeUndefined();
+               });
+       
+               it('should use the default close html if toast.closeHtml is 
undefined', function () {
+                       var container = compileContainer();
+       
+                       toaster.pop({ type: 'info', body: 'With a close 
button', showCloseButton: true });
+       
+                       rootScope.$digest();
+       
+                       var buttons = container.find('button');
+                       
+                       expect(buttons.length).toBe(1);
+                       expect(buttons[0].outerHTML).toBe('<button 
class="toast-close-button" type="button">×</button>');
+               });
+               
+               it('should use the toast.closeHtml argument if passed', 
function () {
+                       var container = compileContainer();
+       
+                       toaster.pop({ type: 'info', body: 'With a close 
button', showCloseButton: true,
+                               closeHtml: '<button>Close</button>'
+                       });
+       
+                       rootScope.$digest();
+       
+                       var buttons = container.find('button');
+                       
+                       expect(buttons.length).toBe(1);
+                       
expect(buttons[0].outerHTML).toBe('<button>Close</button>');
+               });
+               
+               it('should render toast.closeHtml argument if not a button 
element', function () {
+                       var container = compileContainer();
+       
+                       toaster.pop({ type: 'info', body: 'With close text', 
showCloseButton: true,
+                               closeHtml: '<span>Close</span>'
+                       });
+       
+                       rootScope.$digest();
+       
+                       var spans = container.find('span');
+                       
+                       expect(spans.length).toBe(1);
+                       expect(spans[0].outerHTML).toBe('<span>Close</span>');
+               });
+               
+               it('should show the close button if mergedConfig close-button 
is an object set to true for toast-info', function () {
+                       var container = angular.element(
+                               '<toaster-container 
toaster-options="{\'close-button\': {\'toast-info\': 
true}}"></toaster-container>');
+       
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+       
+                       toaster.pop({ type: 'info' });
+       
+                       rootScope.$digest();
+       
+                       var buttons = container.find('button');
+                       
+                       expect(buttons.length).toBe(1);
+                       expect(buttons[0].outerHTML).toBe('<button 
class="toast-close-button" type="button">×</button>');
+               });
+               
+               it('should not render the close button if mergedConfig 
close-button type cannot be found', function () {
+                       var container = angular.element(
+                               '<toaster-container 
toaster-options="{\'close-button\': {\'toast-invalid\': 
true}}"></toaster-container>');
+       
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+       
+                       toaster.pop({ type: 'info' });
+       
+                       rootScope.$digest();
+       
+                       var buttons = container.find('button');
+                       
+                       expect(buttons.length).toBe(0);
+                       expect(buttons[0]).toBeUndefined();
+               });
+               
+               it('should not render the close button if mergedConfig 
close-button is not an object', function () {
+                       var container = angular.element(
+                               '<toaster-container 
toaster-options="{\'close-button\': 1 }"></toaster-container>');
+       
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+       
+                       toaster.pop({ type: 'info' });
+       
+                       rootScope.$digest();
+       
+                       var buttons = container.find('button');
+                       
+                       expect(buttons.length).toBe(0);
+                       expect(buttons[0]).toBeUndefined();
+               });
+               
+               it('should render trustedHtml bodyOutputType', function () {
+                       var container = compileContainer();
+                       
+                       toaster.pop({ bodyOutputType: 'trustedHtml', body: 
'<section>Body</section>' });
+                       
+                       rootScope.$digest();
+       
+                       var body = container.find('section');
+                       
+                       expect(body.length).toBe(1);
+                       
expect(body[0].outerHTML).toBe('<section>Body</section>');
+               });
+               
+               it('should render template bodyOutputType when body is passed', 
function () {
+                       inject(function($templateCache) {
+                               
$templateCache.put('/templatepath/template.html', 
'<section>Template</section>');
+                       });
+                       
+                       var container = compileContainer();
+                       
+                       toaster.pop({ bodyOutputType: 'template', body: 
'/templatepath/template.html' });
+                       
+                       rootScope.$digest();
+       
+                       
expect(toaster.toast.body).toBe('/templatepath/template.html');
+       
+                       var body = container.find('section');
+                       
+                       expect(body.length).toBe(1);
+                       expect(body[0].outerHTML).toBe('<section 
class="ng-scope">Template</section>');
+               });
+               
+               it('should render default template bodyOutputType when body is 
not passed', function () {
+                       inject(function($templateCache) {
+                               $templateCache.put('toasterBodyTmpl.html', 
'<section>Template</section>');
+                       });
+                       
+                       var container = compileContainer();
+                       
+                       toaster.pop({ bodyOutputType: 'template' });
+                       
+                       rootScope.$digest();
+       
+                       
expect(toaster.toast.bodyTemplate).toBe('toasterBodyTmpl.html');
+       
+                       var body = container.find('section');
+                       
+                       expect(body.length).toBe(1);
+                       expect(body[0].outerHTML).toBe('<section 
class="ng-scope">Template</section>');
+               });
+               
+               it('should render templateWithData bodyOutputType when body is 
passed', function () {
+                       inject(function($templateCache) {
+                               $templateCache.put('template.html', 
'<section>Template {{toaster.data}}</section>');
+                       });
+                       
+                       var container = compileContainer();
+                       
+                       toaster.pop({ bodyOutputType: 'templateWithData', body: 
"{template: 'template.html', data: 123 }" });
+                       
+                       rootScope.$digest();
+       
+                       var body = container.find('section');
+                       
+                       expect(body.length).toBe(1);
+                       expect(body[0].outerHTML).toBe('<section 
class="ng-binding ng-scope">Template 123</section>');
+               });
+               
+               it('should throw exception for default templateWithData 
bodyOutputType when body is not passed', function () {
+                       // TODO:  If the default fallback template cannot be 
parsed to an object
+                       // composed of template and data, an exception is 
thrown.  This seems to 
+                       // be undesirable behavior.  A clearer exception should 
be thrown, or better
+                       // handling should be handled, or the fallback option 
should be removed.
+                       inject(function($templateCache) {
+                               $templateCache.put('template.html', 
'<section>Template {{toaster.data}}</section>');
+                       });
+                       
+                       compileContainer();
+                       var hasException = false;
+                       
+                       try {
+                               toaster.pop({ bodyOutputType: 
'templateWithData' });
+                       } catch (e) {
+                               expect(e.message).toBe("Cannot read property 
'template' of undefined");
+                               hasException = true;    
+                       }
+                       
+                       expect(hasException).toBe(true); 
+               });
+               
+               it('should remove first in toast if limit is met and 
newest-on-top is true', function () {
+                       var container = angular.element(
+                               '<toaster-container 
toaster-options="{\'limit\': 2, \'newest-on-top\': true 
}"></toaster-container>');
+                               
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       
+                       var scope = container.scope();
+                       
+                       toaster.pop({ type: 'info', body: 'first' });
+                       toaster.pop({ type: 'info', body: 'second' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(2);
+                       expect(scope.toasters[0].body).toBe('second');
+                       expect(scope.toasters[1].body).toBe('first');
+                       
+                       toaster.pop({ type: 'info', body: 'third' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(2);
+                       expect(scope.toasters[0].body).toBe('third');
+                       expect(scope.toasters[1].body).toBe('second');
+               });
+               
+               it('should remove last in toast if limit is met and 
newest-on-top is false', function () {
+                       var container = angular.element(
+                               '<toaster-container 
toaster-options="{\'limit\': 2, \'newest-on-top\': false 
}"></toaster-container>');
+                               
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       
+                       var scope = container.scope();
+                       
+                       toaster.pop({ type: 'info', body: 'first' });
+                       toaster.pop({ type: 'info', body: 'second' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(2);
+                       expect(scope.toasters[0].body).toBe('first');
+                       expect(scope.toasters[1].body).toBe('second');
+                       
+                       toaster.pop({ type: 'info', body: 'third' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(2);
+                       expect(scope.toasters[0].body).toBe('second');
+                       expect(scope.toasters[1].body).toBe('third');
+               });
+        
+        it('should invoke onShowCallback if it exists when toast is added', 
function () {
+                       compileContainer();
+                       var mock = {
+                               callback : function () { }
+                       };
+                       
+                       spyOn(mock, 'callback');
+                       
+                       toaster.pop({ type: 'info', body: 'toast 1', 
onShowCallback: mock.callback });
+                       
+                       rootScope.$digest();
+                       
+                       expect(mock.callback).toHaveBeenCalled();
+               });
+        
+        it('should not invoke onShowCallback if it does not exist when toast 
is added', function () {
+                       compileContainer();
+                       var mock = {
+                               callback : function () { }
+                       };
+                       
+                       spyOn(mock, 'callback');
+                       
+                       toaster.pop({ type: 'info', body: 'toast 1' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(mock.callback).not.toHaveBeenCalled();
+               });
+       });
+       
+       
+       describe('removeToast', function () {
+               it('should not remove toast if id does not match a toast id', 
function() {
+                       var container = compileContainer();
+                       var scope = container.scope();
+                       
+                       toaster.pop({ type: 'info', body: 'toast 1' });
+                       toaster.pop({ type: 'info', body: 'toast 2' });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(2);
+                       expect(scope.toasters[0].id).toBe(2)
+                       expect(scope.toasters[1].id).toBe(1)
+                       
+                       scope.removeToast(3);
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(2);
+               });
+       
+               it('should invoke onHideCallback if it exists when toast is 
removed', function () {
+                       var container = compileContainer();
+                       var scope = container.scope();
+                       
+                       var mock = {
+                               callback : function () { }
+                       };
+                       
+                       spyOn(mock, 'callback');
+                       
+                       toaster.pop({ type: 'info', body: 'toast 1', 
onHideCallback: mock.callback });
+                       
+                       rootScope.$digest();
+                       scope.removeToast(1);
+                       rootScope.$digest();
+                       
+                       expect(mock.callback).toHaveBeenCalled();
+               });
+       });
+
+
+       describe('scope._onNewTest', function () {
+               it('should not add toast if toasterId is passed to 
scope._onNewToast but toasterId is not set via config', function () {
+                       var container = compileContainer();
+                       var scope = container.scope();
+       
+                       expect(scope.config.toasterId).toBeUndefined();
+                       
+                       toaster.pop({ type: 'info', body: 'toast 1', toasterId: 
1 });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(0);
+               });
+               
+               it('should add toast if toasterId is passed to 
scope._onNewToast and toasterId is set via config', function () {
+                       var container = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 1 }"></toaster-container>');
+                                       
+                       $compile(container)(rootScope);
+                       rootScope.$digest();
+                       var scope = container.scope();
+       
+                       expect(scope.config.toasterId).toBe(1);
+                       
+                       toaster.pop({ type: 'info', body: 'toast 1', toasterId: 
1 });
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope.toasters.length).toBe(1);
+               });
+       
+               it('should add toasts to their respective container based on 
toasterId', function () {
+                       var container1 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 1 }"></toaster-container>');
+                       var container2 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 2 }"></toaster-container>');
+                                       
+                       $compile(container1)(rootScope);
+                       $compile(container2)(rootScope);
+                       rootScope.$digest();
+                       
+                       var scope1 = container1.scope();
+                       var scope2 = container2.scope();
+                               
+                       toaster.pop({ type: 'info', body: 'toast 1', toasterId: 
1 });
+                       toaster.pop({ type: 'info', body: 'toast 2', toasterId: 
2 });
+                               
+                       rootScope.$digest();
+                               
+                       expect(scope1.toasters.length).toBe(1);
+                       expect(scope2.toasters.length).toBe(1);
+               });
+       });
+
+       describe('scope._onClearToasts', function (){
+               it('should remove all toasts from all containers if toasterId 
is *', function () {
+                       var container1 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 1 }"></toaster-container>');
+                       var container2 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 2 }"></toaster-container>');
+                                       
+                       $compile(container1)(rootScope);
+                       $compile(container2)(rootScope);
+                       rootScope.$digest();
+                       
+                       var scope1 = container1.scope();
+                       var scope2 = container2.scope();
+                               
+                       toaster.pop({ type: 'info', body: 'toast 1', toasterId: 
1 });
+                       toaster.pop({ type: 'info', body: 'toast 2', toasterId: 
2 });
+                               
+                       rootScope.$digest();
+                               
+                       expect(scope1.toasters.length).toBe(1);
+                       expect(scope2.toasters.length).toBe(1);
+                       
+                       toaster.clear('*');
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope1.toasters.length).toBe(0);
+                       expect(scope2.toasters.length).toBe(0); 
+               });
+               
+               it('should remove all toasts from all containers if 
config.toasterId and toastId are undefined', function () {
+                       var container1 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'close-button\': false }"></toaster-container>');
+                       var container2 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'close-button\': true }" ></toaster-container>');
+                                       
+                       $compile(container1)(rootScope);
+                       $compile(container2)(rootScope);
+                       rootScope.$digest();
+                       
+                       var scope1 = container1.scope();
+                       var scope2 = container2.scope();
+                               
+                       toaster.pop({ type: 'info', body: 'toast 1' });
+                       toaster.pop({ type: 'info', body: 'toast 2' });
+                               
+                       rootScope.$digest();
+                               
+                       // since there are two separate instances of the 
container  
+                       // without a toasterId, both receive the newToast event
+                       expect(scope1.toasters.length).toBe(2);
+                       expect(scope2.toasters.length).toBe(2);
+                       
+                       toaster.clear();
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope1.toasters.length).toBe(0);
+                       expect(scope2.toasters.length).toBe(0);
+               });
+               
+               it('should not remove by toasterId / toastId from the correct 
container if toast.toasterId is defined and toast.toastId is undefined', 
function () {
+                       var container1 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 1 }"></toaster-container>');
+                       var container2 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 2 }"></toaster-container>');
+                                       
+                       $compile(container1)(rootScope);
+                       $compile(container2)(rootScope);
+                       rootScope.$digest();
+                       
+                       var scope1 = container1.scope();
+                       var scope2 = container2.scope();
+                       
+                       // removeAllToasts explicitly looks for toast.uid, 
which is only set
+                       // if toastId is passed as a parameter
+                       toaster.pop({ type: 'info', body: 'toast 1', toasterId: 
1 });
+                       toaster.pop({ type: 'info', body: 'toast 2', toasterId: 
2 });
+                       toaster.pop({ type: 'info', body: 'toast 3', toasterId: 
2 });
+                               
+                       rootScope.$digest();
+                               
+                       expect(scope1.toasters.length).toBe(1);
+                       expect(scope2.toasters.length).toBe(2);
+                       
+                       toaster.clear(2, 1);
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope1.toasters.length).toBe(1);
+                       expect(scope2.toasters.length).toBe(2);
+               });
+               
+               it('should remove by toasterId / toastId from the correct 
container if toasterId is defined and toastId is defined', function () {
+                       var container1 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 1 }"></toaster-container>');
+                       var container2 = angular.element(
+                               '<toaster-container toaster-options="{ 
\'toaster-id\': 2 }"></toaster-container>');
+                                       
+                       $compile(container1)(rootScope);
+                       $compile(container2)(rootScope);
+                       rootScope.$digest();
+                       
+                       var scope1 = container1.scope();
+                       var scope2 = container2.scope();
+                       
+                       // removeAllToasts explicitly looks for toast.uid, 
which is only set
+                       // if toastId is passed as a parameter
+                       toaster.pop({ type: 'info', body: 'toast 1', toasterId: 
1, toastId: 1 });
+                       toaster.pop({ type: 'info', body: 'toast 2', toasterId: 
2, toastId: 1 });
+                       toaster.pop({ type: 'info', body: 'toast 3', toasterId: 
2, toastId: 2 });
+                               
+                       rootScope.$digest();
+                               
+                       expect(scope1.toasters.length).toBe(1);
+                       expect(scope2.toasters.length).toBe(2);
+                       
+                       toaster.clear(2, 1);
+                       
+                       rootScope.$digest();
+                       
+                       expect(scope1.toasters.length).toBe(1);
+                       expect(scope2.toasters.length).toBe(1);
+               });
+       });
+});
+
+
+describe('toasterContainer', function () {
+       var $interval, $intervalSpy;
+
+       inject(function (_$interval_) {
+               $interval = _$interval_;
+       });
+
+       beforeEach(function () {
+               $intervalSpy = jasmine.createSpy('$interval', $interval);
+
+               module('toaster', function ($provide) {
+                       $provide.value('$interval', $intervalSpy);
+               });
+
+               // inject the toaster service
+               inject(function (_toaster_, _$rootScope_, _$compile_) {
+                       toaster = _toaster_;
+                       rootScope = _$rootScope_;
+                       $compile = _$compile_;
+               });
+       });
+
+       it('should use the toast.timeout argument if it is a valid number', 
function () {
+               var container = compileContainer();
+               var scope = container.scope();
+
+               spyOn(scope, 'configureTimer').and.callThrough();
+
+               toaster.pop({ timeout: 2 });
+
+               expect(scope.configureTimer).toHaveBeenCalled();
+               
expect(scope.configureTimer.calls.allArgs()[0][0].timeout).toBe(2);
+               expect($intervalSpy.calls.first().args[1]).toBe(2)
+       });
+
+       it('should not use the toast.timeout argument if not a valid number', 
function () {
+               var container = compileContainer();
+               var scope = container.scope();
+
+               spyOn(scope, 'configureTimer').and.callThrough();
+
+               toaster.pop({ timeout: "2" });
+
+               expect(scope.configureTimer).toHaveBeenCalled();
+               
expect(scope.configureTimer.calls.allArgs()[0][0].timeout).toBe("2");
+               expect($intervalSpy.calls.first().args[1]).toBe(5000);
+       });
+
+       it('should call scope.removeToast when toast.timeoutPromise expires', 
function () {
+               var container = compileContainer();
+               var scope = container.scope();
+
+               spyOn(scope, 'removeToast').and.callThrough();
+
+               toaster.pop({ timeout: 2 });
+
+               $intervalSpy.calls.first().args[0]();
+
+               rootScope.$digest();
+
+               expect(scope.removeToast).toHaveBeenCalled();
+       });
+
+       it('should retrieve timeout by toast type if mergedConfig toast-timeout 
is an object', function () {
+               var element = angular.element(
+                       '<toaster-container toaster-options="{\'time-out\': 
{\'toast-info\': 5}}"></toaster-container>');
+
+               $compile(element)(rootScope);
+               rootScope.$digest();
+
+               toaster.pop({ type: 'info' });
+
+               expect($intervalSpy.calls.first().args[1]).toBe(5);
+       });
+
+       it('should not set a timeout if mergedConfig toast-timeout is an object 
and does not match toast type', function () {
+               // TODO: this seems to be a bug in the toast-timeout 
configuration option.
+               // It should fall back to a default value if the toast type 
configuration
+               // does not match the target toast type or throw an exception 
to warn of an
+               // invalid configuration.
+               
+               var element = angular.element(
+                       '<toaster-container toaster-options="{\'time-out\': 
{\'toast-info\': 5}}"></toaster-container>');
+
+               $compile(element)(rootScope);
+               rootScope.$digest();
+
+               toaster.pop({ type: 'warning' });
+
+               expect($intervalSpy.calls.all().length).toBe(0);
+       });
+});
+
+
+function compileContainer() {
+       var element = 
angular.element('<toaster-container></toaster-container>');
+       $compile(element)(rootScope);
+       rootScope.$digest();
+
+       return element;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/test/toasterEventRegistrySpec.js
----------------------------------------------------------------------
diff --git 
a/ui/bower_components/AngularJS-Toaster/test/toasterEventRegistrySpec.js 
b/ui/bower_components/AngularJS-Toaster/test/toasterEventRegistrySpec.js
new file mode 100644
index 0000000..4c8991f
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/test/toasterEventRegistrySpec.js
@@ -0,0 +1,76 @@
+/* global describe global it global beforeEach global angular global inject 
global expect */
+
+'use strict';
+
+describe('toasterEventRegistry', function () {
+       var toaster, toasterConfig, toasterEventRegistry, rootScope, $compile;
+
+       beforeEach(function () {
+               // load dependencies
+               module('testApp');
+               module('toaster')
+               
+               // inject the toaster service
+        inject(function (_toaster_, _toasterConfig_, _toasterEventRegistry_, 
_$rootScope_, _$compile_) {
+                       toaster = _toaster_;
+                       toasterConfig = _toasterConfig_;        
+                       toasterEventRegistry = _toasterEventRegistry_;
+                       rootScope = _$rootScope_;
+                       $compile = _$compile_;
+               });
+       });
+       
+       it('unsubscribeToNewToastEvent will throw error if 
newToastEventSubscribers is empty and deregisterNewToast is not defined', 
function () {
+               var hasError = false;
+               
+               try {
+                       
toasterEventRegistry.unsubscribeToNewToastEvent(function () {});        
+               } catch(e) {
+                       expect(e.message.indexOf(' is not a 
function')).toBeGreaterThan(-1);
+                       hasError = true;
+               }
+               
+               expect(hasError).toBe(true);
+       });
+       
+       it('unsubscribeToNewToastEvent will not splice if index not found and 
will not throw error', function () {
+               var hasError = false;
+               var fakeHandler = function (fakeHandlerId) {};
+               toasterEventRegistry.subscribeToNewToastEvent(fakeHandler);
+               
+               try {
+                       
toasterEventRegistry.unsubscribeToNewToastEvent(function () {});        
+               } catch(e) {
+                       hasError = true;
+               }
+               
+               expect(hasError).toBe(false);
+       });
+       
+       it('unsubscribeToClearToastsEvent will throw error if 
clearToastsEventSubscribers is empty and deregisterClearToasts is not defined', 
function () {
+               var hasError = false;
+               
+               try {
+                       
toasterEventRegistry.unsubscribeToClearToastsEvent(function () {});     
+               } catch(e) {
+                       expect(e.message.indexOf(' is not a 
function')).toBeGreaterThan(-1);
+                       hasError = true;
+               }
+               
+               expect(hasError).toBe(true);
+       });
+       
+       it('unsubscribeToClearToastsEvent will not splice if index not found 
and will not throw error', function () {
+               var hasError = false;
+               var fakeHandler = function (fakeHandlerId) {};
+               toasterEventRegistry.subscribeToClearToastsEvent(fakeHandler);
+               
+               try {
+                       
toasterEventRegistry.unsubscribeToClearToastsEvent(function () {});     
+               } catch(e) {
+                       hasError = true;
+               }
+               
+               expect(hasError).toBe(false);
+       });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/test/toasterServiceSpec.js
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/test/toasterServiceSpec.js 
b/ui/bower_components/AngularJS-Toaster/test/toasterServiceSpec.js
new file mode 100644
index 0000000..77de3dd
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/test/toasterServiceSpec.js
@@ -0,0 +1,136 @@
+/* global describe global it global beforeEach global angular global inject 
global expect */
+
+'use strict';
+
+describe('toasterService', function () {
+       var toaster, toasterConfig, rootScope, $compile;
+
+       beforeEach(function () {
+               // load dependencies
+               module('testApp');
+               module('toaster')
+               
+               // inject the toaster service
+        inject(function (_toaster_, _toasterConfig_, _$rootScope_, _$compile_) 
{
+                       toaster = _toaster_;
+                       toasterConfig = _toasterConfig_;        
+                       rootScope = _$rootScope_;
+                       $compile = _$compile_;
+               });
+       });
+       
+       
+       it('should create an error method from error icon class', function () {
+               var container = 
angular.element('<toaster-container></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+               
+               expect(scope.toasters.length).toBe(0)
+               
+               expect(toasterConfig['icon-classes'].error).toBe('toast-error');
+               
+               toaster.error('test', 'test');
+               
+               rootScope.$digest();
+               
+               expect(scope.toasters.length).toBe(1)
+               expect(scope.toasters[0].type).toBe('toast-error');
+       });
+       
+       it('should create an error method from info icon class', function () {
+               var container = 
angular.element('<toaster-container></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+               
+               expect(scope.toasters.length).toBe(0)
+               
+               expect(toasterConfig['icon-classes'].info).toBe('toast-info');
+               
+               toaster.info('test', 'test');
+               
+               rootScope.$digest();
+               
+               expect(scope.toasters.length).toBe(1)
+               expect(scope.toasters[0].type).toBe('toast-info');
+       });
+       
+       it('should create an error method from wait icon class', function () {
+               var container = 
angular.element('<toaster-container></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+               
+               expect(scope.toasters.length).toBe(0)
+               
+               expect(toasterConfig['icon-classes'].wait).toBe('toast-wait');
+               
+               toaster.wait('test', 'test');
+               
+               rootScope.$digest();
+               
+               expect(scope.toasters.length).toBe(1)
+               expect(scope.toasters[0].type).toBe('toast-wait');
+       });
+       
+       it('should create an error method from success icon class', function () 
{
+               var container = 
angular.element('<toaster-container></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+               
+               expect(scope.toasters.length).toBe(0)
+               
+               
expect(toasterConfig['icon-classes'].success).toBe('toast-success');
+               
+               toaster.success('test', 'test');
+               
+               rootScope.$digest();
+               
+               expect(scope.toasters.length).toBe(1)
+               expect(scope.toasters[0].type).toBe('toast-success');
+       });
+       
+       it('should create an error method from warning icon class', function () 
{
+               var container = 
angular.element('<toaster-container></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+               
+               expect(scope.toasters.length).toBe(0)
+               
+               
expect(toasterConfig['icon-classes'].warning).toBe('toast-warning');
+               
+               toaster.warning('test', 'test');
+               
+               rootScope.$digest();
+               
+               expect(scope.toasters.length).toBe(1)
+               expect(scope.toasters[0].type).toBe('toast-warning');
+       });
+       
+       it('should create a  method from the icon class that takes an object', 
function () {
+               var container = 
angular.element('<toaster-container></toaster-container>');
+
+               $compile(container)(rootScope);
+               rootScope.$digest();
+               var scope = container.scope();
+               
+               expect(scope.toasters.length).toBe(0)
+               
+               expect(toasterConfig['icon-classes'].error).toBe('toast-error');
+               
+               toaster.error({ title: 'test', body: 'test'});
+               
+               rootScope.$digest();
+               
+               expect(scope.toasters.length).toBe(1)
+               expect(scope.toasters[0].type).toBe('toast-error');
+       });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/AngularJS-Toaster/toaster.scss
----------------------------------------------------------------------
diff --git a/ui/bower_components/AngularJS-Toaster/toaster.scss 
b/ui/bower_components/AngularJS-Toaster/toaster.scss
new file mode 100644
index 0000000..4e9377b
--- /dev/null
+++ b/ui/bower_components/AngularJS-Toaster/toaster.scss
@@ -0,0 +1,292 @@
+/*
+ * Toastr
+ * Version 2.0.1
+ * Copyright 2012 John Papa and Hans Fjällemark.  
+ * All Rights Reserved.  
+ * Use, reproduction, distribution, and modification of this code is subject 
to the terms and 
+ * conditions of the MIT license, available at 
http://www.opensource.org/licenses/mit-license.php
+ *
+ * Author: John Papa and Hans Fjällemark
+ * Project: https://github.com/CodeSeven/toastr
+ *
+ *
+ * SCSS File
+ * Author: Damian Szymczuk
+ * GitHub: https://github.com/dszymczuk
+ *
+ */
+
+
+/* Variables */
+$textColor: #ffffff !default;
+$textColorHover: #cccccc !default;
+$closeButton: #ffffff !default;
+$closeButtonHover: #000000 !default;
+
+$fontSize: 20px !default;
+
+$toast: #030303 !default;
+$toastSuccess: #51a351 !default;
+$toastError: #bd362f !default;
+$toastInfo: #2f96b4 !default;
+$toastWarning: #f89406 !default;
+
+
+$toastPositionFullWidthTop: 0 !default;
+$toastPositionFullWidthBottom: 0 !default;
+
+$toastPossitionTop: 12px !default;
+$toastPossitionLeft: 12px !default;
+$toastPossitionRight: 12px !default;
+$toastPossitionBottom: 12px !default;
+
+$toastContainerColor: #ffffff !default;
+$toastContainerShadowColor: #999999 !default;
+$toastContainerShadowColorHover: #000000 !default;
+
+
+.toast-title {
+  font-weight: bold;
+}
+
+.toast-message {
+  -ms-word-wrap: break-word;
+  word-wrap: break-word;
+  a, label {
+    color: $textColor;
+  }
+  a:hover {
+    color: $textColorHover;
+    text-decoration: none;
+  }
+}
+
+.toast-close-button {
+  position: relative;
+  right: -0.3em;
+  top: -0.3em;
+  float: right;
+  font-size: $fontSize;
+  font-weight: bold;
+  color: $closeButton;
+  -webkit-text-shadow: 0 1px 0 $closeButton;
+  text-shadow: 0 1px 0 $closeButton;
+  opacity: 0.8;
+  -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
+  filter: alpha(opacity = 80);
+  &:hover, &:focus {
+    color: $closeButtonHover;
+    text-decoration: none;
+    cursor: pointer;
+    opacity: 0.4;
+    -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
+    filter: alpha(opacity = 40);
+  }
+}
+
+/*Additional properties for button version
+ iOS requires the button element instead of an anchor tag.
+ If you want the anchor version, it requires `href="#"`.*/
+
+button.toast-close-button {
+  padding: 0;
+  cursor: pointer;
+  background: transparent;
+  border: 0;
+  -webkit-appearance: none;
+}
+
+.toast-top-full-width {
+  top: $toastPositionFullWidthTop;
+  right: 0;
+  width: 100%;
+}
+
+.toast-bottom-full-width {
+  bottom: $toastPositionFullWidthBottom;
+  right: 0;
+  width: 100%;
+}
+
+.toast-top-left {
+  top: $toastPossitionTop;
+  left: $toastPossitionLeft;
+}
+
+.toast-top-center {
+  top: $toastPossitionTop;
+}
+
+.toast-top-right {
+  top: $toastPossitionTop;
+  right: $toastPossitionRight;
+}
+
+.toast-bottom-right {
+  right: $toastPossitionRight;
+  bottom: $toastPossitionBottom;
+}
+
+.toast-bottom-center {
+  bottom: $toastPossitionBottom;
+}
+
+.toast-bottom-left {
+  bottom: $toastPossitionBottom;
+  left: $toastPossitionLeft;
+}
+
+.toast-center {
+  top: 45%;
+}
+
+#toast-container {
+  position: fixed;
+  z-index: 999999;
+  /*overrides*/
+  &.toast-center, &.toast-top-center, &.toast-bottom-center {
+    width: 100%;
+    pointer-events: none;
+  }
+  &.toast-center > div, &.toast-top-center > div, &.toast-bottom-center > div {
+    margin: auto;
+    pointer-events: auto;
+  }
+  &.toast-center > button, &.toast-top-cente > button, &.toast-bottom-center > 
button {
+    pointer-events: auto;
+  }
+  * {
+    -moz-box-sizing: border-box;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+  }
+  > {
+    div {
+      margin: 0 0 6px;
+      padding: 15px 15px 15px 50px;
+      width: 300px;
+      -moz-border-radius: 3px 3px 3px 3px;
+      -webkit-border-radius: 3px 3px 3px 3px;
+      border-radius: 3px 3px 3px 3px;
+      background-position: 15px center;
+      background-repeat: no-repeat;
+      -moz-box-shadow: 0 0 12px $toastContainerShadowColor;
+      -webkit-box-shadow: 0 0 12px $toastContainerShadowColor;
+      box-shadow: 0 0 12px $toastContainerShadowColor;
+      color: $toastContainerColor;
+      opacity: 0.8;
+      -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
+      filter: alpha(opacity = 80);
+    }
+    :hover {
+      -moz-box-shadow: 0 0 12px $toastContainerShadowColorHover;
+      -webkit-box-shadow: 0 0 12px $toastContainerShadowColorHover;
+      box-shadow: 0 0 12px $toastContainerShadowColorHover;
+      opacity: 1;
+      -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
+      filter: alpha(opacity = 100);
+      cursor: pointer;
+    }
+    .toast-info {
+      background-image: 
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=")
 !important;
+    }
+    .toast-wait {
+      background-image: 
url("data:image/gif;base64,R0lGODlhIAAgAIQAAAQCBISGhMzKzERCROTm5CQiJKyurHx+fPz+/ExOTOzu7Dw+PIyOjCwqLFRWVAwKDIyKjMzOzOzq7CQmJLy6vFRSVPTy9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCQAXACwAAAAAIAAgAAAF3eAljmRpnmh6VRSVqLDpIDTixOdUlFSNUDhSQUAT7ES9GnD0SFQAKWItMqr4bqKHVPDI+WiTkaOFFVlrFe83rDrT0qeIjwrT0iLdU0GOiBxhAA4VeSk6QYeIOAsQEAuJKgw+EI8nA18IA48JBAQvFxCXDI8SNAQikV+iiaQIpheWX5mJmxKeF6g0qpQmA4yOu8C7EwYWCgZswRcTFj4KyMAGlwYxDwcHhCXMXxYxBzQHKNo+3DDeCOAn0V/TddbYJA0K48gAEAFQicMWFsfwNA3JSgAIAAFfwIMIL4QAACH5BAkJABoALAAAAAAgACAAhAQCBIyKjERCRMzOzCQiJPTy9DQyNGRmZMTCxOTm5CwqLHx+fBQWFJyenNTW1Pz6/Dw6PGxubAwKDIyOjNTS1CQmJCwuLPz+/Dw+PHRydAAAAAAAAAAAAAAAAAAAAAAAAAXboCaOZGmeaKoxWcSosMkk15W8cZ7VdZaXkcEgQtrxfD9RhHchima1GwlCGUBSFCaFxMrgRtnLFhWujWHhs2nJc8KoVlWGQnEn7/i8XgOwWAB7JwoONQ4KgSQAZRcOgHgSCwsSIhZMNRZ5CzULIgaWF5h4mhecfIQ8jXmQkiODhYeIiRYGjrG2PxgBARi3IhNMAbcCnwI5BAQpAZ8TIwK6vCQVDwUVKL+WzAANTA210g/VJ8OWxQefByQE4dZMzBoInwh4zrtgn2p725YNthUFTNRuGYB3AYGBHCEAACH5BA
 
kJAB0ALAAAAAAgACAAhAQCBISChFRWVMzKzCQiJOTm5GxqbCwuLJSWlPz6/NTW1AwODJSSlGRmZCwqLOzu7HR2dDQ2NAQGBISGhFxaXNTS1CQmJOzq7GxubDQyNKSmpPz+/Nza3AAAAAAAAAAAAAXfYCeOZGmeaKqurHBdAiuP17Zdc0lMAVHWt9yI8LA9fCPB4xEjARoNSWpis01kBpshFahurqzsZosiGpErScMAUO0maKF8Tq/bTQCIQgFp30cQXhB1BHEcXhx0FgkJFiOHVYlzi42AgoRxeRx8fn+en3UABwedKgsBAwMBCygOCjYKDisLFV4VrCUAtVUKpSZdXl8mB8EbByQWcQPFAyYZxccdB7sV0cvBzbmvvG0LBV4FrFTBYCWuNhyyHRTFFB20trh4BxmdYl4YIqepq0IRxRE+IfDCAFQHARo0NGERAgAh+QQJCQAgACwAAAAAIAAgAIUEAgSEgoRMTkzMyswcHhzk5uR0cnQUFhRcXlwsKiz09vQMCgyMiozU1tQkJiR8fnxkZmT8/vwEBgSEhoRcWlzU0tQkIiT08vR0dnQcGhxkYmQ0MjT8+vwMDgyMjozc2twAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG+UCQcEgsGo/IpHLJXDweC6Z0+IhEHlOjRGIMWLHZoUZx0RQlAajxkFFKFFYFl5m5KNpIySU+X2bIBEoQZBBZGQdMElFhjI2Oj5AgHQEDAw8dQxYeDBaNHRVWVhWYCXsRFwmMXqFWEyAerB6MA6xWA6+xs7URt6VWqIwTu64gDh4eDp6goaORQ5OVAZjO1EgEGhB4RwAYDQ0YAEwIcBEKFEgYrBhLBORxgUYfrB9LELuF8fNDAAaVBuEg7NXCVyRdqHVCGLBiIIQ
 
AB1Yc4BXh9uEbwAXuyi2iQI7DuSwHdiFqCEGDtizLRFUDsaGAlQIbVoJYIEDAIiZBAAAh+QQJCQAbACwAAAAAIAAgAIQEAgSMioxcWlz08vQcHhysqqwMDgx8enwsKiykoqRkZmT8+vzEwsQMCgyUlpQkJiS0srQEBgSMjoxcXlz09vQkIiSsrqwUEhQ0MjRsamz8/vwAAAAAAAAAAAAAAAAAAAAF7+AmjmRpnmiqruz2PG0sIssCj4CQJAIgj4/abRNJaI6agu9kCAQaphdJgEQKUIFjgGWsahJYLdf7RTWfLKr3+jsBClVlG5Xb9eb4fImgUBBKDVB4ExRHFGwbGRQLGXMEhUgUfw2QC4IyCmSNDQtHlm2ZXgoiGQsUjW0EnUgLfyKBeYSeiHojfH61uS0GBisVEgEVLRcWRxAXKAgDRwMILMVIECgSVRIrBmS9JtRI1iMVBweuGxerSNolyszOIhjLGs0jEFXSKA8SEkMbcEgWIxfzNBxrw6AKgxIGkM05UOWALhERHJhysOThBgAVWYQAACH5BAkJABkALAAAAAAgACAAhAQGBIyKjERCRMzOzCwuLGRiZPz6/OTm5AwODLSytFRSVNTW1Dw6PHx6fAwKDJSSlERGRNTS1DQyNGxqbPz+/BQSFLy6vFRWVNza3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAXqYCaO5FgFwxBUZeu61ULNFMa+eBvQdJD/owFvFhkBBAwHsBQZUooZyWF2YOQkBNJu6ANMaQeli0AxSEwymi0DcUJeEgPlbEJFAghRe/h+Eeg/Dl9UYks5DF9VhksOAgKFi5GSSwh5kzgVCXIJNxknD5aSCTwJIw8zD5MITpanFKmSCHI8NxUPoJejNKWXLZkznL0vCJ3CxsckDpA/ChYJFzkTBgYTSxc80C4OswbLLhY8Fi/bMwYAJVgl4DTiL9LUJADrFuci1zTZLwD1IwU8BSQuWLCQb1EDHg2QiSDALYvC
 
DAISJLDy8FIIACH5BAkJAB4ALAAAAAAgACAAhAQGBISGhFRSVNTW1CQiJKyqrGRmZOzu7CwuLIyOjGxubPz6/BQSFGRiZOTi5CwqLLy6vDQ2NIyKjFRWVCQmJKyurGxqbPT29DQyNJSSlHRydPz+/BQWFOzq7AAAAAAAAAXhoCeOJElYClGubOs117YtjWuvxCLLi3qbhc6h4FPsdorfiNI5dige43GT9AAkHUcCwCpMNxVP7tgTJY4J1uF7EBl0M8Ooueuo2SOCIkVa11kVX2E2EmgsFH4yBz4uAAkdHVstBAUHQ4xKmZqbnJ2bAhAQAiURGJ4eE0cTIxgzpp0QRxCsrp6xO7MjpaepO6unKxOhv8DFxsfIJBwaChw2DAkZDEocDjIOzi0ZMhlKUjIaLtsb3T8aR+EtDBkJ0yQUBQVQI9XX2ZsDMgMlyxr3mzE2XEgmotCGAARFIHiQ0FMIACH5BAkJABgALAAAAAAgACAAhAQCBISGhDw+POTi5CwuLLS2tPTy9BQSFJyenGRiZDQ2NIyOjLy+vPz6/BweHIyKjFRSVOzq7DQyNLy6vBQWFHRydDw6PPz+/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXICaOZHkcZaquIjVd10SxtFrAcFGrVhBYIwoON9uNAsOA6DCEFTEKBEKxEjQvAtELNxkpGrAGNfW4Plpb2QgxRKjKzfPoVGLj3CnLNUv7hscpSDhKOxJSgDwPP0ZGAACMjAQFDQYFBJA0BAZDBpeYGBQVFUU3TV2YFAMwAzNgTQ2PkBVDFRiuQ7CYszi1pUOnkKmrM5qcnqiiTwQTDQ2Wn9DR0tPUfRKQEBEREDQSFw3XRhEwEd3f4TvjF+XWKgJ8JNnb0QkwCdUlCzAL+CQODAwc9BtIMAQAOw==")
 !important;
+    }
+    .toast-error {
+      background-image: 
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=")
 !important;
+    }
+    .toast-success {
+      background-image: 
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==")
 !important;
+    }
+    .toast-warning {
+      background-image: 
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=")
 !important;
+    }
+  }
+  &.toast-top-full-width > div, &.toast-bottom-full-width > div {
+    width: 96%;
+    margin: auto;
+  }
+}
+
+.toast {
+  background-color: $toast;
+}
+
+.toast-success {
+  background-color: $toastSuccess;
+}
+
+.toast-error {
+  background-color: $toastError;
+}
+
+.toast-info, .toast-wait {
+  background-color: $toastInfo;
+}
+
+.toast-warning {
+  background-color: $toastWarning;
+}
+
+/*Responsive Design*/
+@media all and (max-width: 240px) {
+  #toast-container {
+    > div {
+      padding: 8px 8px 8px 50px;
+      width: 11em;
+    }
+    .toast-close-button {
+      right: -0.2em;
+      top: -0.2em;
+    }
+  }
+}
+
+@media all and (min-width: 241px) and (max-width: 480px) {
+  #toast-container {
+    > div {
+      padding: 8px 8px 8px 50px;
+      width: 18em;
+    }
+    .toast-close-button {
+      right: -0.2em;
+      top: -0.2em;
+    }
+  }
+}
+
+@media all and (min-width: 481px) and (max-width: 768px) {
+  #toast-container > div {
+    padding: 15px 15px 15px 50px;
+    width: 25em;
+  }
+}
+
+/*
+ * AngularJS-Toaster
+ * Version 0.3
+*/
+
+:not(.no-enter)#toast-container > div.ng-enter, 
:not(.no-leave)#toast-container > div.ng-leave {
+  -webkit-transition: 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75) all;
+  -moz-transition: 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75) all;
+  -ms-transition: 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75) all;
+  -o-transition: 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75) all;
+  transition: 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75) all;
+}
+
+:not(.no-enter)#toast-container > div.ng-enter.ng-enter-active {
+  opacity: 0.8;
+}
+
+:not(.no-leave)#toast-container > div.ng-leave {
+  opacity: 0.8;
+  &.ng-leave-active {
+    opacity: 0;
+  }
+}
+
+:not(.no-enter)#toast-container > div.ng-enter {
+  opacity: 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/angular-animate/.bower.json
----------------------------------------------------------------------
diff --git a/ui/bower_components/angular-animate/.bower.json 
b/ui/bower_components/angular-animate/.bower.json
new file mode 100644
index 0000000..7850c64
--- /dev/null
+++ b/ui/bower_components/angular-animate/.bower.json
@@ -0,0 +1,20 @@
+{
+  "name": "angular-animate",
+  "version": "1.6.4",
+  "license": "MIT",
+  "main": "./angular-animate.js",
+  "ignore": [],
+  "dependencies": {
+    "angular": "1.6.4"
+  },
+  "homepage": "https://github.com/angular/bower-angular-animate";,
+  "_release": "1.6.4",
+  "_resolution": {
+    "type": "version",
+    "tag": "v1.6.4",
+    "commit": "a6c7e14a23096348e55a342a8f671466239ed3ab"
+  },
+  "_source": "https://github.com/angular/bower-angular-animate.git";,
+  "_target": ">1.2.8",
+  "_originalSource": "angular-animate"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/angular-animate/LICENSE.md
----------------------------------------------------------------------
diff --git a/ui/bower_components/angular-animate/LICENSE.md 
b/ui/bower_components/angular-animate/LICENSE.md
new file mode 100644
index 0000000..2c395ee
--- /dev/null
+++ b/ui/bower_components/angular-animate/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Angular
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/bc1ffbcd/ui/bower_components/angular-animate/README.md
----------------------------------------------------------------------
diff --git a/ui/bower_components/angular-animate/README.md 
b/ui/bower_components/angular-animate/README.md
new file mode 100644
index 0000000..8313da6
--- /dev/null
+++ b/ui/bower_components/angular-animate/README.md
@@ -0,0 +1,68 @@
+# packaged angular-animate
+
+This repo is for distribution on `npm` and `bower`. The source for this module 
is in the
+[main AngularJS 
repo](https://github.com/angular/angular.js/tree/master/src/ngAnimate).
+Please file issues and pull requests against that repo.
+
+## Install
+
+You can install this package either with `npm` or with `bower`.
+
+### npm
+
+```shell
+npm install angular-animate
+```
+
+Then add `ngAnimate` as a dependency for your app:
+
+```javascript
+angular.module('myApp', [require('angular-animate')]);
+```
+
+### bower
+
+```shell
+bower install angular-animate
+```
+
+Then add a `<script>` to your `index.html`:
+
+```html
+<script src="/bower_components/angular-animate/angular-animate.js"></script>
+```
+
+Then add `ngAnimate` as a dependency for your app:
+
+```javascript
+angular.module('myApp', ['ngAnimate']);
+```
+
+## Documentation
+
+Documentation is available on the
+[AngularJS docs site](http://docs.angularjs.org/api/ngAnimate).
+
+## License
+
+The MIT License
+
+Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.


Reply via email to