Compare commits
158 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 106b0f3de8 | |||
| 91968b9f4b | |||
| b3d41c6504 | |||
| d881d97c06 | |||
| 1dd1ce2f47 | |||
| 640c0b39c1 | |||
| 855a473d68 | |||
| 25a4607ad6 | |||
| 064ffe2a7c | |||
| e3f90575bf | |||
| 4d759fbfb9 | |||
| 9c854cb39d | |||
| 27799e7033 | |||
| 3ac1ac6086 | |||
| f984770906 | |||
| 01db85ebc7 | |||
| cdf4767d7d | |||
| 9bdd96527c | |||
| 13e3fed351 | |||
| 115a293e58 | |||
| 07dd08a6d0 | |||
| 9c985e63ee | |||
| c57a75f78f | |||
| a96298db59 | |||
| 061921af8e | |||
| 6719c84a7a | |||
| 13f3335098 | |||
| a79c26a7a4 | |||
| abccb316f8 | |||
| f14e954a9b | |||
| 5d4a2a7c25 | |||
| ce90515c95 | |||
| 5a51495432 | |||
| b99fe5bf55 | |||
| d243a83c5c | |||
| 6cb658a1ef | |||
| 8a7f8f7c37 | |||
| b7daf91723 | |||
| 5dfc079afd | |||
| f7949eb1d3 | |||
| 7e057bb3bb | |||
| 66894e7596 | |||
| 7748adf0e6 | |||
| 758ac41648 | |||
| 3db2008c39 | |||
| 34a3f8186a | |||
| 8e0baf0e37 | |||
| 017863bfc0 | |||
| f4ef63b8f8 | |||
| 394f2f2387 | |||
| 5dd094e0b1 | |||
| 7d073fdf0d | |||
| 07e10317fc | |||
| e806214d0b | |||
| 419bf0aa77 | |||
| cad8a6dc91 | |||
| ef3596ff32 | |||
| 74dcec8b6b | |||
| c0ae0d8d3f | |||
| 4637c5fbbd | |||
| 74c92de1d3 | |||
| 1688dbd8a0 | |||
| f167f5b49e | |||
| 968efed6c3 | |||
| c10dc38a89 | |||
| 2bc6b2995d | |||
| edd6a41d6e | |||
| 9e713b7b81 | |||
| 1f8f08e998 | |||
| 008884ec59 | |||
| 5a21923f5b | |||
| 230bfd15ac | |||
| 7cda9a0c7b | |||
| 6012453d2d | |||
| 2514672c35 | |||
| ae3d1c305d | |||
| d04b17efeb | |||
| 0520fa739a | |||
| 964eac6d53 | |||
| ede37e2e79 | |||
| 6080e097da | |||
| 14bfc57870 | |||
| 32a4703a53 | |||
| f40d645fb3 | |||
| 961b72072a | |||
| d6c524f960 | |||
| c971189286 | |||
| f29eaa28ba | |||
| 68ab1a302d | |||
| 9afe57e0ec | |||
| fb0a0827b6 | |||
| 22cdb94e5d | |||
| 974ed1fe05 | |||
| 66ba60c475 | |||
| 65f7e32f94 | |||
| dd21a4025b | |||
| 2223c8f1d1 | |||
| 3635e95138 | |||
| 1843836f22 | |||
| 059115e523 | |||
| 44ba4e8bb8 | |||
| 2adaa33fdb | |||
| b257216194 | |||
| df5e7bc867 | |||
| 53041efaf1 | |||
| 87131f4ae3 | |||
| f66bcfd4ea | |||
| 1fb7338838 | |||
| da60a70f43 | |||
| 8848d2d53c | |||
| ad6e0acab1 | |||
| 14d4397868 | |||
| f32aadedfb | |||
| b888081f54 | |||
| b00b61ce25 | |||
| 9284e4b451 | |||
| 716e25703e | |||
| 65d0b0110d | |||
| 9baa85f2e4 | |||
| 22287e6a56 | |||
| 0d4274fdeb | |||
| 4d9d66e9f9 | |||
| c7ebe9d881 | |||
| c798a95fb3 | |||
| d11c849a0f | |||
| 8abb445bd4 | |||
| dabb1926a5 | |||
| 829ff5da73 | |||
| aead1fcc29 | |||
| ccd27f203d | |||
| 3d251fdba4 | |||
| 4b4edc6c22 | |||
| 990456dbdd | |||
| fd68039cb9 | |||
| a80971dd27 | |||
| 687ed7bac2 | |||
| 314fc51f6d | |||
| f75d298fe1 | |||
| 4682ae4ca7 | |||
| c8213bbf33 | |||
| 4d22a04484 | |||
| 76d7e280f9 | |||
| 84b7da1164 | |||
| 77062bec84 | |||
| f65fed8d0a | |||
| 2974f69932 | |||
| 190087e6ca | |||
| 1784719047 | |||
| 275d771ea9 | |||
| f46b736c65 | |||
| 8eb381a899 | |||
| 04418e2e68 | |||
| ab2819addd | |||
| e507af62aa | |||
| 468ba72253 | |||
| 58bd6faa47 | |||
| e601ae4370 | |||
| 403daff934 |
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
bower_components
|
||||
.git
|
||||
Dockerfile
|
||||
@@ -1,4 +1,7 @@
|
||||
logs/*
|
||||
!.gitkeep
|
||||
dockerui
|
||||
*.esproj/*
|
||||
node_modules
|
||||
bower_components
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
FROM crosbymichael/golang
|
||||
FROM scratch
|
||||
|
||||
COPY dist /
|
||||
|
||||
ADD . /app/
|
||||
WORKDIR /app/
|
||||
RUN go build dockerui.go
|
||||
EXPOSE 9000
|
||||
ENTRYPOINT ["./dockerui"]
|
||||
ENTRYPOINT ["/dockerui"]
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
.PHONY: build run
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
OPEN = $(shell which xdg-open || which open)
|
||||
PORT ?= 9000
|
||||
|
||||
build:
|
||||
docker build --rm -t dockerui .
|
||||
|
||||
run:
|
||||
-docker stop dockerui
|
||||
-docker rm dockerui
|
||||
docker run -d -p $(PORT):9000 -v /var/run/docker.sock:/docker.sock --name dockerui dockerui -e /docker.sock
|
||||
|
||||
open:
|
||||
$(OPEN) localhost:$(PORT)
|
||||
|
||||
|
||||
@@ -1,40 +1,47 @@
|
||||
## DockerUI
|
||||
|
||||

|
||||
DockerUI is a web interface to interact with the Remote API. The goal is to provide a pure client side implementation so it is effortless to connect and manage docker. This project is not complete and is still under heavy development.
|
||||
DockerUI is a web interface for the Docker Remote API. The goal is to provide a pure client side implementation so it is effortless to connect and manage docker. This project is not complete and is still under heavy development.
|
||||
|
||||

|
||||
|
||||
|
||||
### Goals
|
||||
* Little to no dependencies - I really want to keep this project a pure html/js app. I know this will have to change so that I can introduce authentication and authorization along with managing multiple docker endpoints.
|
||||
* Minimal dependencies - I really want to keep this project a pure html/js app.
|
||||
* Consistency - The web UI should be consistent with the commands found on the docker CLI.
|
||||
|
||||
### Container Quickstart
|
||||
1. Run: `docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock dockerui/dockerui`
|
||||
|
||||
* Run `docker build -t crosbymichael/dockerui github.com/crosbymichael/dockerui`
|
||||
* `docker run -d -p 9000:9000 -v /var/run/docker.sock:/docker.sock crosbymichael/dockerui -e /docker.sock`
|
||||
* Open your browser to `http://<dockerd host ip>:9000`
|
||||
2. Open your browser to `http://<dockerd host ip>:9000`
|
||||
|
||||
|
||||
Bind mounting the unix socket into the dockerui container is much more secure than exposing your docker
|
||||
daemon over tcp. You should still secure your dockerui instance behind some type of auth. Maybe running
|
||||
nginx infront of dockerui with basic auth.
|
||||
Bind mounting the Unix socket into the DockerUI container is much more secure than exposing your docker daemon over TCP. The `--privileged` flag is required for hosts using SELinux. You should still secure your DockerUI instance behind some type of auth. Directions for using Nginx auth are [here](https://github.com/crosbymichael/dockerui/wiki/Dockerui-with-Nginx-HTTP-Auth).
|
||||
|
||||
### Connect via a unix socket
|
||||
If you want to connect to docker via the unix socket you can pass the socket path to the `-e` variable. If you are running dockerui in a container you can bind mount the unix socket into the container.
|
||||
### Specify socket to connect to Docker daemon
|
||||
|
||||
```bash
|
||||
docker run -d -p 9000:9000 -v /var/run/docker.sock:/docker.sock crosbymichael/dockerui -e /docker.sock
|
||||
```
|
||||
By default DockerUI connects to the Docker daemon with`/var/run/docker.sock`. For this to work you need to bind mount the unix socket into the container with `-v /var/run/docker.sock:/var/run/docker.sock`.
|
||||
|
||||
You can use the `-e` flag to change this socket:
|
||||
|
||||
# Connect to a tcp socket:
|
||||
$ docker run -d -p 9000:9000 --privileged dockerui/dockerui -e http://127.0.0.1:2375
|
||||
|
||||
### Change address/port DockerUI is served on
|
||||
DockerUI listens on port 9000 by default. If you run DockerUI inside a container then you can bind the container's internal port to any external address and port:
|
||||
|
||||
# Expose DockerUI on 10.20.30.1:80
|
||||
$ docker run -d -p 10.20.30.1:80:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock dockerui/dockerui
|
||||
|
||||
### Check the [wiki](//github.com/crosbymichael/dockerui/wiki) for more info about using dockerui
|
||||
|
||||
### Stack
|
||||
* Angular.js
|
||||
* Flatstrap ( Flat Twitter Bootstrap )
|
||||
* Spin.js
|
||||
* Ace editor
|
||||
* [Angular.js](https://github.com/angular/angular.js)
|
||||
* [Bootstrap](http://getbootstrap.com/)
|
||||
* [Gritter](https://github.com/jboesch/Gritter)
|
||||
* [Spin.js](https://github.com/fgnass/spin.js/)
|
||||
* [Golang](https://golang.org/)
|
||||
* [Vis.js](http://visjs.org/)
|
||||
|
||||
|
||||
### Todo:
|
||||
@@ -45,11 +52,11 @@ docker run -d -p 9000:9000 -v /var/run/docker.sock:/docker.sock crosbymichael/do
|
||||
|
||||
|
||||
### License - MIT
|
||||
The DockerUI code is licensed under the MIT license. Flatstrap(bootstrap) is licensed under the Apache License v2.0 and Angular.js is licensed under MIT.
|
||||
The DockerUI code is licensed under the MIT license.
|
||||
|
||||
|
||||
**DockerUI:**
|
||||
Copyright (c) 2013 Michael Crosby. crosbymichael.com
|
||||
Copyright (c) 2014-2015 Michael Crosby (crosbymichael.com), Kevan Ahlquist (kevanahlquist.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,19 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('dockerui', ['ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'images', 'image', 'startContainer', 'sidebar', 'settings', 'builder', 'containerLogs'])
|
||||
angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'containersNetwork', 'images', 'image', 'pullImage', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop', 'events', 'stats'])
|
||||
.config(['$routeProvider', function ($routeProvider) {
|
||||
$routeProvider.when('/', {templateUrl: 'app/components/dashboard/dashboard.html', controller: 'DashboardController'});
|
||||
$routeProvider.when('/containers/', {templateUrl: 'app/components/containers/containers.html', controller: 'ContainersController'});
|
||||
$routeProvider.when('/containers/:id/', {templateUrl: 'app/components/container/container.html', controller: 'ContainerController'});
|
||||
$routeProvider.when('/containers/:id/logs/', {templateUrl: 'app/components/containerLogs/containerlogs.html', controller: 'ContainerLogsController'});
|
||||
$routeProvider.when('/images/', {templateUrl: 'app/components/images/images.html', controller: 'ImagesController'});
|
||||
$routeProvider.when('/images/:id/', {templateUrl: 'app/components/image/image.html', controller: 'ImageController'});
|
||||
$routeProvider.when('/settings', {templateUrl: 'app/components/settings/settings.html', controller: 'SettingsController'});
|
||||
'use strict';
|
||||
$routeProvider.when('/', {
|
||||
templateUrl: 'app/components/dashboard/dashboard.html',
|
||||
controller: 'DashboardController'
|
||||
});
|
||||
$routeProvider.when('/containers/', {
|
||||
templateUrl: 'app/components/containers/containers.html',
|
||||
controller: 'ContainersController'
|
||||
});
|
||||
$routeProvider.when('/containers/:id/', {
|
||||
templateUrl: 'app/components/container/container.html',
|
||||
controller: 'ContainerController'
|
||||
});
|
||||
$routeProvider.when('/containers/:id/logs/', {
|
||||
templateUrl: 'app/components/containerLogs/containerlogs.html',
|
||||
controller: 'ContainerLogsController'
|
||||
});
|
||||
$routeProvider.when('/containers/:id/top', {
|
||||
templateUrl: 'app/components/containerTop/containerTop.html',
|
||||
controller: 'ContainerTopController'
|
||||
});
|
||||
$routeProvider.when('/containers/:id/stats', {
|
||||
templateUrl: 'app/components/stats/stats.html',
|
||||
controller: 'StatsController'
|
||||
});
|
||||
$routeProvider.when('/containers_network', {
|
||||
templateUrl: 'app/components/containersNetwork/containersNetwork.html',
|
||||
controller: 'ContainersNetworkController'
|
||||
});
|
||||
$routeProvider.when('/images/', {
|
||||
templateUrl: 'app/components/images/images.html',
|
||||
controller: 'ImagesController'
|
||||
});
|
||||
$routeProvider.when('/images/:id*/', {
|
||||
templateUrl: 'app/components/image/image.html',
|
||||
controller: 'ImageController'
|
||||
});
|
||||
$routeProvider.when('/info', {templateUrl: 'app/components/info/info.html', controller: 'InfoController'});
|
||||
$routeProvider.when('/events', {
|
||||
templateUrl: 'app/components/events/events.html',
|
||||
controller: 'EventsController'
|
||||
});
|
||||
$routeProvider.otherwise({redirectTo: '/'});
|
||||
}])
|
||||
// This is your docker url that the api will use to make requests
|
||||
// You need to set this to the api endpoint without the port i.e. http://192.168.1.9
|
||||
.constant('DOCKER_ENDPOINT', '/dockerapi')
|
||||
.constant('DOCKER_ENDPOINT', 'dockerapi')
|
||||
.constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243
|
||||
.constant('UI_VERSION', 'v0.5')
|
||||
.constant('DOCKER_API_VERSION', 'v1.15');
|
||||
.constant('UI_VERSION', 'v0.8.0')
|
||||
.constant('DOCKER_API_VERSION', 'v1.20');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module('builder', [])
|
||||
.controller('BuilderController', ['$scope', 'Dockerfile', 'Messages',
|
||||
function($scope, Dockerfile, Messages) {
|
||||
$scope.template = 'app/components/builder/builder.html';
|
||||
}]);
|
||||
.controller('BuilderController', ['$scope', 'Dockerfile', 'Messages',
|
||||
function ($scope, Dockerfile, Messages) {
|
||||
$scope.template = 'app/components/builder/builder.html';
|
||||
}]);
|
||||
|
||||
@@ -1,111 +1,176 @@
|
||||
<div class="detail">
|
||||
|
||||
<h4>Container: {{ container.Name }}</h4>
|
||||
|
||||
<div ng-if="!container.edit">
|
||||
<h4>Container: {{ container.Name }}
|
||||
<button class="btn btn-primary"
|
||||
ng-click="container.edit = true;">Rename
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
<div ng-if="container.edit">
|
||||
<h4>
|
||||
Container:
|
||||
<input type="text" ng-model="container.newContainerName">
|
||||
<button class="btn btn-success"
|
||||
ng-click="renameContainer()">Save
|
||||
</button>
|
||||
<button class="btn btn-danger"
|
||||
ng-click="container.edit = false;">×</button>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="btn-group detail">
|
||||
<button class="btn btn-success"
|
||||
ng-click="start()"
|
||||
ng-show="!container.State.Running">Start</button>
|
||||
<button class="btn btn-warning"
|
||||
ng-click="stop()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Stop</button>
|
||||
<button class="btn btn-danger"
|
||||
ng-click="kill()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Kill</button>
|
||||
<button class="btn btn-info"
|
||||
ng-click="pause()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Pause</button>
|
||||
<button class="btn btn-success"
|
||||
ng-click="unpause()"
|
||||
ng-show="container.State.Running && container.State.Paused">Unpause</button>
|
||||
<button class="btn btn-success"
|
||||
ng-click="start()"
|
||||
ng-show="!container.State.Running">Start
|
||||
</button>
|
||||
<button class="btn btn-warning"
|
||||
ng-click="stop()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Stop
|
||||
</button>
|
||||
<button class="btn btn-danger"
|
||||
ng-click="kill()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Kill
|
||||
</button>
|
||||
<button class="btn btn-info"
|
||||
ng-click="pause()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Pause
|
||||
</button>
|
||||
<button class="btn btn-success"
|
||||
ng-click="unpause()"
|
||||
ng-show="container.State.Running && container.State.Paused">Unpause
|
||||
</button>
|
||||
<button class="btn btn-success"
|
||||
ng-click="restart()"
|
||||
ng-show="container.State.Running && !container.State.Stopped">Restart
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
ng-click="commit()"
|
||||
ng-show="container.State.Running && !container.State.Paused">Commit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Created:</td>
|
||||
<td>{{ container.Created }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Path:</td>
|
||||
<td>{{ container.Path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Args:</td>
|
||||
<td>{{ container.Args }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Exposed Ports:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="(k, v) in container.Config.ExposedPorts">{{ k }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Environment:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="k in container.Config.Env">{{ k }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Created:</td>
|
||||
<td>{{ container.Created | date: 'medium' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Path:</td>
|
||||
<td>{{ container.Path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Args:</td>
|
||||
<td>
|
||||
<pre>{{ container.Args.join(' ') || 'None' }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Exposed Ports:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="(k, v) in container.Config.ExposedPorts">{{ k }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Environment:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="k in container.Config.Env">{{ k }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Labels:</td>
|
||||
<td>
|
||||
<table role="table" class="table">
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
<tr ng-repeat="(k, v) in container.Config.Labels">
|
||||
<td>{{ k }}</td>
|
||||
<td>{{ v }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Publish All:</td>
|
||||
<td>{{ container.HostConfig.PublishAllPorts }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ports:</td>
|
||||
<td>
|
||||
<ul style="display:inline-table">
|
||||
<li ng-repeat="(containerport, hostports) in container.HostConfig.PortBindings">
|
||||
{{ containerport }} => <span class="label" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<tr>
|
||||
<td>Publish All:</td>
|
||||
<td>{{ container.HostConfig.PublishAllPorts }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ports:</td>
|
||||
<td>
|
||||
<ul style="display:inline-table">
|
||||
<li ng-repeat="(containerport, hostports) in container.HostConfig.PortBindings">
|
||||
{{ containerport }} => <span class="label label-default" ng-repeat="(k,v) in hostports">{{ v.HostIp }}:{{ v.HostPort }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hostname:</td>
|
||||
<td>{{ container.Config.Hostname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IPAddress:</td>
|
||||
<td>{{ container.NetworkSettings.IPAddress }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cmd:</td>
|
||||
<td>{{ container.Config.Cmd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entrypoint:</td>
|
||||
<td>{{ container.Config.Entrypoint }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes:</td>
|
||||
<td>{{ container.Volumes }}</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hostname:</td>
|
||||
<td>{{ container.Config.Hostname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IPAddress:</td>
|
||||
<td>{{ container.NetworkSettings.IPAddress }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cmd:</td>
|
||||
<td>{{ container.Config.Cmd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entrypoint:</td>
|
||||
<td>
|
||||
<pre>{{ container.Config.Entrypoint.join(' ') }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes:</td>
|
||||
<td>{{ container.Volumes }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>SysInitpath:</td>
|
||||
<td>{{ container.SysInitPath }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Image:</td>
|
||||
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>State:</td>
|
||||
<td><span class="label {{ container.State|getstatelabel }}">{{ container.State|getstatetext }}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Logs:</td>
|
||||
<td><a href="#/containers/{{ container.Id }}/logs">stdout/stderr</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SysInitpath:</td>
|
||||
<td>{{ container.SysInitPath }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Image:</td>
|
||||
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>State:</td>
|
||||
<td>
|
||||
<accordion close-others="true">
|
||||
<accordion-group heading="{{ container.State|getstatetext }}">
|
||||
<ul>
|
||||
<li ng-repeat="(key, val) in container.State">{{key}} : {{ val }}</li>
|
||||
</ul>
|
||||
</accordion-group>
|
||||
</accordion>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Logs:</td>
|
||||
<td><a href="#/containers/{{ container.Id }}/logs">stdout/stderr</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Stats:</td>
|
||||
<td><a href="#/containers/{{ container.Id }}/stats">stats</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Top:</td>
|
||||
<td><a href="#/containers/{{ container.Id }}/top">Top</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span1">
|
||||
Changes:
|
||||
@@ -113,7 +178,7 @@
|
||||
<div class="span5">
|
||||
<i class="icon-refresh" style="width:32px;height:32px;" ng-click="getChanges()"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="well well-large">
|
||||
<ul>
|
||||
@@ -123,7 +188,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<hr/>
|
||||
|
||||
<div class="btn-remove">
|
||||
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="remove()">Remove Container</button>
|
||||
|
||||
@@ -1,105 +1,143 @@
|
||||
angular.module('container', [])
|
||||
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'ViewSpinner',
|
||||
function($scope, $routeParams, $location, Container, Messages, ViewSpinner) {
|
||||
$scope.changes = [];
|
||||
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner',
|
||||
function ($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner) {
|
||||
$scope.changes = [];
|
||||
$scope.edit = false;
|
||||
|
||||
var update = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.get({id: $routeParams.id}, function(d) {
|
||||
$scope.container = d;
|
||||
ViewSpinner.stop();
|
||||
}, function(e) {
|
||||
if (e.status === 404) {
|
||||
$('.detail').hide();
|
||||
Messages.error("Not found", "Container not found.");
|
||||
} else {
|
||||
Messages.error("Failure", e.data);
|
||||
}
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
};
|
||||
var update = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.get({id: $routeParams.id}, function (d) {
|
||||
$scope.container = d;
|
||||
$scope.container.edit = false;
|
||||
$scope.container.newContainerName = d.Name;
|
||||
ViewSpinner.stop();
|
||||
}, function (e) {
|
||||
if (e.status === 404) {
|
||||
$('.detail').hide();
|
||||
Messages.error("Not found", "Container not found.");
|
||||
} else {
|
||||
Messages.error("Failure", e.data);
|
||||
}
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.start = function(){
|
||||
ViewSpinner.spin();
|
||||
Container.start({
|
||||
id: $scope.container.Id,
|
||||
HostConfig: $scope.container.HostConfig
|
||||
}, function(d) {
|
||||
update();
|
||||
Messages.send("Container started", $routeParams.id);
|
||||
}, function(e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to start." + e.data);
|
||||
});
|
||||
};
|
||||
$scope.start = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.start({
|
||||
id: $scope.container.Id,
|
||||
HostConfig: $scope.container.HostConfig
|
||||
}, function (d) {
|
||||
update();
|
||||
Messages.send("Container started", $routeParams.id);
|
||||
}, function (e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to start." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.stop = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.stop({id: $routeParams.id}, function(d) {
|
||||
update();
|
||||
Messages.send("Container stopped", $routeParams.id);
|
||||
}, function(e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to stop." + e.data);
|
||||
});
|
||||
};
|
||||
$scope.stop = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.stop({id: $routeParams.id}, function (d) {
|
||||
update();
|
||||
Messages.send("Container stopped", $routeParams.id);
|
||||
}, function (e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to stop." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.kill = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.kill({id: $routeParams.id}, function(d) {
|
||||
update();
|
||||
Messages.send("Container killed", $routeParams.id);
|
||||
}, function(e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to die." + e.data);
|
||||
});
|
||||
};
|
||||
$scope.kill = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.kill({id: $routeParams.id}, function (d) {
|
||||
update();
|
||||
Messages.send("Container killed", $routeParams.id);
|
||||
}, function (e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to die." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.pause = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.pause({id: $routeParams.id}, function(d) {
|
||||
update();
|
||||
Messages.send("Container paused", $routeParams.id);
|
||||
}, function(e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to pause." + e.data);
|
||||
});
|
||||
};
|
||||
$scope.commit = function () {
|
||||
ViewSpinner.spin();
|
||||
ContainerCommit.commit({id: $routeParams.id, repo: $scope.container.Config.Image}, function (d) {
|
||||
update();
|
||||
Messages.send("Container commited", $routeParams.id);
|
||||
}, function (e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to commit." + e.data);
|
||||
});
|
||||
};
|
||||
$scope.pause = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.pause({id: $routeParams.id}, function (d) {
|
||||
update();
|
||||
Messages.send("Container paused", $routeParams.id);
|
||||
}, function (e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to pause." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.unpause = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.unpause({id: $routeParams.id}, function(d) {
|
||||
update();
|
||||
Messages.send("Container unpaused", $routeParams.id);
|
||||
}, function(e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to unpause." + e.data);
|
||||
});
|
||||
};
|
||||
$scope.unpause = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.unpause({id: $routeParams.id}, function (d) {
|
||||
update();
|
||||
Messages.send("Container unpaused", $routeParams.id);
|
||||
}, function (e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to unpause." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.remove = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.remove({id: $routeParams.id}, function (d) {
|
||||
update();
|
||||
Messages.send("Container removed", $routeParams.id);
|
||||
}, function (e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to remove." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.restart = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.restart({id: $routeParams.id}, function (d) {
|
||||
update();
|
||||
Messages.send("Container restarted", $routeParams.id);
|
||||
}, function (e) {
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to restart." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.hasContent = function (data) {
|
||||
return data !== null && data !== undefined;
|
||||
};
|
||||
|
||||
$scope.getChanges = function () {
|
||||
ViewSpinner.spin();
|
||||
Container.changes({id: $routeParams.id}, function (d) {
|
||||
$scope.changes = d;
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.renameContainer = function () {
|
||||
// #FIXME fix me later to handle http status to show the correct error message
|
||||
Container.rename({id: $routeParams.id, 'name': $scope.container.newContainerName}, function (data) {
|
||||
if (data.name) {
|
||||
$scope.container.Name = data.name;
|
||||
Messages.send("Container renamed", $routeParams.id);
|
||||
} else {
|
||||
$scope.container.newContainerName = $scope.container.Name;
|
||||
Messages.error("Failure", "Container failed to rename.");
|
||||
}
|
||||
});
|
||||
$scope.container.edit = false;
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.remove({id: $routeParams.id}, function(d) {
|
||||
update();
|
||||
Messages.send("Container removed", $routeParams.id);
|
||||
}, function(e){
|
||||
update();
|
||||
Messages.error("Failure", "Container failed to remove." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.hasContent = function(data) {
|
||||
return data !== null && data !== undefined;
|
||||
};
|
||||
|
||||
$scope.getChanges = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.changes({id: $routeParams.id}, function(d) {
|
||||
$scope.changes = d;
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
};
|
||||
|
||||
update();
|
||||
$scope.getChanges();
|
||||
}]);
|
||||
$scope.getChanges();
|
||||
}]);
|
||||
|
||||
@@ -1,48 +1,76 @@
|
||||
angular.module('containerLogs', [])
|
||||
.controller('ContainerLogsController', ['$scope', '$routeParams', '$location', '$anchorScroll', 'ContainerLogs', 'Container', 'ViewSpinner',
|
||||
function($scope, $routeParams, $location, $anchorScroll, ContainerLogs, Container, ViewSpinner) {
|
||||
$scope.stdout = '';
|
||||
$scope.stderr = '';
|
||||
$scope.showTimestamps = false;
|
||||
.controller('ContainerLogsController', ['$scope', '$routeParams', '$location', '$anchorScroll', 'ContainerLogs', 'Container', 'ViewSpinner',
|
||||
function ($scope, $routeParams, $location, $anchorScroll, ContainerLogs, Container, ViewSpinner) {
|
||||
$scope.stdout = '';
|
||||
$scope.stderr = '';
|
||||
$scope.showTimestamps = false;
|
||||
$scope.tailLines = 2000;
|
||||
|
||||
ViewSpinner.spin();
|
||||
Container.get({id: $routeParams.id}, function(d) {
|
||||
$scope.container = d;
|
||||
ViewSpinner.stop();
|
||||
}, function(e) {
|
||||
if (e.status === 404) {
|
||||
Messages.error("Not found", "Container not found.");
|
||||
} else {
|
||||
Messages.error("Failure", e.data);
|
||||
}
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
ViewSpinner.spin();
|
||||
Container.get({id: $routeParams.id}, function (d) {
|
||||
$scope.container = d;
|
||||
ViewSpinner.stop();
|
||||
}, function (e) {
|
||||
if (e.status === 404) {
|
||||
Messages.error("Not found", "Container not found.");
|
||||
} else {
|
||||
Messages.error("Failure", e.data);
|
||||
}
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
|
||||
function getLogs() {
|
||||
ContainerLogs.get($routeParams.id, {stdout: 1, stderr: 0, timestamps: $scope.showTimestamps}, function(data, status, headers, config) {
|
||||
// Replace carriage returns twith newlines to clean up output
|
||||
$scope.stdout = data.replace(/[\r]/g, '\n');
|
||||
});
|
||||
ContainerLogs.get($routeParams.id, {stdout: 0, stderr: 1}, function(data, status, headers, config) {
|
||||
$scope.stderr = data.replace(/[\r]/g, '\n');
|
||||
});
|
||||
}
|
||||
function getLogs() {
|
||||
ViewSpinner.spin();
|
||||
ContainerLogs.get($routeParams.id, {
|
||||
stdout: 1,
|
||||
stderr: 0,
|
||||
timestamps: $scope.showTimestamps,
|
||||
tail: $scope.tailLines
|
||||
}, function (data, status, headers, config) {
|
||||
// Replace carriage returns with newlines to clean up output
|
||||
data = data.replace(/[\r]/g, '\n');
|
||||
// Strip 8 byte header from each line of output
|
||||
data = data.substring(8);
|
||||
data = data.replace(/\n(.{8})/g, '\n');
|
||||
$scope.stdout = data;
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
|
||||
// initial call
|
||||
getLogs();
|
||||
var logIntervalId = window.setInterval(getLogs, 5000);
|
||||
ContainerLogs.get($routeParams.id, {
|
||||
stdout: 0,
|
||||
stderr: 1,
|
||||
timestamps: $scope.showTimestamps,
|
||||
tail: $scope.tailLines
|
||||
}, function (data, status, headers, config) {
|
||||
// Replace carriage returns with newlines to clean up output
|
||||
data = data.replace(/[\r]/g, '\n');
|
||||
// Strip 8 byte header from each line of output
|
||||
data = data.substring(8);
|
||||
data = data.replace(/\n(.{8})/g, '\n');
|
||||
$scope.stderr = data;
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.$on("$destroy", function(){
|
||||
// clearing interval when view changes
|
||||
clearInterval(logIntervalId);
|
||||
});
|
||||
// initial call
|
||||
getLogs();
|
||||
var logIntervalId = window.setInterval(getLogs, 5000);
|
||||
|
||||
$scope.scrollTo = function(id) {
|
||||
$location.hash(id);
|
||||
$anchorScroll();
|
||||
}
|
||||
$scope.$on("$destroy", function () {
|
||||
// clearing interval when view changes
|
||||
clearInterval(logIntervalId);
|
||||
});
|
||||
|
||||
$scope.toggleTimestamps = function() {
|
||||
getLogs();
|
||||
}
|
||||
}]);
|
||||
$scope.scrollTo = function (id) {
|
||||
$location.hash(id);
|
||||
$anchorScroll();
|
||||
};
|
||||
|
||||
$scope.toggleTimestamps = function () {
|
||||
getLogs();
|
||||
};
|
||||
|
||||
$scope.toggleTail = function () {
|
||||
getLogs();
|
||||
};
|
||||
}]);
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
<div class="row logs">
|
||||
<div class="col-xs-12">
|
||||
<h4>Logs for container: <a href="#/containers/{{ container.Id }}/">{{ container.Name }}</a></td></h4>
|
||||
|
||||
<div class="btn-group detail">
|
||||
<button class="btn btn-info" ng-click="scrollTo('stdout')">stdout</button>
|
||||
<button class="btn btn-warning" ng-click="scrollTo('stderr')">stderr</button>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<input id="timestampToggle" type="checkbox" ng-model="showTimestamps"
|
||||
ng-change="toggleTimestamps()"/> <label for="timestampToggle">Display Timestamps</label>
|
||||
<div class="pull-right col-xs-6">
|
||||
<div class="col-xs-6">
|
||||
<a class="btn btn-primary" ng-click="toggleTail()" role="button">Reload logs</a>
|
||||
<input id="tailLines" type="number" ng-style="{width: '45px'}"
|
||||
ng-model="tailLines" ng-keypress="($event.which === 13)? toggleTail() : 0"/>
|
||||
<label for="tailLines">lines</label>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<input id="timestampToggle" type="checkbox" ng-model="showTimestamps"
|
||||
ng-change="toggleTimestamps()"/> <label for="timestampToggle">Timestamps</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<div class="containerTop">
|
||||
<div class="form-group col-xs-2">
|
||||
<input type="text" class="form-control" placeholder="[options] (aux)" ng-model="ps_args">
|
||||
</div>
|
||||
<button type="button" class="btn btn-default" ng-click="getTop()">Submit</button>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-repeat="title in containerTop.Titles">{{title}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="processInfos in containerTop.Processes">
|
||||
<td ng-repeat="processInfo in processInfos track by $index">{{processInfo}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
angular.module('containerTop', [])
|
||||
.controller('ContainerTopController', ['$scope', '$routeParams', 'ContainerTop', 'ViewSpinner', function ($scope, $routeParams, ContainerTop, ViewSpinner) {
|
||||
$scope.ps_args = '';
|
||||
|
||||
/**
|
||||
* Get container processes
|
||||
*/
|
||||
$scope.getTop = function () {
|
||||
ViewSpinner.spin();
|
||||
ContainerTop.get($routeParams.id, {
|
||||
ps_args: $scope.ps_args
|
||||
}, function (data) {
|
||||
$scope.containerTop = data;
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getTop();
|
||||
}]);
|
||||
@@ -8,6 +8,7 @@
|
||||
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
|
||||
<li><a tabindex="-1" href="" ng-click="startAction()">Start</a></li>
|
||||
<li><a tabindex="-1" href="" ng-click="stopAction()">Stop</a></li>
|
||||
<li><a tabindex="-1" href="" ng-click="restartAction()">Restart</a></li>
|
||||
<li><a tabindex="-1" href="" ng-click="killAction()">Kill</a></li>
|
||||
<li><a tabindex="-1" href="" ng-click="pauseAction()">Pause</a></li>
|
||||
<li><a tabindex="-1" href="" ng-click="unpauseAction()">Unpause</a></li>
|
||||
@@ -16,9 +17,9 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="pull-right">
|
||||
<input type="checkbox" ng-model="displayAll"
|
||||
ng-change="toggleGetAll()"/> Display All
|
||||
<div class="pull-right form-inline">
|
||||
<input type="checkbox" ng-model="displayAll" id="displayAll" ng-change="toggleGetAll()"/> <label for="displayAll">Display All</label>
|
||||
<input type="text" class="form-control" style="vertical-align: center" id="filter" placeholder="Filter" ng-model="filter"/> <label class="sr-only" for="filter">Filter</label>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-striped">
|
||||
@@ -33,7 +34,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="container in containers|orderBy:predicate">
|
||||
<tr ng-repeat="container in containers | filter:filter | orderBy:predicate">
|
||||
<td><input type="checkbox" ng-model="container.Checked" /></td>
|
||||
<td><a href="#/containers/{{ container.Id }}/">{{ container|containername}}</a></td>
|
||||
<td><a href="#/images/{{ container.Image }}/">{{ container.Image }}</a></td>
|
||||
|
||||
@@ -1,81 +1,112 @@
|
||||
angular.module('containers', [])
|
||||
.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner',
|
||||
function($scope, Container, Settings, Messages, ViewSpinner) {
|
||||
$scope.predicate = '-Created';
|
||||
$scope.toggle = false;
|
||||
$scope.displayAll = Settings.displayAll;
|
||||
.controller('ContainersController', ['$scope', 'Container', 'Settings', 'Messages', 'ViewSpinner',
|
||||
function ($scope, Container, Settings, Messages, ViewSpinner) {
|
||||
$scope.predicate = '-Created';
|
||||
$scope.toggle = false;
|
||||
$scope.displayAll = Settings.displayAll;
|
||||
|
||||
var update = function(data) {
|
||||
ViewSpinner.spin();
|
||||
Container.query(data, function(d) {
|
||||
$scope.containers = d.map(function(item) {
|
||||
return new ContainerViewModel(item); });
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
};
|
||||
|
||||
var batch = function(items, action, msg) {
|
||||
ViewSpinner.spin();
|
||||
var counter = 0;
|
||||
var complete = function() {
|
||||
counter = counter -1;
|
||||
if (counter === 0) {
|
||||
ViewSpinner.stop();
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
}
|
||||
};
|
||||
angular.forEach(items, function(c) {
|
||||
if (c.Checked) {
|
||||
counter = counter + 1;
|
||||
action({id: c.Id}, function(d) {
|
||||
Messages.send("Container " + msg, c.Id);
|
||||
var index = $scope.containers.indexOf(c);
|
||||
complete();
|
||||
}, function(e) {
|
||||
Messages.error("Failure", e.data);
|
||||
complete();
|
||||
var update = function (data) {
|
||||
ViewSpinner.spin();
|
||||
Container.query(data, function (d) {
|
||||
$scope.containers = d.map(function (item) {
|
||||
return new ContainerViewModel(item);
|
||||
});
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
}
|
||||
});
|
||||
if (counter === 0) {
|
||||
ViewSpinner.stop();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
$scope.toggleSelectAll = function() {
|
||||
angular.forEach($scope.containers, function(i) {
|
||||
i.Checked = $scope.toggle;
|
||||
});
|
||||
};
|
||||
var batch = function (items, action, msg) {
|
||||
ViewSpinner.spin();
|
||||
var counter = 0;
|
||||
var complete = function () {
|
||||
counter = counter - 1;
|
||||
if (counter === 0) {
|
||||
ViewSpinner.stop();
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
}
|
||||
};
|
||||
angular.forEach(items, function (c) {
|
||||
if (c.Checked) {
|
||||
if (action === Container.start) {
|
||||
Container.get({id: c.Id}, function (d) {
|
||||
c = d;
|
||||
counter = counter + 1;
|
||||
action({id: c.Id, HostConfig: c.HostConfig || {}}, function (d) {
|
||||
Messages.send("Container " + msg, c.Id);
|
||||
var index = $scope.containers.indexOf(c);
|
||||
complete();
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e.data);
|
||||
complete();
|
||||
});
|
||||
}, function (e) {
|
||||
if (e.status === 404) {
|
||||
$('.detail').hide();
|
||||
Messages.error("Not found", "Container not found.");
|
||||
} else {
|
||||
Messages.error("Failure", e.data);
|
||||
}
|
||||
complete();
|
||||
});
|
||||
}
|
||||
else {
|
||||
counter = counter + 1;
|
||||
action({id: c.Id}, function (d) {
|
||||
Messages.send("Container " + msg, c.Id);
|
||||
var index = $scope.containers.indexOf(c);
|
||||
complete();
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e.data);
|
||||
complete();
|
||||
});
|
||||
|
||||
$scope.toggleGetAll = function() {
|
||||
Settings.displayAll = $scope.displayAll;
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.startAction = function() {
|
||||
batch($scope.containers, Container.start, "Started");
|
||||
};
|
||||
}
|
||||
});
|
||||
if (counter === 0) {
|
||||
ViewSpinner.stop();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.stopAction = function() {
|
||||
batch($scope.containers, Container.stop, "Stopped");
|
||||
};
|
||||
$scope.toggleSelectAll = function () {
|
||||
angular.forEach($scope.containers, function (i) {
|
||||
i.Checked = $scope.toggle;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.killAction = function() {
|
||||
batch($scope.containers, Container.kill, "Killed");
|
||||
};
|
||||
$scope.toggleGetAll = function () {
|
||||
Settings.displayAll = $scope.displayAll;
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
};
|
||||
|
||||
$scope.pauseAction = function() {
|
||||
batch($scope.containers, Container.pause, "Paused");
|
||||
};
|
||||
$scope.startAction = function () {
|
||||
batch($scope.containers, Container.start, "Started");
|
||||
};
|
||||
|
||||
$scope.unpauseAction = function() {
|
||||
batch($scope.containers, Container.unpause, "Unpaused");
|
||||
};
|
||||
$scope.stopAction = function () {
|
||||
batch($scope.containers, Container.stop, "Stopped");
|
||||
};
|
||||
|
||||
$scope.removeAction = function() {
|
||||
batch($scope.containers, Container.remove, "Removed");
|
||||
};
|
||||
$scope.restartAction = function () {
|
||||
batch($scope.containers, Container.restart, "Restarted");
|
||||
};
|
||||
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
}]);
|
||||
$scope.killAction = function () {
|
||||
batch($scope.containers, Container.kill, "Killed");
|
||||
};
|
||||
|
||||
$scope.pauseAction = function () {
|
||||
batch($scope.containers, Container.pause, "Paused");
|
||||
};
|
||||
|
||||
$scope.unpauseAction = function () {
|
||||
batch($scope.containers, Container.unpause, "Unpaused");
|
||||
};
|
||||
|
||||
$scope.removeAction = function () {
|
||||
batch($scope.containers, Container.remove, "Removed");
|
||||
};
|
||||
|
||||
update({all: Settings.displayAll ? 1 : 0});
|
||||
}]);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<div class="detail">
|
||||
<h2>Containers Network</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="query" autofocus="true" class="form-control"
|
||||
placeholder="Search" ng-change="network.selectContainers(query)"/>
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-warning" ng-click="network.hideSelected()">Hide Selected</button>
|
||||
<button class="btn btn-info" ng-click="network.showSelectedDownstream()">Show Selected Downstream</button>
|
||||
<button class="btn btn-info" ng-click="network.showSelectedUpstream()">Show Selected Upstream</button>
|
||||
<button class="btn btn-success" ng-click="network.showAll()">Show All</button>
|
||||
</div>
|
||||
<input type="checkbox" ng-model="includeStopped" id="includeStopped" ng-change="toggleIncludeStopped()"/> <label
|
||||
for="includeStopped">Include stopped containers</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<vis-network data="network.data" options="network.options" events="network.events"
|
||||
component="network.component"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,271 @@
|
||||
angular.module('containersNetwork', ['ngVis'])
|
||||
.controller('ContainersNetworkController', ['$scope', '$location', 'Container', 'Messages', 'VisDataSet', function ($scope, $location, Container, Messages, VisDataSet) {
|
||||
|
||||
function ContainerNode(data) {
|
||||
this.Id = data.Id;
|
||||
// names have the following format: /Name
|
||||
this.Name = data.Name.substring(1);
|
||||
this.Image = data.Config.Image;
|
||||
this.Running = data.State.Running;
|
||||
var dataLinks = data.HostConfig.Links;
|
||||
if (dataLinks != null) {
|
||||
this.Links = {};
|
||||
for (var i = 0; i < dataLinks.length; i++) {
|
||||
// links have the following format: /TargetContainerName:/SourceContainerName/LinkAlias
|
||||
var link = dataLinks[i].split(":");
|
||||
var target = link[0].substring(1);
|
||||
var alias = link[1].substring(link[1].lastIndexOf("/") + 1);
|
||||
// only keep shortest alias
|
||||
if (this.Links[target] == null || alias.length < this.Links[target].length) {
|
||||
this.Links[target] = alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
var dataVolumes = data.HostConfig.VolumesFrom;
|
||||
//converting array into properties for simpler and faster access
|
||||
if (dataVolumes != null) {
|
||||
this.VolumesFrom = {};
|
||||
for (var j = 0; j < dataVolumes.length; j++) {
|
||||
this.VolumesFrom[dataVolumes[j]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ContainersNetworkData() {
|
||||
this.nodes = new VisDataSet();
|
||||
this.edges = new VisDataSet();
|
||||
|
||||
this.addContainerNode = function (container) {
|
||||
this.nodes.add({
|
||||
id: container.Id,
|
||||
label: container.Name,
|
||||
title: "<ul style=\"list-style-type:none; padding: 0px; margin: 0px\">" +
|
||||
"<li><strong>ID:</strong> " + container.Id + "</li>" +
|
||||
"<li><strong>Image:</strong> " + container.Image + "</li>" +
|
||||
"</ul>",
|
||||
color: (container.Running ? "#8888ff" : "#cccccc")
|
||||
});
|
||||
};
|
||||
|
||||
this.hasEdge = function (from, to) {
|
||||
return this.edges.getIds({
|
||||
filter: function (item) {
|
||||
return item.from === from.Id && item.to === to.Id;
|
||||
}
|
||||
}).length > 0;
|
||||
};
|
||||
|
||||
this.addLinkEdgeIfExists = function (from, to) {
|
||||
if (from.Links != null && from.Links[to.Name] != null && !this.hasEdge(from, to)) {
|
||||
this.edges.add({
|
||||
from: from.Id,
|
||||
to: to.Id,
|
||||
label: from.Links[to.Name]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.addVolumeEdgeIfExists = function (from, to) {
|
||||
if (from.VolumesFrom != null && (from.VolumesFrom[to.Id] != null || from.VolumesFrom[to.Name] != null) && !this.hasEdge(from, to)) {
|
||||
this.edges.add({
|
||||
from: from.Id,
|
||||
to: to.Id,
|
||||
color: {color: '#A0A0A0', highlight: '#A0A0A0', hover: '#848484'}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.removeContainersNodes = function (containersIds) {
|
||||
this.nodes.remove(containersIds);
|
||||
};
|
||||
}
|
||||
|
||||
function ContainersNetwork() {
|
||||
this.data = new ContainersNetworkData();
|
||||
this.containers = {};
|
||||
this.selectedContainersIds = [];
|
||||
this.shownContainersIds = [];
|
||||
this.events = {
|
||||
select: function (event) {
|
||||
$scope.network.selectedContainersIds = event.nodes;
|
||||
$scope.$apply(function () {
|
||||
$scope.query = '';
|
||||
});
|
||||
},
|
||||
doubleClick: function (event) {
|
||||
$scope.$apply(function () {
|
||||
$location.path('/containers/' + event.nodes[0]);
|
||||
});
|
||||
}
|
||||
};
|
||||
this.options = {
|
||||
navigation: true,
|
||||
keyboard: true,
|
||||
height: '500px', width: '700px',
|
||||
nodes: {
|
||||
shape: 'box'
|
||||
},
|
||||
edges: {
|
||||
style: 'arrow'
|
||||
},
|
||||
physics: {
|
||||
barnesHut: {
|
||||
springLength: 200
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.addContainer = function (data) {
|
||||
var container = new ContainerNode(data);
|
||||
this.containers[container.Id] = container;
|
||||
this.shownContainersIds.push(container.Id);
|
||||
this.data.addContainerNode(container);
|
||||
for (var otherContainerId in this.containers) {
|
||||
var otherContainer = this.containers[otherContainerId];
|
||||
this.data.addLinkEdgeIfExists(container, otherContainer);
|
||||
this.data.addLinkEdgeIfExists(otherContainer, container);
|
||||
this.data.addVolumeEdgeIfExists(container, otherContainer);
|
||||
this.data.addVolumeEdgeIfExists(otherContainer, container);
|
||||
}
|
||||
};
|
||||
|
||||
this.selectContainers = function (query) {
|
||||
if (this.component != null) {
|
||||
this.selectedContainersIds = this.searchContainers(query);
|
||||
this.component.selectNodes(this.selectedContainersIds);
|
||||
}
|
||||
};
|
||||
|
||||
this.searchContainers = function (query) {
|
||||
if (query.trim() === "") {
|
||||
return [];
|
||||
}
|
||||
var selectedContainersIds = [];
|
||||
for (var i = 0; i < this.shownContainersIds.length; i++) {
|
||||
var container = this.containers[this.shownContainersIds[i]];
|
||||
if (container.Name.indexOf(query) > -1 ||
|
||||
container.Image.indexOf(query) > -1 ||
|
||||
container.Id.indexOf(query) > -1) {
|
||||
selectedContainersIds.push(container.Id);
|
||||
}
|
||||
}
|
||||
return selectedContainersIds;
|
||||
};
|
||||
|
||||
this.hideSelected = function () {
|
||||
var i = 0;
|
||||
while (i < this.shownContainersIds.length) {
|
||||
if (this.selectedContainersIds.indexOf(this.shownContainersIds[i]) > -1) {
|
||||
this.shownContainersIds.splice(i, 1);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
this.data.removeContainersNodes(this.selectedContainersIds);
|
||||
$scope.query = '';
|
||||
this.selectedContainersIds = [];
|
||||
};
|
||||
|
||||
this.searchDownstream = function (containerId, downstreamContainersIds) {
|
||||
if (downstreamContainersIds.indexOf(containerId) > -1) {
|
||||
return;
|
||||
}
|
||||
downstreamContainersIds.push(containerId);
|
||||
var container = this.containers[containerId];
|
||||
if (container.Links == null && container.VolumesFrom == null) {
|
||||
return;
|
||||
}
|
||||
for (var otherContainerId in this.containers) {
|
||||
var otherContainer = this.containers[otherContainerId];
|
||||
if (container.Links != null && container.Links[otherContainer.Name] != null) {
|
||||
this.searchDownstream(otherContainer.Id, downstreamContainersIds);
|
||||
} else if (container.VolumesFrom != null &&
|
||||
container.VolumesFrom[otherContainer.Id] != null) {
|
||||
this.searchDownstream(otherContainer.Id, downstreamContainersIds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.updateShownContainers = function (newShownContainersIds) {
|
||||
for (var containerId in this.containers) {
|
||||
if (newShownContainersIds.indexOf(containerId) > -1 &&
|
||||
this.shownContainersIds.indexOf(containerId) === -1) {
|
||||
this.data.addContainerNode(this.containers[containerId]);
|
||||
} else if (newShownContainersIds.indexOf(containerId) === -1 &&
|
||||
this.shownContainersIds.indexOf(containerId) > -1) {
|
||||
this.data.removeContainersNodes(containerId);
|
||||
}
|
||||
}
|
||||
this.shownContainersIds = newShownContainersIds;
|
||||
};
|
||||
|
||||
this.showSelectedDownstream = function () {
|
||||
var downstreamContainersIds = [];
|
||||
for (var i = 0; i < this.selectedContainersIds.length; i++) {
|
||||
this.searchDownstream(this.selectedContainersIds[i], downstreamContainersIds);
|
||||
}
|
||||
this.updateShownContainers(downstreamContainersIds);
|
||||
};
|
||||
|
||||
this.searchUpstream = function (containerId, upstreamContainersIds) {
|
||||
if (upstreamContainersIds.indexOf(containerId) > -1) {
|
||||
return;
|
||||
}
|
||||
upstreamContainersIds.push(containerId);
|
||||
var container = this.containers[containerId];
|
||||
for (var otherContainerId in this.containers) {
|
||||
var otherContainer = this.containers[otherContainerId];
|
||||
if (otherContainer.Links != null && otherContainer.Links[container.Name] != null) {
|
||||
this.searchUpstream(otherContainer.Id, upstreamContainersIds);
|
||||
} else if (otherContainer.VolumesFrom != null &&
|
||||
otherContainer.VolumesFrom[container.Id] != null) {
|
||||
this.searchUpstream(otherContainer.Id, upstreamContainersIds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.showSelectedUpstream = function () {
|
||||
var upstreamContainersIds = [];
|
||||
for (var i = 0; i < this.selectedContainersIds.length; i++) {
|
||||
this.searchUpstream(this.selectedContainersIds[i], upstreamContainersIds);
|
||||
}
|
||||
this.updateShownContainers(upstreamContainersIds);
|
||||
};
|
||||
|
||||
this.showAll = function () {
|
||||
for (var containerId in this.containers) {
|
||||
if (this.shownContainersIds.indexOf(containerId) === -1) {
|
||||
this.data.addContainerNode(this.containers[containerId]);
|
||||
this.shownContainersIds.push(containerId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
$scope.network = new ContainersNetwork();
|
||||
|
||||
var showFailure = function (event) {
|
||||
Messages.error('Failure', e.data);
|
||||
};
|
||||
|
||||
var addContainer = function (container) {
|
||||
$scope.network.addContainer(container);
|
||||
};
|
||||
|
||||
var update = function (data) {
|
||||
Container.query(data, function (d) {
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
Container.get({id: d[i].Id}, addContainer, showFailure);
|
||||
}
|
||||
});
|
||||
};
|
||||
update({all: 0});
|
||||
|
||||
$scope.includeStopped = false;
|
||||
$scope.toggleIncludeStopped = function () {
|
||||
$scope.network.updateShownContainers([]);
|
||||
update({all: $scope.includeStopped ? 1 : 0});
|
||||
};
|
||||
|
||||
}]);
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
<div class="col-xs-offset-1">
|
||||
<!--<div class="sidebar span4">
|
||||
<div ng-include="template" ng-controller="SideBarController"></div>
|
||||
@@ -7,12 +6,13 @@
|
||||
<div class="col-xs-10" id="masthead" style="display:none">
|
||||
<div class="jumbotron">
|
||||
<h1>DockerUI</h1>
|
||||
|
||||
<p class="lead">The Linux container engine</p>
|
||||
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
|
||||
</div>
|
||||
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-10">
|
||||
<div class="col-xs-5">
|
||||
@@ -27,7 +27,8 @@
|
||||
<div class="col-xs-5 text-right">
|
||||
<h3>Status</h3>
|
||||
<canvas id="containers-chart" class="pull-right">
|
||||
Get a better browser... Your holding everyone back.
|
||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
||||
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
</canvas>
|
||||
<div id="chart-legend"></div>
|
||||
</div>
|
||||
@@ -37,13 +38,15 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-10" id="stats">
|
||||
<h4>Containers created</h4>
|
||||
<canvas id="containers-started-chart" width="700">
|
||||
Get a better browser... You're holding everyone back.
|
||||
</canvas>
|
||||
<canvas id="containers-started-chart" width="700">
|
||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
||||
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
</canvas>
|
||||
<h4>Images created</h4>
|
||||
<canvas id="images-created-chart" width="700">
|
||||
Get a better browser... You're holding everyone back.
|
||||
</canvas>
|
||||
<canvas id="images-created-chart" width="700">
|
||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
||||
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,72 +1,74 @@
|
||||
angular.module('dashboard', [])
|
||||
.controller('DashboardController', ['$scope', 'Container', 'Image', 'Settings', 'LineChart', function($scope, Container, Image, Settings, LineChart) {
|
||||
$scope.predicate = '-Created';
|
||||
$scope.containers = [];
|
||||
.controller('DashboardController', ['$scope', 'Container', 'Image', 'Settings', 'LineChart', function ($scope, Container, Image, Settings, LineChart) {
|
||||
$scope.predicate = '-Created';
|
||||
$scope.containers = [];
|
||||
|
||||
var getStarted = function(data) {
|
||||
$scope.totalContainers = data.length;
|
||||
LineChart.build('#containers-started-chart', data, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
|
||||
var s = $scope;
|
||||
Image.query({}, function(d) {
|
||||
s.totalImages = d.length;
|
||||
LineChart.build('#images-created-chart', d, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
|
||||
var getStarted = function (data) {
|
||||
$scope.totalContainers = data.length;
|
||||
LineChart.build('#containers-started-chart', data, function (c) {
|
||||
return new Date(c.Created * 1000).toLocaleDateString();
|
||||
});
|
||||
var s = $scope;
|
||||
Image.query({}, function (d) {
|
||||
s.totalImages = d.length;
|
||||
LineChart.build('#images-created-chart', d, function (c) {
|
||||
return new Date(c.Created * 1000).toLocaleDateString();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var opts = {animation: false};
|
||||
if (Settings.firstLoad) {
|
||||
opts.animation = true;
|
||||
Settings.firstLoad = false;
|
||||
$('#masthead').show();
|
||||
|
||||
setTimeout(function () {
|
||||
$('#masthead').slideUp('slow');
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
Container.query({all: 1}, function (d) {
|
||||
var running = 0;
|
||||
var ghost = 0;
|
||||
var stopped = 0;
|
||||
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var item = d[i];
|
||||
|
||||
if (item.Status === "Ghost") {
|
||||
ghost += 1;
|
||||
} else if (item.Status.indexOf('Exit') !== -1) {
|
||||
stopped += 1;
|
||||
} else {
|
||||
running += 1;
|
||||
$scope.containers.push(new ContainerViewModel(item));
|
||||
}
|
||||
}
|
||||
|
||||
getStarted(d);
|
||||
|
||||
var c = new Chart($('#containers-chart').get(0).getContext("2d"));
|
||||
var data = [
|
||||
{
|
||||
value: running,
|
||||
color: '#5bb75b',
|
||||
title: 'Running'
|
||||
}, // running
|
||||
{
|
||||
value: stopped,
|
||||
color: '#C7604C',
|
||||
title: 'Stopped'
|
||||
}, // stopped
|
||||
{
|
||||
value: ghost,
|
||||
color: '#E2EAE9',
|
||||
title: 'Ghost'
|
||||
} // ghost
|
||||
];
|
||||
|
||||
c.Doughnut(data, opts);
|
||||
var lgd = $('#chart-legend').get(0);
|
||||
legend(lgd, data);
|
||||
});
|
||||
};
|
||||
|
||||
var opts = {animation:false};
|
||||
if (Settings.firstLoad) {
|
||||
$('#stats').hide();
|
||||
opts.animation = true;
|
||||
Settings.firstLoad = false;
|
||||
$('#masthead').show();
|
||||
|
||||
setTimeout(function() {
|
||||
$('#masthead').slideUp('slow');
|
||||
$('#stats').slideDown('slow');
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
Container.query({all: 1}, function(d) {
|
||||
var running = 0
|
||||
var ghost = 0;
|
||||
var stopped = 0;
|
||||
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var item = d[i];
|
||||
|
||||
if (item.Status === "Ghost") {
|
||||
ghost += 1;
|
||||
} else if (item.Status.indexOf('Exit') !== -1) {
|
||||
stopped += 1;
|
||||
} else {
|
||||
running += 1;
|
||||
$scope.containers.push(new ContainerViewModel(item));
|
||||
}
|
||||
}
|
||||
|
||||
getStarted(d);
|
||||
|
||||
var c = new Chart($('#containers-chart').get(0).getContext("2d"));
|
||||
var data = [
|
||||
{
|
||||
value: running,
|
||||
color: '#5bb75b',
|
||||
title: 'Running'
|
||||
}, // running
|
||||
{
|
||||
value: stopped,
|
||||
color: '#C7604C',
|
||||
title: 'Stopped'
|
||||
}, // stopped
|
||||
{
|
||||
value: ghost,
|
||||
color: '#E2EAE9',
|
||||
title: 'Ghost'
|
||||
} // ghost
|
||||
];
|
||||
|
||||
c.Doughnut(data, opts);
|
||||
var lgd = $('#chart-legend').get(0);
|
||||
legend(lgd, data);
|
||||
});
|
||||
}]);
|
||||
}]);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h2>Events</h2>
|
||||
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="since">Since:</label>
|
||||
<input id="since" type="datetime-local" ng-model="model.since" class="form-control" step="any"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="until">Until:</label>
|
||||
<input id="until" type="datetime-local" ng-model="model.until" class="form-control" step="any"/>
|
||||
</div>
|
||||
<button ng-click="updateEvents()" class="btn btn-primary">Update</button>
|
||||
</form>
|
||||
<br>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Event</th>
|
||||
<th>From</th>
|
||||
<th>ID</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
<tr ng-repeat="event in dockerEvents">
|
||||
<td ng-bind="event.status"/>
|
||||
<td ng-bind="event.from"/>
|
||||
<td ng-bind="event.id"/>
|
||||
<td ng-bind="event.time * 1000 | date:'medium'"/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,42 @@
|
||||
angular.module('events', ['ngOboe'])
|
||||
.controller('EventsController', ['Settings', '$scope', 'Oboe', 'Messages', '$timeout', function (Settings, $scope, oboe, Messages, $timeout) {
|
||||
$scope.updateEvents = function () {
|
||||
$scope.dockerEvents = [];
|
||||
|
||||
// TODO: Clean up URL building
|
||||
var url = Settings.url + '/events?';
|
||||
|
||||
if ($scope.model.since) {
|
||||
var sinceSecs = Math.floor($scope.model.since.getTime() / 1000);
|
||||
url += 'since=' + sinceSecs + '&';
|
||||
}
|
||||
if ($scope.model.until) {
|
||||
var untilSecs = Math.floor($scope.model.until.getTime() / 1000);
|
||||
url += 'until=' + untilSecs;
|
||||
}
|
||||
|
||||
oboe({
|
||||
url: url,
|
||||
pattern: '{id status time}'
|
||||
})
|
||||
.then(function (node) {
|
||||
// finished loading
|
||||
$timeout(function () {
|
||||
$scope.$apply();
|
||||
});
|
||||
}, function (error) {
|
||||
// handle errors
|
||||
Messages.error("Failure", error.data);
|
||||
}, function (node) {
|
||||
// node received
|
||||
$scope.dockerEvents.push(node);
|
||||
});
|
||||
};
|
||||
|
||||
// Init
|
||||
$scope.model = {};
|
||||
$scope.model.since = new Date(Date.now() - 86400000); // 24 hours in the past
|
||||
$scope.model.until = new Date();
|
||||
$scope.updateEvents();
|
||||
|
||||
}]);
|
||||
@@ -1,7 +1,9 @@
|
||||
angular.module('footer', [])
|
||||
.controller('FooterController', ['$scope', 'Settings', function($scope, Settings) {
|
||||
$scope.template = 'app/components/footer/statusbar.html';
|
||||
.controller('FooterController', ['$scope', 'Settings', 'Docker', function ($scope, Settings, Docker) {
|
||||
$scope.template = 'app/components/footer/statusbar.html';
|
||||
|
||||
$scope.uiVersion = Settings.uiVersion;
|
||||
$scope.apiVersion = Settings.version;
|
||||
}]);
|
||||
$scope.uiVersion = Settings.uiVersion;
|
||||
Docker.get({}, function (d) {
|
||||
$scope.apiVersion = d.ApiVersion;
|
||||
});
|
||||
}]);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<footer class="center well">
|
||||
<p><small>Docker API Version: <strong>{{ apiVersion }}</strong> UI Version: <strong>{{ uiVersion }}</strong> <a class="pull-right" href="https://github.com/crosbymichael/dockerui">dockerui</a></small></p>
|
||||
<p>
|
||||
<small>Docker API Version: <strong>{{ apiVersion }}</strong> UI Version: <strong>{{ uiVersion }}</strong> <a
|
||||
class="pull-right" href="https://github.com/crosbymichael/dockerui">dockerui</a></small>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
@@ -5,59 +5,70 @@
|
||||
</div>
|
||||
|
||||
<div class="detail">
|
||||
|
||||
<h4>Image: {{ tag }}</h4>
|
||||
|
||||
<h4>Image: {{ id }}</h4>
|
||||
|
||||
<div class="btn-group detail">
|
||||
<button class="btn btn-success" data-toggle="modal" data-target="#create-modal">Create</button>
|
||||
<button class="btn btn-success" data-toggle="modal" data-target="#create-modal">Start Container</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Containers created:</h4>
|
||||
<canvas id="containers-started-chart" width="750">
|
||||
Get a better broswer... Your holding everyone back.
|
||||
</canvas>
|
||||
<h4>Containers created:</h4>
|
||||
<canvas id="containers-started-chart" width="750">
|
||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a
|
||||
href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Created:</td>
|
||||
<td>{{ image.created }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parent:</td>
|
||||
<td><a href="#/images/{{ image.parent }}/">{{ image.parent }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size:</td>
|
||||
<td>{{ image.Size|humansize }}</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Tags:</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="tag in RepoTags">{{ tag }}
|
||||
<button ng-click="removeImage(tag)" class="btn btn-sm btn-danger">Remove tag</button>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Created:</td>
|
||||
<td>{{ image.Created | date: 'medium'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parent:</td>
|
||||
<td><a href="#/images/{{ image.Parent }}/">{{ image.Parent }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size (Virtual Size):</td>
|
||||
<td>{{ image.Size|humansize }} ({{ image.VirtualSize|humansize }})</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Hostname:</td>
|
||||
<td>{{ image.container_config.Hostname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User:</td>
|
||||
<td>{{ image.container_config.User }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cmd:</td>
|
||||
<td>{{ image.container_config.Cmd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes:</td>
|
||||
<td>{{ image.container_config.Volumes }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes from:</td>
|
||||
<td>{{ image.container_config.VolumesFrom }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Comment:</td>
|
||||
<td>{{ image.comment }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hostname:</td>
|
||||
<td>{{ image.ContainerConfig.Hostname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User:</td>
|
||||
<td>{{ image.ContainerConfig.User }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cmd:</td>
|
||||
<td>{{ image.ContainerConfig.Cmd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes:</td>
|
||||
<td>{{ image.ContainerConfig.Volumes }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes from:</td>
|
||||
<td>{{ image.ContainerConfig.VolumesFrom }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Built with:</td>
|
||||
<td>Docker {{ image.DockerVersion }} on {{ image.Os}}, {{ image.Architecture }}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -69,17 +80,18 @@
|
||||
<div class="span5">
|
||||
<i class="icon-refresh" style="width:32px;height:32px;" ng-click="getHistory()"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="well well-large">
|
||||
<ul>
|
||||
<li ng-repeat="change in history">
|
||||
<strong>{{ change.Id }}</strong>: Created: {{ change.Created|getdate }} Created by: {{ change.CreatedBy }}
|
||||
<strong>{{ change.Id }}</strong>: Created: {{ change.Created|getdate }} Created by: {{ change.CreatedBy
|
||||
}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<hr/>
|
||||
|
||||
<div class="row-fluid">
|
||||
<form class="form-inline" role="form">
|
||||
@@ -87,21 +99,22 @@
|
||||
<legend>Tag image</legend>
|
||||
<div class="form-group">
|
||||
<label>Tag:</label>
|
||||
<input type="text" placeholder="repo..." ng-model="tag.repo" class="form-control">
|
||||
<input type="text" placeholder="repo" ng-model="tagInfo.repo" class="form-control">
|
||||
<input type="text" placeholder="version" ng-model="tagInfo.version" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" ng-model="tag.force" class="form-control"/> Force?
|
||||
<input type="checkbox" ng-model="tagInfo.force" class="form-control"/> Force?
|
||||
</label>
|
||||
</div>
|
||||
<input type="button" ng-click="updateTag()" value="Tag" class="btn btn-primary"/>
|
||||
<input type="button" ng-click="addTag()" value="Add Tag" class="btn btn-primary"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<hr/>
|
||||
|
||||
<div class="btn-remove">
|
||||
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="remove()">Remove Image</button>
|
||||
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="removeImage(id)">Remove Image</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,72 +1,87 @@
|
||||
angular.module('image', [])
|
||||
.controller('ImageController', ['$scope', '$q', '$routeParams', '$location', 'Image', 'Container', 'Messages', 'LineChart',
|
||||
function($scope, $q, $routeParams, $location, Image, Container, Messages, LineChart) {
|
||||
$scope.history = [];
|
||||
$scope.tag = {repo: '', force: false};
|
||||
.controller('ImageController', ['$scope', '$q', '$routeParams', '$location', 'Image', 'Container', 'Messages', 'LineChart',
|
||||
function ($scope, $q, $routeParams, $location, Image, Container, Messages, LineChart) {
|
||||
$scope.history = [];
|
||||
$scope.tagInfo = {repo: '', version: '', force: false};
|
||||
$scope.id = '';
|
||||
$scope.repoTags = [];
|
||||
|
||||
$scope.remove = function() {
|
||||
Image.remove({id: $routeParams.id}, function(d) {
|
||||
Messages.send("Image Removed", $routeParams.id);
|
||||
}, function(e) {
|
||||
$scope.error = e.data;
|
||||
$('#error-message').show();
|
||||
});
|
||||
};
|
||||
$scope.removeImage = function (id) {
|
||||
Image.remove({id: id}, function (d) {
|
||||
d.forEach(function(msg){
|
||||
var key = Object.keys(msg)[0];
|
||||
Messages.send(key, msg[key]);
|
||||
});
|
||||
// If last message key is 'Deleted' then assume the image is gone and send to images page
|
||||
if (d[d.length-1].Deleted) {
|
||||
$location.path('/images');
|
||||
} else {
|
||||
$location.path('/images/' + $scope.id); // Refresh the current page.
|
||||
}
|
||||
}, function (e) {
|
||||
$scope.error = e.data;
|
||||
$('#error-message').show();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getHistory = function() {
|
||||
Image.history({id: $routeParams.id}, function(d) {
|
||||
$scope.history = d;
|
||||
});
|
||||
};
|
||||
$scope.getHistory = function () {
|
||||
Image.history({id: $routeParams.id}, function (d) {
|
||||
$scope.history = d;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.updateTag = function() {
|
||||
var tag = $scope.tag;
|
||||
Image.tag({id: $routeParams.id, repo: tag.repo, force: tag.force ? 1 : 0}, function(d) {
|
||||
Messages.send("Tag Added", $routeParams.id);
|
||||
}, function(e) {
|
||||
$scope.error = e.data;
|
||||
$('#error-message').show();
|
||||
});
|
||||
};
|
||||
$scope.addTag = function () {
|
||||
var tag = $scope.tagInfo;
|
||||
Image.tag({
|
||||
id: $routeParams.id,
|
||||
repo: tag.repo,
|
||||
tag: tag.version,
|
||||
force: tag.force ? 1 : 0
|
||||
}, function (d) {
|
||||
Messages.send("Tag Added", $routeParams.id);
|
||||
$location.path('/images/' + $scope.id);
|
||||
}, function (e) {
|
||||
$scope.error = e.data;
|
||||
$('#error-message').show();
|
||||
});
|
||||
};
|
||||
|
||||
function getContainersFromImage($q, Container, tag) {
|
||||
var defer = $q.defer();
|
||||
|
||||
Container.query({all:1, notruc:1}, function(d) {
|
||||
var containers = [];
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var c = d[i];
|
||||
if (c.Image == tag) {
|
||||
containers.push(new ContainerViewModel(c));
|
||||
}
|
||||
function getContainersFromImage($q, Container, imageId) {
|
||||
var defer = $q.defer();
|
||||
|
||||
Container.query({all: 1, notruc: 1}, function (d) {
|
||||
var containers = [];
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var c = d[i];
|
||||
if (c.ImageID === imageId) {
|
||||
containers.push(new ContainerViewModel(c));
|
||||
}
|
||||
}
|
||||
defer.resolve(containers);
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
}
|
||||
defer.resolve(containers);
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
}
|
||||
Image.get({id: $routeParams.id}, function (d) {
|
||||
$scope.image = d;
|
||||
$scope.id = d.Id;
|
||||
$scope.RepoTags = d.RepoTags;
|
||||
|
||||
Image.get({id: $routeParams.id}, function(d) {
|
||||
$scope.image = d;
|
||||
$scope.tag = d.id;
|
||||
var t = $routeParams.tag;
|
||||
if (t && t !== ":") {
|
||||
$scope.tag = t;
|
||||
var promise = getContainersFromImage($q, Container, t);
|
||||
|
||||
promise.then(function(containers) {
|
||||
LineChart.build('#containers-started-chart', containers, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
|
||||
getContainersFromImage($q, Container, $scope.id).then(function (containers) {
|
||||
LineChart.build('#containers-started-chart', containers, function (c) {
|
||||
return new Date(c.Created * 1000).toLocaleDateString();
|
||||
});
|
||||
});
|
||||
}, function (e) {
|
||||
if (e.status === 404) {
|
||||
$('.detail').hide();
|
||||
$scope.error = "Image not found.<br />" + $routeParams.id;
|
||||
} else {
|
||||
$scope.error = e.data;
|
||||
}
|
||||
$('#error-message').show();
|
||||
});
|
||||
}
|
||||
}, function(e) {
|
||||
if (e.status === 404) {
|
||||
$('.detail').hide();
|
||||
$scope.error = "Image not found.<br />" + $routeParams.id;
|
||||
} else {
|
||||
$scope.error = e.data;
|
||||
}
|
||||
$('#error-message').show();
|
||||
});
|
||||
|
||||
$scope.getHistory();
|
||||
}]);
|
||||
$scope.getHistory();
|
||||
}]);
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
|
||||
<div ng-include="template" ng-controller="BuilderController"></div>
|
||||
<div ng-include="template" ng-controller="PullImageController"></div>
|
||||
|
||||
<h2>Images:</h2>
|
||||
|
||||
<ul class="nav nav-pills">
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b class="caret"></b></a>
|
||||
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
|
||||
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
<ul class="nav nav-pills pull-left">
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" data-target="#">Actions <b class="caret"></b></a>
|
||||
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
|
||||
<li><a tabindex="-1" href="" ng-click="removeAction()">Remove</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a data-toggle="modal" data-target="#pull-modal" href="">Pull</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="pull-right form-inline">
|
||||
<input type="text" class="form-control" id="filter" placeholder="Filter" ng-model="filter"/> <label class="sr-only" for="filter">Filter</label>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -22,7 +29,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="image in images | orderBy:predicate">
|
||||
<tr ng-repeat="image in images | filter:filter | orderBy:predicate">
|
||||
<td><input type="checkbox" ng-model="image.Checked" /></td>
|
||||
<td><a href="#/images/{{ image.Id }}/?tag={{ image|repotag }}">{{ image.Id|truncate:20}}</a></td>
|
||||
<td>{{ image|repotag }}</td>
|
||||
|
||||
@@ -1,52 +1,54 @@
|
||||
angular.module('images', [])
|
||||
.controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages',
|
||||
function($scope, Image, ViewSpinner, Messages) {
|
||||
$scope.toggle = false;
|
||||
$scope.predicate = '-Created';
|
||||
.controller('ImagesController', ['$scope', 'Image', 'ViewSpinner', 'Messages',
|
||||
function ($scope, Image, ViewSpinner, Messages) {
|
||||
$scope.toggle = false;
|
||||
$scope.predicate = '-Created';
|
||||
|
||||
$scope.showBuilder = function() {
|
||||
$('#build-modal').modal('show');
|
||||
};
|
||||
$scope.showBuilder = function () {
|
||||
$('#build-modal').modal('show');
|
||||
};
|
||||
|
||||
$scope.removeAction = function() {
|
||||
ViewSpinner.spin();
|
||||
var counter = 0;
|
||||
var complete = function() {
|
||||
counter = counter - 1;
|
||||
if (counter === 0) {
|
||||
ViewSpinner.stop();
|
||||
}
|
||||
};
|
||||
angular.forEach($scope.images, function(i) {
|
||||
if (i.Checked) {
|
||||
counter = counter + 1;
|
||||
Image.remove({id: i.Id}, function(d) {
|
||||
angular.forEach(d, function(resource) {
|
||||
Messages.send("Image deleted", resource.Deleted);
|
||||
});
|
||||
var index = $scope.images.indexOf(i);
|
||||
$scope.images.splice(index, 1);
|
||||
complete();
|
||||
}, function(e) {
|
||||
Messages.error("Failure", e.data);
|
||||
complete();
|
||||
$scope.removeAction = function () {
|
||||
ViewSpinner.spin();
|
||||
var counter = 0;
|
||||
var complete = function () {
|
||||
counter = counter - 1;
|
||||
if (counter === 0) {
|
||||
ViewSpinner.stop();
|
||||
}
|
||||
};
|
||||
angular.forEach($scope.images, function (i) {
|
||||
if (i.Checked) {
|
||||
counter = counter + 1;
|
||||
Image.remove({id: i.Id}, function (d) {
|
||||
angular.forEach(d, function (resource) {
|
||||
Messages.send("Image deleted", resource.Deleted);
|
||||
});
|
||||
var index = $scope.images.indexOf(i);
|
||||
$scope.images.splice(index, 1);
|
||||
complete();
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e.data);
|
||||
complete();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
$scope.toggleSelectAll = function() {
|
||||
angular.forEach($scope.images, function(i) {
|
||||
i.Checked = $scope.toggle;
|
||||
});
|
||||
};
|
||||
$scope.toggleSelectAll = function () {
|
||||
angular.forEach($scope.images, function (i) {
|
||||
i.Checked = $scope.toggle;
|
||||
});
|
||||
};
|
||||
|
||||
ViewSpinner.spin();
|
||||
Image.query({}, function(d) {
|
||||
$scope.images = d.map(function(item) { return new ImageViewModel(item); });
|
||||
ViewSpinner.stop();
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e.data);
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
}]);
|
||||
ViewSpinner.spin();
|
||||
Image.query({}, function (d) {
|
||||
$scope.images = d.map(function (item) {
|
||||
return new ImageViewModel(item);
|
||||
});
|
||||
ViewSpinner.stop();
|
||||
}, function (e) {
|
||||
Messages.error("Failure", e.data);
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
}]);
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<div class="detail">
|
||||
<h2>Docker Information</h2>
|
||||
|
||||
<div>
|
||||
<p class="lead">
|
||||
<strong>API Endpoint: </strong>{{ endpoint }}<br/>
|
||||
<strong>API Version: </strong>{{ docker.ApiVersion }}<br/>
|
||||
<strong>Docker version: </strong>{{ docker.Version }}<br/>
|
||||
<strong>Git Commit: </strong>{{ docker.GitCommit }}<br/>
|
||||
<strong>Go Version: </strong>{{ docker.GoVersion }}<br/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Containers:</td>
|
||||
<td>{{ info.Containers }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Images:</td>
|
||||
<td>{{ info.Images }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Debug:</td>
|
||||
<td>{{ info.Debug }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CPUs:</td>
|
||||
<td>{{ info.NCPU }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Memory:</td>
|
||||
<td>{{ info.MemTotal|humansize }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Operating System:</td>
|
||||
<td>{{ info.OperatingSystem }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kernel Version:</td>
|
||||
<td>{{ info.KernelVersion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID:</td>
|
||||
<td>{{ info.ID }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Labels:</td>
|
||||
<td>{{ info.Labels }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>File Descriptors:</td>
|
||||
<td>{{ info.NFd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Goroutines:</td>
|
||||
<td>{{ info.NGoroutines }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Storage Driver:</td>
|
||||
<td>{{ info.Driver }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Storage Driver Status:</td>
|
||||
<td>
|
||||
<p ng-repeat="val in info.DriverStatus">
|
||||
{{ val[0] }}: {{ val[1] }}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Execution Driver:</td>
|
||||
<td>{{ info.ExecutionDriver }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Events:</td>
|
||||
<td><a href="#/events">Events</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IPv4 Forwarding:</td>
|
||||
<td>{{ info.IPv4Forwarding }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Index Server Address:</td>
|
||||
<td>{{ info.IndexServerAddress }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Init Path:</td>
|
||||
<td>{{ info.InitPath }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Docker Root Directory:</td>
|
||||
<td>{{ info.DockerRootDir }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Init SHA1</td>
|
||||
<td>{{ info.InitSha1 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Memory Limit:</td>
|
||||
<td>{{ info.MemoryLimit }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Swap Limit:</td>
|
||||
<td>{{ info.SwapLimit }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
angular.module('info', [])
|
||||
.controller('InfoController', ['$scope', 'System', 'Docker', 'Settings', 'Messages',
|
||||
function ($scope, System, Docker, Settings, Messages) {
|
||||
$scope.info = {};
|
||||
$scope.docker = {};
|
||||
$scope.endpoint = Settings.endpoint;
|
||||
$scope.apiVersion = Settings.version;
|
||||
|
||||
Docker.get({}, function (d) {
|
||||
$scope.docker = d;
|
||||
});
|
||||
System.get({}, function (d) {
|
||||
$scope.info = d;
|
||||
});
|
||||
}]);
|
||||
@@ -1,9 +1,10 @@
|
||||
<div class="masthead">
|
||||
<div class="masthead">
|
||||
<h3 class="text-muted">DockerUI</h3>
|
||||
<ul class="nav well">
|
||||
<li><a href="#">Dashboard</a></li>
|
||||
<ul class="nav well">
|
||||
<li><a href="#/">Dashboard</a></li>
|
||||
<li><a href="#/containers/">Containers</a></li>
|
||||
<li><a href="#/containers_network/">Containers Network</a></li>
|
||||
<li><a href="#/images/">Images</a></li>
|
||||
<li><a href="#/settings/">Settings</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<li><a href="#/info/">Info</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('masthead', [])
|
||||
.controller('MastheadController', ['$scope', function($scope) {
|
||||
$scope.template = 'app/components/masthead/masthead.html';
|
||||
}]);
|
||||
.controller('MastheadController', ['$scope', function ($scope) {
|
||||
$scope.template = 'app/components/masthead/masthead.html';
|
||||
}]);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<div id="pull-modal" class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Pull Image</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form novalidate role="form" name="pullForm">
|
||||
<!--<div class="input-group">
|
||||
<span class="input-group-addon" id="basic-addon1">Image name</span>
|
||||
<input type="text" class="form-control" placeholder="imageName" aria-describedby="basic-addon1">
|
||||
</div>-->
|
||||
<div class="form-group">
|
||||
<label>Registry:</label>
|
||||
<input type="text" ng-model="config.registry" class="form-control"
|
||||
placeholder="Registry. Leave empty to user docker hub"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Repo:</label>
|
||||
<input type="text" ng-model="config.repo" class="form-control"
|
||||
placeholder="Repository - usually your username."/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Image Name:</label>
|
||||
<input type="text" ng-model="config.fromImage" class="form-control" placeholder="Image name"
|
||||
required/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Tag Name:</label>
|
||||
<input type="text" ng-model="config.tag" class="form-control"
|
||||
placeholder="Tag name. If empty it will download ALL tags."/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="alert alert-error" id="error-message" style="display:none">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="btn btn-primary" ng-click="pull()">Pull</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,56 @@
|
||||
angular.module('pullImage', [])
|
||||
.controller('PullImageController', ['$scope', '$log', 'Dockerfile', 'Messages', 'Image', 'ViewSpinner',
|
||||
function ($scope, $log, Dockerfile, Messages, Image, ViewSpinner) {
|
||||
$scope.template = 'app/components/pullImage/pullImage.html';
|
||||
|
||||
$scope.init = function () {
|
||||
$scope.config = {
|
||||
registry: '',
|
||||
repo: '',
|
||||
fromImage: '',
|
||||
tag: 'latest'
|
||||
};
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
function failedRequestHandler(e, Messages) {
|
||||
Messages.error('Error', errorMsgFilter(e));
|
||||
}
|
||||
|
||||
$scope.pull = function () {
|
||||
$('#error-message').hide();
|
||||
var config = angular.copy($scope.config);
|
||||
var imageName = (config.registry ? config.registry + '/' : '' ) +
|
||||
(config.repo ? config.repo + '/' : '') +
|
||||
(config.fromImage) +
|
||||
(config.tag ? ':' + config.tag : '');
|
||||
|
||||
ViewSpinner.spin();
|
||||
$('#pull-modal').modal('hide');
|
||||
Image.create(config, function (data) {
|
||||
ViewSpinner.stop();
|
||||
if (data.constructor === Array) {
|
||||
var f = data.length > 0 && data[data.length - 1].hasOwnProperty('error');
|
||||
//check for error
|
||||
if (f) {
|
||||
var d = data[data.length - 1];
|
||||
$scope.error = "Cannot pull image " + imageName + " Reason: " + d.error;
|
||||
$('#pull-modal').modal('show');
|
||||
$('#error-message').show();
|
||||
} else {
|
||||
Messages.send("Image Added", imageName);
|
||||
$scope.init();
|
||||
}
|
||||
} else {
|
||||
Messages.send("Image Added", imageName);
|
||||
$scope.init();
|
||||
}
|
||||
}, function (e) {
|
||||
ViewSpinner.stop();
|
||||
$scope.error = "Cannot pull image " + imageName + " Reason: " + e.data;
|
||||
$('#pull-modal').modal('show');
|
||||
$('#error-message').show();
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@@ -1,49 +0,0 @@
|
||||
<div class="detail">
|
||||
<h2>Docker Information</h2>
|
||||
<div>
|
||||
<p class="lead">
|
||||
<strong>Endpoint: </strong>{{ endpoint }}<br />
|
||||
<strong>Api Version: </strong>{{ apiVersion }}<br />
|
||||
<strong>Version: </strong>{{ docker.Version }}<br />
|
||||
<strong>Git Commit: </strong>{{ docker.GitCommit }}<br />
|
||||
<strong>Go Version: </strong>{{ docker.GoVersion }}<br />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Containers:</td>
|
||||
<td>{{ info.Containers }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Images:</td>
|
||||
<td>{{ info.Images }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Debug:</td>
|
||||
<td>{{ info.Debug }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NFd:</td>
|
||||
<td>{{ info.NFd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NGoroutines:</td>
|
||||
<td>{{ info.NGoroutines }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MemoryLimit:</td>
|
||||
<td>{{ info.MemoryLimit }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SwapLimit:</td>
|
||||
<td>{{ info.SwapLimit }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NFd:</td>
|
||||
<td>{{ info.NFd }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1,11 +0,0 @@
|
||||
angular.module('settings', [])
|
||||
.controller('SettingsController', ['$scope', 'System', 'Docker', 'Settings', 'Messages',
|
||||
function($scope, System, Docker, Settings, Messages) {
|
||||
$scope.info = {};
|
||||
$scope.docker = {};
|
||||
$scope.endpoint = Settings.endpoint;
|
||||
$scope.apiVersion = Settings.version;
|
||||
|
||||
Docker.get({}, function(d) { $scope.docker = d; });
|
||||
System.get({}, function(d) { $scope.info = d; });
|
||||
}]);
|
||||
@@ -1,11 +1,11 @@
|
||||
<div class="well">
|
||||
<strong>Running containers:</strong>
|
||||
<br />
|
||||
<br/>
|
||||
<strong>Endpoint: </strong>{{ endpoint }}
|
||||
<ul>
|
||||
<li ng-repeat="container in containers">
|
||||
<a href="#/containers/{{ container.Id }}/">{{ container.Id|truncate:20 }}</a>
|
||||
<span class="pull-right label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span>
|
||||
<span class="pull-right label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
angular.module('sidebar', [])
|
||||
.controller('SideBarController', ['$scope', 'Container', 'Settings',
|
||||
function($scope, Container, Settings) {
|
||||
$scope.template = 'partials/sidebar.html';
|
||||
$scope.containers = [];
|
||||
$scope.endpoint = Settings.endpoint;
|
||||
.controller('SideBarController', ['$scope', 'Container', 'Settings',
|
||||
function ($scope, Container, Settings) {
|
||||
$scope.template = 'partials/sidebar.html';
|
||||
$scope.containers = [];
|
||||
$scope.endpoint = Settings.endpoint;
|
||||
|
||||
Container.query({all: 0}, function(d) {
|
||||
$scope.containers = d;
|
||||
});
|
||||
}]);
|
||||
Container.query({all: 0}, function (d) {
|
||||
$scope.containers = d;
|
||||
});
|
||||
}]);
|
||||
|
||||
@@ -1,53 +1,159 @@
|
||||
angular.module('startContainer', [])
|
||||
.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages',
|
||||
function($scope, $routeParams, $location, Container, Messages) {
|
||||
$scope.template = 'app/components/startContainer/startcontainer.html';
|
||||
$scope.config = {
|
||||
name: '',
|
||||
memory: 0,
|
||||
memorySwap: 0,
|
||||
cpuShares: 1024,
|
||||
env: '',
|
||||
commands: '',
|
||||
volumesFrom: ''
|
||||
};
|
||||
$scope.commandPlaceholder = '["/bin/echo", "Hello world"]';
|
||||
angular.module('startContainer', ['ui.bootstrap'])
|
||||
.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'containernameFilter', 'errorMsgFilter',
|
||||
function ($scope, $routeParams, $location, Container, Messages, containernameFilter, errorMsgFilter) {
|
||||
$scope.template = 'app/components/startContainer/startcontainer.html';
|
||||
|
||||
function failedRequestHandler(e, Messages) {
|
||||
Messages.send({class: 'text-error', data: e.data});
|
||||
}
|
||||
Container.query({all: 1}, function (d) {
|
||||
$scope.containerNames = d.map(function (container) {
|
||||
return containernameFilter(container);
|
||||
});
|
||||
});
|
||||
|
||||
$scope.create = function() {
|
||||
var cmds = null;
|
||||
if ($scope.config.commands !== '') {
|
||||
cmds = angular.fromJson($scope.config.commands);
|
||||
}
|
||||
var id = $routeParams.id;
|
||||
var ctor = Container;
|
||||
var loc = $location;
|
||||
var s = $scope;
|
||||
|
||||
Container.create({
|
||||
Image: id,
|
||||
name: $scope.config.name,
|
||||
Memory: $scope.config.memory,
|
||||
MemorySwap: $scope.config.memorySwap,
|
||||
CpuShares: $scope.config.cpuShares,
|
||||
Cmd: cmds,
|
||||
VolumesFrom: $scope.config.volumesFrom
|
||||
}, function(d) {
|
||||
if (d.Id) {
|
||||
ctor.start({id: d.Id}, function(cd) {
|
||||
$('#create-modal').modal('hide');
|
||||
loc.path('/containers/' + d.Id + '/');
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
});
|
||||
} else {
|
||||
failedRequestHandler(d, Messages);
|
||||
$scope.config = {
|
||||
Env: [],
|
||||
Labels: [],
|
||||
Volumes: [],
|
||||
SecurityOpts: [],
|
||||
HostConfig: {
|
||||
PortBindings: [],
|
||||
Binds: [],
|
||||
Links: [],
|
||||
Dns: [],
|
||||
DnsSearch: [],
|
||||
VolumesFrom: [],
|
||||
CapAdd: [],
|
||||
CapDrop: [],
|
||||
Devices: [],
|
||||
LxcConf: [],
|
||||
ExtraHosts: []
|
||||
}
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
});
|
||||
};
|
||||
}]);
|
||||
};
|
||||
|
||||
$scope.menuStatus = {
|
||||
containerOpen: true,
|
||||
hostConfigOpen: false
|
||||
};
|
||||
|
||||
function failedRequestHandler(e, Messages) {
|
||||
Messages.error('Error', errorMsgFilter(e));
|
||||
}
|
||||
|
||||
function rmEmptyKeys(col) {
|
||||
for (var key in col) {
|
||||
if (col[key] === null || col[key] === undefined || col[key] === '' || $.isEmptyObject(col[key]) || col[key].length === 0) {
|
||||
delete col[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNames(arr) {
|
||||
return arr.map(function (item) {
|
||||
return item.name;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.create = function () {
|
||||
// Copy the config before transforming fields to the remote API format
|
||||
var config = angular.copy($scope.config);
|
||||
|
||||
config.Image = $routeParams.id;
|
||||
|
||||
if (config.Cmd && config.Cmd[0] === "[") {
|
||||
config.Cmd = angular.fromJson(config.Cmd);
|
||||
} else if (config.Cmd) {
|
||||
config.Cmd = config.Cmd.split(' ');
|
||||
}
|
||||
|
||||
config.Env = config.Env.map(function (envar) {
|
||||
return envar.name + '=' + envar.value;
|
||||
});
|
||||
var labels = {};
|
||||
config.Labels = config.Labels.forEach(function(label) {
|
||||
labels[label.key] = label.value;
|
||||
});
|
||||
config.Labels = labels;
|
||||
|
||||
config.Volumes = getNames(config.Volumes);
|
||||
config.SecurityOpts = getNames(config.SecurityOpts);
|
||||
|
||||
config.HostConfig.VolumesFrom = getNames(config.HostConfig.VolumesFrom);
|
||||
config.HostConfig.Binds = getNames(config.HostConfig.Binds);
|
||||
config.HostConfig.Links = getNames(config.HostConfig.Links);
|
||||
config.HostConfig.Dns = getNames(config.HostConfig.Dns);
|
||||
config.HostConfig.DnsSearch = getNames(config.HostConfig.DnsSearch);
|
||||
config.HostConfig.CapAdd = getNames(config.HostConfig.CapAdd);
|
||||
config.HostConfig.CapDrop = getNames(config.HostConfig.CapDrop);
|
||||
config.HostConfig.LxcConf = config.HostConfig.LxcConf.reduce(function (prev, cur, idx) {
|
||||
prev[cur.name] = cur.value;
|
||||
return prev;
|
||||
}, {});
|
||||
config.HostConfig.ExtraHosts = config.HostConfig.ExtraHosts.map(function (entry) {
|
||||
return entry.host + ':' + entry.ip;
|
||||
});
|
||||
|
||||
var ExposedPorts = {};
|
||||
var PortBindings = {};
|
||||
config.HostConfig.PortBindings.forEach(function (portBinding) {
|
||||
var intPort = portBinding.intPort + "/tcp";
|
||||
if (portBinding.protocol === "udp") {
|
||||
intPort = portBinding.intPort + "/udp";
|
||||
}
|
||||
var binding = {
|
||||
HostIp: portBinding.ip,
|
||||
HostPort: portBinding.extPort
|
||||
};
|
||||
if (portBinding.intPort) {
|
||||
ExposedPorts[intPort] = {};
|
||||
if (intPort in PortBindings) {
|
||||
PortBindings[intPort].push(binding);
|
||||
} else {
|
||||
PortBindings[intPort] = [binding];
|
||||
}
|
||||
} else {
|
||||
Messages.send('Warning', 'Internal port must be specified for PortBindings');
|
||||
}
|
||||
});
|
||||
config.ExposedPorts = ExposedPorts;
|
||||
config.HostConfig.PortBindings = PortBindings;
|
||||
|
||||
// Remove empty fields from the request to avoid overriding defaults
|
||||
rmEmptyKeys(config.HostConfig);
|
||||
rmEmptyKeys(config);
|
||||
|
||||
var ctor = Container;
|
||||
var loc = $location;
|
||||
var s = $scope;
|
||||
Container.create(config, function (d) {
|
||||
if (d.Id) {
|
||||
var reqBody = config.HostConfig || {};
|
||||
reqBody.id = d.Id;
|
||||
ctor.start(reqBody, function (cd) {
|
||||
if (cd.id) {
|
||||
Messages.send('Container Started', d.Id);
|
||||
$('#create-modal').modal('hide');
|
||||
loc.path('/containers/' + d.Id + '/');
|
||||
} else {
|
||||
failedRequestHandler(cd, Messages);
|
||||
ctor.remove({id: d.Id}, function () {
|
||||
Messages.send('Container Removed', d.Id);
|
||||
});
|
||||
}
|
||||
}, function (e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
});
|
||||
} else {
|
||||
failedRequestHandler(d, Messages);
|
||||
}
|
||||
}, function (e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addEntry = function (array, entry) {
|
||||
array.push(entry);
|
||||
};
|
||||
$scope.rmEntry = function (array, entry) {
|
||||
var idx = array.indexOf(entry);
|
||||
array.splice(idx, 1);
|
||||
};
|
||||
}]);
|
||||
|
||||
@@ -7,37 +7,443 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form role="form">
|
||||
<fieldset>
|
||||
<accordion close-others="true">
|
||||
<accordion-group heading="Container options" is-open="menuStatus.containerOpen">
|
||||
<fieldset>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label>Cmd:</label>
|
||||
<input type="text" placeholder='["/bin/echo", "Hello world"]'
|
||||
ng-model="config.Cmd" class="form-control"/>
|
||||
<small>Input commands as a raw string or JSON array</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Entrypoint:</label>
|
||||
<input type="text" ng-model="config.Entrypoint" class="form-control"
|
||||
placeholder="./entrypoint.sh"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" ng-model="config.name" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Hostname:</label>
|
||||
<input type="text" ng-model="config.Hostname" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Domainname:</label>
|
||||
<input type="text" ng-model="config.Domainname" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>User:</label>
|
||||
<input type="text" ng-model="config.User" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Memory:</label>
|
||||
<input type="number" ng-model="config.Memory" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Volumes:</label>
|
||||
|
||||
<div ng-repeat="volume in config.Volumes">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="volume.name" class="form-control"
|
||||
placeholder="/var/data"/>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
ng-click="rmEntry(config.Volumes, volume)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.Volumes, {name: ''})">Add Volume
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label>MemorySwap:</label>
|
||||
<input type="number" ng-model="config.MemorySwap" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>CpuShares:</label>
|
||||
<input type="number" ng-model="config.CpuShares" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Cpuset:</label>
|
||||
<input type="text" ng-model="config.Cpuset" class="form-control"
|
||||
placeholder="1,2"/>
|
||||
<small>Input as comma-separated list of numbers</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>WorkingDir:</label>
|
||||
<input type="text" ng-model="config.WorkingDir" class="form-control"
|
||||
placeholder="/app"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>MacAddress:</label>
|
||||
<input type="text" ng-model="config.MacAddress" class="form-control"
|
||||
placeholder="12:34:56:78:9a:bc"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="networkDisabled">NetworkDisabled:</label>
|
||||
<input id="networkDisabled" type="checkbox"
|
||||
ng-model="config.NetworkDisabled"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tty">Tty:</label>
|
||||
<input id="tty" type="checkbox" ng-model="config.Tty"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="openStdin">OpenStdin:</label>
|
||||
<input id="openStdin" type="checkbox" ng-model="config.OpenStdin"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="stdinOnce">StdinOnce:</label>
|
||||
<input id="stdinOnce" type="checkbox" ng-model="config.StdinOnce"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>SecurityOpts:</label>
|
||||
|
||||
<div ng-repeat="opt in config.SecurityOpts">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="opt.name" class="form-control"
|
||||
placeholder="label:type:svirt_apache"/>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
ng-click="rmEntry(config.SecurityOpts, opt)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.SecurityOpts, {name: ''})">Add Option
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label>Cmd:</label>
|
||||
<input type="text" placeholder="{{ commandPlaceholder }}" ng-model="config.commands" class="form-control"/>
|
||||
<small>Input commands as an array</small>
|
||||
<label>Env:</label>
|
||||
|
||||
<div ng-repeat="envar in config.Env">
|
||||
<div class="form-group form-inline">
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Variable Name:</label>
|
||||
<input type="text" ng-model="envar.name" class="form-control"
|
||||
placeholder="NAME"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Variable Value:</label>
|
||||
<input type="text" ng-model="envar.value" class="form-control"
|
||||
placeholder="value"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-danger btn-xs form-control"
|
||||
ng-click="rmEntry(config.Env, envar)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.Env, {name: '', value: ''})">Add environment
|
||||
variable
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Name:</label>
|
||||
<input type="text" ng-model="config.name" class="form-control"/>
|
||||
<label>Labels:</label>
|
||||
|
||||
<div ng-repeat="label in config.Labels">
|
||||
<div class="form-group form-inline">
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Key:</label>
|
||||
<input type="text" ng-model="label.key" class="form-control"
|
||||
placeholder="key"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Value:</label>
|
||||
<input type="text" ng-model="label.value" class="form-control"
|
||||
placeholder="value"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-danger btn-xs form-control"
|
||||
ng-click="rmEntry(config.Labels, label)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.Labels, {key: '', value: ''})">Add Label
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</accordion-group>
|
||||
<accordion-group heading="HostConfig options" is-open="menuStatus.hostConfigOpen">
|
||||
<fieldset>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label>Binds:</label>
|
||||
|
||||
<div ng-repeat="bind in config.HostConfig.Binds">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="bind.name" class="form-control"
|
||||
placeholder="/host:/container"/>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
ng-click="rmEntry(config.HostConfig.Binds, bind)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.Binds, {name: ''})">Add Bind
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Links:</label>
|
||||
|
||||
<div ng-repeat="link in config.HostConfig.Links">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="link.name" class="form-control"
|
||||
placeholder="web:db">
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
ng-click="rmEntry(config.HostConfig.Links, link)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.Links, {name: ''})">Add Link
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Dns:</label>
|
||||
|
||||
<div ng-repeat="entry in config.HostConfig.Dns">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="entry.name" class="form-control"
|
||||
placeholder="8.8.8.8"/>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
ng-click="rmEntry(config.HostConfig.Dns, entry)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.Dns, {name: ''})">Add entry
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>DnsSearch:</label>
|
||||
|
||||
<div ng-repeat="entry in config.HostConfig.DnsSearch">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="entry.name" class="form-control"
|
||||
placeholder="example.com"/>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
ng-click="rmEntry(config.HostConfig.DnsSearch, entry)">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.DnsSearch, {name: ''})">Add
|
||||
entry
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>CapAdd:</label>
|
||||
|
||||
<div ng-repeat="entry in config.HostConfig.CapAdd">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="entry.name" class="form-control"
|
||||
placeholder="cap_sys_admin"/>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
ng-click="rmEntry(config.HostConfig.CapAdd, entry)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.CapAdd, {name: ''})">Add entry
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>CapDrop:</label>
|
||||
|
||||
<div ng-repeat="entry in config.HostConfig.CapDrop">
|
||||
<div class="form-group form-inline">
|
||||
<input type="text" ng-model="entry.name" class="form-control"
|
||||
placeholder="cap_sys_admin"/>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
ng-click="rmEntry(config.HostConfig.CapDrop, entry)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.CapDrop, {name: ''})">Add entry
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label>NetworkMode:</label>
|
||||
<input type="text" ng-model="config.HostConfig.NetworkMode"
|
||||
class="form-control" placeholder="bridge"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="publishAllPorts">PublishAllPorts:</label>
|
||||
<input id="publishAllPorts" type="checkbox"
|
||||
ng-model="config.HostConfig.PublishAllPorts"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="privileged">Privileged:</label>
|
||||
<input id="privileged" type="checkbox"
|
||||
ng-model="config.HostConfig.Privileged"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>VolumesFrom:</label>
|
||||
|
||||
<div ng-repeat="volume in config.HostConfig.VolumesFrom">
|
||||
<div class="form-group form-inline">
|
||||
<select ng-model="volume.name"
|
||||
ng-options="name for name in containerNames track by name"
|
||||
class="form-control">
|
||||
</select>
|
||||
<button class="btn btn-danger btn-xs form-control"
|
||||
ng-click="rmEntry(config.HostConfig.VolumesFrom, volume)">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.VolumesFrom, {name: ''})">Add
|
||||
volume
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>RestartPolicy:</label>
|
||||
<select ng-model="config.HostConfig.RestartPolicy.name">
|
||||
<option value="">disabled</option>
|
||||
<option value="always">always</option>
|
||||
<option value="on-failure">on-failure</option>
|
||||
</select>
|
||||
<label>MaximumRetryCount:</label>
|
||||
<input type="number"
|
||||
ng-model="config.HostConfig.RestartPolicy.MaximumRetryCount"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label>ExtraHosts:</label>
|
||||
|
||||
<div ng-repeat="entry in config.HostConfig.ExtraHosts">
|
||||
<div class="form-group form-inline">
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Hostname:</label>
|
||||
<input type="text" ng-model="entry.host" class="form-control"
|
||||
placeholder="hostname"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only">IP Address:</label>
|
||||
<input type="text" ng-model="entry.ip" class="form-control"
|
||||
placeholder="127.0.0.1"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-danger btn-xs form-control"
|
||||
ng-click="rmEntry(config.HostConfig.ExtraHosts, entry)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.ExtraHosts, {host: '', ip: ''})">Add
|
||||
extra host
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Memory:</label>
|
||||
<input type="number" ng-model="config.memory" class="form-control"/>
|
||||
<label>LxcConf:</label>
|
||||
|
||||
<div ng-repeat="entry in config.HostConfig.LxcConf">
|
||||
<div class="form-group form-inline">
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Name:</label>
|
||||
<input type="text" ng-model="entry.name" class="form-control"
|
||||
placeholder="lxc.utsname"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only">Value:</label>
|
||||
<input type="text" ng-model="entry.value" class="form-control"
|
||||
placeholder="docker"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-danger btn-xs form-control"
|
||||
ng-click="rmEntry(config.HostConfig.LxcConf, entry)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.LxcConf, {name: '', value: ''})">Add
|
||||
Entry
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Memory Swap:</label>
|
||||
<input type="number" ng-model="config.memorySwap" class="form-control"/>
|
||||
<label>Devices:</label>
|
||||
|
||||
<div ng-repeat="device in config.HostConfig.Devices">
|
||||
<div class="form-group form-inline inline-four">
|
||||
<label class="sr-only">PathOnHost:</label>
|
||||
<input type="text" ng-model="device.PathOnHost" class="form-control"
|
||||
placeholder="PathOnHost"/>
|
||||
<label class="sr-only">PathInContainer:</label>
|
||||
<input type="text" ng-model="device.PathInContainer" class="form-control"
|
||||
placeholder="PathInContainer"/>
|
||||
<label class="sr-only">CgroupPermissions:</label>
|
||||
<input type="text" ng-model="device.CgroupPermissions" class="form-control"
|
||||
placeholder="CgroupPermissions"/>
|
||||
<button class="btn btn-danger btn-xs form-control"
|
||||
ng-click="rmEntry(config.HostConfig.Devices, device)">Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.Devices, { PathOnHost: '', PathInContainer: '', CgroupPermissions: ''})">
|
||||
Add Device
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>CPU Shares:</label>
|
||||
<input type="number" ng-model="config.cpuShares" class="form-control"/>
|
||||
<label>PortBindings:</label>
|
||||
|
||||
<div ng-repeat="portBinding in config.HostConfig.PortBindings">
|
||||
<div class="form-group form-inline inline-four">
|
||||
<label class="sr-only">Host IP:</label>
|
||||
<input type="text" ng-model="portBinding.ip" class="form-control"
|
||||
placeholder="Host IP Address"/>
|
||||
<label class="sr-only">Host Port:</label>
|
||||
<input type="text" ng-model="portBinding.extPort" class="form-control"
|
||||
placeholder="Host Port"/>
|
||||
<label class="sr-only">Container port:</label>
|
||||
<input type="text" ng-model="portBinding.intPort" class="form-control"
|
||||
placeholder="Container Port"/>
|
||||
<select ng-model="portBinding.protocol">
|
||||
<option value="">tcp</option>
|
||||
<option value="udp">udp</option>
|
||||
</select>
|
||||
<button class="btn btn-danger btn-xs form-control"
|
||||
ng-click="rmEntry(config.HostConfig.PortBindings, portBinding)">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm"
|
||||
ng-click="addEntry(config.HostConfig.PortBindings, {ip: '', extPort: '', intPort: ''})">
|
||||
Add Port Binding
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Volumes From:</label>
|
||||
<input type="text" ng-model="config.volumesFrom" class="form-control"/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
</accordion-group>
|
||||
</accordion>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="btn btn-primary" ng-click="create()">Create</a>
|
||||
<a href="" class="btn btn-primary btn-lg" ng-click="create()">Create</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1>Stats</h1>
|
||||
|
||||
<h2>CPU</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<canvas id="cpu-stats-chart" width="650" height="300"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Memory</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<canvas id="memory-stats-chart" width="650" height="300"></canvas>
|
||||
</div>
|
||||
<div class="col-sm-offset-1 col-sm-4">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td>Max usage</td>
|
||||
<td>{{ data.memory_stats.max_usage | humansize }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Limit</td>
|
||||
<td>{{ data.memory_stats.limit | humansize }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail count</td>
|
||||
<td>{{ data.memory_stats.failcnt }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<accordion>
|
||||
<accordion-group heading="Other stats">
|
||||
<table class="table">
|
||||
<tr ng-repeat="(key, value) in data.memory_stats.stats">
|
||||
<td>{{ key }}</td>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</accordion-group>
|
||||
</accordion>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Network</h1>
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<canvas id="network-stats-chart" width="650" height="300"></canvas>
|
||||
</div>
|
||||
<div class="col-sm-offset-1 col-sm-4">
|
||||
<div id="network-legend" style="margin-bottom: 20px;"></div>
|
||||
<accordion>
|
||||
<accordion-group heading="Other stats">
|
||||
<table class="table">
|
||||
<tr ng-repeat="(key, value) in data.network">
|
||||
<td>{{ key }}</td>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</accordion-group>
|
||||
</accordion>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,187 @@
|
||||
angular.module('stats', [])
|
||||
.controller('StatsController', ['Settings', '$scope', 'Messages', '$timeout', 'Container', '$routeParams', 'humansizeFilter', '$sce', function (Settings, $scope, Messages, $timeout, Container, $routeParams, humansizeFilter, $sce) {
|
||||
// TODO: Implement memory chart, force scale to 0-100 for cpu, 0 to limit for memory, fix charts on dashboard,
|
||||
// TODO: Force memory scale to 0 - max memory
|
||||
//var initialStats = {}; // Used to set scale of memory graph.
|
||||
//
|
||||
//Container.stats({id: $routeParams.id}, function (d) {
|
||||
// var arr = Object.keys(d).map(function (key) {
|
||||
// return d[key];
|
||||
// });
|
||||
// if (arr.join('').indexOf('no such id') !== -1) {
|
||||
// Messages.error('Unable to retrieve stats', 'Is this container running?');
|
||||
// return;
|
||||
// }
|
||||
// initialStats = d;
|
||||
//}, function () {
|
||||
// Messages.error('Unable to retrieve stats', 'Is this container running?');
|
||||
//});
|
||||
|
||||
var cpuLabels = [];
|
||||
var cpuData = [];
|
||||
var memoryLabels = [];
|
||||
var memoryData = [];
|
||||
var networkLabels = [];
|
||||
var networkTxData = [];
|
||||
var networkRxData = [];
|
||||
for (var i = 0; i < 20; i++) {
|
||||
cpuLabels.push('');
|
||||
cpuData.push(0);
|
||||
memoryLabels.push('');
|
||||
memoryData.push(0);
|
||||
networkLabels.push('');
|
||||
networkTxData.push(0);
|
||||
networkRxData.push(0);
|
||||
}
|
||||
var cpuDataset = { // CPU Usage
|
||||
fillColor: "rgba(151,187,205,0.5)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
data: cpuData
|
||||
};
|
||||
var memoryDataset = {
|
||||
fillColor: "rgba(151,187,205,0.5)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
data: memoryData
|
||||
};
|
||||
var networkRxDataset = {
|
||||
label: "Rx Bytes",
|
||||
fillColor: "rgba(151,187,205,0.5)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
data: networkRxData
|
||||
};
|
||||
var networkTxDataset = {
|
||||
label: "Tx Bytes",
|
||||
fillColor: "rgba(255,180,174,0.5)",
|
||||
strokeColor: "rgba(255,180,174,1)",
|
||||
pointColor: "rgba(255,180,174,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
data: networkTxData
|
||||
};
|
||||
var networkLegendData = [
|
||||
{
|
||||
//value: '',
|
||||
color: 'rgba(151,187,205,0.5)',
|
||||
title: 'Rx Data'
|
||||
},
|
||||
{
|
||||
//value: '',
|
||||
color: 'rgba(255,180,174,0.5)',
|
||||
title: 'Rx Data'
|
||||
}];
|
||||
legend($('#network-legend').get(0), networkLegendData);
|
||||
|
||||
Chart.defaults.global.animationSteps = 30; // Lower from 60 to ease CPU load.
|
||||
var cpuChart = new Chart($('#cpu-stats-chart').get(0).getContext("2d")).Line({
|
||||
labels: cpuLabels,
|
||||
datasets: [cpuDataset]
|
||||
}, {
|
||||
responsive: true
|
||||
});
|
||||
|
||||
var memoryChart = new Chart($('#memory-stats-chart').get(0).getContext('2d')).Line({
|
||||
labels: memoryLabels,
|
||||
datasets: [memoryDataset]
|
||||
},
|
||||
{
|
||||
scaleLabel: function (valueObj) {
|
||||
return humansizeFilter(parseInt(valueObj.value, 10));
|
||||
},
|
||||
responsive: true
|
||||
//scaleOverride: true,
|
||||
//scaleSteps: 10,
|
||||
//scaleStepWidth: Math.ceil(initialStats.memory_stats.limit / 10),
|
||||
//scaleStartValue: 0
|
||||
});
|
||||
var networkChart = new Chart($('#network-stats-chart').get(0).getContext("2d")).Line({
|
||||
labels: networkLabels,
|
||||
datasets: [networkRxDataset, networkTxDataset]
|
||||
}, {
|
||||
scaleLabel: function (valueObj) {
|
||||
return humansizeFilter(parseInt(valueObj.value, 10));
|
||||
},
|
||||
responsive: true
|
||||
});
|
||||
$scope.networkLegend = $sce.trustAsHtml(networkChart.generateLegend());
|
||||
|
||||
function updateStats() {
|
||||
Container.stats({id: $routeParams.id}, function (d) {
|
||||
var arr = Object.keys(d).map(function (key) {
|
||||
return d[key];
|
||||
});
|
||||
if (arr.join('').indexOf('no such id') !== -1) {
|
||||
Messages.error('Unable to retrieve stats', 'Is this container running?');
|
||||
return;
|
||||
}
|
||||
|
||||
// Update graph with latest data
|
||||
$scope.data = d;
|
||||
updateCpuChart(d);
|
||||
updateMemoryChart(d);
|
||||
updateNetworkChart(d);
|
||||
timeout = $timeout(updateStats, 2000);
|
||||
}, function () {
|
||||
Messages.error('Unable to retrieve stats', 'Is this container running?');
|
||||
});
|
||||
}
|
||||
|
||||
var timeout;
|
||||
$scope.$on('$destroy', function () {
|
||||
$timeout.cancel(timeout);
|
||||
});
|
||||
|
||||
updateStats();
|
||||
|
||||
function updateCpuChart(data) {
|
||||
console.log('updateCpuChart', data);
|
||||
cpuChart.addData([calculateCPUPercent(data)], new Date(data.read).toLocaleTimeString());
|
||||
cpuChart.removeData();
|
||||
}
|
||||
|
||||
function updateMemoryChart(data) {
|
||||
console.log('updateMemoryChart', data);
|
||||
memoryChart.addData([data.memory_stats.usage], new Date(data.read).toLocaleTimeString());
|
||||
memoryChart.removeData();
|
||||
}
|
||||
|
||||
var lastRxBytes = 0, lastTxBytes = 0;
|
||||
|
||||
function updateNetworkChart(data) {
|
||||
var rxBytes = 0, txBytes = 0;
|
||||
if (lastRxBytes !== 0 || lastTxBytes !== 0) {
|
||||
// These will be zero on first call, ignore to prevent large graph spike
|
||||
rxBytes = data.network.rx_bytes - lastRxBytes;
|
||||
txBytes = data.network.tx_bytes - lastTxBytes;
|
||||
}
|
||||
lastRxBytes = data.network.rx_bytes;
|
||||
lastTxBytes = data.network.tx_bytes;
|
||||
console.log('updateNetworkChart', data);
|
||||
networkChart.addData([rxBytes, txBytes], new Date(data.read).toLocaleTimeString());
|
||||
networkChart.removeData();
|
||||
}
|
||||
|
||||
function calculateCPUPercent(stats) {
|
||||
// Same algorithm the official client uses: https://github.com/docker/docker/blob/master/api/client/stats.go#L195-L208
|
||||
var prevCpu = stats.precpu_stats;
|
||||
var curCpu = stats.cpu_stats;
|
||||
|
||||
var cpuPercent = 0.0;
|
||||
|
||||
// calculate the change for the cpu usage of the container in between readings
|
||||
var cpuDelta = curCpu.cpu_usage.total_usage - prevCpu.cpu_usage.total_usage;
|
||||
// calculate the change for the entire system between readings
|
||||
var systemDelta = curCpu.system_cpu_usage - prevCpu.system_cpu_usage;
|
||||
|
||||
if (systemDelta > 0.0 && cpuDelta > 0.0) {
|
||||
//console.log('size thing:', curCpu.cpu_usage.percpu_usage);
|
||||
cpuPercent = (cpuDelta / systemDelta) * curCpu.cpu_usage.percpu_usage.length * 100.0;
|
||||
}
|
||||
return cpuPercent;
|
||||
}
|
||||
}])
|
||||
;
|
||||
@@ -1,36 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('dockerui.filters', [])
|
||||
.filter('truncate', function() {
|
||||
return function(text, length, end) {
|
||||
if (isNaN(length))
|
||||
.filter('truncate', function () {
|
||||
'use strict';
|
||||
return function (text, length, end) {
|
||||
if (isNaN(length)) {
|
||||
length = 10;
|
||||
}
|
||||
|
||||
if (end === undefined)
|
||||
end = "...";
|
||||
if (end === undefined) {
|
||||
end = '...';
|
||||
}
|
||||
|
||||
if (text.length <= length || text.length - end.length <= length) {
|
||||
return text;
|
||||
}
|
||||
else {
|
||||
return String(text).substring(0, length-end.length) + end;
|
||||
return String(text).substring(0, length - end.length) + end;
|
||||
}
|
||||
};
|
||||
})
|
||||
.filter('statusbadge', function() {
|
||||
return function(text) {
|
||||
.filter('statusbadge', function () {
|
||||
'use strict';
|
||||
return function (text) {
|
||||
if (text === 'Ghost') {
|
||||
return 'important';
|
||||
} else if (text.indexOf('Exit') != -1 && text !== 'Exit 0') {
|
||||
} else if (text.indexOf('Exit') !== -1 && text !== 'Exit 0') {
|
||||
return 'warning';
|
||||
}
|
||||
return 'success';
|
||||
};
|
||||
})
|
||||
.filter('getstatetext', function() {
|
||||
return function(state) {
|
||||
if (state == undefined) return '';
|
||||
|
||||
.filter('getstatetext', function () {
|
||||
'use strict';
|
||||
return function (state) {
|
||||
if (state === undefined) {
|
||||
return '';
|
||||
}
|
||||
if (state.Ghost && state.Running) {
|
||||
return 'Ghost';
|
||||
}
|
||||
@@ -43,9 +47,12 @@ angular.module('dockerui.filters', [])
|
||||
return 'Stopped';
|
||||
};
|
||||
})
|
||||
.filter('getstatelabel', function() {
|
||||
return function(state) {
|
||||
if (state == undefined) return '';
|
||||
.filter('getstatelabel', function () {
|
||||
'use strict';
|
||||
return function (state) {
|
||||
if (state === undefined) {
|
||||
return 'label-default';
|
||||
}
|
||||
|
||||
if (state.Ghost && state.Running) {
|
||||
return 'label-important';
|
||||
@@ -53,39 +60,58 @@ angular.module('dockerui.filters', [])
|
||||
if (state.Running) {
|
||||
return 'label-success';
|
||||
}
|
||||
return '';
|
||||
return 'label-default';
|
||||
};
|
||||
})
|
||||
.filter('humansize', function() {
|
||||
return function(bytes) {
|
||||
.filter('humansize', function () {
|
||||
'use strict';
|
||||
return function (bytes) {
|
||||
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes == 0) {
|
||||
if (bytes === 0) {
|
||||
return 'n/a';
|
||||
}
|
||||
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[[i]];
|
||||
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
|
||||
var value = bytes / Math.pow(1024, i);
|
||||
var decimalPlaces = (i < 1) ? 0 : (i - 1);
|
||||
return value.toFixed(decimalPlaces) + ' ' + sizes[[i]];
|
||||
};
|
||||
})
|
||||
.filter('containername', function() {
|
||||
return function(container) {
|
||||
.filter('containername', function () {
|
||||
'use strict';
|
||||
return function (container) {
|
||||
var name = container.Names[0];
|
||||
return name.substring(1, name.length);
|
||||
};
|
||||
})
|
||||
.filter('repotag', function() {
|
||||
return function(image) {
|
||||
.filter('repotag', function () {
|
||||
'use strict';
|
||||
return function (image) {
|
||||
if (image.RepoTags && image.RepoTags.length > 0) {
|
||||
var tag = image.RepoTags[0];
|
||||
if (tag == '<none>:<none>') { tag = ''; }
|
||||
if (tag === '<none>:<none>') {
|
||||
tag = '';
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
return '';
|
||||
return '';
|
||||
};
|
||||
})
|
||||
.filter('getdate', function() {
|
||||
return function(data) {
|
||||
.filter('getdate', function () {
|
||||
'use strict';
|
||||
return function (data) {
|
||||
//Multiply by 1000 for the unix format
|
||||
var date = new Date(data * 1000);
|
||||
return date.toDateString();
|
||||
};
|
||||
})
|
||||
.filter('errorMsg', function () {
|
||||
return function (object) {
|
||||
var idx = 0;
|
||||
var msg = '';
|
||||
while (object[idx] && typeof(object[idx]) === 'string') {
|
||||
msg += object[idx];
|
||||
idx++;
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,76 +1,124 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('dockerui.services', ['ngResource'])
|
||||
.factory('Container', function($resource, Settings) {
|
||||
.factory('Container', ['$resource', 'Settings', function ContainerFactory($resource, Settings) {
|
||||
'use strict';
|
||||
// Resource for interacting with the docker containers
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#containers
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#2-1-containers
|
||||
return $resource(Settings.url + '/containers/:id/:action', {
|
||||
name: '@name'
|
||||
}, {
|
||||
query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: { action:'json'}},
|
||||
query: {method: 'GET', params: {all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: {action: 'json'}},
|
||||
start: {method: 'POST', params: {id: '@id', action: 'start'}},
|
||||
stop: {method: 'POST', params: {id: '@id', t: 5, action: 'stop'}},
|
||||
restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart' }},
|
||||
restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart'}},
|
||||
kill: {method: 'POST', params: {id: '@id', action: 'kill'}},
|
||||
pause: {method: 'POST', params: {id: '@id', action: 'pause'}},
|
||||
unpause: {method: 'POST', params: {id: '@id', action: 'unpause'}},
|
||||
changes: {method: 'GET', params: {action:'changes'}, isArray: true},
|
||||
create: {method: 'POST', params: {action:'create'}},
|
||||
remove: {method: 'DELETE', params: {id: '@id', v:0}}
|
||||
changes: {method: 'GET', params: {action: 'changes'}, isArray: true},
|
||||
create: {method: 'POST', params: {action: 'create'}},
|
||||
remove: {method: 'DELETE', params: {id: '@id', v: 0}},
|
||||
rename: {method: 'POST', params: {id: '@id', action: 'rename'}, isArray: false},
|
||||
stats: {method: 'GET', params: {id: '@id', stream: false, action: 'stats'}, timeout: 2000}
|
||||
});
|
||||
})
|
||||
.factory('ContainerLogs', function($resource, $http, Settings) {
|
||||
}])
|
||||
.factory('ContainerCommit', ['$resource', '$http', 'Settings', function ContainerCommitFactory($resource, $http, Settings) {
|
||||
'use strict';
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#create-a-new-image-from-a-container-s-changes
|
||||
return {
|
||||
get: function(id, params, callback) {
|
||||
commit: function (params, callback) {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: Settings.url + '/containers/'+id+'/logs',
|
||||
params: {'stdout': params.stdout || 0, 'stderr': params.stderr || 0, 'timestamps': params.timestamps || 0, 'tail': params.tail || 'all'}
|
||||
}).success(callback).error(function(data, status, headers, config) {
|
||||
method: 'POST',
|
||||
url: Settings.url + '/commit',
|
||||
params: {
|
||||
'container': params.id,
|
||||
'repo': params.repo
|
||||
}
|
||||
}).success(callback).error(function (data, status, headers, config) {
|
||||
console.log(error, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.factory('Image', function($resource, Settings) {
|
||||
// Resource for docker images
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#images
|
||||
};
|
||||
}])
|
||||
.factory('ContainerLogs', ['$resource', '$http', 'Settings', function ContainerLogsFactory($resource, $http, Settings) {
|
||||
'use strict';
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#get-container-logs
|
||||
return {
|
||||
get: function (id, params, callback) {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: Settings.url + '/containers/' + id + '/logs',
|
||||
params: {
|
||||
'stdout': params.stdout || 0,
|
||||
'stderr': params.stderr || 0,
|
||||
'timestamps': params.timestamps || 0,
|
||||
'tail': params.tail || 'all'
|
||||
}
|
||||
}).success(callback).error(function (data, status, headers, config) {
|
||||
console.log(error, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
}])
|
||||
.factory('ContainerTop', ['$http', 'Settings', function ($http, Settings) {
|
||||
'use strict';
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#list-processes-running-inside-a-container
|
||||
return {
|
||||
get: function (id, params, callback, errorCallback) {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: Settings.url + '/containers/' + id + '/top',
|
||||
params: {
|
||||
ps_args: params.ps_args
|
||||
}
|
||||
}).success(callback);
|
||||
}
|
||||
};
|
||||
}])
|
||||
.factory('Image', ['$resource', 'Settings', function ImageFactory($resource, Settings) {
|
||||
'use strict';
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#2-2-images
|
||||
return $resource(Settings.url + '/images/:id/:action', {}, {
|
||||
query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: { action:'json'}},
|
||||
search: {method: 'GET', params: { action:'search'}},
|
||||
history: {method: 'GET', params: { action:'history'}, isArray: true},
|
||||
create: {method: 'POST', params: {action:'create'}},
|
||||
insert: {method: 'POST', params: {id: '@id', action:'insert'}},
|
||||
push: {method: 'POST', params: {id: '@id', action:'push'}},
|
||||
tag: {method: 'POST', params: {id: '@id', action:'tag', force: 0, repo: '@repo'}},
|
||||
query: {method: 'GET', params: {all: 0, action: 'json'}, isArray: true},
|
||||
get: {method: 'GET', params: {action: 'json'}},
|
||||
search: {method: 'GET', params: {action: 'search'}},
|
||||
history: {method: 'GET', params: {action: 'history'}, isArray: true},
|
||||
create: {
|
||||
method: 'POST', isArray: true, transformResponse: [function f(data) {
|
||||
var str = data.replace(/\n/g, " ").replace(/\}\W*\{/g, "}, {");
|
||||
return angular.fromJson("[" + str + "]");
|
||||
}],
|
||||
params: {action: 'create', fromImage: '@fromImage', repo: '@repo', tag: '@tag', registry: '@registry'}
|
||||
},
|
||||
insert: {method: 'POST', params: {id: '@id', action: 'insert'}},
|
||||
push: {method: 'POST', params: {id: '@id', action: 'push'}},
|
||||
tag: {method: 'POST', params: {id: '@id', action: 'tag', force: 0, repo: '@repo', tag: '@tag'}},
|
||||
remove: {method: 'DELETE', params: {id: '@id'}, isArray: true}
|
||||
});
|
||||
})
|
||||
.factory('Docker', function($resource, Settings) {
|
||||
// Information for docker
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information
|
||||
}])
|
||||
.factory('Docker', ['$resource', 'Settings', function DockerFactory($resource, Settings) {
|
||||
'use strict';
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#show-the-docker-version-information
|
||||
return $resource(Settings.url + '/version', {}, {
|
||||
get: {method: 'GET'}
|
||||
});
|
||||
})
|
||||
.factory('Auth', function($resource, Settings) {
|
||||
// Auto Information for docker
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#set-auth-configuration
|
||||
}])
|
||||
.factory('Auth', ['$resource', 'Settings', function AuthFactory($resource, Settings) {
|
||||
'use strict';
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#check-auth-configuration
|
||||
return $resource(Settings.url + '/auth', {}, {
|
||||
get: {method: 'GET'},
|
||||
update: {method: 'POST'}
|
||||
});
|
||||
})
|
||||
.factory('System', function($resource, Settings) {
|
||||
// System for docker
|
||||
// http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information
|
||||
}])
|
||||
.factory('System', ['$resource', 'Settings', function SystemFactory($resource, Settings) {
|
||||
'use strict';
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#display-system-wide-information
|
||||
return $resource(Settings.url + '/info', {}, {
|
||||
get: {method: 'GET'}
|
||||
});
|
||||
})
|
||||
.factory('Settings', function(DOCKER_ENDPOINT, DOCKER_PORT, DOCKER_API_VERSION, UI_VERSION) {
|
||||
}])
|
||||
.factory('Settings', ['DOCKER_ENDPOINT', 'DOCKER_PORT', 'DOCKER_API_VERSION', 'UI_VERSION', function SettingsFactory(DOCKER_ENDPOINT, DOCKER_PORT, DOCKER_API_VERSION, UI_VERSION) {
|
||||
'use strict';
|
||||
var url = DOCKER_ENDPOINT;
|
||||
if (DOCKER_PORT) {
|
||||
url = url + DOCKER_PORT + '\\' + DOCKER_PORT;
|
||||
@@ -82,52 +130,60 @@ angular.module('dockerui.services', ['ngResource'])
|
||||
rawUrl: DOCKER_ENDPOINT + DOCKER_PORT + '/' + DOCKER_API_VERSION,
|
||||
uiVersion: UI_VERSION,
|
||||
url: url,
|
||||
firstLoad: true,
|
||||
firstLoad: true
|
||||
};
|
||||
})
|
||||
.factory('ViewSpinner', function() {
|
||||
}])
|
||||
.factory('ViewSpinner', function ViewSpinnerFactory() {
|
||||
'use strict';
|
||||
var spinner = new Spinner();
|
||||
var target = document.getElementById('view');
|
||||
|
||||
return {
|
||||
spin: function() { spinner.spin(target); },
|
||||
stop: function() { spinner.stop(); }
|
||||
spin: function () {
|
||||
spinner.spin(target);
|
||||
},
|
||||
stop: function () {
|
||||
spinner.stop();
|
||||
}
|
||||
};
|
||||
})
|
||||
.factory('Messages', function($rootScope) {
|
||||
.factory('Messages', ['$rootScope', function MessagesFactory($rootScope) {
|
||||
'use strict';
|
||||
return {
|
||||
send: function(title, text) {
|
||||
send: function (title, text) {
|
||||
$.gritter.add({
|
||||
title: title,
|
||||
text: text,
|
||||
time: 2000,
|
||||
before_open: function() {
|
||||
if($('.gritter-item-wrapper').length == 3) {
|
||||
before_open: function () {
|
||||
if ($('.gritter-item-wrapper').length === 3) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
error: function(title, text) {
|
||||
error: function (title, text) {
|
||||
$.gritter.add({
|
||||
title: title,
|
||||
text: text,
|
||||
time: 6000,
|
||||
before_open: function() {
|
||||
if($('.gritter-item-wrapper').length == 4) {
|
||||
time: 10000,
|
||||
before_open: function () {
|
||||
if ($('.gritter-item-wrapper').length === 4) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
.factory('Dockerfile', function(Settings) {
|
||||
var url = Settings.rawUrl + '/build';
|
||||
}])
|
||||
.factory('Dockerfile', ['Settings', function DockerfileFactory(Settings) {
|
||||
'use strict';
|
||||
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#build-image-from-a-dockerfile
|
||||
var url = Settings.rawUrl + '/build';
|
||||
return {
|
||||
build: function(file, callback) {
|
||||
build: function (file, callback) {
|
||||
var data = new FormData();
|
||||
var dockerfile = new Blob([file], { type: 'text/text' });
|
||||
var dockerfile = new Blob([file], {type: 'text/text'});
|
||||
data.append('Dockerfile', dockerfile);
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
@@ -136,18 +192,18 @@ angular.module('dockerui.services', ['ngResource'])
|
||||
request.send(data);
|
||||
}
|
||||
};
|
||||
})
|
||||
.factory('LineChart', function(Settings) {
|
||||
var url = Settings.rawUrl + '/build';
|
||||
}])
|
||||
.factory('LineChart', ['Settings', function LineChartFactory(Settings) {
|
||||
'use strict';
|
||||
return {
|
||||
build: function(id, data, getkey){
|
||||
build: function (id, data, getkey) {
|
||||
var chart = new Chart($(id).get(0).getContext("2d"));
|
||||
var map = {};
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var c = data[i];
|
||||
var key = getkey(c);
|
||||
|
||||
|
||||
var count = map[key];
|
||||
if (count === undefined) {
|
||||
count = 0;
|
||||
@@ -157,31 +213,35 @@ angular.module('dockerui.services', ['ngResource'])
|
||||
}
|
||||
|
||||
var labels = [];
|
||||
var data = [];
|
||||
data = [];
|
||||
var keys = Object.keys(map);
|
||||
var max = 1;
|
||||
|
||||
for (var i = keys.length - 1; i > -1; i--) {
|
||||
for (i = keys.length - 1; i > -1; i--) {
|
||||
var k = keys[i];
|
||||
labels.push(k);
|
||||
data.push(map[k]);
|
||||
if (map[k] > max) {
|
||||
max = map[k];
|
||||
}
|
||||
}
|
||||
var dataset = {
|
||||
fillColor : "rgba(151,187,205,0.5)",
|
||||
strokeColor : "rgba(151,187,205,1)",
|
||||
pointColor : "rgba(151,187,205,1)",
|
||||
pointStrokeColor : "#fff",
|
||||
data : data
|
||||
fillColor: "rgba(151,187,205,0.5)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
data: data
|
||||
};
|
||||
chart.Line({
|
||||
labels: labels,
|
||||
datasets: [dataset]
|
||||
},
|
||||
{
|
||||
scaleStepWidth: 1,
|
||||
pointDotRadius:1,
|
||||
scaleOverride: true,
|
||||
scaleSteps: labels.length
|
||||
});
|
||||
labels: labels,
|
||||
datasets: [dataset]
|
||||
},
|
||||
{
|
||||
scaleStepWidth: 1,
|
||||
pointDotRadius: 1,
|
||||
scaleOverride: true,
|
||||
scaleSteps: max
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}]);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
function ImageViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
this.Tag = data.Tag;
|
||||
@@ -10,12 +9,12 @@ function ImageViewModel(data) {
|
||||
}
|
||||
|
||||
function ContainerViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
this.Image = data.Image;
|
||||
this.Command = data.Command;
|
||||
this.Created = data.Created;
|
||||
this.SizeRw = data.SizeRw;
|
||||
this.Status = data.Status;
|
||||
this.Checked = false;
|
||||
this.Names = data.Names;
|
||||
this.Id = data.Id;
|
||||
this.Image = data.Image;
|
||||
this.Command = data.Command;
|
||||
this.Created = data.Created;
|
||||
this.SizeRw = data.SizeRw;
|
||||
this.Status = data.Status;
|
||||
this.Checked = false;
|
||||
this.Names = data.Names;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
body {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.container > hr {
|
||||
margin: 60px 0;
|
||||
}
|
||||
@@ -22,8 +18,8 @@ body {
|
||||
}
|
||||
|
||||
.jumbotron .btn {
|
||||
font-size: 21px;
|
||||
padding: 14px 24px;
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.marketing {
|
||||
@@ -46,15 +42,15 @@ body {
|
||||
|
||||
.masthead .nav li {
|
||||
display: table-cell;
|
||||
width: 1%;
|
||||
float: none;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
.masthead .nav li a {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-left: 1px solid rgba(255,255,255,.75);
|
||||
border-right: 1px solid rgba(0,0,0,.1);
|
||||
border-left: 1px solid rgba(255,255,255,.75);
|
||||
}
|
||||
|
||||
.masthead .nav li:first-child a {
|
||||
@@ -81,8 +77,8 @@ body {
|
||||
}
|
||||
|
||||
.btn-remove {
|
||||
margin: 0 auto;
|
||||
max-width: 70%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@@ -97,33 +93,24 @@ body {
|
||||
padding: 10px 15px 0 15px;
|
||||
}
|
||||
|
||||
#response {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#editor {
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
border: 1px solid #DDD;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.messages {
|
||||
max-height: 50px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.legend .title {
|
||||
padding: 0 0.3em;
|
||||
margin: 0.5em;
|
||||
border-style: solid;
|
||||
border-width: 0 0 0 1em;
|
||||
padding: 0 0.3em;
|
||||
}
|
||||
|
||||
pre.pre-x-scrollable {
|
||||
max-height: 700px;
|
||||
word-wrap: normal;
|
||||
white-space: pre;
|
||||
.inline-four .form-control {
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/* the norm */
|
||||
#gritter-notice-wrapper {
|
||||
position:fixed;
|
||||
top:20px;
|
||||
right:20px;
|
||||
width:301px;
|
||||
z-index:9999;
|
||||
}
|
||||
#gritter-notice-wrapper.top-left {
|
||||
left: 20px;
|
||||
right: auto;
|
||||
}
|
||||
#gritter-notice-wrapper.bottom-right {
|
||||
top: auto;
|
||||
left: auto;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
#gritter-notice-wrapper.bottom-left {
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
}
|
||||
.gritter-item-wrapper {
|
||||
position:relative;
|
||||
margin:0 0 10px 0;
|
||||
background:url('../images/ie-spacer.gif'); /* ie7/8 fix */
|
||||
}
|
||||
.gritter-top {
|
||||
background:url(../images/gritter.png) no-repeat left -30px;
|
||||
height:10px;
|
||||
}
|
||||
.hover .gritter-top {
|
||||
background-position:right -30px;
|
||||
}
|
||||
.gritter-bottom {
|
||||
background:url(../images/gritter.png) no-repeat left bottom;
|
||||
height:8px;
|
||||
margin:0;
|
||||
}
|
||||
.hover .gritter-bottom {
|
||||
background-position: bottom right;
|
||||
}
|
||||
.gritter-item {
|
||||
display:block;
|
||||
background:url(../images/gritter.png) no-repeat left -40px;
|
||||
color:#eee;
|
||||
padding:2px 11px 8px 11px;
|
||||
font-size: 11px;
|
||||
font-family:verdana;
|
||||
}
|
||||
.hover .gritter-item {
|
||||
background-position:right -40px;
|
||||
}
|
||||
.gritter-item p {
|
||||
padding:0;
|
||||
margin:0;
|
||||
word-wrap:break-word;
|
||||
}
|
||||
.gritter-close {
|
||||
display:none;
|
||||
position:absolute;
|
||||
top:5px;
|
||||
left:3px;
|
||||
background:url(../images/gritter.png) no-repeat left top;
|
||||
cursor:pointer;
|
||||
width:30px;
|
||||
height:30px;
|
||||
}
|
||||
.gritter-title {
|
||||
font-size:14px;
|
||||
font-weight:bold;
|
||||
padding:0 0 7px 0;
|
||||
display:block;
|
||||
text-shadow:1px 1px 0 #000; /* Not supported by IE :( */
|
||||
}
|
||||
.gritter-image {
|
||||
width:48px;
|
||||
height:48px;
|
||||
float:left;
|
||||
}
|
||||
.gritter-with-image,
|
||||
.gritter-without-image {
|
||||
padding:0;
|
||||
}
|
||||
.gritter-with-image {
|
||||
width:220px;
|
||||
float:right;
|
||||
}
|
||||
/* for the light (white) version of the gritter notice */
|
||||
.gritter-light .gritter-item,
|
||||
.gritter-light .gritter-bottom,
|
||||
.gritter-light .gritter-top,
|
||||
.gritter-light .gritter-close {
|
||||
background-image: url(../images/gritter-light.png);
|
||||
color: #222;
|
||||
}
|
||||
.gritter-light .gritter-title {
|
||||
text-shadow: none;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
var Chart=function(s){function v(a,c,b){a=A((a-c.graphMin)/(c.steps*c.stepValue),1,0);return b*c.steps*a}function x(a,c,b,e){function h(){g+=f;var k=a.animation?A(d(g),null,0):1;e.clearRect(0,0,q,u);a.scaleOverlay?(b(k),c()):(c(),b(k));if(1>=g)D(h);else if("function"==typeof a.onAnimationComplete)a.onAnimationComplete()}var f=a.animation?1/A(a.animationSteps,Number.MAX_VALUE,1):1,d=B[a.animationEasing],g=a.animation?0:1;"function"!==typeof c&&(c=function(){});D(h)}function C(a,c,b,e,h,f){var d;a=
|
||||
Math.floor(Math.log(e-h)/Math.LN10);h=Math.floor(h/(1*Math.pow(10,a)))*Math.pow(10,a);e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-h;a=Math.pow(10,a);for(d=Math.round(e/a);d<b||d>c;)a=d<b?a/2:2*a,d=Math.round(e/a);c=[];z(f,c,d,h,a);return{steps:d,stepValue:a,graphMin:h,labels:c}}function z(a,c,b,e,h){if(a)for(var f=1;f<b+1;f++)c.push(E(a,{value:(e+h*f).toFixed(0!=h%1?h.toString().split(".")[1].length:0)}))}function A(a,c,b){return!isNaN(parseFloat(c))&&isFinite(c)&&a>c?c:!isNaN(parseFloat(b))&&
|
||||
isFinite(b)&&a<b?b:a}function y(a,c){var b={},e;for(e in a)b[e]=a[e];for(e in c)b[e]=c[e];return b}function E(a,c){var b=!/\W/.test(a)?F[a]=F[a]||E(document.getElementById(a).innerHTML):new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c?
|
||||
b(c):b}var r=this,B={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=0.5)?0.5*a*a:-0.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=0.5)?0.5*a*a*a:0.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>(a/=0.5)?
|
||||
0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=0.5)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0==a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1==
|
||||
a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0==a?0:1==a?1:1>(a/=0.5)?0.5*Math.pow(2,10*(a-1)):0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=0.5)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*
|
||||
Math.PI)*Math.asin(1/e);return-(e*Math.pow(2,10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b))},easeOutElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/e);return e*Math.pow(2,-10*a)*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInOutElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(2==(a/=0.5))return 1;b||(b=1*0.3*1.5);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/e);return 1>a?-0.5*e*Math.pow(2,10*
|
||||
(a-=1))*Math.sin((1*a-c)*2*Math.PI/b):0.5*e*Math.pow(2,-10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var c=1.70158;return 1>(a/=0.5)?0.5*a*a*(((c*=1.525)+1)*a-c):0.5*((a-=2)*a*(((c*=1.525)+1)*a+c)+2)},easeInBounce:function(a){return 1-B.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?1*7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)*
|
||||
a+0.75):a<2.5/2.75?1*(7.5625*(a-=2.25/2.75)*a+0.9375):1*(7.5625*(a-=2.625/2.75)*a+0.984375)},easeInOutBounce:function(a){return 0.5>a?0.5*B.easeInBounce(2*a):0.5*B.easeOutBounce(2*a-1)+0.5}},q=s.canvas.width,u=s.canvas.height;window.devicePixelRatio&&(s.canvas.style.width=q+"px",s.canvas.style.height=u+"px",s.canvas.height=u*window.devicePixelRatio,s.canvas.width=q*window.devicePixelRatio,s.scale(window.devicePixelRatio,window.devicePixelRatio));this.PolarArea=function(a,c){r.PolarArea.defaults={scaleOverlay:!0,
|
||||
scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",
|
||||
animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.PolarArea.defaults,c):r.PolarArea.defaults;return new G(a,b,s)};this.Radar=function(a,c){r.Radar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!1,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",
|
||||
scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,angleShowLineOut:!0,angleLineColor:"rgba(0,0,0,.1)",angleLineWidth:1,pointLabelFontFamily:"'Arial'",pointLabelFontStyle:"normal",pointLabelFontSize:12,pointLabelFontColor:"#666",pointDot:!0,pointDotRadius:3,pointDotStrokeWidth:1,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Radar.defaults,c):r.Radar.defaults;return new H(a,b,s)};this.Pie=function(a,
|
||||
c){r.Pie.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.Pie.defaults,c):r.Pie.defaults;return new I(a,b,s)};this.Doughnut=function(a,c){r.Doughnut.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,
|
||||
onAnimationComplete:null};var b=c?y(r.Doughnut.defaults,c):r.Doughnut.defaults;return new J(a,b,s)};this.Line=function(a,c){r.Line.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,bezierCurve:!0,
|
||||
pointDot:!0,pointDotRadius:4,pointDotStrokeWidth:2,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Line.defaults,c):r.Line.defaults;return new K(a,b,s)};this.Bar=function(a,c){r.Bar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",
|
||||
scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Bar.defaults,c):r.Bar.defaults;return new L(a,b,s)};var G=function(a,c,b){var e,h,f,d,g,k,j,l,m;g=Math.min.apply(Math,[q,u])/2;g-=Math.max.apply(Math,[0.5*c.scaleFontSize,0.5*c.scaleLineWidth]);
|
||||
d=2*c.scaleFontSize;c.scaleShowLabelBackdrop&&(d+=2*c.scaleBackdropPaddingY,g-=1.5*c.scaleBackdropPaddingY);l=g;d=d?d:5;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.length;f++)a[f].value>e&&(e=a[f].value),a[f].value<h&&(h=a[f].value);f=Math.floor(l/(0.66*d));d=Math.floor(0.5*(l/d));m=c.scaleShowLabels?c.scaleLabel:null;c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(m,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(l,f,d,e,h,
|
||||
m);k=g/j.steps;x(c,function(){for(var a=0;a<j.steps;a++)if(c.scaleShowLine&&(b.beginPath(),b.arc(q/2,u/2,k*(a+1),0,2*Math.PI,!0),b.strokeStyle=c.scaleLineColor,b.lineWidth=c.scaleLineWidth,b.stroke()),c.scaleShowLabels){b.textAlign="center";b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;var e=j.labels[a];if(c.scaleShowLabelBackdrop){var d=b.measureText(e).width;b.fillStyle=c.scaleBackdropColor;b.beginPath();b.rect(Math.round(q/2-d/2-c.scaleBackdropPaddingX),Math.round(u/2-k*(a+
|
||||
1)-0.5*c.scaleFontSize-c.scaleBackdropPaddingY),Math.round(d+2*c.scaleBackdropPaddingX),Math.round(c.scaleFontSize+2*c.scaleBackdropPaddingY));b.fill()}b.textBaseline="middle";b.fillStyle=c.scaleFontColor;b.fillText(e,q/2,u/2-k*(a+1))}},function(e){var d=-Math.PI/2,g=2*Math.PI/a.length,f=1,h=1;c.animation&&(c.animateScale&&(f=e),c.animateRotate&&(h=e));for(e=0;e<a.length;e++)b.beginPath(),b.arc(q/2,u/2,f*v(a[e].value,j,k),d,d+h*g,!1),b.lineTo(q/2,u/2),b.closePath(),b.fillStyle=a[e].color,b.fill(),
|
||||
c.segmentShowStroke&&(b.strokeStyle=c.segmentStrokeColor,b.lineWidth=c.segmentStrokeWidth,b.stroke()),d+=h*g},b)},H=function(a,c,b){var e,h,f,d,g,k,j,l,m;a.labels||(a.labels=[]);g=Math.min.apply(Math,[q,u])/2;d=2*c.scaleFontSize;for(e=l=0;e<a.labels.length;e++)b.font=c.pointLabelFontStyle+" "+c.pointLabelFontSize+"px "+c.pointLabelFontFamily,h=b.measureText(a.labels[e]).width,h>l&&(l=h);g-=Math.max.apply(Math,[l,1.5*(c.pointLabelFontSize/2)]);g-=c.pointLabelFontSize;l=g=A(g,null,0);d=d?d:5;e=Number.MIN_VALUE;
|
||||
h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(m=0;m<a.datasets[f].data.length;m++)a.datasets[f].data[m]>e&&(e=a.datasets[f].data[m]),a.datasets[f].data[m]<h&&(h=a.datasets[f].data[m]);f=Math.floor(l/(0.66*d));d=Math.floor(0.5*(l/d));m=c.scaleShowLabels?c.scaleLabel:null;c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(m,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(l,f,d,e,h,m);k=g/j.steps;x(c,function(){var e=2*Math.PI/
|
||||
a.datasets[0].data.length;b.save();b.translate(q/2,u/2);if(c.angleShowLineOut){b.strokeStyle=c.angleLineColor;b.lineWidth=c.angleLineWidth;for(var d=0;d<a.datasets[0].data.length;d++)b.rotate(e),b.beginPath(),b.moveTo(0,0),b.lineTo(0,-g),b.stroke()}for(d=0;d<j.steps;d++){b.beginPath();if(c.scaleShowLine){b.strokeStyle=c.scaleLineColor;b.lineWidth=c.scaleLineWidth;b.moveTo(0,-k*(d+1));for(var f=0;f<a.datasets[0].data.length;f++)b.rotate(e),b.lineTo(0,-k*(d+1));b.closePath();b.stroke()}c.scaleShowLabels&&
|
||||
(b.textAlign="center",b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily,b.textBaseline="middle",c.scaleShowLabelBackdrop&&(f=b.measureText(j.labels[d]).width,b.fillStyle=c.scaleBackdropColor,b.beginPath(),b.rect(Math.round(-f/2-c.scaleBackdropPaddingX),Math.round(-k*(d+1)-0.5*c.scaleFontSize-c.scaleBackdropPaddingY),Math.round(f+2*c.scaleBackdropPaddingX),Math.round(c.scaleFontSize+2*c.scaleBackdropPaddingY)),b.fill()),b.fillStyle=c.scaleFontColor,b.fillText(j.labels[d],0,-k*(d+
|
||||
1)))}for(d=0;d<a.labels.length;d++){b.font=c.pointLabelFontStyle+" "+c.pointLabelFontSize+"px "+c.pointLabelFontFamily;b.fillStyle=c.pointLabelFontColor;var f=Math.sin(e*d)*(g+c.pointLabelFontSize),h=Math.cos(e*d)*(g+c.pointLabelFontSize);b.textAlign=e*d==Math.PI||0==e*d?"center":e*d>Math.PI?"right":"left";b.textBaseline="middle";b.fillText(a.labels[d],f,-h)}b.restore()},function(d){var e=2*Math.PI/a.datasets[0].data.length;b.save();b.translate(q/2,u/2);for(var g=0;g<a.datasets.length;g++){b.beginPath();
|
||||
b.moveTo(0,d*-1*v(a.datasets[g].data[0],j,k));for(var f=1;f<a.datasets[g].data.length;f++)b.rotate(e),b.lineTo(0,d*-1*v(a.datasets[g].data[f],j,k));b.closePath();b.fillStyle=a.datasets[g].fillColor;b.strokeStyle=a.datasets[g].strokeColor;b.lineWidth=c.datasetStrokeWidth;b.fill();b.stroke();if(c.pointDot){b.fillStyle=a.datasets[g].pointColor;b.strokeStyle=a.datasets[g].pointStrokeColor;b.lineWidth=c.pointDotStrokeWidth;for(f=0;f<a.datasets[g].data.length;f++)b.rotate(e),b.beginPath(),b.arc(0,d*-1*
|
||||
v(a.datasets[g].data[f],j,k),c.pointDotRadius,2*Math.PI,!1),b.fill(),b.stroke()}b.rotate(e)}b.restore()},b)},I=function(a,c,b){for(var e=0,h=Math.min.apply(Math,[u/2,q/2])-5,f=0;f<a.length;f++)e+=a[f].value;x(c,null,function(d){var g=-Math.PI/2,f=1,j=1;c.animation&&(c.animateScale&&(f=d),c.animateRotate&&(j=d));for(d=0;d<a.length;d++){var l=j*a[d].value/e*2*Math.PI;b.beginPath();b.arc(q/2,u/2,f*h,g,g+l);b.lineTo(q/2,u/2);b.closePath();b.fillStyle=a[d].color;b.fill();c.segmentShowStroke&&(b.lineWidth=
|
||||
c.segmentStrokeWidth,b.strokeStyle=c.segmentStrokeColor,b.stroke());g+=l}},b)},J=function(a,c,b){for(var e=0,h=Math.min.apply(Math,[u/2,q/2])-5,f=h*(c.percentageInnerCutout/100),d=0;d<a.length;d++)e+=a[d].value;x(c,null,function(d){var k=-Math.PI/2,j=1,l=1;c.animation&&(c.animateScale&&(j=d),c.animateRotate&&(l=d));for(d=0;d<a.length;d++){var m=l*a[d].value/e*2*Math.PI;b.beginPath();b.arc(q/2,u/2,j*h,k,k+m,!1);b.arc(q/2,u/2,j*f,k+m,k,!0);b.closePath();b.fillStyle=a[d].color;b.fill();c.segmentShowStroke&&
|
||||
(b.lineWidth=c.segmentStrokeWidth,b.strokeStyle=c.segmentStrokeColor,b.stroke());k+=m}},b)},K=function(a,c,b){var e,h,f,d,g,k,j,l,m,t,r,n,p,s=0;g=u;b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;t=1;for(d=0;d<a.labels.length;d++)e=b.measureText(a.labels[d]).width,t=e>t?e:t;q/a.labels.length<t?(s=45,q/a.labels.length<Math.cos(s)*t?(s=90,g-=t):g-=Math.sin(s)*t):g-=c.scaleFontSize;d=c.scaleFontSize;g=g-5-d;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(l=
|
||||
0;l<a.datasets[f].data.length;l++)a.datasets[f].data[l]>e&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]<h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;
|
||||
for(e=0;e<j.labels.length;e++)h=b.measureText(j.labels[e]).width,d=h>d?h:d;d+=10}r=q-d-t;m=Math.floor(r/(a.labels.length-1));n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0<s?(b.save(),b.textAlign="right"):b.textAlign="center";b.fillStyle=c.scaleFontColor;for(var d=0;d<a.labels.length;d++)b.save(),0<s?(b.translate(n+d*m,p+c.scaleFontSize),b.rotate(-(s*(Math.PI/180))),b.fillText(a.labels[d],
|
||||
0,0),b.restore()):b.fillText(a.labels[d],n+d*m,p+c.scaleFontSize+3),b.beginPath(),b.moveTo(n+d*m,p+3),c.scaleShowGridLines&&0<d?(b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+d*m,5)):b.lineTo(n+d*m,p+3),b.stroke();b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(n,p+5);b.lineTo(n,5);b.stroke();b.textAlign="right";b.textBaseline="middle";for(d=0;d<j.steps;d++)b.beginPath(),b.moveTo(n-3,p-(d+1)*k),c.scaleShowGridLines?(b.lineWidth=c.scaleGridLineWidth,
|
||||
b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+r+5,p-(d+1)*k)):b.lineTo(n-0.5,p-(d+1)*k),b.stroke(),c.scaleShowLabels&&b.fillText(j.labels[d],n-8,p-(d+1)*k)},function(d){function e(b,c){return p-d*v(a.datasets[b].data[c],j,k)}for(var f=0;f<a.datasets.length;f++){b.strokeStyle=a.datasets[f].strokeColor;b.lineWidth=c.datasetStrokeWidth;b.beginPath();b.moveTo(n,p-d*v(a.datasets[f].data[0],j,k));for(var g=1;g<a.datasets[f].data.length;g++)c.bezierCurve?b.bezierCurveTo(n+m*(g-0.5),e(f,g-1),n+m*(g-0.5),
|
||||
e(f,g),n+m*g,e(f,g)):b.lineTo(n+m*g,e(f,g));b.stroke();c.datasetFill?(b.lineTo(n+m*(a.datasets[f].data.length-1),p),b.lineTo(n,p),b.closePath(),b.fillStyle=a.datasets[f].fillColor,b.fill()):b.closePath();if(c.pointDot){b.fillStyle=a.datasets[f].pointColor;b.strokeStyle=a.datasets[f].pointStrokeColor;b.lineWidth=c.pointDotStrokeWidth;for(g=0;g<a.datasets[f].data.length;g++)b.beginPath(),b.arc(n+m*g,p-d*v(a.datasets[f].data[g],j,k),c.pointDotRadius,0,2*Math.PI,!0),b.fill(),b.stroke()}}},b)},L=function(a,
|
||||
c,b){var e,h,f,d,g,k,j,l,m,t,r,n,p,s,w=0;g=u;b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;t=1;for(d=0;d<a.labels.length;d++)e=b.measureText(a.labels[d]).width,t=e>t?e:t;q/a.labels.length<t?(w=45,q/a.labels.length<Math.cos(w)*t?(w=90,g-=t):g-=Math.sin(w)*t):g-=c.scaleFontSize;d=c.scaleFontSize;g=g-5-d;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(l=0;l<a.datasets[f].data.length;l++)a.datasets[f].data[l]>e&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]<
|
||||
h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;for(e=0;e<j.labels.length;e++)h=b.measureText(j.labels[e]).width,d=h>d?h:d;d+=10}r=q-d-t;m=
|
||||
Math.floor(r/a.labels.length);s=(m-2*c.scaleGridLineWidth-2*c.barValueSpacing-(c.barDatasetSpacing*a.datasets.length-1)-(c.barStrokeWidth/2*a.datasets.length-1))/a.datasets.length;n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0<w?(b.save(),b.textAlign="right"):b.textAlign="center";b.fillStyle=c.scaleFontColor;for(var d=0;d<a.labels.length;d++)b.save(),0<w?(b.translate(n+
|
||||
d*m,p+c.scaleFontSize),b.rotate(-(w*(Math.PI/180))),b.fillText(a.labels[d],0,0),b.restore()):b.fillText(a.labels[d],n+d*m+m/2,p+c.scaleFontSize+3),b.beginPath(),b.moveTo(n+(d+1)*m,p+3),b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+(d+1)*m,5),b.stroke();b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(n,p+5);b.lineTo(n,5);b.stroke();b.textAlign="right";b.textBaseline="middle";for(d=0;d<j.steps;d++)b.beginPath(),b.moveTo(n-3,p-(d+1)*
|
||||
k),c.scaleShowGridLines?(b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+r+5,p-(d+1)*k)):b.lineTo(n-0.5,p-(d+1)*k),b.stroke(),c.scaleShowLabels&&b.fillText(j.labels[d],n-8,p-(d+1)*k)},function(d){b.lineWidth=c.barStrokeWidth;for(var e=0;e<a.datasets.length;e++){b.fillStyle=a.datasets[e].fillColor;b.strokeStyle=a.datasets[e].strokeColor;for(var f=0;f<a.datasets[e].data.length;f++){var g=n+c.barValueSpacing+m*f+s*e+c.barDatasetSpacing*e+c.barStrokeWidth*e;b.beginPath();
|
||||
b.moveTo(g,p);b.lineTo(g,p-d*v(a.datasets[e].data[f],j,k)+c.barStrokeWidth/2);b.lineTo(g+s,p-d*v(a.datasets[e].data[f],j,k)+c.barStrokeWidth/2);b.lineTo(g+s,p);c.barShowStroke&&b.stroke();b.closePath();b.fill()}}},b)},D=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)},F={}};
|
||||
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
AngularJS v1.2.6
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(H,a,A){'use strict';function D(p,g){g=g||{};a.forEach(g,function(a,c){delete g[c]});for(var c in p)p.hasOwnProperty(c)&&("$"!==c.charAt(0)&&"$"!==c.charAt(1))&&(g[c]=p[c]);return g}var v=a.$$minErr("$resource"),C=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;a.module("ngResource",["ng"]).factory("$resource",["$http","$q",function(p,g){function c(a,c){this.template=a;this.defaults=c||{};this.urlParams={}}function t(n,w,l){function r(h,d){var e={};d=x({},w,d);s(d,function(b,d){u(b)&&(b=b());var k;if(b&&
|
||||
b.charAt&&"@"==b.charAt(0)){k=h;var a=b.substr(1);if(null==a||""===a||"hasOwnProperty"===a||!C.test("."+a))throw v("badmember",a);for(var a=a.split("."),f=0,c=a.length;f<c&&k!==A;f++){var g=a[f];k=null!==k?k[g]:A}}else k=b;e[d]=k});return e}function e(a){return a.resource}function f(a){D(a||{},this)}var F=new c(n);l=x({},B,l);s(l,function(h,d){var c=/^(POST|PUT|PATCH)$/i.test(h.method);f[d]=function(b,d,k,w){var q={},n,l,y;switch(arguments.length){case 4:y=w,l=k;case 3:case 2:if(u(d)){if(u(b)){l=
|
||||
b;y=d;break}l=d;y=k}else{q=b;n=d;l=k;break}case 1:u(b)?l=b:c?n=b:q=b;break;case 0:break;default:throw v("badargs",arguments.length);}var t=this instanceof f,m=t?n:h.isArray?[]:new f(n),z={},B=h.interceptor&&h.interceptor.response||e,C=h.interceptor&&h.interceptor.responseError||A;s(h,function(a,b){"params"!=b&&("isArray"!=b&&"interceptor"!=b)&&(z[b]=G(a))});c&&(z.data=n);F.setUrlParams(z,x({},r(n,h.params||{}),q),h.url);q=p(z).then(function(b){var d=b.data,k=m.$promise;if(d){if(a.isArray(d)!==!!h.isArray)throw v("badcfg",
|
||||
h.isArray?"array":"object",a.isArray(d)?"array":"object");h.isArray?(m.length=0,s(d,function(b){m.push(new f(b))})):(D(d,m),m.$promise=k)}m.$resolved=!0;b.resource=m;return b},function(b){m.$resolved=!0;(y||E)(b);return g.reject(b)});q=q.then(function(b){var a=B(b);(l||E)(a,b.headers);return a},C);return t?q:(m.$promise=q,m.$resolved=!1,m)};f.prototype["$"+d]=function(b,a,k){u(b)&&(k=a,a=b,b={});b=f[d].call(this,b,this,a,k);return b.$promise||b}});f.bind=function(a){return t(n,x({},w,a),l)};return f}
|
||||
var B={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},E=a.noop,s=a.forEach,x=a.extend,G=a.copy,u=a.isFunction;c.prototype={setUrlParams:function(c,g,l){var r=this,e=l||r.template,f,p,h=r.urlParams={};s(e.split(/\W/),function(a){if("hasOwnProperty"===a)throw v("badname");!/^\d+$/.test(a)&&(a&&RegExp("(^|[^\\\\]):"+a+"(\\W|$)").test(e))&&(h[a]=!0)});e=e.replace(/\\:/g,":");g=g||{};s(r.urlParams,function(d,c){f=g.hasOwnProperty(c)?
|
||||
g[c]:r.defaults[c];a.isDefined(f)&&null!==f?(p=encodeURIComponent(f).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),e=e.replace(RegExp(":"+c+"(\\W|$)","g"),p+"$1")):e=e.replace(RegExp("(/?):"+c+"(\\W|$)","g"),function(a,c,d){return"/"==d.charAt(0)?d:c+d})});e=e.replace(/\/+$/,"");e=e.replace(/\/\.(?=\w+($|\?))/,".");c.url=e.replace(/\/\\\./,"/.");s(g,function(a,e){r.urlParams[e]||
|
||||
(c.params=c.params||{},c.params[e]=a)})}};return t}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-resource.min.js.map
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
AngularJS v1.2.6
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(h,e,A){'use strict';function u(w,q,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,n){function y(){l&&(l.$destroy(),l=null);g&&(k.leave(g),g=null)}function v(){var b=w.current&&w.current.locals;if(b&&b.$template){var b=a.$new(),f=w.current;g=n(b,function(d){k.enter(d,null,g||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||q()});y()});l=f.scope=b;l.$emit("$viewContentLoaded");l.$eval(h)}else y()}var l,g,t=b.autoscroll,h=b.onload||"";a.$on("$routeChangeSuccess",
|
||||
v);v()}}}function z(e,h,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var n=e(c.contents());b.controller&&(f.$scope=a,f=h(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));n(a)}}}h=e.module("ngRoute",["ng"]).provider("$route",function(){function h(a,c){return e.extend(new (e.extend(function(){},{prototype:a})),c)}function q(a,e){var b=e.caseInsensitiveMatch,
|
||||
f={originalPath:a,regexp:a},h=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;h.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&q(a,c));if(a){var b="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[b]=e.extend({redirectTo:a},
|
||||
q(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,n,q,v,l){function g(){var d=t(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!x)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)x=!1,a.$broadcast("$routeChangeStart",d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?
|
||||
c.path(u(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?n.get(d):n.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=l.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl=b,c=q.get(b,{cache:v}).then(function(a){return a.data})));
|
||||
e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function t(){var a,b;e.forEach(k,function(f,k){var p;if(p=!b){var s=c.path();p=f.keys;var l={};if(f.regexp)if(s=f.regexp.exec(s)){for(var g=1,q=s.length;g<q;++g){var n=p[g-1],r="string"==typeof s[g]?decodeURIComponent(s[g]):s[g];n&&r&&(l[n.name]=r)}p=l}else p=null;else p=null;
|
||||
p=a=p}p&&(b=h(f,{params:e.extend({},c.search(),a),pathParams:a}),b.$$route=f)});return b||k[null]&&h(k[null],{params:{},pathParams:{}})}function u(a,c){var b=[];e.forEach((a||"").split(":"),function(a,d){if(0===d)b.push(a);else{var e=a.match(/(\w+)(.*)/),f=e[1];b.push(c[f]);b.push(e[2]||"");delete c[f]}});return b.join("")}var x=!1,r={routes:k,reload:function(){x=!0;a.$evalAsync(g)}};a.$on("$locationChangeSuccess",g);return r}]});h.provider("$routeParams",function(){this.$get=function(){return{}}});
|
||||
h.directive("ngView",u);h.directive("ngView",z);u.$inject=["$route","$anchorScroll","$animate"];z.$inject=["$compile","$controller","$route"]})(window,window.angular);
|
||||
//# sourceMappingURL=angular-route.min.js.map
|
||||
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
AngularJS v1.2.6
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(Y,O,r){'use strict';function s(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.2.6/"+(b?b+"/":"")+a;for(c=1;c<arguments.length;c++)a=a+(1==c?"?":"&")+"p"+(c-1)+"="+encodeURIComponent("function"==typeof arguments[c]?arguments[c].toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof arguments[c]?"undefined":"string"!=typeof arguments[c]?JSON.stringify(arguments[c]):arguments[c]);return Error(a)}}function ob(b){if(null==b||ya(b))return!1;var a=
|
||||
b.length;return 1===b.nodeType&&a?!0:E(b)||I(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function q(b,a,c){var d;if(b)if(J(b))for(d in b)"prototype"==d||("length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d))||a.call(c,b[d],d);else if(b.forEach&&b.forEach!==q)b.forEach(a,c);else if(ob(b))for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function Nb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function Nc(b,
|
||||
a,c){for(var d=Nb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function Ob(b){return function(a,c){b(c,a)}}function Xa(){for(var b=ia.length,a;b;){b--;a=ia[b].charCodeAt(0);if(57==a)return ia[b]="A",ia.join("");if(90==a)ia[b]="0";else return ia[b]=String.fromCharCode(a+1),ia.join("")}ia.unshift("0");return ia.join("")}function Pb(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function x(b){var a=b.$$hashKey;q(arguments,function(a){a!==b&&q(a,function(a,c){b[c]=a})});Pb(b,a);return b}function P(b){return parseInt(b,
|
||||
10)}function Qb(b,a){return x(new (x(function(){},{prototype:b})),a)}function y(){}function za(b){return b}function Z(b){return function(){return b}}function D(b){return"undefined"===typeof b}function v(b){return"undefined"!==typeof b}function V(b){return null!=b&&"object"===typeof b}function E(b){return"string"===typeof b}function pb(b){return"number"===typeof b}function Ja(b){return"[object Date]"===Ya.call(b)}function I(b){return"[object Array]"===Ya.call(b)}function J(b){return"function"===typeof b}
|
||||
function Za(b){return"[object RegExp]"===Ya.call(b)}function ya(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function Oc(b){return!(!b||!(b.nodeName||b.on&&b.find))}function Pc(b,a,c){var d=[];q(b,function(b,g,f){d.push(a.call(c,b,g,f))});return d}function $a(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Ka(b,a){var c=$a(b,a);0<=c&&b.splice(c,1);return a}function ea(b,a){if(ya(b)||b&&b.$evalAsync&&b.$watch)throw La("cpws");if(a){if(b===
|
||||
a)throw La("cpi");if(I(b))for(var c=a.length=0;c<b.length;c++)a.push(ea(b[c]));else{c=a.$$hashKey;q(a,function(b,c){delete a[c]});for(var d in b)a[d]=ea(b[d]);Pb(a,c)}}else(a=b)&&(I(b)?a=ea(b,[]):Ja(b)?a=new Date(b.getTime()):Za(b)?a=RegExp(b.source):V(b)&&(a=ea(b,{})));return a}function Rb(b,a){a=a||{};for(var c in b)b.hasOwnProperty(c)&&("$"!==c.charAt(0)&&"$"!==c.charAt(1))&&(a[c]=b[c]);return a}function ta(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,
|
||||
d;if(c==typeof a&&"object"==c)if(I(b)){if(!I(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ta(b[d],a[d]))return!1;return!0}}else{if(Ja(b))return Ja(a)&&b.getTime()==a.getTime();if(Za(b)&&Za(a))return b.toString()==a.toString();if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||ya(b)||ya(a)||I(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!J(b[d])){if(!ta(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==r&&!J(a[d]))return!1;return!0}return!1}
|
||||
function Sb(){return O.securityPolicy&&O.securityPolicy.isActive||O.querySelector&&!(!O.querySelector("[ng-csp]")&&!O.querySelector("[data-ng-csp]"))}function qb(b,a){var c=2<arguments.length?ua.call(arguments,2):[];return!J(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(ua.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Qc(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)?c=r:ya(a)?c="$WINDOW":
|
||||
a&&O===a?c="$DOCUMENT":a&&(a.$evalAsync&&a.$watch)&&(c="$SCOPE");return c}function oa(b,a){return"undefined"===typeof b?r:JSON.stringify(b,Qc,a?" ":null)}function Tb(b){return E(b)?JSON.parse(b):b}function Ma(b){b&&0!==b.length?(b=C(""+b),b=!("f"==b||"0"==b||"false"==b||"no"==b||"n"==b||"[]"==b)):b=!1;return b}function fa(b){b=u(b).clone();try{b.empty()}catch(a){}var c=u("<div>").append(b).html();try{return 3===b[0].nodeType?C(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+
|
||||
C(b)})}catch(d){return C(c)}}function Ub(b){try{return decodeURIComponent(b)}catch(a){}}function Vb(b){var a={},c,d;q((b||"").split("&"),function(b){b&&(c=b.split("="),d=Ub(c[0]),v(d)&&(b=v(c[1])?Ub(c[1]):!0,a[d]?I(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Wb(b){var a=[];q(b,function(b,d){I(b)?q(b,function(b){a.push(va(d,!0)+(!0===b?"":"="+va(b,!0)))}):a.push(va(d,!0)+(!0===b?"":"="+va(b,!0)))});return a.length?a.join("&"):""}function rb(b){return va(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,
|
||||
"=").replace(/%2B/gi,"+")}function va(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function Rc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,f=["ng:app","ng-app","x-ng-app","data-ng-app"],h=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;q(f,function(a){f[a]=!0;c(O.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(q(b.querySelectorAll("."+a),c),q(b.querySelectorAll("."+a+"\\:"),c),q(b.querySelectorAll("["+
|
||||
a+"]"),c))});q(d,function(a){if(!e){var b=h.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):q(a.attributes,function(b){!e&&f[b.name]&&(e=a,g=b.value)})}});e&&a(e,g?[g]:[])}function Xb(b,a){var c=function(){b=u(b);if(b.injector()){var c=b[0]===O?"document":fa(b);throw La("btstrpd",c);}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");c=Yb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector","$animate",function(a,b,c,d,e){a.$apply(function(){b.data("$injector",
|
||||
d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(Y&&!d.test(Y.name))return c();Y.name=Y.name.replace(d,"");Na.resumeBootstrap=function(b){q(b,function(b){a.push(b)});c()}}function ab(b,a){a=a||"_";return b.replace(Sc,function(b,d){return(d?a:"")+b.toLowerCase()})}function sb(b,a,c){if(!b)throw La("areq",a||"?",c||"required");return b}function Oa(b,a,c){c&&I(b)&&(b=b[b.length-1]);sb(J(b),a,"not a function, got "+(b&&"object"==typeof b?b.constructor.name||"Object":typeof b));return b}function wa(b,
|
||||
a){if("hasOwnProperty"===b)throw La("badname",a);}function tb(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,g=a.length,f=0;f<g;f++)d=a[f],b&&(b=(e=b)[d]);return!c&&J(b)?qb(e,b):b}function ub(b){var a=b[0];b=b[b.length-1];if(a===b)return u(a);var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return u(c)}function Tc(b){var a=s("$injector"),c=s("ng");b=b.angular||(b.angular={});b.$$minErr=b.$$minErr||s;return b.module||(b.module=function(){var b={};return function(e,g,f){if("hasOwnProperty"===
|
||||
e)throw c("badname","module");g&&b.hasOwnProperty(e)&&(b[e]=null);return b[e]||(b[e]=function(){function b(a,d,e){return function(){c[e||"push"]([a,d,arguments]);return n}}if(!g)throw a("nomod",e);var c=[],d=[],m=b("$injector","invoke"),n={_invokeQueue:c,_runBlocks:d,requires:g,name:e,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:b("$provide","value"),constant:b("$provide","constant","unshift"),animation:b("$animateProvider","register"),filter:b("$filterProvider",
|
||||
"register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:m,run:function(a){d.push(a);return this}};f&&m(f);return n}())}}())}function Pa(b){return b.replace(Uc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(Vc,"Moz$1")}function vb(b,a,c,d){function e(b){var e=c&&b?[this.filter(b)]:[this],l=a,k,m,n,p,t,w;if(!d||null!=b)for(;e.length;)for(k=e.shift(),m=0,n=k.length;m<n;m++)for(p=u(k[m]),l?p.triggerHandler("$destroy"):l=!l,t=0,p=(w=p.children()).length;t<
|
||||
p;t++)e.push(Aa(w[t]));return g.apply(this,arguments)}var g=Aa.fn[b],g=g.$original||g;e.$original=g;Aa.fn[b]=e}function N(b){if(b instanceof N)return b;if(!(this instanceof N)){if(E(b)&&"<"!=b.charAt(0))throw wb("nosel");return new N(b)}if(E(b)){var a=O.createElement("div");a.innerHTML="<div> </div>"+b;a.removeChild(a.firstChild);xb(this,a.childNodes);u(O.createDocumentFragment()).append(this)}else xb(this,b)}function yb(b){return b.cloneNode(!0)}function Ba(b){Zb(b);var a=0;for(b=b.childNodes||
|
||||
[];a<b.length;a++)Ba(b[a])}function $b(b,a,c,d){if(v(d))throw wb("offargs");var e=ja(b,"events");ja(b,"handle")&&(D(a)?q(e,function(a,c){zb(b,c,a);delete e[c]}):q(a.split(" "),function(a){D(c)?(zb(b,a,e[a]),delete e[a]):Ka(e[a]||[],c)}))}function Zb(b,a){var c=b[bb],d=Qa[c];d&&(a?delete Qa[c].data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),$b(b)),delete Qa[c],b[bb]=r))}function ja(b,a,c){var d=b[bb],d=Qa[d||-1];if(v(c))d||(b[bb]=d=++Wc,d=Qa[d]={}),d[a]=c;else return d&&d[a]}function ac(b,
|
||||
a,c){var d=ja(b,"data"),e=v(c),g=!e&&v(a),f=g&&!V(a);d||f||ja(b,"data",d={});if(e)d[a]=c;else if(g){if(f)return d&&d[a];x(d,a)}else return d}function Ab(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function Bb(b,a){a&&b.setAttribute&&q(a.split(" "),function(a){b.setAttribute("class",aa((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+aa(a)+" "," ")))})}function Cb(b,a){if(a&&b.setAttribute){var c=(" "+
|
||||
(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(a.split(" "),function(a){a=aa(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",aa(c))}}function xb(b,a){if(a){a=a.nodeName||!v(a.length)||ya(a)?[a]:a;for(var c=0;c<a.length;c++)b.push(a[c])}}function bc(b,a){return cb(b,"$"+(a||"ngController")+"Controller")}function cb(b,a,c){b=u(b);9==b[0].nodeType&&(b=b.find("html"));for(a=I(a)?a:[a];b.length;){for(var d=0,e=a.length;d<e;d++)if((c=b.data(a[d]))!==r)return c;b=b.parent()}}
|
||||
function cc(b){for(var a=0,c=b.childNodes;a<c.length;a++)Ba(c[a]);for(;b.firstChild;)b.removeChild(b.firstChild)}function dc(b,a){var c=db[a.toLowerCase()];return c&&ec[b.nodeName]&&c}function Xc(b,a){var c=function(c,e){c.preventDefault||(c.preventDefault=function(){c.returnValue=!1});c.stopPropagation||(c.stopPropagation=function(){c.cancelBubble=!0});c.target||(c.target=c.srcElement||O);if(D(c.defaultPrevented)){var g=c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;g.call(c)};
|
||||
c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||!1===c.returnValue};var f=Rb(a[e||c.type]||[]);q(f,function(a){a.call(b,c)});8>=L?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Ca(b){var a=typeof b,c;"object"==a&&null!==b?"function"==typeof(c=b.$$hashKey)?c=b.$$hashKey():c===r&&(c=b.$$hashKey=Xa()):c=b;return a+":"+c}function Ra(b){q(b,
|
||||
this.put,this)}function fc(b){var a,c;"function"==typeof b?(a=b.$inject)||(a=[],b.length&&(c=b.toString().replace(Yc,""),c=c.match(Zc),q(c[1].split($c),function(b){b.replace(ad,function(b,c,d){a.push(d)})})),b.$inject=a):I(b)?(c=b.length-1,Oa(b[c],"fn"),a=b.slice(0,c)):Oa(b,"fn",!0);return a}function Yb(b){function a(a){return function(b,c){if(V(b))q(b,Ob(a));else return a(b,c)}}function c(a,b){wa(a,"service");if(J(b)||I(b))b=n.instantiate(b);if(!b.$get)throw Sa("pget",a);return m[a+h]=b}function d(a,
|
||||
b){return c(a,{$get:b})}function e(a){var b=[],c,d,g,h;q(a,function(a){if(!k.get(a)){k.put(a,!0);try{if(E(a))for(c=Ta(a),b=b.concat(e(c.requires)).concat(c._runBlocks),d=c._invokeQueue,g=0,h=d.length;g<h;g++){var f=d[g],l=n.get(f[0]);l[f[1]].apply(l,f[2])}else J(a)?b.push(n.invoke(a)):I(a)?b.push(n.invoke(a)):Oa(a,"module")}catch(m){throw I(a)&&(a=a[a.length-1]),m.message&&(m.stack&&-1==m.stack.indexOf(m.message))&&(m=m.message+"\n"+m.stack),Sa("modulerr",a,m.stack||m.message||m);}}});return b}function g(a,
|
||||
b){function c(d){if(a.hasOwnProperty(d)){if(a[d]===f)throw Sa("cdep",l.join(" <- "));return a[d]}try{return l.unshift(d),a[d]=f,a[d]=b(d)}finally{l.shift()}}function d(a,b,e){var g=[],h=fc(a),f,k,l;k=0;for(f=h.length;k<f;k++){l=h[k];if("string"!==typeof l)throw Sa("itkn",l);g.push(e&&e.hasOwnProperty(l)?e[l]:c(l))}a.$inject||(a=a[f]);return a.apply(b,g)}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype=(I(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return V(e)||J(e)?
|
||||
e:c},get:c,annotate:fc,has:function(b){return m.hasOwnProperty(b+h)||a.hasOwnProperty(b)}}}var f={},h="Provider",l=[],k=new Ra,m={$provide:{provider:a(c),factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,Z(b))}),constant:a(function(a,b){wa(a,"constant");m[a]=b;p[a]=b}),decorator:function(a,b){var c=n.get(a+h),d=c.$get;c.$get=function(){var a=t.invoke(d,c);return t.invoke(b,null,{$delegate:a})}}}},n=m.$injector=g(m,
|
||||
function(){throw Sa("unpr",l.join(" <- "));}),p={},t=p.$injector=g(p,function(a){a=n.get(a+h);return t.invoke(a.$get,a)});q(e(b),function(a){t.invoke(a||y)});return t}function bd(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;q(a,function(a){b||"a"!==C(a.nodeName)||(b=a)});return b}function g(){var b=c.hash(),d;b?(d=f.getElementById(b))?d.scrollIntoView():(d=e(f.getElementsByName(b)))?d.scrollIntoView():
|
||||
"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var f=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(g)});return g}]}function cd(b,a,c,d){function e(a){try{a.apply(null,ua.call(arguments,1))}finally{if(w--,0===w)for(;A.length;)try{A.pop()()}catch(b){c.error(b)}}}function g(a,b){(function T(){q(F,function(a){a()});H=b(T,a)})()}function f(){B=null;S!=h.url()&&(S=h.url(),q($,function(a){a(h.url())}))}var h=this,l=a[0],k=b.location,m=b.history,n=b.setTimeout,p=b.clearTimeout,t=
|
||||
{};h.isMock=!1;var w=0,A=[];h.$$completeOutstandingRequest=e;h.$$incOutstandingRequestCount=function(){w++};h.notifyWhenNoOutstandingRequests=function(a){q(F,function(a){a()});0===w?a():A.push(a)};var F=[],H;h.addPollFn=function(a){D(H)&&g(100,n);F.push(a);return a};var S=k.href,z=a.find("base"),B=null;h.url=function(a,c){k!==b.location&&(k=b.location);if(a){if(S!=a)return S=a,d.history?c?m.replaceState(null,"",a):(m.pushState(null,"",a),z.attr("href",z.attr("href"))):(B=a,c?k.replace(a):k.href=a),
|
||||
h}else return B||k.href.replace(/%27/g,"'")};var $=[],M=!1;h.onUrlChange=function(a){if(!M){if(d.history)u(b).on("popstate",f);if(d.hashchange)u(b).on("hashchange",f);else h.addPollFn(f);M=!0}$.push(a);return a};h.baseHref=function(){var a=z.attr("href");return a?a.replace(/^https?\:\/\/[^\/]*/,""):""};var W={},ka="",Q=h.baseHref();h.cookies=function(a,b){var d,e,g,h;if(a)b===r?l.cookie=escape(a)+"=;path="+Q+";expires=Thu, 01 Jan 1970 00:00:00 GMT":E(b)&&(d=(l.cookie=escape(a)+"="+escape(b)+";path="+
|
||||
Q).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(l.cookie!==ka)for(ka=l.cookie,d=ka.split("; "),W={},g=0;g<d.length;g++)e=d[g],h=e.indexOf("="),0<h&&(a=unescape(e.substring(0,h)),W[a]===r&&(W[a]=unescape(e.substring(h+1))));return W}};h.defer=function(a,b){var c;w++;c=n(function(){delete t[c];e(a)},b||0);t[c]=!0;return c};h.defer.cancel=function(a){return t[a]?(delete t[a],p(a),e(y),!0):!1}}function dd(){this.$get=
|
||||
["$window","$log","$sniffer","$document",function(b,a,c,d){return new cd(b,d,a,c)}]}function ed(){this.$get=function(){function b(b,d){function e(a){a!=n&&(p?p==a&&(p=a.n):p=a,g(a.n,a.p),g(a,n),n=a,n.n=null)}function g(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw s("$cacheFactory")("iid",b);var f=0,h=x({},d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,m={},n=null,p=null;return a[b]={put:function(a,b){var c=m[a]||(m[a]={key:a});e(c);if(!D(b))return a in l||f++,l[a]=b,f>k&&this.remove(p.key),
|
||||
b},get:function(a){var b=m[a];if(b)return e(b),l[a]},remove:function(a){var b=m[a];b&&(b==n&&(n=b.p),b==p&&(p=b.n),g(b.n,b.p),delete m[a],delete l[a],f--)},removeAll:function(){l={};f=0;m={};n=p=null},destroy:function(){m=h=l=null;delete a[b]},info:function(){return x({},h,{size:f})}}}var a={};b.info=function(){var b={};q(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function fd(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function hc(b,a){var c=
|
||||
{},d="Directive",e=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,g=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,f=/^(on[a-z]+|formaction)$/;this.directive=function l(a,e){wa(a,"directive");E(a)?(sb(e,"directiveFactory"),c.hasOwnProperty(a)||(c[a]=[],b.factory(a+d,["$injector","$exceptionHandler",function(b,d){var e=[];q(c[a],function(c,g){try{var f=b.invoke(c);J(f)?f={compile:Z(f)}:!f.compile&&f.link&&(f.compile=Z(f.link));f.priority=f.priority||0;f.index=g;f.name=f.name||a;f.require=f.require||f.controller&&f.name;
|
||||
f.restrict=f.restrict||"A";e.push(f)}catch(l){d(l)}});return e}])),c[a].push(e)):q(a,Ob(l));return this};this.aHrefSanitizationWhitelist=function(b){return v(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return v(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document","$sce","$animate",
|
||||
"$$sanitizeUri",function(a,b,m,n,p,t,w,A,F,H,S,z){function B(a,b,c,d,e){a instanceof u||(a=u(a));q(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=u(b).wrap("<span></span>").parent()[0])});var g=M(a,b,a,c,d,e);$(a,"ng-scope");return function(b,c,d){sb(b,"scope");var e=c?Da.clone.call(a):a;q(d,function(a,b){e.data("$"+b+"Controller",a)});d=0;for(var f=e.length;d<f;d++){var k=e[d].nodeType;1!==k&&9!==k||e.eq(d).data("$scope",b)}c&&c(e,b);g&&g(b,e,e);return e}}function $(a,b){try{a.addClass(b)}catch(c){}}
|
||||
function M(a,b,c,d,e,g){function f(a,c,d,e){var g,l,m,p,n,t,w;g=c.length;var K=Array(g);for(n=0;n<g;n++)K[n]=c[n];w=n=0;for(t=k.length;n<t;w++)l=K[w],c=k[n++],g=k[n++],m=u(l),c?(c.scope?(p=a.$new(),m.data("$scope",p)):p=a,(m=c.transclude)||!e&&b?c(g,p,l,d,W(a,m||b)):c(g,p,l,d,e)):g&&g(a,l.childNodes,r,e)}for(var k=[],l,m,p,n,t=0;t<a.length;t++)l=new Db,m=ka(a[t],[],l,0===t?d:r,e),(g=m.length?ga(m,a[t],l,b,c,null,[],[],g):null)&&g.scope&&$(u(a[t]),"ng-scope"),l=g&&g.terminal||!(p=a[t].childNodes)||
|
||||
!p.length?null:M(p,g?g.transclude:b),k.push(g,l),n=n||g||l,g=null;return n?f:null}function W(a,b){return function(c,d,e){var g=!1;c||(c=a.$new(),g=c.$$transcluded=!0);d=b(c,d,e);if(g)d.on("$destroy",qb(c,c.$destroy));return d}}function ka(a,b,c,d,f){var k=c.$attr,l;switch(a.nodeType){case 1:T(b,la(Ea(a).toLowerCase()),"E",d,f);var m,p,n;l=a.attributes;for(var t=0,w=l&&l.length;t<w;t++){var A=!1,B=!1;m=l[t];if(!L||8<=L||m.specified){p=m.name;n=la(p);U.test(n)&&(p=ab(n.substr(6),"-"));var S=n.replace(/(Start|End)$/,
|
||||
"");n===S+"Start"&&(A=p,B=p.substr(0,p.length-5)+"end",p=p.substr(0,p.length-6));n=la(p.toLowerCase());k[n]=p;c[n]=m=aa(m.value);dc(a,n)&&(c[n]=!0);P(a,b,m,n);T(b,n,"A",d,f,A,B)}}a=a.className;if(E(a)&&""!==a)for(;l=g.exec(a);)n=la(l[2]),T(b,n,"C",d,f)&&(c[n]=aa(l[3])),a=a.substr(l.index+l[0].length);break;case 3:s(b,a.nodeValue);break;case 8:try{if(l=e.exec(a.nodeValue))n=la(l[1]),T(b,n,"M",d,f)&&(c[n]=aa(l[2]))}catch(W){}}b.sort(D);return b}function Q(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ha("uterdir",
|
||||
b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return u(d)}function R(a,b,c){return function(d,e,g,f,l){e=Q(e[0],b,c);return a(d,e,g,f,l)}}function ga(a,c,d,e,g,f,l,p,n){function A(a,b,c,d){if(a){c&&(a=R(a,c,d));a.require=G.require;if(z===G||G.$$isolateScope)a=ic(a,{isolateScope:!0});l.push(a)}if(b){c&&(b=R(b,c,d));b.require=G.require;if(z===G||G.$$isolateScope)b=ic(b,{isolateScope:!0});p.push(b)}}function S(a,b,c){var d,e="data",
|
||||
g=!1;if(E(a)){for(;"^"==(d=a.charAt(0))||"?"==d;)a=a.substr(1),"^"==d&&(e="inheritedData"),g=g||"?"==d;d=null;c&&"data"===e&&(d=c[a]);d=d||b[e]("$"+a+"Controller");if(!d&&!g)throw ha("ctreq",a,ba);}else I(a)&&(d=[],q(a,function(a){d.push(S(a,b,c))}));return d}function W(a,e,g,f,n){function A(a,b){var c;2>arguments.length&&(b=a,a=r);D&&(c=eb);return n(a,b,c)}var K,B,F,M,R,Q,eb={},s;K=c===g?d:Rb(d,new Db(u(g),d.$attr));B=K.$$element;if(z){var ka=/^\s*([@=&])(\??)\s*(\w*)\s*$/;f=u(g);Q=e.$new(!0);ga&&
|
||||
ga===z.$$originalDirective?f.data("$isolateScope",Q):f.data("$isolateScopeNoTemplate",Q);$(f,"ng-isolate-scope");q(z.scope,function(a,c){var d=a.match(ka)||[],g=d[3]||c,f="?"==d[2],d=d[1],l,m,n,p;Q.$$isolateBindings[c]=d+g;switch(d){case "@":K.$observe(g,function(a){Q[c]=a});K.$$observers[g].$$scope=e;K[g]&&(Q[c]=b(K[g])(e));break;case "=":if(f&&!K[g])break;m=t(K[g]);p=m.literal?ta:function(a,b){return a===b};n=m.assign||function(){l=Q[c]=m(e);throw ha("nonassign",K[g],z.name);};l=Q[c]=m(e);Q.$watch(function(){var a=
|
||||
m(e);p(a,Q[c])||(p(a,l)?n(e,a=Q[c]):Q[c]=a);return l=a},null,m.literal);break;case "&":m=t(K[g]);Q[c]=function(a){return m(e,a)};break;default:throw ha("iscp",z.name,c,a);}})}s=n&&A;H&&q(H,function(a){var b={$scope:a===z||a.$$isolateScope?Q:e,$element:B,$attrs:K,$transclude:s},c;R=a.controller;"@"==R&&(R=K[a.name]);c=w(R,b);eb[a.name]=c;D||B.data("$"+a.name+"Controller",c);a.controllerAs&&(b.$scope[a.controllerAs]=c)});f=0;for(F=l.length;f<F;f++)try{M=l[f],M(M.isolateScope?Q:e,B,K,M.require&&S(M.require,
|
||||
B,eb),s)}catch(T){m(T,fa(B))}f=e;z&&(z.template||null===z.templateUrl)&&(f=Q);a&&a(f,g.childNodes,r,n);for(f=p.length-1;0<=f;f--)try{M=p[f],M(M.isolateScope?Q:e,B,K,M.require&&S(M.require,B,eb),s)}catch(G){m(G,fa(B))}}n=n||{};var F=-Number.MAX_VALUE,M,H=n.controllerDirectives,z=n.newIsolateScopeDirective,ga=n.templateDirective;n=n.nonTlbTranscludeDirective;for(var T=!1,D=!1,x=d.$$element=u(c),G,ba,s,N=e,ma,L=0,Fa=a.length;L<Fa;L++){G=a[L];var P=G.$$start,U=G.$$end;P&&(x=Q(c,P,U));s=r;if(F>G.priority)break;
|
||||
if(s=G.scope)M=M||G,G.templateUrl||(C("new/isolated scope",z,G,x),V(s)&&(z=G));ba=G.name;!G.templateUrl&&G.controller&&(s=G.controller,H=H||{},C("'"+ba+"' controller",H[ba],G,x),H[ba]=G);if(s=G.transclude)T=!0,G.$$tlb||(C("transclusion",n,G,x),n=G),"element"==s?(D=!0,F=G.priority,s=Q(c,P,U),x=d.$$element=u(O.createComment(" "+ba+": "+d[ba]+" ")),c=x[0],fb(g,u(ua.call(s,0)),c),N=B(s,e,F,f&&f.name,{nonTlbTranscludeDirective:n})):(s=u(yb(c)).contents(),x.empty(),N=B(s,e));if(G.template)if(C("template",
|
||||
ga,G,x),ga=G,s=J(G.template)?G.template(x,d):G.template,s=X(s),G.replace){f=G;s=u("<div>"+aa(s)+"</div>").contents();c=s[0];if(1!=s.length||1!==c.nodeType)throw ha("tplrt",ba,"");fb(g,x,c);Fa={$attr:{}};s=ka(c,[],Fa);var Y=a.splice(L+1,a.length-(L+1));z&&gc(s);a=a.concat(s).concat(Y);v(d,Fa);Fa=a.length}else x.html(s);if(G.templateUrl)C("template",ga,G,x),ga=G,G.replace&&(f=G),W=y(a.splice(L,a.length-L),x,d,g,N,l,p,{controllerDirectives:H,newIsolateScopeDirective:z,templateDirective:ga,nonTlbTranscludeDirective:n}),
|
||||
Fa=a.length;else if(G.compile)try{ma=G.compile(x,d,N),J(ma)?A(null,ma,P,U):ma&&A(ma.pre,ma.post,P,U)}catch(Z){m(Z,fa(x))}G.terminal&&(W.terminal=!0,F=Math.max(F,G.priority))}W.scope=M&&!0===M.scope;W.transclude=T&&N;return W}function gc(a){for(var b=0,c=a.length;b<c;b++)a[b]=Qb(a[b],{$$isolateScope:!0})}function T(b,e,g,f,k,p,n){if(e===k)return null;k=null;if(c.hasOwnProperty(e)){var t;e=a.get(e+d);for(var w=0,A=e.length;w<A;w++)try{t=e[w],(f===r||f>t.priority)&&-1!=t.restrict.indexOf(g)&&(p&&(t=
|
||||
Qb(t,{$$start:p,$$end:n})),b.push(t),k=t)}catch(B){m(B)}}return k}function v(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});q(b,function(b,g){"class"==g?($(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==g?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==g.charAt(0)||a.hasOwnProperty(g)||(a[g]=b,d[g]=c[g])})}function y(a,b,c,d,e,g,f,l){var k=[],m,t,w=b[0],A=a.shift(),
|
||||
B=x({},A,{templateUrl:null,transclude:null,replace:null,$$originalDirective:A}),S=J(A.templateUrl)?A.templateUrl(b,c):A.templateUrl;b.empty();n.get(H.getTrustedResourceUrl(S),{cache:p}).success(function(p){var n,F;p=X(p);if(A.replace){p=u("<div>"+aa(p)+"</div>").contents();n=p[0];if(1!=p.length||1!==n.nodeType)throw ha("tplrt",A.name,S);p={$attr:{}};fb(d,b,n);var $=ka(n,[],p);V(A.scope)&&gc($);a=$.concat(a);v(c,p)}else n=w,b.html(p);a.unshift(B);m=ga(a,n,c,e,b,A,g,f,l);q(d,function(a,c){a==n&&(d[c]=
|
||||
b[0])});for(t=M(b[0].childNodes,e);k.length;){p=k.shift();F=k.shift();var z=k.shift(),H=k.shift(),$=b[0];F!==w&&($=yb(n),fb(z,u(F),$));F=m.transclude?W(p,m.transclude):H;m(t,p,$,d,F)}k=null}).error(function(a,b,c,d){throw ha("tpload",d.url);});return function(a,b,c,d,e){k?(k.push(b),k.push(c),k.push(d),k.push(e)):m(t,b,c,d,e)}}function D(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function C(a,b,c,d){if(b)throw ha("multidir",b.name,c.name,a,fa(d));
|
||||
}function s(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:Z(function(a,b){var c=b.parent(),e=c.data("$binding")||[];e.push(d);$(c.data("$binding",e),"ng-binding");a.$watch(d,function(a){b[0].nodeValue=a})})})}function N(a,b){if("srcdoc"==b)return H.HTML;var c=Ea(a);if("xlinkHref"==b||"FORM"==c&&"action"==b||"IMG"!=c&&("src"==b||"ngSrc"==b))return H.RESOURCE_URL}function P(a,c,d,e){var g=b(d,!0);if(g){if("multiple"===e&&"SELECT"===Ea(a))throw ha("selmulti",fa(a));c.push({priority:100,compile:function(){return{pre:function(c,
|
||||
d,l){d=l.$$observers||(l.$$observers={});if(f.test(e))throw ha("nodomevents");if(g=b(l[e],!0,N(a,e)))l[e]=g(c),(d[e]||(d[e]=[])).$$inter=!0,(l.$$observers&&l.$$observers[e].$$scope||c).$watch(g,function(a,b){"class"===e&&a!=b?l.$updateClass(a,b):l.$set(e,a)})}}}})}}function fb(a,b,c){var d=b[0],e=b.length,g=d.parentNode,f,l;if(a)for(f=0,l=a.length;f<l;f++)if(a[f]==d){a[f++]=c;l=f+e-1;for(var k=a.length;f<k;f++,l++)l<k?a[f]=a[l]:delete a[f];a.length-=e-1;break}g&&g.replaceChild(c,d);a=O.createDocumentFragment();
|
||||
a.appendChild(d);c[u.expando]=d[u.expando];d=1;for(e=b.length;d<e;d++)g=b[d],u(g).remove(),a.appendChild(g),delete b[d];b[0]=c;b.length=1}function ic(a,b){return x(function(){return a.apply(null,arguments)},a,b)}var Db=function(a,b){this.$$element=a;this.$attr=b||{}};Db.prototype={$normalize:la,$addClass:function(a){a&&0<a.length&&S.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&S.removeClass(this.$$element,a)},$updateClass:function(a,b){this.$removeClass(jc(b,a));this.$addClass(jc(a,
|
||||
b))},$set:function(a,b,c,d){var e=dc(this.$$element[0],a);e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=ab(a,"-"));e=Ea(this.$$element);if("A"===e&&"href"===a||"IMG"===e&&"src"===a)this[a]=b=z(b,"src"===a);!1!==c&&(null===b||b===r?this.$$element.removeAttr(d):this.$$element.attr(d,b));(c=this.$$observers)&&q(c[a],function(a){try{a(b)}catch(c){m(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);
|
||||
A.$evalAsync(function(){e.$$inter||b(c[a])});return b}};var ba=b.startSymbol(),ma=b.endSymbol(),X="{{"==ba||"}}"==ma?za:function(a){return a.replace(/\{\{/g,ba).replace(/}}/g,ma)},U=/^ngAttr[A-Z]/;return B}]}function la(b){return Pa(b.replace(gd,""))}function jc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),g=0;a:for(;g<d.length;g++){for(var f=d[g],h=0;h<e.length;h++)if(f==e[h])continue a;c+=(0<c.length?" ":"")+f}return c}function hd(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,
|
||||
d){wa(a,"controller");V(a)?x(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,g){var f,h,l;E(e)&&(f=e.match(a),h=f[1],l=f[3],e=b.hasOwnProperty(h)?b[h]:tb(g.$scope,h,!0)||tb(d,h,!0),Oa(e,h,!0));f=c.instantiate(e,g);if(l){if(!g||"object"!=typeof g.$scope)throw s("$controller")("noscp",h||e.name,l);g.$scope[l]=f}return f}}]}function id(){this.$get=["$window",function(b){return u(b.document)}]}function jd(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,
|
||||
arguments)}}]}function kc(b){var a={},c,d,e;if(!b)return a;q(b.split("\n"),function(b){e=b.indexOf(":");c=C(aa(b.substr(0,e)));d=aa(b.substr(e+1));c&&(a[c]=a[c]?a[c]+(", "+d):d)});return a}function lc(b){var a=V(b)?b:r;return function(c){a||(a=kc(b));return c?a[C(c)]||null:a}}function mc(b,a,c){if(J(c))return c(b,a);q(c,function(c){b=c(b,a)});return b}function kd(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults={transformResponse:[function(d){E(d)&&
|
||||
(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=Tb(d)));return d}],transformRequest:[function(a){return V(a)&&"[object File]"!==Ya.call(a)?oa(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:d,put:d,patch:d},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},g=this.interceptors=[],f=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,n,p){function t(a){function c(a){var b=x({},a,{data:mc(a.data,
|
||||
a.headers,d.transformResponse)});return 200<=a.status&&300>a.status?b:n.reject(b)}var d={transformRequest:e.transformRequest,transformResponse:e.transformResponse},g=function(a){function b(a){var c;q(a,function(b,d){J(b)&&(c=b(),null!=c?a[d]=c:delete a[d])})}var c=e.headers,d=x({},a.headers),g,f,c=x({},c.common,c[C(a.method)]);b(c);b(d);a:for(g in c){a=C(g);for(f in d)if(C(f)===a)continue a;d[g]=c[g]}return d}(a);x(d,a);d.headers=g;d.method=Ga(d.method);(a=Eb(d.url)?b.cookies()[d.xsrfCookieName||
|
||||
e.xsrfCookieName]:r)&&(g[d.xsrfHeaderName||e.xsrfHeaderName]=a);var f=[function(a){g=a.headers;var b=mc(a.data,lc(g),a.transformRequest);D(a.data)&&q(g,function(a,b){"content-type"===C(b)&&delete g[b]});D(a.withCredentials)&&!D(e.withCredentials)&&(a.withCredentials=e.withCredentials);return w(a,b,g).then(c,c)},r],h=n.when(d);for(q(H,function(a){(a.request||a.requestError)&&f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();
|
||||
var k=f.shift(),h=h.then(a,k)}h.success=function(a){h.then(function(b){a(b.data,b.status,b.headers,d)});return h};h.error=function(a){h.then(null,function(b){a(b.data,b.status,b.headers,d)});return h};return h}function w(b,c,g){function f(a,b,c){q&&(200<=a&&300>a?q.put(r,[a,b,kc(c)]):q.remove(r));l(b,a,c);d.$$phase||d.$apply()}function l(a,c,d){c=Math.max(c,0);(200<=c&&300>c?p.resolve:p.reject)({data:a,status:c,headers:lc(d),config:b})}function k(){var a=$a(t.pendingRequests,b);-1!==a&&t.pendingRequests.splice(a,
|
||||
1)}var p=n.defer(),w=p.promise,q,H,r=A(b.url,b.params);t.pendingRequests.push(b);w.then(k,k);(b.cache||e.cache)&&(!1!==b.cache&&"GET"==b.method)&&(q=V(b.cache)?b.cache:V(e.cache)?e.cache:F);if(q)if(H=q.get(r),v(H)){if(H.then)return H.then(k,k),H;I(H)?l(H[1],H[0],ea(H[2])):l(H,200,{})}else q.put(r,w);D(H)&&a(b.method,r,c,f,g,b.timeout,b.withCredentials,b.responseType);return w}function A(a,b){if(!b)return a;var c=[];Nc(b,function(a,b){null===a||D(a)||(I(a)||(a=[a]),q(a,function(a){V(a)&&(a=oa(a));
|
||||
c.push(va(b)+"="+va(a))}))});return a+(-1==a.indexOf("?")?"?":"&")+c.join("&")}var F=c("$http"),H=[];q(g,function(a){H.unshift(E(a)?p.get(a):p.invoke(a))});q(f,function(a,b){var c=E(a)?p.get(a):p.invoke(a);H.splice(b,0,{response:function(a){return c(n.when(a))},responseError:function(a){return c(n.reject(a))}})});t.pendingRequests=[];(function(a){q(arguments,function(a){t[a]=function(b,c){return t(x(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){t[a]=
|
||||
function(b,c,d){return t(x(d||{},{method:a,url:b,data:c}))}})})("post","put");t.defaults=e;return t}]}function ld(){this.$get=["$browser","$window","$document",function(b,a,c){return md(b,nd,b.defer,a.angular.callbacks,c[0])}]}function md(b,a,c,d,e){function g(a,b){var c=e.createElement("script"),d=function(){c.onreadystatechange=c.onload=c.onerror=null;e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;L&&8>=L?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:
|
||||
c.onload=c.onerror=function(){d()};e.body.appendChild(c);return d}var f=-1;return function(e,l,k,m,n,p,t,w){function A(){H=f;z&&z();B&&B.abort()}function F(a,d,e,g){var f=pa(l).protocol;r&&c.cancel(r);z=B=null;d="file"==f&&0===d?e?200:404:d;a(1223==d?204:d,e,g);b.$$completeOutstandingRequest(y)}var H;b.$$incOutstandingRequestCount();l=l||b.url();if("jsonp"==C(e)){var S="_"+(d.counter++).toString(36);d[S]=function(a){d[S].data=a};var z=g(l.replace("JSON_CALLBACK","angular.callbacks."+S),function(){d[S].data?
|
||||
F(m,200,d[S].data):F(m,H||-2);delete d[S]})}else{var B=new a;B.open(e,l,!0);q(n,function(a,b){v(a)&&B.setRequestHeader(b,a)});B.onreadystatechange=function(){if(4==B.readyState){var a=null,b=null;H!==f&&(a=B.getAllResponseHeaders(),b=B.responseType?B.response:B.responseText);F(m,H||B.status,b,a)}};t&&(B.withCredentials=!0);w&&(B.responseType=w);B.send(k||null)}if(0<p)var r=c(A,p);else p&&p.then&&p.then(A)}}function od(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=
|
||||
function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function g(g,k,m){for(var n,p,t=0,w=[],A=g.length,F=!1,q=[];t<A;)-1!=(n=g.indexOf(b,t))&&-1!=(p=g.indexOf(a,n+f))?(t!=n&&w.push(g.substring(t,n)),w.push(t=c(F=g.substring(n+f,p))),t.exp=F,t=p+h,F=!0):(t!=A&&w.push(g.substring(t)),t=A);(A=w.length)||(w.push(""),A=1);if(m&&1<w.length)throw nc("noconcat",g);if(!k||F)return q.length=A,t=function(a){try{for(var b=0,c=A,f;b<c;b++)"function"==typeof(f=w[b])&&
|
||||
(f=f(a),f=m?e.getTrusted(m,f):e.valueOf(f),null===f||D(f)?f="":"string"!=typeof f&&(f=oa(f))),q[b]=f;return q.join("")}catch(h){a=nc("interr",g,h.toString()),d(a)}},t.exp=g,t.parts=w,t}var f=b.length,h=a.length;g.startSymbol=function(){return b};g.endSymbol=function(){return a};return g}]}function pd(){this.$get=["$rootScope","$window","$q",function(b,a,c){function d(d,f,h,l){var k=a.setInterval,m=a.clearInterval,n=c.defer(),p=n.promise,t=0,w=v(l)&&!l;h=v(h)?h:0;p.then(null,null,d);p.$$intervalId=
|
||||
k(function(){n.notify(t++);0<h&&t>=h&&(n.resolve(t),m(p.$$intervalId),delete e[p.$$intervalId]);w||b.$apply()},f);e[p.$$intervalId]=n;return p}var e={};d.cancel=function(a){return a&&a.$$intervalId in e?(e[a.$$intervalId].reject("canceled"),clearInterval(a.$$intervalId),delete e[a.$$intervalId],!0):!1};return d}]}function qd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,
|
||||
lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",
|
||||
fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function oc(b){b=b.split("/");for(var a=b.length;a--;)b[a]=rb(b[a]);return b.join("/")}function pc(b,a,c){b=pa(b,c);a.$$protocol=b.protocol;a.$$host=b.hostname;a.$$port=P(b.port)||rd[b.protocol]||null}function qc(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=pa(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?
|
||||
b.pathname.substring(1):b.pathname);a.$$search=Vb(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function na(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ua(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Fb(b){return b.substr(0,Ua(b).lastIndexOf("/")+1)}function rc(b,a){this.$$html5=!0;a=a||"";var c=Fb(b);pc(b,this,b);this.$$parse=function(a){var e=na(c,a);if(!E(e))throw Gb("ipthprfx",a,c);qc(e,this,b);this.$$path||
|
||||
(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Wb(this.$$search),b=this.$$hash?"#"+rb(this.$$hash):"";this.$$url=oc(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;if((e=na(b,d))!==r)return d=e,(e=na(a,e))!==r?c+(na("/",e)||e):b+d;if((e=na(c,d))!==r)return c+e;if(c==d+"/")return c}}function Hb(b,a){var c=Fb(b);pc(b,this,b);this.$$parse=function(d){var e=na(b,d)||na(c,d),e="#"==e.charAt(0)?na(a,e):this.$$html5?e:"";if(!E(e))throw Gb("ihshprfx",
|
||||
d,a);qc(e,this,b);d=this.$$path;var g=/^\/?.*?:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));g.exec(e)||(d=(e=g.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Wb(this.$$search),e=this.$$hash?"#"+rb(this.$$hash):"";this.$$url=oc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(Ua(b)==Ua(a))return a}}function sc(b,a){this.$$html5=!0;Hb.apply(this,arguments);var c=Fb(b);this.$$rewrite=function(d){var e;if(b==Ua(d))return d;
|
||||
if(e=na(c,d))return b+a+e;if(c===d+"/")return c}}function gb(b){return function(){return this[b]}}function tc(b,a){return function(c){if(D(c))return this[b];this[b]=a(c);this.$$compose();return this}}function sd(){var b="",a=!1;this.hashPrefix=function(a){return v(a)?(b=a,this):b};this.html5Mode=function(b){return v(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,g){function f(a){c.$broadcast("$locationChangeSuccess",h.absUrl(),a)}var h,l=d.baseHref(),
|
||||
k=d.url();a?(l=k.substring(0,k.indexOf("/",k.indexOf("//")+2))+(l||"/"),e=e.history?rc:sc):(l=Ua(k),e=Hb);h=new e(l,"#"+b);h.$$parse(h.$$rewrite(k));g.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var b=u(a.target);"a"!==C(b[0].nodeName);)if(b[0]===g[0]||!(b=b.parent())[0])return;var e=b.prop("href");V(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=pa(e.animVal).href);var f=h.$$rewrite(e);e&&(!b.attr("target")&&f&&!a.isDefaultPrevented())&&(a.preventDefault(),f!=d.url()&&
|
||||
(h.$$parse(f),c.$apply(),Y.angular["ff-684208-preventDefault"]=!0))}});h.absUrl()!=k&&d.url(h.absUrl(),!0);d.onUrlChange(function(a){h.absUrl()!=a&&(c.$broadcast("$locationChangeStart",a,h.absUrl()).defaultPrevented?d.url(h.absUrl()):(c.$evalAsync(function(){var b=h.absUrl();h.$$parse(a);f(b)}),c.$$phase||c.$digest()))});var m=0;c.$watch(function(){var a=d.url(),b=h.$$replace;m&&a==h.absUrl()||(m++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",h.absUrl(),a).defaultPrevented?h.$$parse(a):
|
||||
(d.url(h.absUrl(),b),f(a))}));h.$$replace=!1;return m});return h}]}function td(){var b=!0,a=this;this.debugEnabled=function(a){return v(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||y;a=!1;try{a=!!e.apply}catch(l){}return a?function(){var a=[];q(arguments,
|
||||
function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function ca(b,a){if("constructor"===b)throw xa("isecfld",a);return b}function Va(b,a){if(b){if(b.constructor===b)throw xa("isecfn",a);if(b.document&&b.location&&b.alert&&b.setInterval)throw xa("isecwindow",a);if(b.children&&(b.nodeName||b.on&&b.find))throw xa("isecdom",
|
||||
a);}return b}function hb(b,a,c,d,e){e=e||{};a=a.split(".");for(var g,f=0;1<a.length;f++){g=ca(a.shift(),d);var h=b[g];h||(h={},b[g]=h);b=h;b.then&&e.unwrapPromises&&(qa(d),"$$v"in b||function(a){a.then(function(b){a.$$v=b})}(b),b.$$v===r&&(b.$$v={}),b=b.$$v)}g=ca(a.shift(),d);return b[g]=c}function uc(b,a,c,d,e,g,f){ca(b,g);ca(a,g);ca(c,g);ca(d,g);ca(e,g);return f.unwrapPromises?function(f,l){var k=l&&l.hasOwnProperty(b)?l:f,m;if(null==k)return k;(k=k[b])&&k.then&&(qa(g),"$$v"in k||(m=k,m.$$v=r,m.then(function(a){m.$$v=
|
||||
a})),k=k.$$v);if(null==k)return a?r:k;(k=k[a])&&k.then&&(qa(g),"$$v"in k||(m=k,m.$$v=r,m.then(function(a){m.$$v=a})),k=k.$$v);if(null==k)return c?r:k;(k=k[c])&&k.then&&(qa(g),"$$v"in k||(m=k,m.$$v=r,m.then(function(a){m.$$v=a})),k=k.$$v);if(null==k)return d?r:k;(k=k[d])&&k.then&&(qa(g),"$$v"in k||(m=k,m.$$v=r,m.then(function(a){m.$$v=a})),k=k.$$v);if(null==k)return e?r:k;(k=k[e])&&k.then&&(qa(g),"$$v"in k||(m=k,m.$$v=r,m.then(function(a){m.$$v=a})),k=k.$$v);return k}:function(g,f){var k=f&&f.hasOwnProperty(b)?
|
||||
f:g;if(null==k)return k;k=k[b];if(null==k)return a?r:k;k=k[a];if(null==k)return c?r:k;k=k[c];if(null==k)return d?r:k;k=k[d];return null==k?e?r:k:k=k[e]}}function ud(b,a){ca(b,a);return function(a,d){return null==a?r:(d&&d.hasOwnProperty(b)?d:a)[b]}}function vd(b,a,c){ca(b,c);ca(a,c);return function(c,e){if(null==c)return r;c=(e&&e.hasOwnProperty(b)?e:c)[b];return null==c?r:c[a]}}function vc(b,a,c){if(Ib.hasOwnProperty(b))return Ib[b];var d=b.split("."),e=d.length,g;if(a.unwrapPromises||1!==e)if(a.unwrapPromises||
|
||||
2!==e)if(a.csp)g=6>e?uc(d[0],d[1],d[2],d[3],d[4],c,a):function(b,g){var f=0,h;do h=uc(d[f++],d[f++],d[f++],d[f++],d[f++],c,a)(b,g),g=r,b=h;while(f<e);return h};else{var f="var p;\n";q(d,function(b,d){ca(b,c);f+="if(s == null) return undefined;\ns="+(d?"s":'((k&&k.hasOwnProperty("'+b+'"))?k:s)')+'["'+b+'"];\n'+(a.unwrapPromises?'if (s && s.then) {\n pw("'+c.replace(/(["\r\n])/g,"\\$1")+'");\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n':"")});
|
||||
var f=f+"return s;",h=new Function("s","k","pw",f);h.toString=Z(f);g=a.unwrapPromises?function(a,b){return h(a,b,qa)}:h}else g=vd(d[0],d[1],c);else g=ud(d[0],c);"hasOwnProperty"!==b&&(Ib[b]=g);return g}function wd(){var b={},a={csp:!1,unwrapPromises:!1,logPromiseWarnings:!0};this.unwrapPromises=function(b){return v(b)?(a.unwrapPromises=!!b,this):a.unwrapPromises};this.logPromiseWarnings=function(b){return v(b)?(a.logPromiseWarnings=b,this):a.logPromiseWarnings};this.$get=["$filter","$sniffer","$log",
|
||||
function(c,d,e){a.csp=d.csp;qa=function(b){a.logPromiseWarnings&&!wc.hasOwnProperty(b)&&(wc[b]=!0,e.warn("[$parse] Promise found in the expression `"+b+"`. Automatic unwrapping of promises in Angular expressions is deprecated."))};return function(d){var e;switch(typeof d){case "string":if(b.hasOwnProperty(d))return b[d];e=new Jb(a);e=(new Wa(e,c,a)).parse(d,!1);"hasOwnProperty"!==d&&(b[d]=e);return e;case "function":return d;default:return y}}}]}function xd(){this.$get=["$rootScope","$exceptionHandler",
|
||||
function(b,a){return yd(function(a){b.$evalAsync(a)},a)}]}function yd(b,a){function c(a){return a}function d(a){return f(a)}var e=function(){var h=[],l,k;return k={resolve:function(a){if(h){var c=h;h=r;l=g(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<d;b++)a=c[b],l.then(a[0],a[1],a[2])})}},reject:function(a){k.resolve(f(a))},notify:function(a){if(h){var c=h;h.length&&b(function(){for(var b,d=0,e=c.length;d<e;d++)b=c[d],b[2](a)})}},promise:{then:function(b,g,f){var k=e(),w=function(d){try{k.resolve((J(b)?
|
||||
b:c)(d))}catch(e){k.reject(e),a(e)}},A=function(b){try{k.resolve((J(g)?g:d)(b))}catch(c){k.reject(c),a(c)}},F=function(b){try{k.notify((J(f)?f:c)(b))}catch(d){a(d)}};h?h.push([w,A,F]):l.then(w,A,F);return k.promise},"catch":function(a){return this.then(null,a)},"finally":function(a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,g){var f=null;try{f=(a||c)()}catch(h){return b(h,!1)}return f&&J(f.then)?f.then(function(){return b(e,g)},function(a){return b(a,!1)}):
|
||||
b(e,g)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},g=function(a){return a&&J(a.then)?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},f=function(c){return{then:function(g,f){var m=e();b(function(){try{m.resolve((J(f)?f:d)(c))}catch(b){m.reject(b),a(b)}});return m.promise}}};return{defer:e,reject:f,when:function(h,l,k,m){var n=e(),p,t=function(b){try{return(J(l)?l:c)(b)}catch(d){return a(d),f(d)}},w=function(b){try{return(J(k)?k:d)(b)}catch(c){return a(c),
|
||||
f(c)}},A=function(b){try{return(J(m)?m:c)(b)}catch(d){a(d)}};b(function(){g(h).then(function(a){p||(p=!0,n.resolve(g(a).then(t,w,A)))},function(a){p||(p=!0,n.resolve(w(a)))},function(a){p||n.notify(A(a))})});return n.promise},all:function(a){var b=e(),c=0,d=I(a)?[]:{};q(a,function(a,e){c++;g(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise}}}function zd(){var b=10,a=s("$rootScope"),c=null;this.digestTtl=
|
||||
function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(d,e,g,f){function h(){this.$id=Xa();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$destroyed=!1;this.$$asyncQueue=[];this.$$postDigestQueue=[];this.$$listeners={};this.$$isolateBindings={}}function l(b){if(n.$$phase)throw a("inprog",n.$$phase);n.$$phase=b}function k(a,b){var c=
|
||||
g(a);Oa(c,b);return c}function m(){}h.prototype={constructor:h,$new:function(a){a?(a=new h,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,a.$$postDigestQueue=this.$$postDigestQueue):(a=function(){},a.prototype=this,a=new a,a.$id=Xa());a["this"]=a;a.$$listeners={};a.$parent=this;a.$$watchers=a.$$nextSibling=a.$$childHead=a.$$childTail=null;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,
|
||||
b,d){var e=k(a,"watch"),g=this.$$watchers,f={fn:b,last:m,get:e,exp:a,eq:!!d};c=null;if(!J(b)){var h=k(b||y,"listener");f.fn=function(a,b,c){h(c)}}if("string"==typeof a&&e.constant){var l=f.fn;f.fn=function(a,b,c){l.call(this,a,b,c);Ka(g,f)}}g||(g=this.$$watchers=[]);g.unshift(f);return function(){Ka(g,f)}},$watchCollection:function(a,b){var c=this,d,e,f=0,h=g(a),l=[],k={},m=0;return this.$watch(function(){e=h(c);var a,b;if(V(e))if(ob(e))for(d!==l&&(d=l,m=d.length=0,f++),a=e.length,m!==a&&(f++,d.length=
|
||||
m=a),b=0;b<a;b++)d[b]!==e[b]&&(f++,d[b]=e[b]);else{d!==k&&(d=k={},m=0,f++);a=0;for(b in e)e.hasOwnProperty(b)&&(a++,d.hasOwnProperty(b)?d[b]!==e[b]&&(f++,d[b]=e[b]):(m++,d[b]=e[b],f++));if(m>a)for(b in f++,d)d.hasOwnProperty(b)&&!e.hasOwnProperty(b)&&(m--,delete d[b])}else d!==e&&(d=e,f++);return f},function(){b(e,d,c)})},$digest:function(){var d,f,g,h,k=this.$$asyncQueue,q=this.$$postDigestQueue,r,z,B=b,s,M=[],W,u,v;l("$digest");c=null;do{z=!1;for(s=this;k.length;){try{v=k.shift(),v.scope.$eval(v.expression)}catch(R){n.$$phase=
|
||||
null,e(R)}c=null}a:do{if(h=s.$$watchers)for(r=h.length;r--;)try{if(d=h[r])if((f=d.get(s))!==(g=d.last)&&!(d.eq?ta(f,g):"number"==typeof f&&"number"==typeof g&&isNaN(f)&&isNaN(g)))z=!0,c=d,d.last=d.eq?ea(f):f,d.fn(f,g===m?f:g,s),5>B&&(W=4-B,M[W]||(M[W]=[]),u=J(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):d.exp,u+="; newVal: "+oa(f)+"; oldVal: "+oa(g),M[W].push(u));else if(d===c){z=!1;break a}}catch(x){n.$$phase=null,e(x)}if(!(h=s.$$childHead||s!==this&&s.$$nextSibling))for(;s!==this&&!(h=s.$$nextSibling);)s=
|
||||
s.$parent}while(s=h);if(z&&!B--)throw n.$$phase=null,a("infdig",b,oa(M));}while(z||k.length);for(n.$$phase=null;q.length;)try{q.shift()()}catch(D){e(D)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this!==n&&(a.$$childHead==this&&(a.$$childHead=this.$$nextSibling),a.$$childTail==this&&(a.$$childTail=this.$$prevSibling),this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling),this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=
|
||||
this.$$prevSibling),this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null)}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a){n.$$phase||n.$$asyncQueue.length||f.defer(function(){n.$$asyncQueue.length&&n.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return l("$apply"),this.$eval(a)}catch(b){e(b)}finally{n.$$phase=null;try{n.$digest()}catch(c){throw e(c),
|
||||
c;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);return function(){c[$a(c,b)]=null}},$emit:function(a,b){var c=[],d,f=this,g=!1,h={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},l=[h].concat(ua.call(arguments,1)),k,m;do{d=f.$$listeners[a]||c;h.currentScope=f;k=0;for(m=d.length;k<m;k++)if(d[k])try{d[k].apply(null,l)}catch(n){e(n)}else d.splice(k,1),k--,m--;if(g)break;f=f.$parent}while(f);
|
||||
return h},$broadcast:function(a,b){var c=this,d=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(ua.call(arguments,1)),h,k;do{c=d;f.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,g)}catch(l){e(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}while(c=d);return f}};var n=new h;return n}]}function Ad(){var b=/^\s*(https?|ftp|mailto|tel|file):/,
|
||||
a=/^\s*(https?|ftp|file):|data:image\//;this.aHrefSanitizationWhitelist=function(a){return v(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return v(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,g;if(!L||8<=L)if(g=pa(c).href,""!==g&&!g.match(e))return"unsafe:"+g;return c}}}function Bd(b){if("self"===b)return b;if(E(b)){if(-1<b.indexOf("***"))throw ra("iwcard",b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*",
|
||||
"[^:/.?&;]*");return RegExp("^"+b+"$")}if(Za(b))return RegExp("^"+b.source+"$");throw ra("imatcher");}function xc(b){var a=[];v(b)&&q(b,function(b){a.push(Bd(b))});return a}function Cd(){this.SCE_CONTEXTS=da;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=xc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=xc(b));return a};this.$get=["$injector",function(c){function d(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=
|
||||
new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var e=function(a){throw ra("unsafe");};c.has("$sanitize")&&(e=c.get("$sanitize"));var g=d(),f={};f[da.HTML]=d(g);f[da.CSS]=d(g);f[da.URL]=d(g);f[da.JS]=d(g);f[da.RESOURCE_URL]=d(f[da.URL]);return{trustAs:function(a,b){var c=f.hasOwnProperty(a)?f[a]:null;if(!c)throw ra("icontext",a,b);if(null===b||b===r||""===b)return b;if("string"!==typeof b)throw ra("itype",
|
||||
a);return new c(b)},getTrusted:function(c,d){if(null===d||d===r||""===d)return d;var g=f.hasOwnProperty(c)?f[c]:null;if(g&&d instanceof g)return d.$$unwrapTrustedValue();if(c===da.RESOURCE_URL){var g=pa(d.toString()),m,n,p=!1;m=0;for(n=b.length;m<n;m++)if("self"===b[m]?Eb(g):b[m].exec(g.href)){p=!0;break}if(p)for(m=0,n=a.length;m<n;m++)if("self"===a[m]?Eb(g):a[m].exec(g.href)){p=!1;break}if(p)return d;throw ra("insecurl",d.toString());}if(c===da.HTML)return e(d);throw ra("unsafe");},valueOf:function(a){return a instanceof
|
||||
g?a.$$unwrapTrustedValue():a}}}]}function Dd(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sniffer","$sceDelegate",function(a,c,d){if(b&&c.msie&&8>c.msieDocumentMode)throw ra("iequirks");var e=ea(da);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b},e.valueOf=za);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,
|
||||
d(a,c))}};var g=e.parseAs,f=e.getTrusted,h=e.trustAs;q(da,function(a,b){var c=C(b);e[Pa("parse_as_"+c)]=function(b){return g(a,b)};e[Pa("get_trusted_"+c)]=function(b){return f(a,b)};e[Pa("trust_as_"+c)]=function(b){return h(a,b)}});return e}]}function Ed(){this.$get=["$window","$document",function(b,a){var c={},d=P((/android (\d+)/.exec(C((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),g=a[0]||{},f=g.documentMode,h,l=/^(Moz|webkit|O|ms)(?=[A-Z])/,k=g.body&&g.body.style,
|
||||
m=!1,n=!1;if(k){for(var p in k)if(m=l.exec(p)){h=m[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in k&&"webkit");m=!!("transition"in k||h+"Transition"in k);n=!!("animation"in k||h+"Animation"in k);!d||m&&n||(m=E(g.body.style.webkitTransition),n=E(g.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!f||7<f),hasEvent:function(a){if("input"==a&&9==L)return!1;if(D(c[a])){var b=g.createElement("div");c[a]="on"+
|
||||
a in b}return c[a]},csp:Sb(),vendorPrefix:h,transitions:m,animations:n,android:d,msie:L,msieDocumentMode:f}}]}function Fd(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,h,l){var k=c.defer(),m=k.promise,n=v(l)&&!l;h=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}finally{delete g[m.$$timeoutId]}n||b.$apply()},h);m.$$timeoutId=h;g[h]=k;return m}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),
|
||||
delete g[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function pa(b,a){var c=b;L&&(X.setAttribute("href",c),c=X.href);X.setAttribute("href",c);return{href:X.href,protocol:X.protocol?X.protocol.replace(/:$/,""):"",host:X.host,search:X.search?X.search.replace(/^\?/,""):"",hash:X.hash?X.hash.replace(/^#/,""):"",hostname:X.hostname,port:X.port,pathname:"/"===X.pathname.charAt(0)?X.pathname:"/"+X.pathname}}function Eb(b){b=E(b)?pa(b):b;return b.protocol===yc.protocol&&b.host===yc.host}
|
||||
function Gd(){this.$get=Z(Y)}function zc(b){function a(d,e){if(V(d)){var g={};q(d,function(b,c){g[c]=a(c,b)});return g}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Ac);a("date",Bc);a("filter",Hd);a("json",Id);a("limitTo",Jd);a("lowercase",Kd);a("number",Cc);a("orderBy",Dc);a("uppercase",Ld)}function Hd(){return function(b,a,c){if(!I(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;b<e.length;b++)if(!e[b](a))return!1;
|
||||
return!0};"function"!==d&&(c="boolean"===d&&c?function(a,b){return Na.equals(a,b)}:function(a,b){b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var g=function(a,b){if("string"==typeof b&&"!"===b.charAt(0))return!g(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,b);case "object":switch(typeof b){case "object":return c(a,b);default:for(var d in a)if("$"!==d.charAt(0)&&g(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(g(a[d],b))return!0;
|
||||
return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var f in a)"$"==f?function(){if(a[f]){var b=f;e.push(function(c){return g(c,a[b])})}}():function(){if("undefined"!=typeof a[f]){var b=f;e.push(function(c){return g(tb(c,b),a[b])})}}();break;case "function":e.push(a);break;default:return b}for(var d=[],h=0;h<b.length;h++){var l=b[h];e.check(l)&&d.push(l)}return d}}function Ac(b){var a=b.NUMBER_FORMATS;return function(b,d){D(d)&&(d=a.CURRENCY_SYM);
|
||||
return Ec(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Cc(b){var a=b.NUMBER_FORMATS;return function(b,d){return Ec(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Ec(b,a,c,d,e){if(isNaN(b)||!isFinite(b))return"";var g=0>b;b=Math.abs(b);var f=b+"",h="",l=[],k=!1;if(-1!==f.indexOf("e")){var m=f.match(/([\d\.]+)e(-?)(\d+)/);m&&"-"==m[2]&&m[3]>e+1?f="0":(h=f,k=!0)}if(k)0<e&&(-1<b&&1>b)&&(h=b.toFixed(e));else{f=(f.split(Fc)[1]||"").length;D(e)&&(e=Math.min(Math.max(a.minFrac,
|
||||
f),a.maxFrac));f=Math.pow(10,e);b=Math.round(b*f)/f;b=(""+b).split(Fc);f=b[0];b=b[1]||"";var m=0,n=a.lgSize,p=a.gSize;if(f.length>=n+p)for(m=f.length-n,k=0;k<m;k++)0===(m-k)%p&&0!==k&&(h+=c),h+=f.charAt(k);for(k=m;k<f.length;k++)0===(f.length-k)%n&&0!==k&&(h+=c),h+=f.charAt(k);for(;b.length<e;)b+="0";e&&"0"!==e&&(h+=d+b.substr(0,e))}l.push(g?a.negPre:a.posPre);l.push(h);l.push(g?a.negSuf:a.posSuf);return l.join("")}function Kb(b,a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=
|
||||
b.substr(b.length-a));return d+b}function U(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Kb(e,a,d)}}function ib(b,a){return function(c,d){var e=c["get"+b](),g=Ga(a?"SHORT"+b:b);return d[g][e]}}function Bc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var g=0,f=0,h=b[8]?a.setUTCFullYear:a.setFullYear,l=b[8]?a.setUTCHours:a.setHours;b[9]&&(g=P(b[9]+b[10]),f=P(b[9]+b[11]));h.call(a,P(b[1]),P(b[2])-1,P(b[3]));g=P(b[4]||0)-g;f=P(b[5]||0)-f;h=
|
||||
P(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));l.call(a,g,f,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var g="",f=[],h,l;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;E(c)&&(c=Md.test(c)?P(c):a(c));pb(c)&&(c=new Date(c));if(!Ja(c))return c;for(;e;)(l=Nd.exec(e))?(f=f.concat(ua.call(l,1)),e=f.pop()):(f.push(e),e=null);q(f,function(a){h=Od[a];g+=h?h(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,
|
||||
"").replace(/''/g,"'")});return g}}function Id(){return function(b){return oa(b,!0)}}function Jd(){return function(b,a){if(!I(b)&&!E(b))return b;a=P(a);if(E(b))return a?0<=a?b.slice(0,a):b.slice(a,b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0<a?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Dc(b){return function(a,c,d){function e(a,b){return Ma(b)?function(b,c){return a(c,b)}:a}if(!I(a)||!c)return a;c=I(c)?c:[c];c=Pc(c,function(a){var c=
|
||||
!1,d=a||za;if(E(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);d=b(a)}return e(function(a,b){var c;c=d(a);var e=d(b),g=typeof c,f=typeof e;g==f?("string"==g&&(c=c.toLowerCase(),e=e.toLowerCase()),c=c===e?0:c<e?-1:1):c=g<f?-1:1;return c},c)});for(var g=[],f=0;f<a.length;f++)g.push(a[f]);return g.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function sa(b){J(b)&&(b={link:b});b.restrict=b.restrict||"AC";return Z(b)}function Gc(b,
|
||||
a){function c(a,c){c=c?"-"+ab(c,"-"):"";b.removeClass((a?jb:kb)+c).addClass((a?kb:jb)+c)}var d=this,e=b.parent().controller("form")||lb,g=0,f=d.$error={},h=[];d.$name=a.name||a.ngForm;d.$dirty=!1;d.$pristine=!0;d.$valid=!0;d.$invalid=!1;e.$addControl(d);b.addClass(Ha);c(!0);d.$addControl=function(a){wa(a.$name,"input");h.push(a);a.$name&&(d[a.$name]=a)};d.$removeControl=function(a){a.$name&&d[a.$name]===a&&delete d[a.$name];q(f,function(b,c){d.$setValidity(c,!0,a)});Ka(h,a)};d.$setValidity=function(a,
|
||||
b,h){var n=f[a];if(b)n&&(Ka(n,h),n.length||(g--,g||(c(b),d.$valid=!0,d.$invalid=!1),f[a]=!1,c(!0,a),e.$setValidity(a,!0,d)));else{g||c(b);if(n){if(-1!=$a(n,h))return}else f[a]=n=[],g++,c(!1,a),e.$setValidity(a,!1,d);n.push(h);d.$valid=!1;d.$invalid=!0}};d.$setDirty=function(){b.removeClass(Ha).addClass(mb);d.$dirty=!0;d.$pristine=!1;e.$setDirty()};d.$setPristine=function(){b.removeClass(mb).addClass(Ha);d.$dirty=!1;d.$pristine=!0;q(h,function(a){a.$setPristine()})}}function nb(b,a,c,d,e,g){if(!e.android){var f=
|
||||
!1;a.on("compositionstart",function(a){f=!0});a.on("compositionend",function(){f=!1})}var h=function(){if(!f){var e=a.val();Ma(c.ngTrim||"T")&&(e=aa(e));d.$viewValue!==e&&b.$apply(function(){d.$setViewValue(e)})}};if(e.hasEvent("input"))a.on("input",h);else{var l,k=function(){l||(l=g.defer(function(){h();l=null}))};a.on("keydown",function(a){a=a.keyCode;91===a||(15<a&&19>a||37<=a&&40>=a)||k()});if(e.hasEvent("paste"))a.on("paste cut",k)}a.on("change",h);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?
|
||||
"":d.$viewValue)};var m=c.ngPattern,n=function(a,b){if(d.$isEmpty(b)||a.test(b))return d.$setValidity("pattern",!0),b;d.$setValidity("pattern",!1);return r};m&&((e=m.match(/^\/(.*)\/([gim]*)$/))?(m=RegExp(e[1],e[2]),e=function(a){return n(m,a)}):e=function(c){var d=b.$eval(m);if(!d||!d.test)throw s("ngPattern")("noregexp",m,d,fa(a));return n(d,c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var p=P(c.ngMinlength);e=function(a){if(!d.$isEmpty(a)&&a.length<p)return d.$setValidity("minlength",
|
||||
!1),r;d.$setValidity("minlength",!0);return a};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var t=P(c.ngMaxlength);e=function(a){if(!d.$isEmpty(a)&&a.length>t)return d.$setValidity("maxlength",!1),r;d.$setValidity("maxlength",!0);return a};d.$parsers.push(e);d.$formatters.push(e)}}function Lb(b,a){b="ngClass"+b;return function(){return{restrict:"AC",link:function(c,d,e){function g(b){if(!0===a||c.$index%2===a){var d=f(b||"");h?ta(b,h)||e.$updateClass(d,f(h)):e.$addClass(d)}h=ea(b)}function f(a){if(I(a))return a.join(" ");
|
||||
if(V(a)){var b=[];q(a,function(a,c){a&&b.push(c)});return b.join(" ")}return a}var h;c.$watch(e[b],g,!0);e.$observe("class",function(a){g(c.$eval(e[b]))});"ngClass"!==b&&c.$watch("$index",function(d,g){var h=d&1;if(h!==g&1){var n=f(c.$eval(e[b]));h===a?e.$addClass(n):e.$removeClass(n)}})}}}}var C=function(b){return E(b)?b.toLowerCase():b},Ga=function(b){return E(b)?b.toUpperCase():b},L,u,Aa,ua=[].slice,Pd=[].push,Ya=Object.prototype.toString,La=s("ng"),Na=Y.angular||(Y.angular={}),Ta,Ea,ia=["0","0",
|
||||
"0"];L=P((/msie (\d+)/.exec(C(navigator.userAgent))||[])[1]);isNaN(L)&&(L=P((/trident\/.*; rv:(\d+)/.exec(C(navigator.userAgent))||[])[1]));y.$inject=[];za.$inject=[];var aa=function(){return String.prototype.trim?function(b){return E(b)?b.trim():b}:function(b){return E(b)?b.replace(/^\s\s*/,"").replace(/\s\s*$/,""):b}}();Ea=9>L?function(b){b=b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?Ga(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};
|
||||
var Sc=/[A-Z]/g,Qd={full:"1.2.6",major:1,minor:2,dot:6,codeName:"taco-salsafication"},Qa=N.cache={},bb=N.expando="ng-"+(new Date).getTime(),Wc=1,Hc=Y.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},zb=Y.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},Uc=/([\:\-\_]+(.))/g,Vc=/^moz([A-Z])/,wb=s("jqLite"),Da=N.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=
|
||||
!1;"complete"===O.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),N(Y).on("load",a))},toString:function(){var b=[];q(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?u(this[b]):u(this[this.length+b])},length:0,push:Pd,sort:[].sort,splice:[].splice},db={};q("multiple selected checked disabled readOnly required open".split(" "),function(b){db[C(b)]=b});var ec={};q("input select option textarea button form details".split(" "),function(b){ec[Ga(b)]=!0});q({data:ac,
|
||||
inheritedData:cb,scope:function(b){return u(b).data("$scope")||cb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return u(b).data("$isolateScope")||u(b).data("$isolateScopeNoTemplate")},controller:bc,injector:function(b){return cb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ab,css:function(b,a,c){a=Pa(a);if(v(c))b.style[a]=c;else{var d;8>=L&&(d=b.currentStyle&&b.currentStyle[a],""===d&&(d="auto"));d=d||b.style[a];8>=L&&(d=""===d?r:d);return d}},attr:function(b,
|
||||
a,c){var d=C(a);if(db[d])if(v(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||y).specified?d:r;else if(v(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?r:b},prop:function(b,a,c){if(v(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];if(D(d))return e?b[e]:"";b[e]=d}var a=[];9>L?(a[1]="innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,
|
||||
a){if(D(a)){if("SELECT"===Ea(b)&&b.multiple){var c=[];q(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(D(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)Ba(d[c]);b.innerHTML=a},empty:cc},function(b,a){N.prototype[a]=function(a,d){var e,g;if(b!==cc&&(2==b.length&&b!==Ab&&b!==bc?a:d)===r){if(V(a)){for(e=0;e<this.length;e++)if(b===ac)b(this[e],a);else for(g in a)b(this[e],g,a[g]);return this}e=b.$dv;
|
||||
g=e===r?Math.min(this.length,1):this.length;for(var f=0;f<g;f++){var h=b(this[f],a,d);e=e?e+h:h}return e}for(e=0;e<this.length;e++)b(this[e],a,d);return this}});q({removeData:Zb,dealoc:Ba,on:function a(c,d,e,g){if(v(g))throw wb("onargs");var f=ja(c,"events"),h=ja(c,"handle");f||ja(c,"events",f={});h||ja(c,"handle",h=Xc(c,f));q(d.split(" "),function(d){var g=f[d];if(!g){if("mouseenter"==d||"mouseleave"==d){var m=O.body.contains||O.body.compareDocumentPosition?function(a,c){var d=9===a.nodeType?a.documentElement:
|
||||
a,e=c&&c.parentNode;return a===e||!!(e&&1===e.nodeType&&(d.contains?d.contains(e):a.compareDocumentPosition&&a.compareDocumentPosition(e)&16))}:function(a,c){if(c)for(;c=c.parentNode;)if(c===a)return!0;return!1};f[d]=[];a(c,{mouseleave:"mouseout",mouseenter:"mouseover"}[d],function(a){var c=a.relatedTarget;c&&(c===this||m(this,c))||h(a,d)})}else Hc(c,d,h),f[d]=[];g=f[d]}g.push(e)})},off:$b,one:function(a,c,d){a=u(a);a.on(c,function g(){a.off(c,d);a.off(c,g)});a.on(c,d)},replaceWith:function(a,c){var d,
|
||||
e=a.parentNode;Ba(a);q(new N(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];q(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.childNodes||[]},append:function(a,c){q(new N(c),function(c){1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)})},prepend:function(a,c){if(1===a.nodeType){var d=a.firstChild;q(new N(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=u(c)[0];var d=a.parentNode;d&&
|
||||
d.replaceChild(c,a);c.appendChild(a)},remove:function(a){Ba(a);var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;q(new N(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:Cb,removeClass:Bb,toggleClass:function(a,c,d){D(d)&&(d=!Ab(a,c));(d?Cb:Bb)(a,c)},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;null!=a&&1!==a.nodeType;)a=a.nextSibling;return a},
|
||||
find:function(a,c){return a.getElementsByTagName?a.getElementsByTagName(c):[]},clone:yb,triggerHandler:function(a,c,d){c=(ja(a,"events")||{})[c];d=d||[];var e=[{preventDefault:y,stopPropagation:y}];q(c,function(c){c.apply(a,e.concat(d))})}},function(a,c){N.prototype[c]=function(c,e,g){for(var f,h=0;h<this.length;h++)D(f)?(f=a(this[h],c,e,g),v(f)&&(f=u(f))):xb(f,a(this[h],c,e,g));return v(f)?f:this};N.prototype.bind=N.prototype.on;N.prototype.unbind=N.prototype.off});Ra.prototype={put:function(a,c){this[Ca(a)]=
|
||||
c},get:function(a){return this[Ca(a)]},remove:function(a){var c=this[a=Ca(a)];delete this[a];return c}};var Zc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,$c=/,/,ad=/^\s*(_?)(\S+?)\1\s*$/,Yc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Sa=s("$injector"),Rd=s("$animate"),Sd=["$provide",function(a){this.$$selectors={};this.register=function(c,d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw Rd("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=
|
||||
a instanceof RegExp?a:null);return this.$$classNameFilter};this.$get=["$timeout",function(a){return{enter:function(d,e,g,f){g?g.after(d):(e&&e[0]||(e=g.parent()),e.append(d));f&&a(f,0,!1)},leave:function(d,e){d.remove();e&&a(e,0,!1)},move:function(a,c,g,f){this.enter(a,c,g,f)},addClass:function(d,e,g){e=E(e)?e:I(e)?e.join(" "):"";q(d,function(a){Cb(a,e)});g&&a(g,0,!1)},removeClass:function(d,e,g){e=E(e)?e:I(e)?e.join(" "):"";q(d,function(a){Bb(a,e)});g&&a(g,0,!1)},enabled:y}}]}],ha=s("$compile");
|
||||
hc.$inject=["$provide","$$sanitizeUriProvider"];var gd=/^(x[\:\-_]|data[\:\-_])/i,nd=Y.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw s("$httpBackend")("noxhr");},nc=s("$interpolate"),Td=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,rd={http:80,https:443,ftp:21},Gb=s("$location");sc.prototype=Hb.prototype=rc.prototype={$$html5:!1,$$replace:!1,absUrl:gb("$$absUrl"),
|
||||
url:function(a,c){if(D(a))return this.$$url;var d=Td.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));(d[2]||d[1])&&this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:gb("$$protocol"),host:gb("$$host"),port:gb("$$port"),path:tc("$$path",function(a){return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(E(a))this.$$search=Vb(a);else if(V(a))this.$$search=a;else throw Gb("isrcharg");break;default:D(c)||null===c?delete this.$$search[a]:
|
||||
this.$$search[a]=c}this.$$compose();return this},hash:tc("$$hash",za),replace:function(){this.$$replace=!0;return this}};var xa=s("$parse"),wc={},qa,Ia={"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:y,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return v(d)?v(e)?d+e:d:v(e)?e:r},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(v(d)?d:0)-(v(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,
|
||||
e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":y,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,
|
||||
c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Ud={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Jb=function(a){this.options=a};Jb.prototype={constructor:Jb,lex:function(a){this.text=a;this.index=0;this.ch=r;this.lastCh=":";this.tokens=[];var c;for(a=[];this.index<this.text.length;){this.ch=this.text.charAt(this.index);if(this.is("\"'"))this.readString(this.ch);else if(this.isNumber(this.ch)||
|
||||
this.is(".")&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(this.ch))this.readIdent(),this.was("{,")&&("{"===a[0]&&(c=this.tokens[this.tokens.length-1]))&&(c.json=-1===c.text.indexOf("."));else if(this.is("(){}[].,;:?"))this.tokens.push({index:this.index,text:this.ch,json:this.was(":[,")&&this.is("{[")||this.is("}]:,")}),this.is("{[")&&a.unshift(this.ch),this.is("}]")&&a.shift(),this.index++;else if(this.isWhitespace(this.ch)){this.index++;continue}else{var d=this.ch+this.peek(),
|
||||
e=d+this.peek(2),g=Ia[this.ch],f=Ia[d],h=Ia[e];h?(this.tokens.push({index:this.index,text:e,fn:h}),this.index+=3):f?(this.tokens.push({index:this.index,text:d,fn:f}),this.index+=2):g?(this.tokens.push({index:this.index,text:this.ch,fn:g,json:this.was("[,:")&&this.is("+-")}),this.index+=1):this.throwError("Unexpected next character ",this.index,this.index+1)}this.lastCh=this.ch}return this.tokens},is:function(a){return-1!==a.indexOf(this.ch)},was:function(a){return-1!==a.indexOf(this.lastCh)},peek:function(a){a=
|
||||
a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=v(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw xa("lexerr",a,c,this.text);
|
||||
},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=C(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}a*=1;this.tokens.push({index:c,text:a,json:!0,fn:function(){return a}})},
|
||||
readIdent:function(){for(var a=this,c="",d=this.index,e,g,f,h;this.index<this.text.length;){h=this.text.charAt(this.index);if("."===h||this.isIdent(h)||this.isNumber(h))"."===h&&(e=this.index),c+=h;else break;this.index++}if(e)for(g=this.index;g<this.text.length;){h=this.text.charAt(g);if("("===h){f=c.substr(e-d+1);c=c.substr(0,e-d);this.index=g;break}if(this.isWhitespace(h))g++;else break}d={index:d,text:c};if(Ia.hasOwnProperty(c))d.fn=Ia[c],d.json=Ia[c];else{var l=vc(c,this.options,this.text);d.fn=
|
||||
x(function(a,c){return l(a,c)},{assign:function(d,e){return hb(d,c,e,a.text,a.options)}})}this.tokens.push(d);f&&(this.tokens.push({index:e,text:".",json:!1}),this.tokens.push({index:e+1,text:f,json:!1}))},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,g=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),e=e+f;if(g)"u"===f?(f=this.text.substring(this.index+1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=
|
||||
4,d+=String.fromCharCode(parseInt(f,16))):d=(g=Ud[f])?d+g:d+f,g=!1;else if("\\"===f)g=!0;else{if(f===a){this.index++;this.tokens.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}d+=f}this.index++}this.throwError("Unterminated quote",c)}};var Wa=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};Wa.ZERO=function(){return 0};Wa.prototype={constructor:Wa,parse:function(a,c){this.text=a;this.json=c;this.tokens=this.lexer.lex(a);c&&(this.assignment=this.logicalOR,this.functionCall=
|
||||
this.fieldAccess=this.objectIndex=this.filterChain=function(){this.throwError("is not valid json",{text:a,index:0})});var d=c?this.primary():this.statements();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);d.literal=!!d.literal;d.constant=!!d.constant;return d},primary:function(){var a;if(this.expect("("))a=this.filterChain(),this.consume(")");else if(this.expect("["))a=this.arrayDeclaration();else if(this.expect("{"))a=this.object();else{var c=this.expect();(a=c.fn)||
|
||||
this.throwError("not a primary expression",c);c.json&&(a.constant=!0,a.literal=!0)}for(var d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,d),d=null):"["===c.text?(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw xa("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw xa("ueoe",this.text);return this.tokens[0]},peek:function(a,
|
||||
c,d,e){if(0<this.tokens.length){var g=this.tokens[0],f=g.text;if(f===a||f===c||f===d||f===e||!(a||c||d||e))return g}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.json&&!a.json&&this.throwError("is not valid json",a),this.tokens.shift(),a):!1},consume:function(a){this.expect(a)||this.throwError("is unexpected, expecting ["+a+"]",this.peek())},unaryFn:function(a,c){return x(function(d,e){return a(d,e,c)},{constant:c.constant})},ternaryFn:function(a,c,d){return x(function(e,g){return a(e,
|
||||
g)?c(e,g):d(e,g)},{constant:a.constant&&c.constant&&d.constant})},binaryFn:function(a,c,d){return x(function(e,g){return c(e,g,a,d)},{constant:a.constant&&d.constant})},statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,d){for(var e,g=0;g<a.length;g++){var f=a[g];f&&(e=f(c,d))}return e}},filterChain:function(){for(var a=this.expression(),c;;)if(c=this.expect("|"))a=this.binaryFn(a,
|
||||
c.fn,this.filter());else return a},filter:function(){for(var a=this.expect(),c=this.$filter(a.text),d=[];;)if(a=this.expect(":"))d.push(this.expression());else{var e=function(a,e,h){h=[h];for(var l=0;l<d.length;l++)h.push(d[l](a,e));return c.apply(a,h)};return function(){return e}}},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",
|
||||
d),c=this.ternary(),function(d,g){return a.assign(d,c(d,g),g)}):a},ternary:function(){var a=this.logicalOR(),c,d;if(this.expect("?")){c=this.ternary();if(d=this.expect(":"))return this.ternaryFn(a,c,this.ternary());this.throwError("expected :",d)}else return a},logicalOR:function(){for(var a=this.logicalAND(),c;;)if(c=this.expect("||"))a=this.binaryFn(a,c.fn,this.logicalAND());else return a},logicalAND:function(){var a=this.equality(),c;if(c=this.expect("&&"))a=this.binaryFn(a,c.fn,this.logicalAND());
|
||||
return a},equality:function(){var a=this.relational(),c;if(c=this.expect("==","!=","===","!=="))a=this.binaryFn(a,c.fn,this.equality());return a},relational:function(){var a=this.additive(),c;if(c=this.expect("<",">","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,
|
||||
c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(Wa.ZERO,a.fn,this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=vc(d,this.options,this.text);return x(function(c,d,h){return e(h||a(c,d),d)},{assign:function(e,f,h){return hb(a(e,h),d,f,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return x(function(e,
|
||||
g){var f=a(e,g),h=d(e,g),l;if(!f)return r;(f=Va(f[h],c.text))&&(f.then&&c.options.unwrapPromises)&&(l=f,"$$v"in f||(l.$$v=r,l.then(function(a){l.$$v=a})),f=f.$$v);return f},{assign:function(e,g,f){var h=d(e,f);return Va(a(e,f),c.text)[h]=g}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this;return function(g,f){for(var h=[],l=c?c(g,f):g,k=0;k<d.length;k++)h.push(d[k](g,f));k=a(g,f,l)||y;Va(l,e.text);
|
||||
Va(k,e.text);h=k.apply?k.apply(l,h):k(h[0],h[1],h[2],h[3],h[4]);return Va(h,e.text)}},arrayDeclaration:function(){var a=[],c=!0;if("]"!==this.peekToken().text){do{var d=this.expression();a.push(d);d.constant||(c=!1)}while(this.expect(","))}this.consume("]");return x(function(c,d){for(var f=[],h=0;h<a.length;h++)f.push(a[h](c,d));return f},{literal:!0,constant:c})},object:function(){var a=[],c=!0;if("}"!==this.peekToken().text){do{var d=this.expect(),d=d.string||d.text;this.consume(":");var e=this.expression();
|
||||
a.push({key:d,value:e});e.constant||(c=!1)}while(this.expect(","))}this.consume("}");return x(function(c,d){for(var e={},l=0;l<a.length;l++){var k=a[l];e[k.key]=k.value(c,d)}return e},{literal:!0,constant:c})}};var Ib={},ra=s("$sce"),da={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},X=O.createElement("a"),yc=pa(Y.location.href,!0);zc.$inject=["$provide"];Ac.$inject=["$locale"];Cc.$inject=["$locale"];var Fc=".",Od={yyyy:U("FullYear",4),yy:U("FullYear",2,0,!0),y:U("FullYear",1),
|
||||
MMMM:ib("Month"),MMM:ib("Month",!0),MM:U("Month",2,1),M:U("Month",1,1),dd:U("Date",2),d:U("Date",1),HH:U("Hours",2),H:U("Hours",1),hh:U("Hours",2,-12),h:U("Hours",1,-12),mm:U("Minutes",2),m:U("Minutes",1),ss:U("Seconds",2),s:U("Seconds",1),sss:U("Milliseconds",3),EEEE:ib("Day"),EEE:ib("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Kb(Math[0<a?"floor":"ceil"](a/60),2)+Kb(Math.abs(a%60),2))}},Nd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,
|
||||
Md=/^\-?\d+$/;Bc.$inject=["$locale"];var Kd=Z(C),Ld=Z(Ga);Dc.$inject=["$parse"];var Vd=Z({restrict:"E",compile:function(a,c){8>=L&&(c.href||c.name||c.$set("href",""),a.append(O.createComment("IE fix")));if(!c.href&&!c.name)return function(a,c){c.on("click",function(a){c.attr("href")||a.preventDefault()})}}}),Mb={};q(db,function(a,c){if("multiple"!=a){var d=la("ng-"+c);Mb[d]=function(){return{priority:100,compile:function(){return function(a,g,f){a.$watch(f[d],function(a){f.$set(c,!!a)})}}}}}});q(["src",
|
||||
"srcset","href"],function(a){var c=la("ng-"+a);Mb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){c&&(g.$set(a,c),L&&e.prop(a,g[a]))})}}}});var lb={$addControl:y,$removeControl:y,$setValidity:y,$setDirty:y,$setPristine:y};Gc.$inject=["$element","$attrs","$scope"];var Ic=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:Gc,compile:function(){return{pre:function(a,e,g,f){if(!g.action){var h=function(a){a.preventDefault?a.preventDefault():
|
||||
a.returnValue=!1};Hc(e[0],"submit",h);e.on("$destroy",function(){c(function(){zb(e[0],"submit",h)},0,!1)})}var l=e.parent().controller("form"),k=g.name||g.ngForm;k&&hb(a,k,f,k);if(l)e.on("$destroy",function(){l.$removeControl(f);k&&hb(a,k,r,k);x(f,lb)})}}}}}]},Wd=Ic(),Xd=Ic(!0),Yd=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,Zd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/,$d=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Jc={text:nb,number:function(a,c,d,e,g,
|
||||
f){nb(a,c,d,e,g,f);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||$d.test(a))return e.$setValidity("number",!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return r});e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=parseFloat(d.min);if(!e.$isEmpty(a)&&a<c)return e.$setValidity("min",!1),r;e.$setValidity("min",!0);return a},e.$parsers.push(a),e.$formatters.push(a));d.max&&(a=function(a){var c=parseFloat(d.max);if(!e.$isEmpty(a)&&a>c)return e.$setValidity("max",
|
||||
!1),r;e.$setValidity("max",!0);return a},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){if(e.$isEmpty(a)||pb(a))return e.$setValidity("number",!0),a;e.$setValidity("number",!1);return r})},url:function(a,c,d,e,g,f){nb(a,c,d,e,g,f);a=function(a){if(e.$isEmpty(a)||Yd.test(a))return e.$setValidity("url",!0),a;e.$setValidity("url",!1);return r};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,f){nb(a,c,d,e,g,f);a=function(a){if(e.$isEmpty(a)||Zd.test(a))return e.$setValidity("email",
|
||||
!0),a;e.$setValidity("email",!1);return r};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){D(d.name)&&c.attr("name",Xa());c.on("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,f=d.ngFalseValue;E(g)||(g=!0);E(f)||(f=!1);c.on("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=
|
||||
e.$viewValue};e.$isEmpty=function(a){return a!==g};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:f})},hidden:y,button:y,submit:y,reset:y},Kc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,f){f&&(Jc[C(g.type)]||Jc.text)(d,e,g,f,c,a)}}}],kb="ng-valid",jb="ng-invalid",Ha="ng-pristine",mb="ng-dirty",ae=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,g){function f(a,c){c=c?"-"+ab(c,"-"):
|
||||
"";e.removeClass((a?jb:kb)+c).addClass((a?kb:jb)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var h=g(d.ngModel),l=h.assign;if(!l)throw s("ngModel")("nonassign",d.ngModel,fa(e));this.$render=y;this.$isEmpty=function(a){return D(a)||""===a||null===a||a!==a};var k=e.inheritedData("$formController")||lb,m=0,n=this.$error={};e.addClass(Ha);f(!0);this.$setValidity=
|
||||
function(a,c){n[a]!==!c&&(c?(n[a]&&m--,m||(f(!0),this.$valid=!0,this.$invalid=!1)):(f(!1),this.$invalid=!0,this.$valid=!1,m++),n[a]=!c,f(c,a),k.$setValidity(a,c,this))};this.$setPristine=function(){this.$dirty=!1;this.$pristine=!0;e.removeClass(mb).addClass(Ha)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,e.removeClass(Ha).addClass(mb),k.$setDirty());q(this.$parsers,function(a){d=a(d)});this.$modelValue!==d&&(this.$modelValue=d,l(a,d),q(this.$viewChangeListeners,
|
||||
function(a){try{a()}catch(d){c(d)}}))};var p=this;a.$watch(function(){var c=h(a);if(p.$modelValue!==c){var d=p.$formatters,e=d.length;for(p.$modelValue=c;e--;)c=d[e](c);p.$viewValue!==c&&(p.$viewValue=c,p.$render())}return c})}],be=function(){return{require:["ngModel","^?form"],controller:ae,link:function(a,c,d,e){var g=e[0],f=e[1]||lb;f.$addControl(g);a.$on("$destroy",function(){f.$removeControl(g)})}}},ce=Z({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),
|
||||
Lc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},de=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!D(a)){var c=[];a&&q(a.split(g),function(a){a&&
|
||||
c.push(aa(a))});return c}});e.$formatters.push(function(a){return I(a)?a.join(", "):r});e.$isEmpty=function(a){return!a||!a.length}}}},ee=/^(true|false|\d+)$/,fe=function(){return{priority:100,compile:function(a,c){return ee.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a)})}}}},ge=sa(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==r?"":a)})}),he=["$interpolate",
|
||||
function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],ie=["$sce","$parse",function(a,c){return function(d,e,g){e.addClass("ng-binding").data("$binding",g.ngBindHtml);var f=c(g.ngBindHtml);d.$watch(function(){return(f(d)||"").toString()},function(c){e.html(a.getTrustedHtml(f(d))||"")})}}],je=Lb("",!0),ke=Lb("Odd",0),le=Lb("Even",1),me=sa({compile:function(a,c){c.$set("ngCloak",r);a.removeClass("ng-cloak")}}),
|
||||
ne=[function(){return{scope:!0,controller:"@",priority:500}}],Mc={};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=la("ng-"+a);Mc[c]=["$parse",function(d){return{compile:function(e,g){var f=d(g[c]);return function(c,d,e){d.on(C(a),function(a){c.$apply(function(){f(c,{$event:a})})})}}}}]});var oe=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",
|
||||
$$tlb:!0,link:function(c,d,e,g,f){var h,l;c.$watch(e.ngIf,function(g){Ma(g)?l||(l=c.$new(),f(l,function(c){c[c.length++]=O.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)})):(l&&(l.$destroy(),l=null),h&&(a.leave(ub(h.clone)),h=null))})}}}],pe=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,g){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:Na.noop,compile:function(f,h){var l=h.ngInclude||h.src,k=h.onload||"",m=
|
||||
h.autoscroll;return function(f,h,q,r,A){var s=0,u,x,z=function(){u&&(u.$destroy(),u=null);x&&(e.leave(x),x=null)};f.$watch(g.parseAsResourceUrl(l),function(g){var l=function(){!v(m)||m&&!f.$eval(m)||d()},q=++s;g?(a.get(g,{cache:c}).success(function(a){if(q===s){var c=f.$new();r.template=a;a=A(c,function(a){z();e.enter(a,null,h,l)});u=c;x=a;u.$emit("$includeContentLoaded");f.$eval(k)}}).error(function(){q===s&&z()}),f.$emit("$includeContentRequested")):(z(),r.template=null)})}}}}],qe=["$compile",function(a){return{restrict:"ECA",
|
||||
priority:-400,require:"ngInclude",link:function(c,d,e,g){d.html(g.template);a(d.contents())(c)}}}],re=sa({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),se=sa({terminal:!0,priority:1E3}),te=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,f){var h=f.count,l=f.$attr.when&&g.attr(f.$attr.when),k=f.offset||0,m=e.$eval(l)||{},n={},p=c.startSymbol(),t=c.endSymbol(),r=/^when(Minus)?(.+)$/;q(f,function(a,c){r.test(c)&&(m[C(c.replace("when",
|
||||
"").replace("Minus","-"))]=g.attr(f.$attr[c]))});q(m,function(a,e){n[e]=c(a.replace(d,p+h+"-"+k+t))});e.$watch(function(){var c=parseFloat(e.$eval(h));if(isNaN(c))return"";c in m||(c=a.pluralCat(c-k));return n[c](e,g,!0)},function(a){g.text(a)})}}}],ue=["$parse","$animate",function(a,c){var d=s("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,link:function(e,g,f,h,l){var k=f.ngRepeat,m=k.match(/^\s*(.+)\s+in\s+([\r\n\s\S]*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),n,p,t,r,s,x,v={$id:Ca};
|
||||
if(!m)throw d("iexp",k);f=m[1];h=m[2];(m=m[4])?(n=a(m),p=function(a,c,d){x&&(v[x]=a);v[s]=c;v.$index=d;return n(e,v)}):(t=function(a,c){return Ca(c)},r=function(a){return a});m=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!m)throw d("iidexp",f);s=m[3]||m[1];x=m[2];var D={};e.$watchCollection(h,function(a){var f,h,m=g[0],n,v={},H,R,E,y,T,C,I=[];if(ob(a))T=a,n=p||t;else{n=p||r;T=[];for(E in a)a.hasOwnProperty(E)&&"$"!=E.charAt(0)&&T.push(E);T.sort()}H=T.length;h=I.length=T.length;for(f=
|
||||
0;f<h;f++)if(E=a===T?f:T[f],y=a[E],y=n(E,y,f),wa(y,"`track by` id"),D.hasOwnProperty(y))C=D[y],delete D[y],v[y]=C,I[f]=C;else{if(v.hasOwnProperty(y))throw q(I,function(a){a&&a.scope&&(D[a.id]=a)}),d("dupes",k,y);I[f]={id:y};v[y]=!1}for(E in D)D.hasOwnProperty(E)&&(C=D[E],f=ub(C.clone),c.leave(f),q(f,function(a){a.$$NG_REMOVED=!0}),C.scope.$destroy());f=0;for(h=T.length;f<h;f++){E=a===T?f:T[f];y=a[E];C=I[f];I[f-1]&&(m=I[f-1].clone[I[f-1].clone.length-1]);if(C.scope){R=C.scope;n=m;do n=n.nextSibling;
|
||||
while(n&&n.$$NG_REMOVED);C.clone[0]!=n&&c.move(ub(C.clone),null,u(m));m=C.clone[C.clone.length-1]}else R=e.$new();R[s]=y;x&&(R[x]=E);R.$index=f;R.$first=0===f;R.$last=f===H-1;R.$middle=!(R.$first||R.$last);R.$odd=!(R.$even=0===(f&1));C.scope||l(R,function(a){a[a.length++]=O.createComment(" end ngRepeat: "+k+" ");c.enter(a,null,u(m));m=a;C.scope=R;C.clone=a;v[C.id]=C})}D=v})}}}],ve=["$animate",function(a){return function(c,d,e){c.$watch(e.ngShow,function(c){a[Ma(c)?"removeClass":"addClass"](d,"ng-hide")})}}],
|
||||
we=["$animate",function(a){return function(c,d,e){c.$watch(e.ngHide,function(c){a[Ma(c)?"addClass":"removeClass"](d,"ng-hide")})}}],xe=sa(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&q(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),ye=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,g){var f,h,l=[];c.$watch(e.ngSwitch||e.on,function(d){for(var m=0,n=l.length;m<n;m++)l[m].$destroy(),a.leave(h[m]);h=[];
|
||||
l=[];if(f=g.cases["!"+d]||g.cases["?"])c.$eval(e.change),q(f,function(d){var e=c.$new();l.push(e);d.transclude(e,function(c){var e=d.element;h.push(c);a.enter(c,e.parent(),e)})})})}}}],ze=sa({transclude:"element",priority:800,require:"^ngSwitch",compile:function(a,c){return function(a,e,g,f,h){f.cases["!"+c.ngSwitchWhen]=f.cases["!"+c.ngSwitchWhen]||[];f.cases["!"+c.ngSwitchWhen].push({transclude:h,element:e})}}}),Ae=sa({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,
|
||||
g){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:g,element:c})}}),Be=sa({controller:["$element","$transclude",function(a,c){if(!c)throw s("ngTransclude")("orphan",fa(a));this.$transclude=c}],link:function(a,c,d,e){e.$transclude(function(a){c.empty();c.append(a)})}}),Ce=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],De=s("ngOptions"),Ee=Z({terminal:!0}),Fe=["$compile","$parse",function(a,c){var d=
|
||||
/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/,e={$setViewValue:y};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var l=this,k={},m=e,n;l.databound=d.ngModel;l.init=function(a,c,d){m=a;n=d};l.addOption=function(c){wa(c,'"option value"');k[c]=!0;m.$viewValue==c&&(a.val(c),n.parent()&&n.remove())};l.removeOption=function(a){this.hasOption(a)&&
|
||||
(delete k[a],m.$viewValue==a&&this.renderUnknownOption(a))};l.renderUnknownOption=function(c){c="? "+Ca(c)+" ?";n.val(c);a.prepend(n);a.val(c);n.prop("selected",!0)};l.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){l.renderUnknownOption=y})}],link:function(e,f,h,l){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(y.parent()&&y.remove(),c.val(a),""===a&&x.prop("selected",!0)):D(a)&&x?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){y.parent()&&
|
||||
y.remove();d.$setViewValue(c.val())})})}function m(a,c,d){var e;d.$render=function(){var a=new Ra(d.$viewValue);q(c.find("option"),function(c){c.selected=v(a.get(c.value))})};a.$watch(function(){ta(e,d.$viewValue)||(e=ea(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];q(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function n(e,f,g){function h(){var a={"":[]},c=[""],d,k,r,s,w;s=g.$modelValue;w=x(e)||[];var B=n?Nb(w):w,D,K,z;K=
|
||||
{};r=!1;var F,J;if(t)if(u&&I(s))for(r=new Ra([]),z=0;z<s.length;z++)K[m]=s[z],r.put(u(e,K),s[z]);else r=new Ra(s);for(z=0;D=B.length,z<D;z++){k=z;if(n){k=B[z];if("$"===k.charAt(0))continue;K[n]=k}K[m]=w[k];d=p(e,K)||"";(k=a[d])||(k=a[d]=[],c.push(d));t?d=v(r.remove(u?u(e,K):q(e,K))):(u?(d={},d[m]=s,d=u(e,d)===u(e,K)):d=s===q(e,K),r=r||d);F=l(e,K);F=v(F)?F:"";k.push({id:u?u(e,K):n?B[z]:z,label:F,selected:d})}t||(A||null===s?a[""].unshift({id:"",label:"",selected:!r}):r||a[""].unshift({id:"?",label:"",
|
||||
selected:!0}));K=0;for(B=c.length;K<B;K++){d=c[K];k=a[d];y.length<=K?(s={element:E.clone().attr("label",d),label:k.label},w=[s],y.push(w),f.append(s.element)):(w=y[K],s=w[0],s.label!=d&&s.element.attr("label",s.label=d));F=null;z=0;for(D=k.length;z<D;z++)r=k[z],(d=w[z+1])?(F=d.element,d.label!==r.label&&F.text(d.label=r.label),d.id!==r.id&&F.val(d.id=r.id),F[0].selected!==r.selected&&F.prop("selected",d.selected=r.selected)):(""===r.id&&A?J=A:(J=C.clone()).val(r.id).attr("selected",r.selected).text(r.label),
|
||||
w.push({element:J,label:r.label,id:r.id,selected:r.selected}),F?F.after(J):s.element.append(J),F=J);for(z++;w.length>z;)w.pop().element.remove()}for(;y.length>K;)y.pop()[0].element.remove()}var k;if(!(k=s.match(d)))throw De("iexp",s,fa(f));var l=c(k[2]||k[1]),m=k[4]||k[6],n=k[5],p=c(k[3]||""),q=c(k[2]?k[1]:m),x=c(k[7]),u=k[8]?c(k[8]):null,y=[[{element:f,label:""}]];A&&(a(A)(e),A.removeClass("ng-scope"),A.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=x(e)||[],d={},h,k,l,
|
||||
p,s,w,v;if(t)for(k=[],p=0,w=y.length;p<w;p++)for(a=y[p],l=1,s=a.length;l<s;l++){if((h=a[l].element)[0].selected){h=h.val();n&&(d[n]=h);if(u)for(v=0;v<c.length&&(d[m]=c[v],u(e,d)!=h);v++);else d[m]=c[h];k.push(q(e,d))}}else if(h=f.val(),"?"==h)k=r;else if(""===h)k=null;else if(u)for(v=0;v<c.length;v++){if(d[m]=c[v],u(e,d)==h){k=q(e,d);break}}else d[m]=c[h],n&&(d[n]=h),k=q(e,d);g.$setViewValue(k)})});g.$render=h;e.$watch(h)}if(l[1]){var p=l[0];l=l[1];var t=h.multiple,s=h.ngOptions,A=!1,x,C=u(O.createElement("option")),
|
||||
E=u(O.createElement("optgroup")),y=C.clone();h=0;for(var B=f.children(),J=B.length;h<J;h++)if(""===B[h].value){x=A=B.eq(h);break}p.init(l,A,y);t&&(l.$isEmpty=function(a){return!a||0===a.length});s?n(e,f,l):t?m(e,f,l):k(e,f,l,p)}}}}],Ge=["$interpolate",function(a){var c={addOption:y,removeOption:y};return{restrict:"E",priority:100,compile:function(d,e){if(D(e.value)){var g=a(d.text(),!0);g||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),m=k.data("$selectController")||k.parent().data("$selectController");
|
||||
m&&m.databound?d.prop("selected",!1):m=c;g?a.$watch(g,function(a,c){e.$set("value",a);a!==c&&m.removeOption(c);m.addOption(a)}):m.addOption(e.value);d.on("$destroy",function(){m.removeOption(e.value)})}}}}],He=Z({restrict:"E",terminal:!0});(Aa=Y.jQuery)?(u=Aa,x(Aa.fn,{scope:Da.scope,isolateScope:Da.isolateScope,controller:Da.controller,injector:Da.injector,inheritedData:Da.inheritedData}),vb("remove",!0,!0,!1),vb("empty",!1,!1,!1),vb("html",!1,!1,!0)):u=N;Na.element=u;(function(a){x(a,{bootstrap:Xb,
|
||||
copy:ea,extend:x,equals:ta,element:u,forEach:q,injector:Yb,noop:y,bind:qb,toJson:oa,fromJson:Tb,identity:za,isUndefined:D,isDefined:v,isString:E,isFunction:J,isObject:V,isNumber:pb,isElement:Oc,isArray:I,version:Qd,isDate:Ja,lowercase:C,uppercase:Ga,callbacks:{counter:0},$$minErr:s,$$csp:Sb});Ta=Tc(Y);try{Ta("ngLocale")}catch(c){Ta("ngLocale",[]).provider("$locale",qd)}Ta("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:Ad});a.provider("$compile",hc).directive({a:Vd,input:Kc,textarea:Kc,
|
||||
form:Wd,script:Ce,select:Fe,style:He,option:Ge,ngBind:ge,ngBindHtml:ie,ngBindTemplate:he,ngClass:je,ngClassEven:le,ngClassOdd:ke,ngCloak:me,ngController:ne,ngForm:Xd,ngHide:we,ngIf:oe,ngInclude:pe,ngInit:re,ngNonBindable:se,ngPluralize:te,ngRepeat:ue,ngShow:ve,ngStyle:xe,ngSwitch:ye,ngSwitchWhen:ze,ngSwitchDefault:Ae,ngOptions:Ee,ngTransclude:Be,ngModel:be,ngList:de,ngChange:ce,required:Lc,ngRequired:Lc,ngValue:fe}).directive({ngInclude:qe}).directive(Mb).directive(Mc);a.provider({$anchorScroll:bd,
|
||||
$animate:Sd,$browser:dd,$cacheFactory:ed,$controller:hd,$document:id,$exceptionHandler:jd,$filter:zc,$interpolate:od,$interval:pd,$http:kd,$httpBackend:ld,$location:sd,$log:td,$parse:wd,$rootScope:zd,$q:xd,$sce:Dd,$sceDelegate:Cd,$sniffer:Ed,$templateCache:fd,$timeout:Fd,$window:Gd})}])})(Na);u(O).ready(function(){Rc(O,Xb)})})(window,document);!angular.$$csp()&&angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}</style>');
|
||||
//# sourceMappingURL=angular.min.js.map
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Gritter for jQuery
|
||||
* http://www.boedesign.com/
|
||||
*
|
||||
* Copyright (c) 2012 Jordan Boesch
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*
|
||||
* Date: February 24, 2012
|
||||
* Version: 1.7.4
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
/**
|
||||
* Set it up as an object under the jQuery namespace
|
||||
*/
|
||||
$.gritter = {};
|
||||
|
||||
/**
|
||||
* Set up global options that the user can over-ride
|
||||
*/
|
||||
$.gritter.options = {
|
||||
position: '',
|
||||
class_name: '', // could be set to 'gritter-light' to use white notifications
|
||||
fade_in_speed: 'medium', // how fast notifications fade in
|
||||
fade_out_speed: 1000, // how fast the notices fade out
|
||||
time: 6000 // hang on the screen for...
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a gritter notification to the screen
|
||||
* @see Gritter#add();
|
||||
*/
|
||||
$.gritter.add = function(params){
|
||||
|
||||
try {
|
||||
return Gritter.add(params || {});
|
||||
} catch(e) {
|
||||
|
||||
var err = 'Gritter Error: ' + e;
|
||||
(typeof(console) != 'undefined' && console.error) ?
|
||||
console.error(err, params) :
|
||||
alert(err);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a gritter notification from the screen
|
||||
* @see Gritter#removeSpecific();
|
||||
*/
|
||||
$.gritter.remove = function(id, params){
|
||||
Gritter.removeSpecific(id, params || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all notifications
|
||||
* @see Gritter#stop();
|
||||
*/
|
||||
$.gritter.removeAll = function(params){
|
||||
Gritter.stop(params || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Big fat Gritter object
|
||||
* @constructor (not really since its object literal)
|
||||
*/
|
||||
var Gritter = {
|
||||
|
||||
// Public - options to over-ride with $.gritter.options in "add"
|
||||
position: '',
|
||||
fade_in_speed: '',
|
||||
fade_out_speed: '',
|
||||
time: '',
|
||||
|
||||
// Private - no touchy the private parts
|
||||
_custom_timer: 0,
|
||||
_item_count: 0,
|
||||
_is_setup: 0,
|
||||
_tpl_close: '<a class="gritter-close" href="#" tabindex="1">Close Notification</a>',
|
||||
_tpl_title: '<span class="gritter-title">[[title]]</span>',
|
||||
_tpl_item: '<div id="gritter-item-[[number]]" class="gritter-item-wrapper [[item_class]]" style="display:none" role="alert"><div class="gritter-top"></div><div class="gritter-item">[[close]][[image]]<div class="[[class_name]]">[[title]]<p>[[text]]</p></div><div style="clear:both"></div></div><div class="gritter-bottom"></div></div>',
|
||||
_tpl_wrap: '<div id="gritter-notice-wrapper"></div>',
|
||||
|
||||
/**
|
||||
* Add a gritter notification to the screen
|
||||
* @param {Object} params The object that contains all the options for drawing the notification
|
||||
* @return {Integer} The specific numeric id to that gritter notification
|
||||
*/
|
||||
add: function(params){
|
||||
// Handle straight text
|
||||
if(typeof(params) == 'string'){
|
||||
params = {text:params};
|
||||
}
|
||||
|
||||
// We might have some issues if we don't have a title or text!
|
||||
if(params.text === null){
|
||||
throw 'You must supply "text" parameter.';
|
||||
}
|
||||
|
||||
// Check the options and set them once
|
||||
if(!this._is_setup){
|
||||
this._runSetup();
|
||||
}
|
||||
|
||||
// Basics
|
||||
var title = params.title,
|
||||
text = params.text,
|
||||
image = params.image || '',
|
||||
sticky = params.sticky || false,
|
||||
item_class = params.class_name || $.gritter.options.class_name,
|
||||
position = $.gritter.options.position,
|
||||
time_alive = params.time || '';
|
||||
|
||||
this._verifyWrapper();
|
||||
|
||||
this._item_count++;
|
||||
var number = this._item_count,
|
||||
tmp = this._tpl_item;
|
||||
|
||||
// Assign callbacks
|
||||
$(['before_open', 'after_open', 'before_close', 'after_close']).each(function(i, val){
|
||||
Gritter['_' + val + '_' + number] = ($.isFunction(params[val])) ? params[val] : function(){}
|
||||
});
|
||||
|
||||
// Reset
|
||||
this._custom_timer = 0;
|
||||
|
||||
// A custom fade time set
|
||||
if(time_alive){
|
||||
this._custom_timer = time_alive;
|
||||
}
|
||||
|
||||
var image_str = (image != '') ? '<img src="' + image + '" class="gritter-image" />' : '',
|
||||
class_name = (image != '') ? 'gritter-with-image' : 'gritter-without-image';
|
||||
|
||||
// String replacements on the template
|
||||
if(title){
|
||||
title = this._str_replace('[[title]]',title,this._tpl_title);
|
||||
}else{
|
||||
title = '';
|
||||
}
|
||||
|
||||
tmp = this._str_replace(
|
||||
['[[title]]', '[[text]]', '[[close]]', '[[image]]', '[[number]]', '[[class_name]]', '[[item_class]]'],
|
||||
[title, text, this._tpl_close, image_str, this._item_count, class_name, item_class], tmp
|
||||
);
|
||||
|
||||
// If it's false, don't show another gritter message
|
||||
if(this['_before_open_' + number]() === false){
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#gritter-notice-wrapper').addClass(position).append(tmp);
|
||||
|
||||
var item = $('#gritter-item-' + this._item_count);
|
||||
|
||||
item.fadeIn(this.fade_in_speed, function(){
|
||||
Gritter['_after_open_' + number]($(this));
|
||||
});
|
||||
|
||||
if(!sticky){
|
||||
this._setFadeTimer(item, number);
|
||||
}
|
||||
|
||||
// Bind the hover/unhover states
|
||||
$(item).bind('mouseenter mouseleave', function(event){
|
||||
if(event.type == 'mouseenter'){
|
||||
if(!sticky){
|
||||
Gritter._restoreItemIfFading($(this), number);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!sticky){
|
||||
Gritter._setFadeTimer($(this), number);
|
||||
}
|
||||
}
|
||||
Gritter._hoverState($(this), event.type);
|
||||
});
|
||||
|
||||
// Clicking (X) makes the perdy thing close
|
||||
$(item).find('.gritter-close').click(function(){
|
||||
Gritter.removeSpecific(number, {}, null, true);
|
||||
return false;
|
||||
});
|
||||
|
||||
return number;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* If we don't have any more gritter notifications, get rid of the wrapper using this check
|
||||
* @private
|
||||
* @param {Integer} unique_id The ID of the element that was just deleted, use it for a callback
|
||||
* @param {Object} e The jQuery element that we're going to perform the remove() action on
|
||||
* @param {Boolean} manual_close Did we close the gritter dialog with the (X) button
|
||||
*/
|
||||
_countRemoveWrapper: function(unique_id, e, manual_close){
|
||||
|
||||
// Remove it then run the callback function
|
||||
e.remove();
|
||||
this['_after_close_' + unique_id](e, manual_close);
|
||||
|
||||
// Check if the wrapper is empty, if it is.. remove the wrapper
|
||||
if($('.gritter-item-wrapper').length == 0){
|
||||
$('#gritter-notice-wrapper').remove();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Fade out an element after it's been on the screen for x amount of time
|
||||
* @private
|
||||
* @param {Object} e The jQuery element to get rid of
|
||||
* @param {Integer} unique_id The id of the element to remove
|
||||
* @param {Object} params An optional list of params to set fade speeds etc.
|
||||
* @param {Boolean} unbind_events Unbind the mouseenter/mouseleave events if they click (X)
|
||||
*/
|
||||
_fade: function(e, unique_id, params, unbind_events){
|
||||
|
||||
var params = params || {},
|
||||
fade = (typeof(params.fade) != 'undefined') ? params.fade : true,
|
||||
fade_out_speed = params.speed || this.fade_out_speed,
|
||||
manual_close = unbind_events;
|
||||
|
||||
this['_before_close_' + unique_id](e, manual_close);
|
||||
|
||||
// If this is true, then we are coming from clicking the (X)
|
||||
if(unbind_events){
|
||||
e.unbind('mouseenter mouseleave');
|
||||
}
|
||||
|
||||
// Fade it out or remove it
|
||||
if(fade){
|
||||
|
||||
e.animate({
|
||||
opacity: 0
|
||||
}, fade_out_speed, function(){
|
||||
e.animate({ height: 0 }, 300, function(){
|
||||
Gritter._countRemoveWrapper(unique_id, e, manual_close);
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
this._countRemoveWrapper(unique_id, e);
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform actions based on the type of bind (mouseenter, mouseleave)
|
||||
* @private
|
||||
* @param {Object} e The jQuery element
|
||||
* @param {String} type The type of action we're performing: mouseenter or mouseleave
|
||||
*/
|
||||
_hoverState: function(e, type){
|
||||
|
||||
// Change the border styles and add the (X) close button when you hover
|
||||
if(type == 'mouseenter'){
|
||||
|
||||
e.addClass('hover');
|
||||
|
||||
// Show close button
|
||||
e.find('.gritter-close').show();
|
||||
|
||||
}
|
||||
// Remove the border styles and hide (X) close button when you mouse out
|
||||
else {
|
||||
|
||||
e.removeClass('hover');
|
||||
|
||||
// Hide close button
|
||||
e.find('.gritter-close').hide();
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a specific notification based on an ID
|
||||
* @param {Integer} unique_id The ID used to delete a specific notification
|
||||
* @param {Object} params A set of options passed in to determine how to get rid of it
|
||||
* @param {Object} e The jQuery element that we're "fading" then removing
|
||||
* @param {Boolean} unbind_events If we clicked on the (X) we set this to true to unbind mouseenter/mouseleave
|
||||
*/
|
||||
removeSpecific: function(unique_id, params, e, unbind_events){
|
||||
|
||||
if(!e){
|
||||
var e = $('#gritter-item-' + unique_id);
|
||||
}
|
||||
|
||||
// We set the fourth param to let the _fade function know to
|
||||
// unbind the "mouseleave" event. Once you click (X) there's no going back!
|
||||
this._fade(e, unique_id, params || {}, unbind_events);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* If the item is fading out and we hover over it, restore it!
|
||||
* @private
|
||||
* @param {Object} e The HTML element to remove
|
||||
* @param {Integer} unique_id The ID of the element
|
||||
*/
|
||||
_restoreItemIfFading: function(e, unique_id){
|
||||
|
||||
clearTimeout(this['_int_id_' + unique_id]);
|
||||
e.stop().css({ opacity: '', height: '' });
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup the global options - only once
|
||||
* @private
|
||||
*/
|
||||
_runSetup: function(){
|
||||
|
||||
for(var opt in $.gritter.options){
|
||||
this[opt] = $.gritter.options[opt];
|
||||
}
|
||||
this._is_setup = 1;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the notification to fade out after a certain amount of time
|
||||
* @private
|
||||
* @param {Object} item The HTML element we're dealing with
|
||||
* @param {Integer} unique_id The ID of the element
|
||||
*/
|
||||
_setFadeTimer: function(e, unique_id){
|
||||
|
||||
var timer_str = (this._custom_timer) ? this._custom_timer : this.time;
|
||||
this['_int_id_' + unique_id] = setTimeout(function(){
|
||||
Gritter._fade(e, unique_id);
|
||||
}, timer_str);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Bring everything to a halt
|
||||
* @param {Object} params A list of callback functions to pass when all notifications are removed
|
||||
*/
|
||||
stop: function(params){
|
||||
|
||||
// callbacks (if passed)
|
||||
var before_close = ($.isFunction(params.before_close)) ? params.before_close : function(){};
|
||||
var after_close = ($.isFunction(params.after_close)) ? params.after_close : function(){};
|
||||
|
||||
var wrap = $('#gritter-notice-wrapper');
|
||||
before_close(wrap);
|
||||
wrap.fadeOut(function(){
|
||||
$(this).remove();
|
||||
after_close();
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* An extremely handy PHP function ported to JS, works well for templating
|
||||
* @private
|
||||
* @param {String/Array} search A list of things to search for
|
||||
* @param {String/Array} replace A list of things to replace the searches with
|
||||
* @return {String} sa The output
|
||||
*/
|
||||
_str_replace: function(search, replace, subject, count){
|
||||
|
||||
var i = 0, j = 0, temp = '', repl = '', sl = 0, fl = 0,
|
||||
f = [].concat(search),
|
||||
r = [].concat(replace),
|
||||
s = subject,
|
||||
ra = r instanceof Array, sa = s instanceof Array;
|
||||
s = [].concat(s);
|
||||
|
||||
if(count){
|
||||
this.window[count] = 0;
|
||||
}
|
||||
|
||||
for(i = 0, sl = s.length; i < sl; i++){
|
||||
|
||||
if(s[i] === ''){
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0, fl = f.length; j < fl; j++){
|
||||
|
||||
temp = s[i] + '';
|
||||
repl = ra ? (r[j] !== undefined ? r[j] : '') : r[0];
|
||||
s[i] = (temp).split(f[j]).join(repl);
|
||||
|
||||
if(count && s[i] !== temp){
|
||||
this.window[count] += (temp.length-s[i].length) / f[j].length;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return sa ? s : s[0];
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* A check to make sure we have something to wrap our notices with
|
||||
* @private
|
||||
*/
|
||||
_verifyWrapper: function(){
|
||||
|
||||
if($('#gritter-notice-wrapper').length == 0){
|
||||
$('body').append(this._tpl_wrap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
@@ -1 +0,0 @@
|
||||
(function(b){b.gritter={};b.gritter.options={position:"",class_name:"",fade_in_speed:"medium",fade_out_speed:1000,time:6000};b.gritter.add=function(f){try{return a.add(f||{})}catch(d){var c="Gritter Error: "+d;(typeof(console)!="undefined"&&console.error)?console.error(c,f):alert(c)}};b.gritter.remove=function(d,c){a.removeSpecific(d,c||{})};b.gritter.removeAll=function(c){a.stop(c||{})};var a={position:"",fade_in_speed:"",fade_out_speed:"",time:"",_custom_timer:0,_item_count:0,_is_setup:0,_tpl_close:'<div class="gritter-close"></div>',_tpl_title:'<span class="gritter-title">[[title]]</span>',_tpl_item:'<div id="gritter-item-[[number]]" class="gritter-item-wrapper [[item_class]]" style="display:none"><div class="gritter-top"></div><div class="gritter-item">[[close]][[image]]<div class="[[class_name]]">[[title]]<p>[[text]]</p></div><div style="clear:both"></div></div><div class="gritter-bottom"></div></div>',_tpl_wrap:'<div id="gritter-notice-wrapper"></div>',add:function(g){if(typeof(g)=="string"){g={text:g}}if(g.text===null){throw'You must supply "text" parameter.'}if(!this._is_setup){this._runSetup()}var k=g.title,n=g.text,e=g.image||"",l=g.sticky||false,m=g.class_name||b.gritter.options.class_name,j=b.gritter.options.position,d=g.time||"";this._verifyWrapper();this._item_count++;var f=this._item_count,i=this._tpl_item;b(["before_open","after_open","before_close","after_close"]).each(function(p,q){a["_"+q+"_"+f]=(b.isFunction(g[q]))?g[q]:function(){}});this._custom_timer=0;if(d){this._custom_timer=d}var c=(e!="")?'<img src="'+e+'" class="gritter-image" />':"",h=(e!="")?"gritter-with-image":"gritter-without-image";if(k){k=this._str_replace("[[title]]",k,this._tpl_title)}else{k=""}i=this._str_replace(["[[title]]","[[text]]","[[close]]","[[image]]","[[number]]","[[class_name]]","[[item_class]]"],[k,n,this._tpl_close,c,this._item_count,h,m],i);if(this["_before_open_"+f]()===false){return false}b("#gritter-notice-wrapper").addClass(j).append(i);var o=b("#gritter-item-"+this._item_count);o.fadeIn(this.fade_in_speed,function(){a["_after_open_"+f](b(this))});if(!l){this._setFadeTimer(o,f)}b(o).bind("mouseenter mouseleave",function(p){if(p.type=="mouseenter"){if(!l){a._restoreItemIfFading(b(this),f)}}else{if(!l){a._setFadeTimer(b(this),f)}}a._hoverState(b(this),p.type)});b(o).find(".gritter-close").click(function(){a.removeSpecific(f,{},null,true)});return f},_countRemoveWrapper:function(c,d,f){d.remove();this["_after_close_"+c](d,f);if(b(".gritter-item-wrapper").length==0){b("#gritter-notice-wrapper").remove()}},_fade:function(g,d,j,f){var j=j||{},i=(typeof(j.fade)!="undefined")?j.fade:true,c=j.speed||this.fade_out_speed,h=f;this["_before_close_"+d](g,h);if(f){g.unbind("mouseenter mouseleave")}if(i){g.animate({opacity:0},c,function(){g.animate({height:0},300,function(){a._countRemoveWrapper(d,g,h)})})}else{this._countRemoveWrapper(d,g)}},_hoverState:function(d,c){if(c=="mouseenter"){d.addClass("hover");d.find(".gritter-close").show()}else{d.removeClass("hover");d.find(".gritter-close").hide()}},removeSpecific:function(c,g,f,d){if(!f){var f=b("#gritter-item-"+c)}this._fade(f,c,g||{},d)},_restoreItemIfFading:function(d,c){clearTimeout(this["_int_id_"+c]);d.stop().css({opacity:"",height:""})},_runSetup:function(){for(opt in b.gritter.options){this[opt]=b.gritter.options[opt]}this._is_setup=1},_setFadeTimer:function(f,d){var c=(this._custom_timer)?this._custom_timer:this.time;this["_int_id_"+d]=setTimeout(function(){a._fade(f,d)},c)},stop:function(e){var c=(b.isFunction(e.before_close))?e.before_close:function(){};var f=(b.isFunction(e.after_close))?e.after_close:function(){};var d=b("#gritter-notice-wrapper");c(d);d.fadeOut(function(){b(this).remove();f()})},_str_replace:function(v,e,o,n){var k=0,h=0,t="",m="",g=0,q=0,l=[].concat(v),c=[].concat(e),u=o,d=c instanceof Array,p=u instanceof Array;u=[].concat(u);if(n){this.window[n]=0}for(k=0,g=u.length;k<g;k++){if(u[k]===""){continue}for(h=0,q=l.length;h<q;h++){t=u[k]+"";m=d?(c[h]!==undefined?c[h]:""):c[0];u[k]=(t).split(l[h]).join(m);if(n&&u[k]!==t){this.window[n]+=(t.length-u[k].length)/l[h].length}}}return p?u:u[0]},_verifyWrapper:function(){if(b("#gritter-notice-wrapper").length==0){b("body").append(this._tpl_wrap)}}}})(jQuery);
|
||||
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
* legend.js v0.2.0
|
||||
* License: MIT
|
||||
*/
|
||||
function legend(parent, data) {
|
||||
parent.className = 'legend';
|
||||
var datas = data.hasOwnProperty('datasets') ? data.datasets : data;
|
||||
|
||||
@@ -1,349 +0,0 @@
|
||||
//fgnass.github.com/spin.js#v1.3
|
||||
|
||||
/**
|
||||
* Copyright (c) 2011-2013 Felix Gnass
|
||||
* Licensed under the MIT license
|
||||
*/
|
||||
(function(root, factory) {
|
||||
|
||||
/* CommonJS */
|
||||
if (typeof exports == 'object') module.exports = factory()
|
||||
|
||||
/* AMD module */
|
||||
else if (typeof define == 'function' && define.amd) define(factory)
|
||||
|
||||
/* Browser global */
|
||||
else root.Spinner = factory()
|
||||
}
|
||||
(this, function() {
|
||||
"use strict";
|
||||
|
||||
var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
|
||||
, animations = {} /* Animation rules keyed by their name */
|
||||
, useCssAnimations /* Whether to use CSS animations or setTimeout */
|
||||
|
||||
/**
|
||||
* Utility function to create elements. If no tag name is given,
|
||||
* a DIV is created. Optionally properties can be passed.
|
||||
*/
|
||||
function createEl(tag, prop) {
|
||||
var el = document.createElement(tag || 'div')
|
||||
, n
|
||||
|
||||
for(n in prop) el[n] = prop[n]
|
||||
return el
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends children and returns the parent.
|
||||
*/
|
||||
function ins(parent /* child1, child2, ...*/) {
|
||||
for (var i=1, n=arguments.length; i<n; i++)
|
||||
parent.appendChild(arguments[i])
|
||||
|
||||
return parent
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new stylesheet to hold the @keyframe or VML rules.
|
||||
*/
|
||||
var sheet = (function() {
|
||||
var el = createEl('style', {type : 'text/css'})
|
||||
ins(document.getElementsByTagName('head')[0], el)
|
||||
return el.sheet || el.styleSheet
|
||||
}())
|
||||
|
||||
/**
|
||||
* Creates an opacity keyframe animation rule and returns its name.
|
||||
* Since most mobile Webkits have timing issues with animation-delay,
|
||||
* we create separate rules for each line/segment.
|
||||
*/
|
||||
function addAnimation(alpha, trail, i, lines) {
|
||||
var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-')
|
||||
, start = 0.01 + i/lines * 100
|
||||
, z = Math.max(1 - (1-alpha) / trail * (100-start), alpha)
|
||||
, prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase()
|
||||
, pre = prefix && '-' + prefix + '-' || ''
|
||||
|
||||
if (!animations[name]) {
|
||||
sheet.insertRule(
|
||||
'@' + pre + 'keyframes ' + name + '{' +
|
||||
'0%{opacity:' + z + '}' +
|
||||
start + '%{opacity:' + alpha + '}' +
|
||||
(start+0.01) + '%{opacity:1}' +
|
||||
(start+trail) % 100 + '%{opacity:' + alpha + '}' +
|
||||
'100%{opacity:' + z + '}' +
|
||||
'}', sheet.cssRules.length)
|
||||
|
||||
animations[name] = 1
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries various vendor prefixes and returns the first supported property.
|
||||
*/
|
||||
function vendor(el, prop) {
|
||||
var s = el.style
|
||||
, pp
|
||||
, i
|
||||
|
||||
if(s[prop] !== undefined) return prop
|
||||
prop = prop.charAt(0).toUpperCase() + prop.slice(1)
|
||||
for(i=0; i<prefixes.length; i++) {
|
||||
pp = prefixes[i]+prop
|
||||
if(s[pp] !== undefined) return pp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets multiple style properties at once.
|
||||
*/
|
||||
function css(el, prop) {
|
||||
for (var n in prop)
|
||||
el.style[vendor(el, n)||n] = prop[n]
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in default values.
|
||||
*/
|
||||
function merge(obj) {
|
||||
for (var i=1; i < arguments.length; i++) {
|
||||
var def = arguments[i]
|
||||
for (var n in def)
|
||||
if (obj[n] === undefined) obj[n] = def[n]
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute page-offset of the given element.
|
||||
*/
|
||||
function pos(el) {
|
||||
var o = { x:el.offsetLeft, y:el.offsetTop }
|
||||
while((el = el.offsetParent))
|
||||
o.x+=el.offsetLeft, o.y+=el.offsetTop
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// Built-in defaults
|
||||
|
||||
var defaults = {
|
||||
lines: 12, // The number of lines to draw
|
||||
length: 7, // The length of each line
|
||||
width: 5, // The line thickness
|
||||
radius: 10, // The radius of the inner circle
|
||||
rotate: 0, // Rotation offset
|
||||
corners: 1, // Roundness (0..1)
|
||||
color: '#000', // #rgb or #rrggbb
|
||||
direction: 1, // 1: clockwise, -1: counterclockwise
|
||||
speed: 1, // Rounds per second
|
||||
trail: 100, // Afterglow percentage
|
||||
opacity: 1/4, // Opacity of the lines
|
||||
fps: 20, // Frames per second when using setTimeout()
|
||||
zIndex: 2e9, // Use a high z-index by default
|
||||
className: 'spinner', // CSS class to assign to the element
|
||||
top: 'auto', // center vertically
|
||||
left: 'auto', // center horizontally
|
||||
position: 'relative' // element position
|
||||
}
|
||||
|
||||
/** The constructor */
|
||||
function Spinner(o) {
|
||||
if (typeof this == 'undefined') return new Spinner(o)
|
||||
this.opts = merge(o || {}, Spinner.defaults, defaults)
|
||||
}
|
||||
|
||||
// Global defaults that override the built-ins:
|
||||
Spinner.defaults = {}
|
||||
|
||||
merge(Spinner.prototype, {
|
||||
|
||||
/**
|
||||
* Adds the spinner to the given target element. If this instance is already
|
||||
* spinning, it is automatically removed from its previous target b calling
|
||||
* stop() internally.
|
||||
*/
|
||||
spin: function(target) {
|
||||
this.stop()
|
||||
|
||||
var self = this
|
||||
, o = self.opts
|
||||
, el = self.el = css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex})
|
||||
, mid = o.radius+o.length+o.width
|
||||
, ep // element position
|
||||
, tp // target position
|
||||
|
||||
if (target) {
|
||||
target.insertBefore(el, target.firstChild||null)
|
||||
tp = pos(target)
|
||||
ep = pos(el)
|
||||
css(el, {
|
||||
left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 1) : parseInt(o.left, 10) + mid) + 'px',
|
||||
top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : parseInt(o.top, 10) + mid) + 'px'
|
||||
})
|
||||
}
|
||||
|
||||
el.setAttribute('role', 'progressbar')
|
||||
self.lines(el, self.opts)
|
||||
|
||||
if (!useCssAnimations) {
|
||||
// No CSS animation support, use setTimeout() instead
|
||||
var i = 0
|
||||
, start = (o.lines - 1) * (1 - o.direction) / 2
|
||||
, alpha
|
||||
, fps = o.fps
|
||||
, f = fps/o.speed
|
||||
, ostep = (1-o.opacity) / (f*o.trail / 100)
|
||||
, astep = f/o.lines
|
||||
|
||||
;(function anim() {
|
||||
i++;
|
||||
for (var j = 0; j < o.lines; j++) {
|
||||
alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity)
|
||||
|
||||
self.opacity(el, j * o.direction + start, alpha, o)
|
||||
}
|
||||
self.timeout = self.el && setTimeout(anim, ~~(1000/fps))
|
||||
})()
|
||||
}
|
||||
return self
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops and removes the Spinner.
|
||||
*/
|
||||
stop: function() {
|
||||
var el = this.el
|
||||
if (el) {
|
||||
clearTimeout(this.timeout)
|
||||
if (el.parentNode) el.parentNode.removeChild(el)
|
||||
this.el = undefined
|
||||
}
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Internal method that draws the individual lines. Will be overwritten
|
||||
* in VML fallback mode below.
|
||||
*/
|
||||
lines: function(el, o) {
|
||||
var i = 0
|
||||
, start = (o.lines - 1) * (1 - o.direction) / 2
|
||||
, seg
|
||||
|
||||
function fill(color, shadow) {
|
||||
return css(createEl(), {
|
||||
position: 'absolute',
|
||||
width: (o.length+o.width) + 'px',
|
||||
height: o.width + 'px',
|
||||
background: color,
|
||||
boxShadow: shadow,
|
||||
transformOrigin: 'left',
|
||||
transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)',
|
||||
borderRadius: (o.corners * o.width>>1) + 'px'
|
||||
})
|
||||
}
|
||||
|
||||
for (; i < o.lines; i++) {
|
||||
seg = css(createEl(), {
|
||||
position: 'absolute',
|
||||
top: 1+~(o.width/2) + 'px',
|
||||
transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
|
||||
opacity: o.opacity,
|
||||
animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite'
|
||||
})
|
||||
|
||||
if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}))
|
||||
|
||||
ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)')))
|
||||
}
|
||||
return el
|
||||
},
|
||||
|
||||
/**
|
||||
* Internal method that adjusts the opacity of a single line.
|
||||
* Will be overwritten in VML fallback mode below.
|
||||
*/
|
||||
opacity: function(el, i, val) {
|
||||
if (i < el.childNodes.length) el.childNodes[i].style.opacity = val
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
function initVML() {
|
||||
|
||||
/* Utility function to create a VML tag */
|
||||
function vml(tag, attr) {
|
||||
return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
|
||||
}
|
||||
|
||||
// No CSS transforms but VML support, add a CSS rule for VML elements:
|
||||
sheet.addRule('.spin-vml', 'behavior:url(#default#VML)')
|
||||
|
||||
Spinner.prototype.lines = function(el, o) {
|
||||
var r = o.length+o.width
|
||||
, s = 2*r
|
||||
|
||||
function grp() {
|
||||
return css(
|
||||
vml('group', {
|
||||
coordsize: s + ' ' + s,
|
||||
coordorigin: -r + ' ' + -r
|
||||
}),
|
||||
{ width: s, height: s }
|
||||
)
|
||||
}
|
||||
|
||||
var margin = -(o.width+o.length)*2 + 'px'
|
||||
, g = css(grp(), {position: 'absolute', top: margin, left: margin})
|
||||
, i
|
||||
|
||||
function seg(i, dx, filter) {
|
||||
ins(g,
|
||||
ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
|
||||
ins(css(vml('roundrect', {arcsize: o.corners}), {
|
||||
width: r,
|
||||
height: o.width,
|
||||
left: o.radius,
|
||||
top: -o.width>>1,
|
||||
filter: filter
|
||||
}),
|
||||
vml('fill', {color: o.color, opacity: o.opacity}),
|
||||
vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (o.shadow)
|
||||
for (i = 1; i <= o.lines; i++)
|
||||
seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)')
|
||||
|
||||
for (i = 1; i <= o.lines; i++) seg(i)
|
||||
return ins(el, g)
|
||||
}
|
||||
|
||||
Spinner.prototype.opacity = function(el, i, val, o) {
|
||||
var c = el.firstChild
|
||||
o = o.shadow && o.lines || 0
|
||||
if (c && i+o < c.childNodes.length) {
|
||||
c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild
|
||||
if (c) c.opacity = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var probe = css(createEl('group'), {behavior: 'url(#default#VML)'})
|
||||
|
||||
if (!vendor(probe, 'transform') && probe.adj) initVML()
|
||||
else useCssAnimations = vendor(probe, 'animation')
|
||||
|
||||
return Spinner
|
||||
|
||||
}));
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "dockerui",
|
||||
"version": "0.8.0",
|
||||
"homepage": "https://github.com/crosbymichael/dockerui",
|
||||
"authors": [
|
||||
"Michael Crosby <crosbymichael@gmail.com>",
|
||||
"Kevan Ahlquist <ahlqu039@umn.edu>"
|
||||
],
|
||||
"description": "A web interface for the Docker Remote API.",
|
||||
"keywords": [
|
||||
"dockerui",
|
||||
"docker",
|
||||
"api"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"Chart.js": "1.0.2",
|
||||
"angular": "1.3.15",
|
||||
"angular-bootstrap": "0.12.0",
|
||||
"angular-mocks": "1.3.15",
|
||||
"angular-oboe": "*",
|
||||
"angular-resource": "1.3.15",
|
||||
"angular-route": "1.3.15",
|
||||
"angular-visjs": "0.0.7",
|
||||
"bootstrap": "3.3.0",
|
||||
"jquery": "1.11.1",
|
||||
"jquery.gritter": "1.7.4",
|
||||
"spin.js": "1.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "1.3.15"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.container>hr{margin:60px 0}.jumbotron{margin:80px 0;text-align:center}.jumbotron h1{font-size:100px;line-height:1}.jumbotron .lead{font-size:24px;line-height:1.25}.jumbotron .btn{padding:14px 24px;font-size:21px}.marketing{margin:60px 0}.marketing p+h4{margin-top:28px}.masthead .nav{width:100%;margin:0;margin:0 0 2em 0}.masthead .nav.well{padding:0}.masthead .nav li{display:table-cell;float:none;width:1%}.masthead .nav li a{font-weight:bold;text-align:center;border-right:1px solid rgba(0,0,0,0.1);border-left:1px solid rgba(255,255,255,0.75)}.masthead .nav li:first-child a{border-left:0;border-radius:3px 0 0 3px}.masthead .nav li:last-child a{border-right:0;border-radius:0 3px 3px 0}.btn-group button{margin:3px}.detail{width:80%;margin:0 auto}.center{width:100%;margin:0 auto}.btn-remove{max-width:70%;margin:0 auto}.actions{margin:0 auto}.container-bottom{height:50px}.well{padding:10px 15px 0 15px}.messages{max-height:50px;overflow-x:hidden;overflow-y:scroll}.legend .title{padding:0 .3em;margin:.5em;border-style:solid;border-width:0 0 0 1em}.inline-four .form-control{max-width:25%}.dropdown{cursor:pointer}
|
||||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 43 B After Width: | Height: | Size: 43 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="dockerui">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>DockerUI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Michael Crosby & Kevan Ahlquist">
|
||||
|
||||
<link href="vendor.css" rel="stylesheet">
|
||||
<link href="dockerui.css" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script src="angular.js"></script>
|
||||
<script src="vendor.js"></script>
|
||||
<script src="dockerui.js"></script>
|
||||
|
||||
<!-- Fav and touch icons -->
|
||||
<link rel="shortcut icon" href="ico/favicon.ico">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="ico/apple-touch-icon-144-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="ico/apple-touch-icon-114-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="ico/apple-touch-icon-72-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-57-precomposed.png">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div ng-include="template" ng-controller="MastheadController"></div>
|
||||
|
||||
<div id="view" ng-view></div>
|
||||
|
||||
<div class="container-bottom"></div>
|
||||
<div ng-include="template" ng-controller="FooterController"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
9d2349d7d4fd3b7a22f87e2b54cd7fb2cfd6d537 dockerui
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main // import "github.com/crosbymichael/dockerui"
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
module.exports = function (grunt) {
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-recess');
|
||||
grunt.loadNpmTasks('grunt-karma');
|
||||
grunt.loadNpmTasks('grunt-html2js');
|
||||
grunt.loadNpmTasks('grunt-shell');
|
||||
grunt.loadNpmTasks('grunt-if');
|
||||
|
||||
// Default task.
|
||||
grunt.registerTask('default', ['jshint', 'build', 'karma:unit']);
|
||||
grunt.registerTask('build', ['clean:app', 'if:binaryNotExist', 'html2js', 'concat', 'clean:tmpl', 'recess:build', 'copy']);
|
||||
grunt.registerTask('release', ['clean:all', 'if:binaryNotExist', 'html2js', 'uglify', 'clean:tmpl', 'jshint', 'karma:unit', 'concat:index', 'recess:min', 'copy']);
|
||||
grunt.registerTask('test-watch', ['karma:watch']);
|
||||
grunt.registerTask('run', ['if:binaryNotExist', 'build', 'shell:buildImage', 'shell:run']);
|
||||
grunt.registerTask('run-dev', ['if:binaryNotExist', 'shell:buildImage', 'shell:run', 'watch:build']);
|
||||
|
||||
// Print a timestamp (useful for when watching)
|
||||
grunt.registerTask('timestamp', function () {
|
||||
grunt.log.subhead(Date());
|
||||
});
|
||||
|
||||
var karmaConfig = function (configFile, customOptions) {
|
||||
var options = {configFile: configFile, keepalive: true};
|
||||
var travisOptions = process.env.TRAVIS && {browsers: ['Firefox'], reporters: 'dots'};
|
||||
return grunt.util._.extend(options, customOptions, travisOptions);
|
||||
};
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
distdir: 'dist',
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
remoteApiVersion: 'v1.20',
|
||||
banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
|
||||
'<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
|
||||
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
|
||||
' * Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n */\n',
|
||||
src: {
|
||||
js: ['app/**/*.js', '!app/**/*.spec.js'],
|
||||
jsTpl: ['<%= distdir %>/templates/**/*.js'],
|
||||
jsVendor: [
|
||||
'bower_components/jquery/dist/jquery.js',
|
||||
'assets/js/jquery.gritter.js', // Using custom version to fix error in minified build due to "use strict"
|
||||
'bower_components/bootstrap/dist/js/bootstrap.js',
|
||||
'bower_components/spin.js/spin.js',
|
||||
'bower_components/vis/dist/vis.js',
|
||||
'bower_components/Chart.js/Chart.js',
|
||||
'bower_components/oboe/dist/oboe-browser.js',
|
||||
'assets/js/legend.js' // Not a bower package
|
||||
],
|
||||
specs: ['test/**/*.spec.js'],
|
||||
scenarios: ['test/**/*.scenario.js'],
|
||||
html: ['index.html'],
|
||||
tpl: ['app/components/**/*.html'],
|
||||
css: ['assets/css/app.css'],
|
||||
cssVendor: [
|
||||
'bower_components/bootstrap/dist/css/bootstrap.css',
|
||||
'bower_components/jquery.gritter/css/jquery.gritter.css',
|
||||
'bower_components/vis/dist/vis.css'
|
||||
]
|
||||
},
|
||||
clean: {
|
||||
all: ['<%= distdir %>/*'],
|
||||
app: ['<%= distdir %>/*', '!<%= distdir %>/dockerui'],
|
||||
tmpl: ['<%= distdir %>/templates']
|
||||
},
|
||||
copy: {
|
||||
assets: {
|
||||
files: [
|
||||
{dest: '<%= distdir %>/fonts/', src: '**', expand: true, cwd: 'bower_components/bootstrap/fonts/'},
|
||||
{
|
||||
dest: '<%= distdir %>/images/',
|
||||
src: ['**', '!trees.jpg'],
|
||||
expand: true,
|
||||
cwd: 'bower_components/jquery.gritter/images/'
|
||||
},
|
||||
{
|
||||
dest: '<%= distdir %>/img',
|
||||
src: [
|
||||
'network/downArrow.png',
|
||||
'network/leftArrow.png',
|
||||
'network/upArrow.png',
|
||||
'network/rightArrow.png',
|
||||
'network/minus.png',
|
||||
'network/plus.png',
|
||||
'network/zoomExtends.png'
|
||||
],
|
||||
expand: true,
|
||||
cwd: 'bower_components/vis/dist/img'
|
||||
},
|
||||
{dest: '<%= distdir %>/ico', src: '**', expand: true, cwd: 'assets/ico'}
|
||||
]
|
||||
}
|
||||
},
|
||||
karma: {
|
||||
unit: {options: karmaConfig('test/unit/karma.conf.js')},
|
||||
watch: {options: karmaConfig('test/unit/karma.conf.js', {singleRun: false, autoWatch: true})}
|
||||
},
|
||||
html2js: {
|
||||
app: {
|
||||
options: {
|
||||
base: '.'
|
||||
},
|
||||
src: ['<%= src.tpl %>'],
|
||||
dest: '<%= distdir %>/templates/app.js',
|
||||
module: '<%= pkg.name %>.templates'
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
dist: {
|
||||
options: {
|
||||
banner: "<%= banner %>",
|
||||
process: true
|
||||
},
|
||||
src: ['<%= src.js %>', '<%= src.jsTpl %>'],
|
||||
dest: '<%= distdir %>/<%= pkg.name %>.js'
|
||||
},
|
||||
vendor: {
|
||||
src: ['<%= src.jsVendor %>'],
|
||||
dest: '<%= distdir %>/vendor.js'
|
||||
},
|
||||
index: {
|
||||
src: ['index.html'],
|
||||
dest: '<%= distdir %>/index.html',
|
||||
options: {
|
||||
process: true
|
||||
}
|
||||
},
|
||||
angular: {
|
||||
src: ['bower_components/angular/angular.js',
|
||||
'bower_components/angular-route/angular-route.js',
|
||||
'bower_components/angular-resource/angular-resource.js',
|
||||
'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
|
||||
'bower_components/angular-oboe/dist/angular-oboe.js',
|
||||
'bower_components/angular-visjs/angular-vis.js'],
|
||||
dest: '<%= distdir %>/angular.js'
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
dist: {
|
||||
options: {
|
||||
banner: "<%= banner %>"
|
||||
},
|
||||
src: ['<%= src.js %>', '<%= src.jsTpl %>'],
|
||||
dest: '<%= distdir %>/<%= pkg.name %>.js'
|
||||
},
|
||||
vendor: {
|
||||
options: {
|
||||
preserveComments: 'some' // Preserve license comments
|
||||
},
|
||||
src: ['<%= src.jsVendor %>'],
|
||||
dest: '<%= distdir %>/vendor.js'
|
||||
},
|
||||
angular: {
|
||||
options: {
|
||||
preserveComments: 'some' // Preserve license comments
|
||||
},
|
||||
src: ['<%= concat.angular.src %>'],
|
||||
dest: '<%= distdir %>/angular.js'
|
||||
}
|
||||
},
|
||||
recess: { // TODO: not maintained, unable to preserve license comments, switch out for something better.
|
||||
build: {
|
||||
files: {
|
||||
'<%= distdir %>/<%= pkg.name %>.css': ['<%= src.css %>'],
|
||||
'<%= distdir %>/vendor.css': ['<%= src.cssVendor %>']
|
||||
},
|
||||
options: {
|
||||
compile: true,
|
||||
noOverqualifying: false // TODO: Added because of .nav class, rename
|
||||
}
|
||||
},
|
||||
min: {
|
||||
files: {
|
||||
'<%= distdir %>/<%= pkg.name %>.css': ['<%= src.css %>'],
|
||||
'<%= distdir %>/vendor.css': ['<%= src.cssVendor %>']
|
||||
},
|
||||
options: {
|
||||
compile: true,
|
||||
compress: true,
|
||||
noOverqualifying: false // TODO: Added because of .nav class, rename
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
all: {
|
||||
files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl %>', '<%= src.html %>'],
|
||||
tasks: ['default', 'timestamp']
|
||||
},
|
||||
build: {
|
||||
files: ['<%= src.js %>', '<%= src.specs %>', '<%= src.css %>', '<%= src.tpl %>', '<%= src.html %>'],
|
||||
tasks: ['build', 'shell:buildImage', 'shell:run', 'shell:cleanImages']
|
||||
/*
|
||||
* Why don't we just use a host volume
|
||||
* http.FileServer uses sendFile which virtualbox hates
|
||||
* Tried using a host volume with -v, copying files with `docker cp`, restating container, none worked
|
||||
* Rebuilding image on each change was only method that worked, takes ~4s per change to update
|
||||
*/
|
||||
}
|
||||
},
|
||||
jshint: {
|
||||
files: ['gruntFile.js', '<%= src.js %>', '<%= src.specs %>', '<%= src.scenarios %>'],
|
||||
options: {
|
||||
curly: true,
|
||||
eqeqeq: true,
|
||||
immed: true,
|
||||
latedef: true,
|
||||
newcap: true,
|
||||
noarg: true,
|
||||
sub: true,
|
||||
boss: true,
|
||||
eqnull: true,
|
||||
globals: {
|
||||
angular: false,
|
||||
'$': false
|
||||
}
|
||||
}
|
||||
},
|
||||
shell: {
|
||||
buildImage: {
|
||||
command: 'docker build --rm -t dockerui .'
|
||||
},
|
||||
buildBinary: {
|
||||
command: [
|
||||
'docker run --rm -v $(pwd):/src centurylink/golang-builder',
|
||||
'shasum dockerui > dockerui-checksum.txt',
|
||||
'mkdir -p dist',
|
||||
'mv dockerui dist/'
|
||||
].join('&&')
|
||||
},
|
||||
run: {
|
||||
command: [
|
||||
'docker stop dockerui',
|
||||
'docker rm dockerui',
|
||||
'docker run --privileged -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock --name dockerui dockerui'
|
||||
].join(';')
|
||||
},
|
||||
cleanImages: {
|
||||
command: 'docker rmi $(docker images -q -f dangling=true)'
|
||||
}
|
||||
},
|
||||
'if': {
|
||||
binaryNotExist: {
|
||||
options: {
|
||||
executable: 'dist/dockerui'
|
||||
},
|
||||
ifFalse: ['shell:buildBinary']
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,73 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="dockerui">
|
||||
<head>
|
||||
<html lang="en" ng-app="<%= pkg.name %>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>DockerUI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Michael Crosby crosbymichael.com">
|
||||
<meta name="author" content="<%= pkg.author %>">
|
||||
|
||||
<link href="assets/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="assets/css/jquery.gritter.css" rel="stylesheet">
|
||||
<link href="vendor.css" rel="stylesheet">
|
||||
<link href="<%= pkg.name %>.css" rel="stylesheet">
|
||||
|
||||
<link href="assets/css/app.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script src="assets/js/jquery-1.11.1.min.js"></script>
|
||||
<script src="assets/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
<script src="assets/js/spin.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="assets/js/angularjs/1.2.6/angular.min.js"></script>
|
||||
<script src="assets/js/angularjs/1.2.6/angular-resource.min.js"></script>
|
||||
<script src="assets/js/angularjs/1.2.6/angular-route.min.js"></script>
|
||||
|
||||
<script src="assets/js/jquery.gritter.min.js"></script>
|
||||
<script src="assets/js/Chart.min.js"></script>
|
||||
<script src="assets/js/legend.js"></script>
|
||||
|
||||
<script src="app/app.js"></script>
|
||||
<script src="app/shared/services.js"></script>
|
||||
<script src="app/shared/filters.js"></script>
|
||||
<script src="app/shared/viewmodel.js"></script>
|
||||
<!-- TODO: Add minification build step -->
|
||||
<script src="app/components/masthead/mastheadController.js"></script>
|
||||
<script src="app/components/footer/footerController.js"></script>
|
||||
<script src="app/components/dashboard/dashboardController.js"></script>
|
||||
<script src="app/components/container/containerController.js"></script>
|
||||
<script src="app/components/containers/containersController.js"></script>
|
||||
<script src="app/components/containerLogs/containerLogsController.js"></script>
|
||||
<script src="app/components/startContainer/startContainerController.js"></script>
|
||||
<script src="app/components/image/imageController.js"></script>
|
||||
<script src="app/components/images/imagesController.js"></script>
|
||||
<script src="app/components/sidebar/sidebarController.js"></script>
|
||||
<script src="app/components/settings/settingsController.js"></script>
|
||||
<script src="app/components/builder/builderController.js"></script>
|
||||
<script src="angular.js"></script>
|
||||
<script src="vendor.js"></script>
|
||||
<script src="<%= pkg.name %>.js"></script>
|
||||
|
||||
<!-- Fav and touch icons -->
|
||||
<link rel="shortcut icon" href="assets/ico/favicon.ico">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="assets/ico/apple-touch-icon-144-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="assets/ico/apple-touch-icon-114-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="assets/ico/apple-touch-icon-72-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" href="assets/ico/apple-touch-icon-57-precomposed.png">
|
||||
</head>
|
||||
<link rel="shortcut icon" href="ico/favicon.ico">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="ico/apple-touch-icon-144-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="ico/apple-touch-icon-114-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="ico/apple-touch-icon-72-precomposed.png">
|
||||
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-57-precomposed.png">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div ng-include="template" ng-controller="MastheadController"></div>
|
||||
<div class="container">
|
||||
<div ng-include="template" ng-controller="MastheadController"></div>
|
||||
|
||||
<div id="view" ng-view></div>
|
||||
<div id="view" ng-view></div>
|
||||
|
||||
<div class="container-bottom"></div>
|
||||
<div ng-include="template" ng-controller="FooterController"></div>
|
||||
</div>
|
||||
<div class="container-bottom"></div>
|
||||
<div ng-include="template" ng-controller="FooterController"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"author": "Michael Crosby & Kevan Ahlquist",
|
||||
"name": "dockerui",
|
||||
"homepage": "https://github.com/crosbymichael/dockerui",
|
||||
"version": "0.8.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:crosbymichael/dockerui.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/crosbymichael/dockerui/issues"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://raw.githubusercontent.com/crosbymichael/dockerui/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.8.4"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"bower": "^1.5.2",
|
||||
"grunt": "~0.4.0",
|
||||
"grunt-contrib-clean": "~0.4.0",
|
||||
"grunt-contrib-concat": "~0.1.3",
|
||||
"grunt-contrib-copy": "~0.4.0",
|
||||
"grunt-contrib-jshint": "~0.2.0",
|
||||
"grunt-contrib-uglify": "^0.9.2",
|
||||
"grunt-contrib-watch": "~0.3.1",
|
||||
"grunt-html2js": "~0.1.0",
|
||||
"grunt-if": "^0.1.5",
|
||||
"grunt-karma": "~0.4.4",
|
||||
"grunt-recess": "~0.3",
|
||||
"grunt-shell": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "bower install"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
describe('ContainerController', function () {
|
||||
var $scope, $httpBackend, mockContainer, $routeParams;
|
||||
|
||||
beforeEach(module('dockerui'));
|
||||
|
||||
|
||||
beforeEach(inject(function ($rootScope, $controller, _$routeParams_) {
|
||||
|
||||
$scope = $rootScope.$new();
|
||||
$routeParams = _$routeParams_;
|
||||
$controller('ContainerController', {
|
||||
$scope: $scope
|
||||
});
|
||||
|
||||
angular.mock.inject(function (_$httpBackend_, _Container_) {
|
||||
mockContainer = _Container_;
|
||||
$httpBackend = _$httpBackend_;
|
||||
});
|
||||
}));
|
||||
|
||||
function expectGetContainer() {
|
||||
$httpBackend.expectGET('dockerapi/containers/json').respond({
|
||||
'Created': 1421817232,
|
||||
'id': 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f',
|
||||
'Image': 'dockerui:latest',
|
||||
'Name': '/dockerui'
|
||||
});
|
||||
}
|
||||
|
||||
it("a correct rename request to the Docker remote API", function () {
|
||||
|
||||
$routeParams.id = 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f';
|
||||
$scope.container = {
|
||||
'Created': 1421817232,
|
||||
'id': 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f',
|
||||
'Image': 'dockerui:latest',
|
||||
'Name': '/dockerui'
|
||||
};
|
||||
$scope.container.newContainerName = "newName";
|
||||
|
||||
var newContainerName = "newName";
|
||||
expectGetContainer();
|
||||
|
||||
$httpBackend.expectGET('dockerapi/containers/changes').respond([{"Kind": 1, "Path": "/docker.sock"}]);
|
||||
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + $routeParams.id + '/rename?name=newName').
|
||||
respond({
|
||||
'name': newContainerName
|
||||
});
|
||||
|
||||
$scope.renameContainer();
|
||||
|
||||
$httpBackend.flush();
|
||||
expect($scope.container.Name).toBe(newContainerName);
|
||||
expect($scope.container.edit).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
describe("ContainerTopController", function () {
|
||||
var $scope, $httpBackend, $routeParams;
|
||||
|
||||
beforeEach(angular.mock.module('dockerui'));
|
||||
|
||||
beforeEach(inject(function (_$rootScope_, _$httpBackend_, $controller, _$routeParams_) {
|
||||
$scope = _$rootScope_.$new();
|
||||
$httpBackend = _$httpBackend_;
|
||||
$routeParams = _$routeParams_;
|
||||
$routeParams.id = 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f';
|
||||
$controller('ContainerTopController', {
|
||||
'$scope': $scope,
|
||||
'$routeParams': $routeParams
|
||||
});
|
||||
}));
|
||||
|
||||
it("should test controller initialize", function () {
|
||||
$httpBackend.expectGET('dockerapi/containers/b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f/top?ps_args=').respond(200);
|
||||
expect($scope.ps_args).toBeDefined();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it("a correct top request to the Docker remote API", function () {
|
||||
$httpBackend.expectGET('dockerapi/containers/' + $routeParams.id + '/top?ps_args=').respond(200);
|
||||
$routeParams.id = '123456789123456789123456789';
|
||||
$scope.ps_args = 'aux';
|
||||
$httpBackend.expectGET('dockerapi/containers/' + $routeParams.id + '/top?ps_args=' + $scope.ps_args).respond(200);
|
||||
$scope.getTop();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,247 @@
|
||||
describe('startContainerController', function () {
|
||||
var scope, $location, createController, mockContainer, $httpBackend;
|
||||
|
||||
beforeEach(angular.mock.module('dockerui'));
|
||||
|
||||
beforeEach(inject(function ($rootScope, $controller, _$location_) {
|
||||
$location = _$location_;
|
||||
scope = $rootScope.$new();
|
||||
|
||||
createController = function () {
|
||||
return $controller('StartContainerController', {
|
||||
'$scope': scope
|
||||
});
|
||||
};
|
||||
|
||||
angular.mock.inject(function (_Container_, _$httpBackend_) {
|
||||
mockContainer = _Container_;
|
||||
$httpBackend = _$httpBackend_;
|
||||
});
|
||||
}));
|
||||
function expectGetContainers() {
|
||||
$httpBackend.expectGET('dockerapi/containers/json?all=1').respond([{
|
||||
'Command': './dockerui -e /docker.sock',
|
||||
'Created': 1421817232,
|
||||
'Id': 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f',
|
||||
'Image': 'dockerui:latest',
|
||||
'Names': ['/dockerui'],
|
||||
'Ports': [{
|
||||
'IP': '0.0.0.0',
|
||||
'PrivatePort': 9000,
|
||||
'PublicPort': 9000,
|
||||
'Type': 'tcp'
|
||||
}],
|
||||
'Status': 'Up 2 minutes'
|
||||
}]);
|
||||
}
|
||||
|
||||
describe('Create and start a container with port bindings', function () {
|
||||
it('should issue a correct create request to the Docker remote API', function () {
|
||||
var controller = createController();
|
||||
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
|
||||
var expectedBody = {
|
||||
'name': 'container-name',
|
||||
'ExposedPorts': {
|
||||
'9000/tcp': {}
|
||||
},
|
||||
'HostConfig': {
|
||||
'PortBindings': {
|
||||
'9000/tcp': [{
|
||||
'HostPort': '9999',
|
||||
'HostIp': '10.20.10.15'
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expectGetContainers();
|
||||
|
||||
$httpBackend.expectPOST('dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
'Id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + id + '/start').respond({
|
||||
'id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
|
||||
scope.config.name = 'container-name';
|
||||
scope.config.HostConfig.PortBindings = [{
|
||||
ip: '10.20.10.15',
|
||||
extPort: '9999',
|
||||
intPort: '9000'
|
||||
}];
|
||||
|
||||
scope.create();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Create and start a container with environment variables', function () {
|
||||
it('should issue a correct create request to the Docker remote API', function () {
|
||||
var controller = createController();
|
||||
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
|
||||
var expectedBody = {
|
||||
'name': 'container-name',
|
||||
'Env': ['SHELL=/bin/bash', 'TERM=xterm-256color']
|
||||
};
|
||||
|
||||
expectGetContainers();
|
||||
|
||||
$httpBackend.expectPOST('dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
'Id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + id + '/start').respond({
|
||||
'id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
|
||||
scope.config.name = 'container-name';
|
||||
scope.config.Env = [{
|
||||
name: 'SHELL',
|
||||
value: '/bin/bash'
|
||||
}, {
|
||||
name: 'TERM',
|
||||
value: 'xterm-256color'
|
||||
}];
|
||||
|
||||
scope.create();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Create and start a container with labels', function () {
|
||||
it('should issue a correct create request to the Docker remote API', function () {
|
||||
var controller = createController();
|
||||
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
|
||||
var expectedBody = {
|
||||
'name': 'container-name',
|
||||
'Labels': {
|
||||
"org.foo.bar": "Baz",
|
||||
"com.biz.baz": "Boo"
|
||||
}
|
||||
};
|
||||
|
||||
expectGetContainers();
|
||||
|
||||
$httpBackend.expectPOST('dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
'Id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + id + '/start').respond({
|
||||
'id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
|
||||
scope.config.name = 'container-name';
|
||||
scope.config.Labels = [{
|
||||
key: 'org.foo.bar',
|
||||
value: 'Baz'
|
||||
}, {
|
||||
key: 'com.biz.baz',
|
||||
value: 'Boo'
|
||||
}];
|
||||
|
||||
scope.create();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Create and start a container with volumesFrom', function () {
|
||||
it('should issue a correct create request to the Docker remote API', function () {
|
||||
var controller = createController();
|
||||
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
|
||||
var expectedBody = {
|
||||
HostConfig: {
|
||||
'VolumesFrom': ['parent', 'other:ro']
|
||||
},
|
||||
'name': 'container-name'
|
||||
};
|
||||
|
||||
expectGetContainers();
|
||||
|
||||
$httpBackend.expectPOST('dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
'Id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + id + '/start').respond({
|
||||
'id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
|
||||
scope.config.name = 'container-name';
|
||||
scope.config.HostConfig.VolumesFrom = [{name: 'parent'}, {name: 'other:ro'}];
|
||||
|
||||
scope.create();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Create and start a container with multiple options', function () {
|
||||
it('should issue a correct create request to the Docker remote API', function () {
|
||||
var controller = createController();
|
||||
var id = '6abd8bfba81cf8a05a76a4bdefcb36c4b66cd02265f4bfcd0e236468696ebc6c';
|
||||
var expectedBody = {
|
||||
Volumes: ['/var/www'],
|
||||
SecurityOpts: ['label:type:svirt_apache'],
|
||||
HostConfig: {
|
||||
Binds: ['/app:/app'],
|
||||
Links: ['web:db'],
|
||||
Dns: ['8.8.8.8'],
|
||||
DnsSearch: ['example.com'],
|
||||
CapAdd: ['cap_sys_admin'],
|
||||
CapDrop: ['cap_foo_bar'],
|
||||
Devices: [{
|
||||
'PathOnHost': '/dev/deviceName',
|
||||
'PathInContainer': '/dev/deviceName',
|
||||
'CgroupPermissions': 'mrw'
|
||||
}],
|
||||
LxcConf: {'lxc.utsname': 'docker'},
|
||||
ExtraHosts: ['hostname:127.0.0.1'],
|
||||
RestartPolicy: {name: 'always', MaximumRetryCount: 5}
|
||||
},
|
||||
name: 'container-name'
|
||||
};
|
||||
|
||||
expectGetContainers();
|
||||
|
||||
$httpBackend.expectPOST('dockerapi/containers/create?name=container-name', expectedBody).respond({
|
||||
'Id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
$httpBackend.expectPOST('dockerapi/containers/' + id + '/start').respond({
|
||||
'id': id,
|
||||
'Warnings': null
|
||||
});
|
||||
|
||||
scope.config.name = 'container-name';
|
||||
scope.config.Volumes = [{name: '/var/www'}];
|
||||
scope.config.SecurityOpts = [{name: 'label:type:svirt_apache'}];
|
||||
scope.config.NetworkDisabled = true;
|
||||
scope.config.Tty = true;
|
||||
scope.config.OpenStdin = true;
|
||||
scope.config.StdinOnce = true;
|
||||
|
||||
scope.config.HostConfig.Binds = [{name: '/app:/app'}];
|
||||
scope.config.HostConfig.Links = [{name: 'web:db'}];
|
||||
scope.config.HostConfig.Dns = [{name: '8.8.8.8'}];
|
||||
scope.config.HostConfig.DnsSearch = [{name: 'example.com'}];
|
||||
scope.config.HostConfig.CapAdd = [{name: 'cap_sys_admin'}];
|
||||
scope.config.HostConfig.CapDrop = [{name: 'cap_foo_bar'}];
|
||||
scope.config.HostConfig.PublishAllPorts = true;
|
||||
scope.config.HostConfig.Privileged = true;
|
||||
scope.config.HostConfig.RestartPolicy = {name: 'always', MaximumRetryCount: 5};
|
||||
scope.config.HostConfig.Devices = [{
|
||||
'PathOnHost': '/dev/deviceName',
|
||||
'PathInContainer': '/dev/deviceName',
|
||||
'CgroupPermissions': 'mrw'
|
||||
}];
|
||||
scope.config.HostConfig.LxcConf = [{name: 'lxc.utsname', value: 'docker'}];
|
||||
scope.config.HostConfig.ExtraHosts = [{host: 'hostname', ip: '127.0.0.1'}];
|
||||
|
||||
scope.create();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
describe("StatsController", function () {
|
||||
var $scope, $httpBackend, $routeParams;
|
||||
|
||||
beforeEach(angular.mock.module('dockerui'));
|
||||
|
||||
beforeEach(inject(function (_$rootScope_, _$httpBackend_, $controller, _$routeParams_) {
|
||||
$scope = _$rootScope_.$new();
|
||||
$httpBackend = _$httpBackend_;
|
||||
$routeParams = _$routeParams_;
|
||||
$routeParams.id = 'b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f';
|
||||
$controller('StatsController', {
|
||||
'$scope': $scope,
|
||||
'$routeParams': $routeParams
|
||||
});
|
||||
}));
|
||||
|
||||
//it("should test controller initialize", function () {
|
||||
// $httpBackend.expectGET('dockerapi/containers/b17882378cee8ec0136f482681b764cca430befd52a9bfd1bde031f49b8bba9f/stats?stream=false').respond(200);
|
||||
// //expect($scope.ps_args).toBeDefined();
|
||||
// $httpBackend.flush();
|
||||
//});
|
||||
//
|
||||
//it("a correct top request to the Docker remote API", function () {
|
||||
// //$httpBackend.expectGET('dockerapi/containers/' + $routeParams.id + '/top?ps_args=').respond(200);
|
||||
// //$routeParams.id = '123456789123456789123456789';
|
||||
// //$scope.ps_args = 'aux';
|
||||
// //$httpBackend.expectGET('dockerapi/containers/' + $routeParams.id + '/top?ps_args=' + $scope.ps_args).respond(200);
|
||||
// //$scope.getTop();
|
||||
// //$httpBackend.flush();
|
||||
//});
|
||||
});
|
||||
@@ -0,0 +1,355 @@
|
||||
describe('filters', function () {
|
||||
beforeEach(module('dockerui.filters'));
|
||||
|
||||
describe('truncate', function () {
|
||||
it('should truncate the string to 10 characters ending in "..." by default', inject(function (truncateFilter) {
|
||||
expect(truncateFilter('this is 20 chars long')).toBe('this is...');
|
||||
}));
|
||||
|
||||
it('should truncate the string to 7 characters ending in "..."', inject(function (truncateFilter) {
|
||||
expect(truncateFilter('this is 20 chars long', 7)).toBe('this...');
|
||||
}));
|
||||
|
||||
it('should truncate the string to 10 characters ending in "???"', inject(function (truncateFilter) {
|
||||
expect(truncateFilter('this is 20 chars long', 10, '???')).toBe('this is???');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('statusbadge', function () {
|
||||
it('should be "important" when input is "Ghost"', inject(function (statusbadgeFilter) {
|
||||
expect(statusbadgeFilter('Ghost')).toBe('important');
|
||||
}));
|
||||
|
||||
it('should be "success" when input is "Exit 0"', inject(function (statusbadgeFilter) {
|
||||
expect(statusbadgeFilter('Exit 0')).toBe('success');
|
||||
}));
|
||||
|
||||
it('should be "warning" when exit code is non-zero', inject(function (statusbadgeFilter) {
|
||||
expect(statusbadgeFilter('Exit 1')).toBe('warning');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('getstatetext', function () {
|
||||
|
||||
it('should return an empty string when state is undefined', inject(function (getstatetextFilter) {
|
||||
expect(getstatetextFilter(undefined)).toBe('');
|
||||
}));
|
||||
|
||||
it('should detect a Ghost state', inject(function (getstatetextFilter) {
|
||||
var state = {
|
||||
Ghost: true,
|
||||
Running: true,
|
||||
Paused: false
|
||||
};
|
||||
expect(getstatetextFilter(state)).toBe('Ghost');
|
||||
}));
|
||||
|
||||
it('should detect a Paused state', inject(function (getstatetextFilter) {
|
||||
var state = {
|
||||
Ghost: false,
|
||||
Running: true,
|
||||
Paused: true
|
||||
};
|
||||
expect(getstatetextFilter(state)).toBe('Running (Paused)');
|
||||
}));
|
||||
|
||||
it('should detect a Running state', inject(function (getstatetextFilter) {
|
||||
var state = {
|
||||
Ghost: false,
|
||||
Running: true,
|
||||
Paused: false
|
||||
};
|
||||
expect(getstatetextFilter(state)).toBe('Running');
|
||||
}));
|
||||
|
||||
it('should detect a Stopped state', inject(function (getstatetextFilter) {
|
||||
var state = {
|
||||
Ghost: false,
|
||||
Running: false,
|
||||
Paused: false
|
||||
};
|
||||
expect(getstatetextFilter(state)).toBe('Stopped');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('getstatelabel', function () {
|
||||
it('should return default when state is undefined', inject(function (getstatelabelFilter) {
|
||||
expect(getstatelabelFilter(undefined)).toBe('label-default');
|
||||
}));
|
||||
|
||||
it('should return label-important when a ghost state is detected', inject(function (getstatelabelFilter) {
|
||||
var state = {
|
||||
Ghost: true,
|
||||
Running: true,
|
||||
Paused: false
|
||||
};
|
||||
expect(getstatelabelFilter(state)).toBe('label-important');
|
||||
}));
|
||||
|
||||
it('should return label-success when a running state is detected', inject(function (getstatelabelFilter) {
|
||||
var state = {
|
||||
Ghost: false,
|
||||
Running: true,
|
||||
Paused: false
|
||||
};
|
||||
expect(getstatelabelFilter(state)).toBe('label-success');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('humansize', function () {
|
||||
it('should return n/a when size is zero', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(0)).toBe('n/a');
|
||||
}));
|
||||
|
||||
it('should handle Bytes values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(512)).toBe('512 Bytes');
|
||||
}));
|
||||
|
||||
it('should handle KB values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(5 * 1024)).toBe('5 KB');
|
||||
}));
|
||||
|
||||
it('should handle MB values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(5 * 1024 * 1024)).toBe('5.0 MB');
|
||||
}));
|
||||
|
||||
it('should handle GB values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(5 * 1024 * 1024 * 1024)).toBe('5.00 GB');
|
||||
}));
|
||||
|
||||
it('should handle TB values', inject(function (humansizeFilter) {
|
||||
expect(humansizeFilter(5 * 1024 * 1024 * 1024 * 1024)).toBe('5.000 TB');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('containername', function () {
|
||||
it('should strip the leading slash from container name', inject(function (containernameFilter) {
|
||||
var container = {
|
||||
Names: ['/elegant_ardinghelli']
|
||||
};
|
||||
|
||||
expect(containernameFilter(container)).toBe('elegant_ardinghelli');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('repotag', function () {
|
||||
it('should not display empty repo tag', inject(function (repotagFilter) {
|
||||
var image = {
|
||||
RepoTags: ['<none>:<none>']
|
||||
};
|
||||
expect(repotagFilter(image)).toBe('');
|
||||
}));
|
||||
|
||||
it('should display a normal repo tag', inject(function (repotagFilter) {
|
||||
var image = {
|
||||
RepoTags: ['ubuntu:latest']
|
||||
};
|
||||
expect(repotagFilter(image)).toBe('ubuntu:latest');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('getdate', function () {
|
||||
it('should convert the Docker date to a human readable form', inject(function (getdateFilter) {
|
||||
expect(getdateFilter(1420424998)).toBe('Sun Jan 04 2015');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('errorMsgFilter', function () {
|
||||
it('should convert the $resource object to a string message',
|
||||
inject(function (errorMsgFilter) {
|
||||
var response = {
|
||||
'0': 'C',
|
||||
'1': 'o',
|
||||
'2': 'n',
|
||||
'3': 'f',
|
||||
'4': 'l',
|
||||
'5': 'i',
|
||||
'6': 'c',
|
||||
'7': 't',
|
||||
'8': ',',
|
||||
'9': ' ',
|
||||
'10': 'T',
|
||||
'11': 'h',
|
||||
'12': 'e',
|
||||
'13': ' ',
|
||||
'14': 'n',
|
||||
'15': 'a',
|
||||
'16': 'm',
|
||||
'17': 'e',
|
||||
'18': ' ',
|
||||
'19': 'u',
|
||||
'20': 'b',
|
||||
'21': 'u',
|
||||
'22': 'n',
|
||||
'23': 't',
|
||||
'24': 'u',
|
||||
'25': '-',
|
||||
'26': 's',
|
||||
'27': 'l',
|
||||
'28': 'e',
|
||||
'29': 'e',
|
||||
'30': 'p',
|
||||
'31': '-',
|
||||
'32': 'r',
|
||||
'33': 'u',
|
||||
'34': 'n',
|
||||
'35': 't',
|
||||
'36': 'i',
|
||||
'37': 'm',
|
||||
'38': 'e',
|
||||
'39': ' ',
|
||||
'40': 'i',
|
||||
'41': 's',
|
||||
'42': ' ',
|
||||
'43': 'a',
|
||||
'44': 'l',
|
||||
'45': 'r',
|
||||
'46': 'e',
|
||||
'47': 'a',
|
||||
'48': 'd',
|
||||
'49': 'y',
|
||||
'50': ' ',
|
||||
'51': 'a',
|
||||
'52': 's',
|
||||
'53': 's',
|
||||
'54': 'i',
|
||||
'55': 'g',
|
||||
'56': 'n',
|
||||
'57': 'e',
|
||||
'58': 'd',
|
||||
'59': ' ',
|
||||
'60': 't',
|
||||
'61': 'o',
|
||||
'62': ' ',
|
||||
'63': 'b',
|
||||
'64': '6',
|
||||
'65': '9',
|
||||
'66': 'e',
|
||||
'67': '5',
|
||||
'68': '3',
|
||||
'69': 'a',
|
||||
'70': '6',
|
||||
'71': '2',
|
||||
'72': '2',
|
||||
'73': 'c',
|
||||
'74': '8',
|
||||
'75': '.',
|
||||
'76': ' ',
|
||||
'77': 'Y',
|
||||
'78': 'o',
|
||||
'79': 'u',
|
||||
'80': ' ',
|
||||
'81': 'h',
|
||||
'82': 'a',
|
||||
'83': 'v',
|
||||
'84': 'e',
|
||||
'85': ' ',
|
||||
'86': 't',
|
||||
'87': 'o',
|
||||
'88': ' ',
|
||||
'89': 'd',
|
||||
'90': 'e',
|
||||
'91': 'l',
|
||||
'92': 'e',
|
||||
'93': 't',
|
||||
'94': 'e',
|
||||
'95': ' ',
|
||||
'96': '(',
|
||||
'97': 'o',
|
||||
'98': 'r',
|
||||
'99': ' ',
|
||||
'100': 'r',
|
||||
'101': 'e',
|
||||
'102': 'n',
|
||||
'103': 'a',
|
||||
'104': 'm',
|
||||
'105': 'e',
|
||||
'106': ')',
|
||||
'107': ' ',
|
||||
'108': 't',
|
||||
'109': 'h',
|
||||
'110': 'a',
|
||||
'111': 't',
|
||||
'112': ' ',
|
||||
'113': 'c',
|
||||
'114': 'o',
|
||||
'115': 'n',
|
||||
'116': 't',
|
||||
'117': 'a',
|
||||
'118': 'i',
|
||||
'119': 'n',
|
||||
'120': 'e',
|
||||
'121': 'r',
|
||||
'122': ' ',
|
||||
'123': 't',
|
||||
'124': 'o',
|
||||
'125': ' ',
|
||||
'126': 'b',
|
||||
'127': 'e',
|
||||
'128': ' ',
|
||||
'129': 'a',
|
||||
'130': 'b',
|
||||
'131': 'l',
|
||||
'132': 'e',
|
||||
'133': ' ',
|
||||
'134': 't',
|
||||
'135': 'o',
|
||||
'136': ' ',
|
||||
'137': 'a',
|
||||
'138': 's',
|
||||
'139': 's',
|
||||
'140': 'i',
|
||||
'141': 'g',
|
||||
'142': 'n',
|
||||
'143': ' ',
|
||||
'144': 'u',
|
||||
'145': 'b',
|
||||
'146': 'u',
|
||||
'147': 'n',
|
||||
'148': 't',
|
||||
'149': 'u',
|
||||
'150': '-',
|
||||
'151': 's',
|
||||
'152': 'l',
|
||||
'153': 'e',
|
||||
'154': 'e',
|
||||
'155': 'p',
|
||||
'156': '-',
|
||||
'157': 'r',
|
||||
'158': 'u',
|
||||
'159': 'n',
|
||||
'160': 't',
|
||||
'161': 'i',
|
||||
'162': 'm',
|
||||
'163': 'e',
|
||||
'164': ' ',
|
||||
'165': 't',
|
||||
'166': 'o',
|
||||
'167': ' ',
|
||||
'168': 'a',
|
||||
'169': ' ',
|
||||
'170': 'c',
|
||||
'171': 'o',
|
||||
'172': 'n',
|
||||
'173': 't',
|
||||
'174': 'a',
|
||||
'175': 'i',
|
||||
'176': 'n',
|
||||
'177': 'e',
|
||||
'178': 'r',
|
||||
'179': ' ',
|
||||
'180': 'a',
|
||||
'181': 'g',
|
||||
'182': 'a',
|
||||
'183': 'i',
|
||||
'184': 'n',
|
||||
'185': '.',
|
||||
'186': '\n',
|
||||
'$promise': {},
|
||||
'$resolved': true
|
||||
};
|
||||
var message = 'Conflict, The name ubuntu-sleep-runtime is already assigned to b69e53a622c8. You have to delete (or rename) that container to be able to assign ubuntu-sleep-runtime to a container again.\n';
|
||||
expect(errorMsgFilter(response)).toBe(message);
|
||||
}));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
// base path, that will be used to resolve files and exclude
|
||||
basePath = '../..';
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files = [
|
||||
JASMINE,
|
||||
JASMINE_ADAPTER,
|
||||
'dist/angular.js',
|
||||
'dist/vendor.js',
|
||||
'dist/dockerui.js',
|
||||
'bower_components/angular-mocks/angular-mocks.js',
|
||||
'test/unit/**/*.spec.js'
|
||||
];
|
||||
|
||||
// use dots reporter, as travis terminal does not support escaping sequences
|
||||
// possible values: 'dots' || 'progress'
|
||||
reporters = 'progress';
|
||||
|
||||
// these are default values, just to show available options
|
||||
|
||||
// web server port
|
||||
port = 8089;
|
||||
|
||||
// cli runner port
|
||||
runnerPort = 9109;
|
||||
|
||||
urlRoot = '/__test/';
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors = true;
|
||||
|
||||
// level of logging
|
||||
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
|
||||
logLevel = LOG_INFO;
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch = false;
|
||||
|
||||
// polling interval in ms (ignored on OS that support inotify)
|
||||
autoWatchInterval = 0;
|
||||
|
||||
// Start these browsers, currently available:
|
||||
// - Chrome
|
||||
// - ChromeCanary
|
||||
// - Firefox
|
||||
// - Opera
|
||||
// - Safari
|
||||
// - PhantomJS
|
||||
browsers = ['Chrome'];
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, it capture browsers, run tests and exit
|
||||
singleRun = true;
|
||||