Compare commits

...

158 Commits

Author SHA1 Message Date
Kevan Ahlquist 106b0f3de8 Merge branch 'crosbymichael-master' into crosbymichael-dist 2015-12-07 00:04:42 -06:00
Kevan Ahlquist 91968b9f4b Switch to local jquery.gritter, fix undefined variable error in minified build due to "use strict"'; 2015-12-07 00:01:08 -06:00
Kevan Ahlquist b3d41c6504 Merge branch 'crosbymichael-master' into crosbymichael-dist 2015-12-06 15:09:33 -06:00
Kevan Ahlquist d881d97c06 Merge pull request #155 from crosbymichael/labels
Labels and Image tags
2015-12-06 15:01:02 -06:00
Kevan Ahlquist 1dd1ce2f47 Allow tagging of repo and version and deletion of multiple tags. 2015-11-27 00:31:03 -06:00
Kevan Ahlquist 640c0b39c1 Add labels to container start and display. 2015-11-26 22:33:41 -06:00
Kevan Ahlquist 855a473d68 Merge branch 'crosbymichael-master' into crosbymichael-dist 2015-11-26 21:19:45 -06:00
Kevan Ahlquist 25a4607ad6 Merge pull request #154 from crosbymichael/restart-policy
Don't use self-closing select element
2015-11-26 21:15:02 -06:00
Kevan Ahlquist 064ffe2a7c Don't use self-closing select element, causes silent render error on Chrome. 2015-11-26 21:12:53 -06:00
Kevan Ahlquist e3f90575bf Merge branch 'crosbymichael-master' into crosbymichael-dist 2015-11-17 01:08:40 -06:00
Kevan Ahlquist 4d759fbfb9 Merge pull request #144 from linquize/padding
Remove body.padding-top, it wasted too much space
2015-10-11 14:38:33 -05:00
Linquize 9c854cb39d Remove body.padding-top, it wasted too much space 2015-10-11 21:13:04 +08:00
Kevan Ahlquist 27799e7033 Merge pull request #143 from linquize/scale
Fix containers, images chart scale too small
2015-10-10 16:33:43 -05:00
User 3ac1ac6086 Fix containers, images chart scale too small 2015-10-10 17:04:19 +08:00
Kevan Ahlquist f984770906 Merge branch 'master' into crosbymichael-dist 2015-09-15 22:45:31 -05:00
Kevan Ahlquist 01db85ebc7 Merge pull request #140 from kevana/bower
Fix builds
2015-09-15 22:28:26 -05:00
Kevan Ahlquist cdf4767d7d Use grunt for building
Add shell commands to gruntfile for local dev.

Build binary if needed for `grunt build`, always build for `grunt release`. Exclude unused assets from jquery.gritter and vis.

Remove Makefile
2015-09-15 00:24:11 -05:00
Kevan Ahlquist 9bdd96527c Convert services.js for safe minification, preserve license comments in javascript files, add missing recess target, remove oboe-browser.min.js from assets. 2015-09-15 00:24:11 -05:00
Kevan Ahlquist 13e3fed351 Use bower for Angular dependencies. 2015-09-15 00:22:11 -05:00
Kevan Ahlquist 115a293e58 Merge pull request #13 from crosbymichael/master
Pull latest from origin
2015-09-13 16:18:12 -05:00
Kevan Ahlquist 07dd08a6d0 Merge branch 'crosbymichael-master' into dist
Conflicts:
	app/app.js
	package.json
2015-09-09 23:06:19 -05:00
Kevan Ahlquist 9c985e63ee Merge pull request #135 from kevana/stats
Add view for container stats endpoint.
2015-09-09 22:58:37 -05:00
Kevan Ahlquist c57a75f78f Merge branch 'master' into stats
Conflicts:
	app/components/containers/containers.html
	app/components/images/images.html
2015-09-09 22:55:07 -05:00
Kevan Ahlquist a96298db59 Merge pull request #137 from dasJ/filter
Allow filtering containers and images
2015-09-09 22:48:19 -05:00
Kevan Ahlquist 061921af8e Merge pull request #136 from dasJ/editlabel
Rename save button when renaming container
2015-09-03 10:53:53 -05:00
Janne Heß 6719c84a7a Allow filtering images 2015-09-03 13:38:23 +02:00
Janne Heß 13f3335098 Allow filtering containers
Closes #84
2015-09-03 13:38:09 +02:00
Janne Heß a79c26a7a4 Rename save button when renaming container
Closes #120
2015-09-03 13:00:43 +02:00
Kevan Ahlquist abccb316f8 Updated docs links in services. 2015-08-29 02:38:03 -05:00
Kevan Ahlquist f14e954a9b Lower animation steps, keep 20 datapoints, update every 2 seconds. 2015-08-29 02:11:13 -05:00
Kevan Ahlquist 5d4a2a7c25 Fixed dashboard graphs, Chart.js doesn't like charts being hidden on creation, fixed routing issue to dashboard, version bump. 2015-08-29 01:57:08 -05:00
Kevan Ahlquist ce90515c95 Added network graph 2015-08-29 00:40:24 -05:00
Kevan Ahlquist 5a51495432 Added remaining memory stats, change humansize filter to give more accurate sizes. 2015-08-28 23:26:39 -05:00
Kevan Ahlquist b99fe5bf55 Added memory usage graph. 2015-08-27 01:41:45 -05:00
Kevan Ahlquist d243a83c5c Implemented CPU usage graph, upgraded to Chart.js 1.0.2, broke dashboard charts, tests. 2015-08-26 01:28:08 -05:00
Kevan Ahlquist 6cb658a1ef Fixed grunt style warnings. 2015-08-25 01:07:08 -05:00
Kevan Ahlquist 8a7f8f7c37 Whitespace diff, ran everything through the formatter. 2015-08-25 00:59:54 -05:00
Kevan Ahlquist b7daf91723 Current progress on stats page, nonfunctional. 2015-08-25 00:55:54 -05:00
Kevan Ahlquist 5dfc079afd Whoah there, back down to 0.8.0-beta. 2015-08-24 20:52:49 -05:00
Kevan Ahlquist f7949eb1d3 Version bump to 0.9.0-beta and remote API 1.20. 2015-08-24 20:51:12 -05:00
Kevan Ahlquist 7e057bb3bb Pulling latest into dist. 2015-08-24 20:46:58 -05:00
Kevan Ahlquist 66894e7596 Merge pull request #133 from kgutwin/f-volume-network
Fix VolumesFrom display issue with Swarm, add option to view stopped containers in network tab.
2015-08-24 20:40:11 -05:00
Karl Gutwin 7748adf0e6 Color and edge cleanup 2015-08-12 16:38:50 -04:00
Karl Gutwin 758ac41648 Color and edge cleanup 2015-08-12 16:37:19 -04:00
Karl Gutwin 3db2008c39 Have to provide a color 2015-08-12 16:22:31 -04:00
Karl Gutwin 34a3f8186a Missed passing run state 2015-08-12 16:20:28 -04:00
Karl Gutwin 8e0baf0e37 Optionally include stopped containers 2015-08-12 16:01:33 -04:00
Karl Gutwin 017863bfc0 VolumesFrom support container names 2015-08-12 14:47:47 -04:00
Kevan Ahlquist f4ef63b8f8 Merge pull request #125 from hmichopoulos/dockerui-118
dockerui-118 Pull images / create container from repo image
2015-07-31 19:48:09 -05:00
Haris Michopoulos 394f2f2387 dockerui-118 How to pull images / create container from repo image ?
-Fixed ViewSpinner variable.
-Hide the modal on submit, show Spinner and show again modal if needed (in case of error)
-Clear modal values after successful pull
2015-06-22 23:30:16 +02:00
Haris Michopoulos 5dd094e0b1 dockerui-118 How to pull images / create container from repo image ?
Added a modal dialog that gets user's input (registry, repo, image name and tag) and performs the pull. Some hack was needed to parse the response, since it is not valid json (actually, it seems to be concatenated json objects).
2015-06-05 17:21:52 +02:00
Kevan Ahlquist 7d073fdf0d Merge pull request #100 from crosbymichael/events
Events
2015-05-28 16:33:36 -05:00
Kevan Ahlquist 07e10317fc Merge branch 'crosbymichael-master' into dist 2015-05-20 23:59:16 -05:00
Kevan Ahlquist e806214d0b Merge branch 'master' into events 2015-05-20 23:44:32 -05:00
Kevan Ahlquist 419bf0aa77 Merge pull request #121 from cristiangauma/master
Adding protocol select menu in portBinding during container creation
2015-05-20 23:32:23 -05:00
Cristiangauma cad8a6dc91 Adding protocol select menu in portBinding during container creation 2015-05-19 22:42:24 +02:00
Kevan Ahlquist ef3596ff32 Merge branch 'master' into events
Conflicts:
	app/app.js
	index.html
2015-05-14 01:33:08 -05:00
Kevan Ahlquist 74dcec8b6b Merge pull request #12 from crosbymichael/master
Sync fork with latest master.
2015-05-14 01:19:42 -05:00
Kevan Ahlquist c0ae0d8d3f Merge branch 'crosbymichael-master' into bharat-add-commit
Conflicts:
	app/components/container/container.html
2015-05-14 01:01:26 -05:00
Kevan Ahlquist 4637c5fbbd Merge pull request #115 from BrockFredin/master
Restart Feature - Implementing restart feature for single containers
2015-05-14 00:50:26 -05:00
Fredin, Brock 74c92de1d3 Revert "DockerUI - Comment in Services was broken link. Updated the comment to include the most accurate link representing the Docker API reference material"
This reverts commit f167f5b49e.
2015-05-14 00:27:55 -05:00
Fredin, Brock 1688dbd8a0 Merge branch 'master' of https://github.com/BrockFredin/dockerui
* 'master' of https://github.com/BrockFredin/dockerui:
  Restart Feature - Implementing restart feature for single containers
2015-05-14 00:22:19 -05:00
Fredin, Brock f167f5b49e DockerUI - Comment in Services was broken link. Updated the comment to include the most accurate link representing the Docker API reference material 2015-05-14 00:21:53 -05:00
Bharath Thiruveedula 968efed6c3 Adding commit button 2015-05-12 22:23:19 +05:30
Fredin, Brock c10dc38a89 Restart Feature - Implementing restart feature for single containers 2015-05-12 00:52:12 -05:00
Kevan Ahlquist 2bc6b2995d Merge pull request #110 from rabelenda/master
Add controls for easy visualization when too many containers
2015-05-06 15:48:04 -05:00
Roger Abelenda edd6a41d6e Add actions to show upstream and downstream dependencies of a container for easy visualization.
Additionally updated angular-vis to use version 0.0.4 which already includes the changes of https://github.com/visjs/angular-visjs/pull/22.
2015-05-02 12:04:31 -03:00
Roger Abelenda 9e713b7b81 Add several
- Add search box to easily find containers in network. This required an addition to angular-vis which is in a pending pull request: https://github.com/visjs/angular-visjs/pull/22
- Add tooltip to easily visualize container id and image name.
- Add options to hide containers (and showAll to reset view) to ease visualization when there are too many containers.
2015-05-01 21:41:10 -03:00
Kevan Ahlquist 1f8f08e998 Merge pull request #109 from rabelenda/master
Fix code for lint checks and karma tests
2015-04-28 22:35:16 -05:00
Roger Abelenda 008884ec59 Change code to pass lint checks and add angular-vis to karma config for tests to pass. 2015-04-28 23:09:03 -03:00
Kevan Ahlquist 5a21923f5b Merge branch 'crosbymichael-master' into dist 2015-04-26 02:25:17 -05:00
Kevan Ahlquist 230bfd15ac Merge pull request #108 from rabelenda/master
Add a containers network tab, visualize links and volumesFrom relations between containers.
2015-04-26 02:19:52 -05:00
Roger Abelenda 7cda9a0c7b Add a containers network tab to be able to easily visualize links and volumesFrom relations between containers. 2015-04-25 14:02:24 -03:00
Kevan Ahlquist 6012453d2d Switch to ngOboe, for DI and promise interface. 2015-04-18 23:06:58 -07:00
Kevan Ahlquist 2514672c35 Merge commit '964eac6d530a7600459f92c312d79ee9365b0f9b' into events
Conflicts:
	app/app.js
	test/unit/app/components/containerController.spec.js
2015-04-18 21:26:15 -07:00
Kevan Ahlquist ae3d1c305d Bump version to 0.7.0. 2015-04-03 11:25:45 -05:00
Kevan Ahlquist d04b17efeb Build latest changes. 2015-04-02 10:01:21 -05:00
Kevan Ahlquist 0520fa739a Merge branch 'github-master' into github-dist
Latest changes from Top feature.
2015-04-02 09:50:34 -05:00
Kevan Ahlquist 964eac6d53 Merge pull request #102 from houssemba/master
Add Docker top feature
2015-04-02 09:40:01 -05:00
Houssem BELHADJ AHMED ede37e2e79 Add Docker top feature 2015-03-31 23:37:03 +02:00
Kevan Ahlquist 6080e097da Added date ranges to events. 2015-03-27 16:25:30 -05:00
Kevan Ahlquist 14bfc57870 Angular version bump to 1.3.15. 2015-03-27 15:16:44 -05:00
Kevan Ahlquist 32a4703a53 Added events endpoint. 2015-03-26 14:12:44 -05:00
Kevan Ahlquist f40d645fb3 Bump other app version. 2015-03-18 20:03:50 -05:00
Kevan Ahlquist 961b72072a Pull latest changes into dist, version bump to 0.7.0-beta 2015-03-18 19:45:51 -05:00
Kevan Ahlquist d6c524f960 Merge pull request #99 from houssemba/master
Rename feature
2015-03-18 19:28:55 -05:00
Houssem BELHADJ AHMED c971189286 Add rename feature 2015-03-12 22:28:01 +01:00
Houssem BELHADJ AHMED f29eaa28ba Jshint & test fix 2015-03-08 18:19:51 +01:00
Kevan Ahlquist 68ab1a302d Pull latest changes from master and build dist. 2015-02-25 00:33:13 -06:00
Kevan Ahlquist 9afe57e0ec Merge pull request #86 from kevana/master
Small fixes
2015-02-24 23:33:15 -06:00
Kevan Ahlquist fb0a0827b6 Merge commit '974ed1fe0598533747c2cf4c2f21d7830d484431'
Conflicts:
	app/components/containerLogs/containerLogsController.js
2015-02-24 23:32:38 -06:00
Kevan Ahlquist 22cdb94e5d Removed dist files. 2015-02-22 02:40:50 -06:00
Kevan Ahlquist 974ed1fe05 Merge pull request #77 from jonny64/logs_tail
logs page: slow on huge logs - 'show last lines' option
2015-02-21 16:03:17 -06:00
jonny64 66ba60c475 logs: added spinner during refresh 2015-02-21 18:09:12 +00:00
Kevan Ahlquist 65f7e32f94 Strip headers from logs before display, fixes #94 2015-02-18 22:56:55 -06:00
Kevan Ahlquist dd21a4025b Merge commit '1843836f229cadcdace5ca0c1b95fdb21dcfea2b'
Conflicts:
	dist/dockerui.js
	dist/templates/app.js
2015-02-18 22:46:37 -06:00
Kevan Ahlquist 2223c8f1d1 Merge branch 'dist' of https://github.com/crosbymichael/dockerui into dist 2015-02-18 01:08:03 -06:00
Kevan Ahlquist 3635e95138 Adding latest build to dist. 2015-02-18 01:06:23 -06:00
Kevan Ahlquist 1843836f22 Fixed release target in Makefile. 2015-02-18 01:05:51 -06:00
Kevan Ahlquist 059115e523 Adding latest build to dist. 2015-02-18 01:04:26 -06:00
Kevan Ahlquist 44ba4e8bb8 Merge commit '22287e6a56c5a8824da94f2f6b9a464fa3da4192' 2015-02-18 00:55:40 -06:00
Kevan Ahlquist 2adaa33fdb Remove dist folder from master. 2015-02-17 22:48:23 -06:00
jonny64 b257216194 logs: reload button 2015-02-16 18:42:46 +00:00
jonny64 df5e7bc867 logs: removed debugging 2015-02-16 18:18:34 +00:00
jonny64 53041efaf1 logs: tail also STDERR 2015-02-16 17:42:02 +00:00
jonny64 87131f4ae3 Merge remote-tracking branch 'origin/master' into logs_tail 2015-02-16 17:37:04 +00:00
Kevan Ahlquist f66bcfd4ea Merge latest changes from crosbymichael/dockerui. 2015-02-14 14:23:48 -06:00
Kevan Ahlquist 1fb7338838 Merge pull request #90 from dasJ/dropdown
Add nice cursor for dropdown menus
2015-02-14 14:10:33 -06:00
Kevan Ahlquist da60a70f43 Merge pull request #92 from dasJ/image
Fix values on images page
2015-02-14 14:08:55 -06:00
Kevan Ahlquist 8848d2d53c Merge pull request #89 from dasJ/master
Improve settings page
2015-02-14 14:02:43 -06:00
Janne Heß ad6e0acab1 Correctly humanize RAM size on info page 2015-02-13 21:35:57 +01:00
Janne Heß 14d4397868 Rename settings page to info
You can't change any settings in the settings page, so the title is
missleading.
2015-02-13 21:35:57 +01:00
Janne Heß f32aadedfb Add more information to the settings page
Now shows all information we can get from the info object.
Exceptions: Name and NEventsListener, those don't seem to work.
2015-02-13 21:35:57 +01:00
Janne Heß b888081f54 Fix values on images page
Update to current Docker API and add virtual size of the image.
Also replace the deprecated Comment with some informations about the
build machine.
2015-02-13 21:22:08 +01:00
Kevan Ahlquist b00b61ce25 Merge pull request #91 from dasJ/checkbox
Make text next to "Display All" clickable
2015-02-13 09:10:07 -06:00
Janne Heß 9284e4b451 Make text next to "Display All" clickable 2015-02-13 14:32:03 +01:00
Janne Heß 716e25703e Add nice cursor for dropdown menus 2015-02-13 14:18:36 +01:00
Kevan Ahlquist 65d0b0110d Consume image IDs that contain slashes, fix container port labels, be nicer about old browsers. 2015-02-13 01:13:04 -06:00
Kevan Ahlquist 9baa85f2e4 Merge pull request #82 from kevana/master
Added restart to container list actions.
2015-02-13 01:00:32 -06:00
William McGann 22287e6a56 Using centurylink's golang-builder to create tiny docker image, going from ~400MB to 5.4MB. 2015-02-13 01:11:50 -05:00
Kevan Ahlquist 0d4274fdeb Added restart to container list actions. 2015-02-09 23:10:05 -06:00
Kevan Ahlquist 4d9d66e9f9 Merge pull request #81 from kevana/master
Add timestamp option to stderr
2015-02-09 23:02:01 -06:00
Kevan Ahlquist c7ebe9d881 Added timestamp option to stderr. 2015-02-09 23:00:34 -06:00
Kevan Ahlquist c798a95fb3 Merge pull request #9 from crosbymichael/master
Pull latest changes
2015-02-09 22:48:32 -06:00
Kevan Ahlquist d11c849a0f Merge pull request #79 from Slamper/master
Fix of HostConfig being lost when starting containers from the list
2015-02-09 22:45:00 -06:00
Hendrik Hofstadt 8abb445bd4 nicer solution for checking the action 2015-02-09 20:06:28 +01:00
Hendrik Hofstadt dabb1926a5 fixed all HostConfigs being lost when starting from the list 2015-02-08 19:59:26 +01:00
Hendrik Hofstadt 829ff5da73 fixed loss of hostconfig when starting with batch 2015-02-07 23:02:35 +01:00
jonny64 aead1fcc29 logs page: slow on huge logs - 'show last lines' option 2015-02-05 19:23:57 +00:00
Kevan Ahlquist ccd27f203d Merge pull request #76 from kevana/master
Fixes #75, HostConfig issues
2015-02-01 17:16:43 -06:00
Kevan Ahlquist 3d251fdba4 Fixes #75 2015-02-01 17:15:58 -06:00
Kevan Ahlquist 4b4edc6c22 Merge pull request #72 from kevana/master
Container start parameters
2015-02-01 01:35:37 -06:00
Kevan Ahlquist 990456dbdd Style tweaks for container start modal. 2015-01-31 14:42:10 -06:00
Kevan Ahlquist fd68039cb9 Added ExtraHosts option. 2015-01-28 19:26:48 -06:00
Kevan Ahlquist a80971dd27 Implemented RestartPolicy, Devices, and LxcConf. Unified label convention. 2015-01-28 02:28:41 -06:00
Kevan Ahlquist 687ed7bac2 Fixed Cmd parsing for strings, added error handling to start step. 2015-01-25 19:18:46 -06:00
Kevan Ahlquist 314fc51f6d Updated /dist for release. 2015-01-25 18:12:34 -06:00
Kevan Ahlquist f75d298fe1 Cleaned up controller methods, Added placeholders, fixed PortBindings and error messages. 2015-01-25 17:59:08 -06:00
Kevan Ahlquist 4682ae4ca7 Implemented remaining container creation options. 2015-01-25 15:52:40 -06:00
Kevan Ahlquist c8213bbf33 Added VolumesFrom to container start. 2015-01-20 23:31:57 -06:00
Kevan Ahlquist 4d22a04484 Added ENV variables to container start. 2015-01-20 01:44:30 -06:00
Kevan Ahlquist 76d7e280f9 Added PortBindings to container start. 2015-01-18 23:27:56 -06:00
Kevan Ahlquist 84b7da1164 Merge pull request #71 from kevana/master
Build and test automation
2015-01-18 16:51:39 -06:00
Kevan Ahlquist 77062bec84 Adding dist folder. 2015-01-18 16:40:07 -06:00
Kevan Ahlquist f65fed8d0a Fixed grunt template issues, version bump. 2015-01-18 16:39:10 -06:00
Kevan Ahlquist 2974f69932 Merge pull request #6 from kevana/feature/build
Build and test automation
2015-01-14 00:25:52 -06:00
Kevan Ahlquist 190087e6ca Added install and test targets to Makefile 2015-01-14 00:25:25 -06:00
Kevan Ahlquist 1784719047 Added unit tests for filters. 2015-01-04 20:35:47 -06:00
Kevan Ahlquist 275d771ea9 Merge branch 'master' into feature/build
Conflicts:
	.gitignore
2015-01-03 19:47:36 -06:00
Kevan Ahlquist f46b736c65 Merge pull request #7 from crosbymichael/master
Pull latest changes
2015-01-03 19:45:50 -06:00
Kevan Ahlquist 8eb381a899 Merge pull request #67 from crosbymichael/docs-update
Updated readme with new build info and more usage instructions.
2015-01-03 19:44:24 -06:00
Kevan Ahlquist 04418e2e68 Added Karma config for unit tests. 2015-01-03 18:59:22 -06:00
Kevan Ahlquist ab2819addd Cleaned up linter errors. 2015-01-03 18:39:40 -06:00
Kevan Ahlquist e507af62aa Merge branch 'master' into feature/build
Conflicts:
	app/app.js
	index.html
2015-01-03 17:01:02 -06:00
Kevan Ahlquist 468ba72253 Merge pull request #4 from crosbymichael/master
Pull latest changes
2015-01-03 16:55:56 -06:00
Kevan Ahlquist 58bd6faa47 Added initial files for build and test automation. 2015-01-03 16:49:28 -06:00
Kevan Ahlquist e601ae4370 Updated readme with new build info and more usage instructions. 2014-12-30 13:42:54 -06:00
Kevan Ahlquist 403daff934 Merge pull request #3 from kevana/bootstrap-update
Major refactoring
2014-12-11 15:29:16 -06:00
100 changed files with 4320 additions and 1742 deletions
+4
View File
@@ -0,0 +1,4 @@
node_modules
bower_components
.git
Dockerfile
+4 -1
View File
@@ -1,4 +1,7 @@
logs/*
!.gitkeep
dockerui
*.esproj/*
node_modules
bower_components
.idea
*.iml
+4 -5
View File
@@ -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"]
-19
View File
@@ -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)
+26 -19
View File
@@ -1,40 +1,47 @@
## DockerUI
![Containers](/containers.png)
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.
![Container](/container.png)
### 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
+46 -13
View File
@@ -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');
+4 -4
View File
@@ -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';
}]);
+164 -99
View File
@@ -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;">&times;</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>
+134 -96
View File
@@ -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();
}]);
+5 -4
View File
@@ -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>&nbsp;
<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>
+100 -69
View File
@@ -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});
};
}]);
+14 -11
View File
@@ -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>
+71 -69
View File
@@ -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);
});
}]);
}]);
+34
View File
@@ -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>
+42
View File
@@ -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();
}]);
+7 -5
View File
@@ -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;
});
}]);
+4 -1
View File
@@ -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>
+65 -52
View File
@@ -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>
+77 -62
View File
@@ -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();
}]);
+17 -10
View File
@@ -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>
+48 -46
View File
@@ -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();
});
}]);
+110
View File
@@ -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>
+15
View File
@@ -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;
});
}]);
+7 -6
View File
@@ -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';
}]);
+44
View File
@@ -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">&times;</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();
});
};
}]);
-49
View File
@@ -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; });
}]);
+2 -2
View File
@@ -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>
+9 -9
View File
@@ -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);
};
}]);
+424 -18
View File
@@ -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>
+67
View File
@@ -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>
+187
View File
@@ -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;
}
}])
;
+58 -32
View File
@@ -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;
};
});
+147 -87
View File
@@ -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
});
}
};
});
}]);
+8 -9
View File
@@ -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;
}
+13 -26
View File
@@ -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;
}
-5
View File
File diff suppressed because one or more lines are too long
-101
View File
@@ -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;
}
-39
View File
@@ -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={}};
-13
View File
@@ -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
File diff suppressed because one or more lines are too long
-14
View File
@@ -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
File diff suppressed because one or more lines are too long
-201
View File
@@ -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>&#160;</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
File diff suppressed because one or more lines are too long
-7
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+419
View File
@@ -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
View File
@@ -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);
+4
View File
@@ -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;
-349
View File
@@ -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
}));
+40
View File
@@ -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"
}
}
+22
View File
File diff suppressed because one or more lines are too long
Vendored Executable
BIN
View File
Binary file not shown.
+1
View File
@@ -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}
+8
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

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

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

+44
View File
@@ -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>
+5
View File
File diff suppressed because one or more lines are too long
+90
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
9d2349d7d4fd3b7a22f87e2b54cd7fb2cfd6d537 dockerui
+1 -1
View File
@@ -1,4 +1,4 @@
package main
package main // import "github.com/crosbymichael/dockerui"
import (
"flag"
+256
View File
@@ -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']
}
}
});
};
+23 -52
View File
@@ -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>
+41
View File
@@ -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"
}
}
View File
@@ -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();
//});
});
+355
View File
@@ -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);
}));
});
});
+53
View File
@@ -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;