2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Upload Image
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{errorMessage}}
27 |
28 |
29 | Back
30 |
31 | Select image
32 | Change
33 |
34 | Upload
35 |
36 |
37 | Image uploading...
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/public/html/template-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Templates
9 |
10 |
11 |
12 |
13 | Create Template
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Name
26 | Width
27 | Height
28 | Unit
29 | Keep Proportions
30 | Archived
31 | Removable
32 |
33 |
34 |
35 |
36 | {{template.id.templateName}}
37 | {{template.resizingConfig.width}}
38 | {{template.resizingConfig.height}}
39 | {{template.resizingConfig.unit}}
40 | {{template.resizingConfig.isKeepProportions}}
41 | {{template.isArchived}}
42 | {{template.isRemovable}}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/public/html/image-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
13 | Upload Image
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Id
26 | Width
27 | Height
28 | Format
29 | Size (bytes)
30 |
31 | Uploaded Time
32 |
33 |
34 |
35 |
36 | {{image.id.imageId}}
37 | {{image.metadata.dimension.width}}px
38 | {{image.metadata.dimension.height}}px
39 | {{image.metadata.format}}
40 | {{image.metadata.size}}
41 |
42 | {{image.dateTime | date:'medium'}}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/public/javascripts/angular-prompt.min.js:
--------------------------------------------------------------------------------
1 | angular.module("cgPrompt",["ui.bootstrap"]),angular.module("cgPrompt").factory("prompt",["$modal","$q",function(a,b){var c=function(c){var d={title:"",message:"",input:!1,label:"",value:"",values:!1,buttons:[{label:"Cancel",cancel:!0},{label:"OK",primary:!0}]};void 0===c&&(c={});for(var e in d)void 0===c[e]&&(c[e]=d[e]);var f=b.defer();return a.open({templateUrl:"angular-prompt.html",controller:"cgPromptCtrl",resolve:{options:function(){return c}}}).result.then(function(a){f.resolve(c.input?a.input:a.button)},function(){f.reject()}),f.promise};return c}]),angular.module("cgPrompt").controller("cgPromptCtrl",["$scope","options","$timeout",function(a,b,c){a.input={name:b.value},a.options=b,a.buttonClicked=function(c){return c.cancel?void a.$dismiss():b.input&&angular.element(document.querySelector("#cgPromptForm")).scope().cgPromptForm.$invalid?void(a.changed=!0):void a.$close({button:c,input:a.input.name})},a.submit=function(){var b;angular.forEach(a.options.buttons,function(a){a.primary&&(b=a)}),b&&a.buttonClicked(b)},c(function(){var a=document.querySelector("#cgPromptInput");a&&(a.select&&a.select(),a.focus&&a.focus())},100)}]),angular.module("cgPrompt").run(["$templateCache",function(a){"use strict";a.put("angular-prompt.html",'
\n \n
\n\n
\n {{options.message}}\n
\n\n
\n\n
\n \n
')}]);
--------------------------------------------------------------------------------
/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | # HTML
6 | GET / controllers.Assets.at(path="/public/html", file="index.html")
7 | GET /404.html controllers.Assets.at(path="/public/html", file="404.html")
8 | GET /html/*file controllers.Assets.at(path="/public/html", file)
9 | GET /javascripts/*file controllers.Assets.at(path="/public/javascripts", file)
10 | GET /stylesheets/*file controllers.Assets.at(path="/public/stylesheets", file)
11 | GET /fonts/*file controllers.Assets.at(path="/public/fonts", file)
12 | GET /images/*file controllers.Assets.at(path="/public/images", file)
13 |
14 | # REST Endpoints
15 | POST /rest/v1/imageplants @com.images3.rest.controllers.ImagePlantController.addImagePlant()
16 | PUT /rest/v1/imageplants/:id @com.images3.rest.controllers.ImagePlantController.updateImagePlant(id:String)
17 | DELETE /rest/v1/imageplants/:id @com.images3.rest.controllers.ImagePlantController.deleteImagePlant(id:String)
18 | GET /rest/v1/imageplants/:id @com.images3.rest.controllers.ImagePlantController.getImagePlant(id:String)
19 | GET /rest/v1/imageplants @com.images3.rest.controllers.ImagePlantController.getAllImagePlants(page:String ?= null)
20 | GET /rest/v1/imageplants/:id/imagereport @com.images3.rest.controllers.ImagePlantController.getImageReport(id:String, templateName:String ?= null, startTime:Long, length:Long, timeUnit:String, types:String)
21 |
22 | POST /rest/v1/imageplants/:imagePlantId/templates @com.images3.rest.controllers.TemplateController.addTemplate(imagePlantId:String)
23 | PUT /rest/v1/imageplants/:imagePlantId/templates/:name @com.images3.rest.controllers.TemplateController.archiveTemplate(imagePlantId:String, name:String)
24 | GET /rest/v1/imageplants/:imagePlantId/templates/:name @com.images3.rest.controllers.TemplateController.getTemplate(imagePlantId:String, name:String)
25 | DELETE /rest/v1/imageplants/:imagePlantId/templates/:name @com.images3.rest.controllers.TemplateController.deleteTemplate(imagePlantId:String, name:String)
26 | GET /rest/v1/imageplants/:imagePlantId/templates @com.images3.rest.controllers.TemplateController.getTemplates(imagePlantId:String, page:String ?= null, archived:String ?= null)
27 |
28 | POST /rest/v1/imageplants/:imagePlantId/images @com.images3.rest.controllers.ImageController.addImage(imagePlantId:String)
29 | GET /rest/v1/imageplants/:imagePlantId/imagefiles/:imageId @com.images3.rest.controllers.ImageController.getImageFile(imagePlantId:String, imageId:String, template:String ?= null)
30 | GET /rest/v1/imageplants/:imagePlantId/images/:imageId @com.images3.rest.controllers.ImageController.getImage(imagePlantId:String, imageId:String, template:String ?= null)
31 | GET /rest/v1/imageplants/:imagePlantId/images @com.images3.rest.controllers.ImageController.getImages(imagePlantId:String, page:String ?= null, template:String ?= null)
32 | DELETE /rest/v1/imageplants/:imagePlantId/images/:imageId @com.images3.rest.controllers.ImageController.deleteImage(imagePlantId:String, imageId:String)
33 |
--------------------------------------------------------------------------------
/public/javascripts/angular-busy.min.js:
--------------------------------------------------------------------------------
1 | angular.module("cgBusy",[]),angular.module("cgBusy").factory("_cgBusyTrackerFactory",["$timeout","$q",function(a,b){return function(){var c={};c.promises=[],c.delayPromise=null,c.durationPromise=null,c.delayJustFinished=!1,c.reset=function(b){c.minDuration=b.minDuration,c.promises=[],angular.forEach(b.promises,function(a){a&&!a.$cgBusyFulfilled&&d(a)}),0!==c.promises.length&&(c.delayJustFinished=!1,b.delay&&(c.delayPromise=a(function(){c.delayPromise=null,c.delayJustFinished=!0},parseInt(b.delay,10))),b.minDuration&&(c.durationPromise=a(function(){c.durationPromise=null},parseInt(b.minDuration,10)+(b.delay?parseInt(b.delay,10):0))))},c.getThen=function(a){var c=a&&(a.then||a.$then||a.$promise&&a.$promise.then);return a.denodeify?b.when(a).then:c};var d=function(a){var b=c.getThen(a);if(!b)throw new Error("cgBusy expects a promise (or something that has a .promise or .$promise");-1===c.promises.indexOf(a)&&(c.promises.push(a),b(function(){a.$cgBusyFulfilled=!0,-1!==c.promises.indexOf(a)&&c.promises.splice(c.promises.indexOf(a),1)},function(){a.$cgBusyFulfilled=!0,-1!==c.promises.indexOf(a)&&c.promises.splice(c.promises.indexOf(a),1)}))};return c.active=function(){return c.delayPromise?!1:c.delayJustFinished?(c.delayJustFinished=!1,c.promises.length>0):c.durationPromise?!0:c.promises.length>0},c}}]),angular.module("cgBusy").value("cgBusyDefaults",{}),angular.module("cgBusy").directive("cgBusy",["$compile","$templateCache","cgBusyDefaults","$http","_cgBusyTrackerFactory",function(a,b,c,d,e){return{restrict:"A",link:function(f,g,h){var i=g.css("position");("static"===i||""===i||"undefined"==typeof i)&&g.css("position","relative");var j,k,l,m,n,o=e(),p={templateUrl:"angular-busy.html",delay:0,minDuration:0,backdrop:!0,message:"Please Wait..."};angular.extend(p,c),f.$watchCollection(h.cgBusy,function(c){if(c||(c={promise:null}),angular.isString(c))throw new Error("Invalid value for cg-busy. cgBusy no longer accepts string ids to represent promises/trackers.");(angular.isArray(c)||o.getThen(c))&&(c={promise:c}),c=angular.extend(angular.copy(p),c),c.templateUrl||(c.templateUrl=p.templateUrl),angular.isArray(c.promise)||(c.promise=[c.promise]),m||(m=f.$new()),m.$message=c.message,angular.equals(o.promises,c.promise)||o.reset({promises:c.promise,delay:c.delay,minDuration:c.minDuration}),m.$cgBusyIsActive=function(){return o.active()},j&&l===c.templateUrl&&n===c.backdrop||(j&&j.remove(),k&&k.remove(),l=c.templateUrl,n=c.backdrop,d.get(l,{cache:b}).success(function(b){if(c.backdrop="undefined"==typeof c.backdrop?!0:c.backdrop,c.backdrop){var d='
';k=a(d)(m),g.append(k)}var e='
'+b+"
";j=a(e)(m),angular.element(j.children()[0]).css("position","absolute").css("top",0).css("left",0).css("right",0).css("bottom",0),g.append(j)}).error(function(a){throw new Error("Template specified for cgBusy ("+c.templateUrl+") could not be loaded. "+a)}))},!0)}}}]),angular.module("cgBusy").run(["$templateCache",function(a){"use strict";a.put("angular-busy.html",'
\n\n
\n\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n\n
{{$message}}
\n\n
\n\n
')}]);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #ImageS3
2 |
3 | ImageS3 is a free and open image hosting service for developers. It is designed to store, resize and manage images for all your web and mobile apps in one place.
4 |
5 | ##Features
6 |
7 | * Apply AmazonS3 as infrastructure for storing your image files.
8 | * Create template to resize your images into different versions.
9 | * REST APIs for easy application integration.
10 | * Support PNG, JPG, BMP and **GIF** formats.
11 | * Web-based admin tool with live monitoring:
12 |
13 | [
](http://i.imgur.com/liGhBmN.png)
14 |
15 |
16 | ##Installation
17 |
18 | ###Installing Requirements
19 |
20 | * **Java** - ImageS3 developed in Java. You can download the latest JRE7 build from [here](http://www.oracle.com/technetwork/java/javase/downloads/jre7-downloads-1880261.html).
21 |
22 | * **Play Framework** - Play Framework is a lightweight and highly-scalable application server. ImageS3 released with Play as a standalone package which lets you don't need to download different jar files separately. Follow this [instruction](https://www.playframework.com/documentation/2.3.x/Installing) to install 2.3.x Play Framework.
23 |
24 | * **MongoDB** - MongoDB is a leading NoSQL database with amazing [features](http://www.mongodb.org/). ImageS3 uses MongoDB to store image metadata and other objects information. Follow this [instruction](http://docs.mongodb.org/manual/installation/) to install the latest MongoDB.
25 |
26 | * **Amazon S3** -- Amazon S3 is a secure, durable and highly-scalable cloud storage service. ImageS3 uses Amazon S3 to store image files. If you already have Amazon AWS account, you can skip this, otherwise, sign up one at [here](http://aws.amazon.com/s3/).
27 |
28 | ###Installing ImageS3
29 |
30 | Download and unzip the compressed package:
31 |
32 | $ wget https://github.com/gogoup/images3-dist/raw/master/images3-play-latest.zip
33 | $ unzip images3-play-latest.zip
34 |
35 | Create default location for image files.
36 |
37 | $ mkdir -p /images3/images
38 |
39 | ##Configuring ImageS3
40 |
41 | All the configuration files are located in image-play-[version]/conf.
42 |
43 | You need to set the following properties before running ImageS3:
44 |
45 | * **mongodb.properties**
46 | - *'mongodb.url'* -- MongoDB server IP address.
47 | - *'mongodb.port'* -- MongoDB server port number. 27017 is the default number.
48 | - *'mongodb.username'* -- Username of MongoDB connection. Leave this value empty, if you don't setup username on MongoDB.
49 | - *'mongodb.password'* -- Password of MongoDB connection. Leave this value empty, if you don't setup password on MongoDB.
50 |
51 | If you have a different location for image files, then setup the following configurations.
52 |
53 | * **imagecontent.properties**
54 | - *'imagecontent.download.dir'* -- A place used to store images uploaded from clients or download from Amazon S3. Make sure the directory exists.
55 |
56 | * **imageprocessor.properties**
57 | - *'image.processing.tempdir'* -- A place used to store resized images. Make sure the directory exists.
58 |
59 |
60 | ##Running ImageS3
61 |
62 | Enable execution persmissions:
63 |
64 | $ chmod +x images3-play-[version]/bin/*
65 |
66 | Use the following command to run ImageS3:
67 |
68 | $ ./images3-play-[version]/bin/images3-play
69 |
70 | And then, open [http://localhost:9000](http://localhost:9000) in your browser, you will see the admin tool:
71 |
72 | #[
](http://i.imgur.com/RcY9QQa.png)
73 |
74 | You can also run ImageS3 on different port, for example 8080:
75 |
76 | $ ./images3-play-[version]/bin/images3-play -Dhttp.port=8080
77 |
78 |
79 | ##How to Use
80 |
81 | Checking the **[wiki](https://github.com/images3/images3-play/wiki)** page.
82 |
83 | ##License
84 | Released under the Apache License 2.0
--------------------------------------------------------------------------------
/public/javascripts/images3-app.js:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2014 Rui Sun
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | /**
17 | *
18 | */
19 | var autoRefreshImageReport = {};
20 | var currentImagePlantId = null;
21 |
22 | var imageS3 = angular.module('imageS3', ['ui.router', 'imageS3Controllers', 'ui.bootstrap','cgPrompt', 'cgBusy', 'flow']);
23 |
24 |
25 | imageS3.run(['$rootScope', '$state', '$stateParams',
26 | function($rootScope, $state, $stateParams) {
27 | $rootScope.$state = $state;
28 | $rootScope.$stateParams = $stateParams;
29 | $rootScope.$on('$locationChangeSuccess', function(event, currRoute, prevRoute) {
30 | var position = currRoute.indexOf('/overview');
31 | if ((position + 9) != currRoute.length) {
32 | autoRefreshImageReport[currentImagePlantId] = false;
33 | currentImagePlantId = null;
34 | }
35 | });
36 | }
37 | ]);
38 |
39 | imageS3.config(['$stateProvider', '$urlRouterProvider',
40 | function($stateProvider, $urlRouterProvider) {
41 | $urlRouterProvider.otherwise('/imageplants');
42 |
43 | $stateProvider
44 | .state('imageplants', {
45 | url: '/imageplants?page',
46 | templateUrl: 'html/imageplant-list.html',
47 | controller: 'ImagePlantController'
48 | })
49 | .state('imageplant-create', {
50 | url: '/imageplant-create',
51 | templateUrl: 'html/imageplant-create.html',
52 | controller: 'ImagePlantController'
53 | })
54 | .state('imageplant', {
55 | url: '/imageplants/:imagePlantId',
56 | templateUrl: 'html/imageplant.html',
57 | controller: 'ImagePlantController'
58 | })
59 | .state('imageplant.overview', {
60 | url: '/overview',
61 | views: {
62 | '': {
63 | templateUrl: 'html/imageplant-overview.html',
64 | controller: 'ImagePlantController'
65 | },
66 | 'report@imageplant.overview': {
67 | templateUrl: 'html/image-report.html',
68 | controller: 'ImageReportController'
69 | }
70 | }
71 | })
72 | .state('imageplant.templates', {
73 | url: '/templates?page&archived',
74 | templateUrl: 'html/template-list.html',
75 | controller: 'TemplateController'
76 | })
77 | .state('imageplant.template-create', {
78 | url: '/createTemplate?page&archived',
79 | templateUrl: 'html/template-create.html',
80 | controller: 'TemplateController'
81 | })
82 | .state('imageplant.template-update', {
83 | url: '/templates/:templateName?page&archived',
84 | templateUrl: 'html/template-update.html',
85 | controller: 'TemplateController'
86 | })
87 | .state('imageplant.images', {
88 | url: '/images?page&template',
89 | templateUrl: 'html/image-list.html',
90 | controller: 'ImageController'
91 | })
92 | .state('imageplant.imagecontent', {
93 | url: '/images/:imageId?template',
94 | templateUrl: 'html/image-content.html',
95 | controller: 'ImageController'
96 | })
97 | .state('imageplant.images-upload', {
98 | url: '/uploadImage?page&template',
99 | templateUrl: 'html/image-upload.html',
100 | controller: 'ImageController'
101 | })
102 | .state('imageplant.update', {
103 | templateUrl: 'html/imageplant-update.html',
104 | controller: 'ImagePlantController',
105 | });
106 | }
107 | ]);
108 |
--------------------------------------------------------------------------------
/public/html/template-create.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Create Template
9 |
10 |
11 |
12 |
13 |
14 |
15 |
73 |
--------------------------------------------------------------------------------
/public/javascripts/images3-services.js:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2014 Rui Sun
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | /**
17 | *
18 | */
19 |
20 | var imageS3Services = angular.module('imageS3Services', ['ngResource']);
21 |
22 | imageS3Services.factory('ImagePlants', ['$resource', function($resource) {
23 | return $resource(
24 | null,
25 | null,
26 | {
27 | getAll: {
28 | url: '/rest/v1/imageplants?page=:pageId',
29 | method: 'GET',
30 | params:{pageId: '@pageId'},
31 | isArray: false},
32 | getById: {
33 | url: '/rest/v1/imageplants/:id',
34 | method: 'GET',
35 | params:{id: '@id'},
36 | isArray: false},
37 | getImageReport: {
38 | url: '/rest/v1/imageplants/:id/imagereport',
39 | method: 'GET',
40 | params:{
41 | id: '@id',
42 | templateName: '@templateName',
43 | startTime: '@startTime',
44 | length: '@length',
45 | timeUnit: '@timeUnit',
46 | types: '@types'
47 | },
48 | isArray: false},
49 | create: {
50 | url: '/rest/v1/imageplants',
51 | method: 'POST'},
52 | remove: {
53 | url: '/rest/v1/imageplants/:imagePlantId',
54 | method: 'DELETE',
55 | params:{
56 | imagePlantId: '@imagePlantId'
57 | }},
58 | update: {
59 | url: '/rest/v1/imageplants/:imagePlantId',
60 | method: 'PUT',
61 | params:{
62 | imagePlantId: '@imagePlantId'
63 | }},
64 | });
65 | }]);
66 |
67 | imageS3Services.factory('Templates', ['$resource', function($resource) {
68 | return $resource(
69 | null,
70 | null,
71 | {
72 | getByImagePlantId: {
73 | url: '/rest/v1/imageplants/:id/templates?page=:pageId&archived=:isArchived',
74 | method: 'GET',
75 | params:{
76 | id: '@id',
77 | pageId: '@pageId',
78 | isArchived: '@isArchived'
79 | },
80 | isArray: false},
81 | getByName: {
82 | url: '/rest/v1/imageplants/:id/templates/:name',
83 | method: 'GET',
84 | params:{
85 | id: '@id',
86 | name: '@name'
87 | },
88 | isArray: false},
89 | create: {
90 | url: '/rest/v1/imageplants/:imagePlantId/templates',
91 | method: 'POST',
92 | params:{
93 | imagePlantId: '@imagePlantId'
94 | }},
95 | remove: {
96 | url: '/rest/v1/imageplants/:imagePlantId/templates/:templateName',
97 | method: 'DELETE',
98 | params:{
99 | imagePlantId: '@imagePlantId',
100 | templateName: '@templateName'
101 | }},
102 | update: {
103 | url: '/rest/v1/imageplants/:imagePlantId/templates/:templateName',
104 | method: 'PUT',
105 | params:{
106 | imagePlantId: '@imagePlantId',
107 | templateName: '@templateName'
108 | }},
109 |
110 | },
111 | {
112 | stripTrailingSlashes: false
113 | }
114 | );
115 | }]);
116 |
117 | imageS3Services.factory('Images', ['$resource', function($resource) {
118 | return $resource(
119 | null,
120 | null,
121 | {
122 | getByImagePlantId: {
123 | url: '/rest/v1/imageplants/:id/images?page=:pageId&template=:template',
124 | method: 'GET',
125 | params:{id: '@id'},
126 | params:{pageId: '@pageId'},
127 | params:{template: '@template'},
128 | isArray: false},
129 | getByVersion: {
130 | url: '/rest/v1/imageplants/:id/images/:imageId?template=:template',
131 | method: 'GET',
132 | params:{id: '@id'},
133 | params:{imageId: '@imageId'},
134 | params:{template: '@template'},
135 | isArray: false}
136 | });
137 | }]);
138 |
139 |
140 |
--------------------------------------------------------------------------------
/public/html/imageplant-update.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Information
10 |
11 |
12 |
13 |
14 |
15 |
16 |
71 |
72 |
73 |
74 |
75 |
76 |
WARNING
77 |
Removing this image plant will remove all associated images and templates completely! There is no way to undo this, so proceed with caution.
78 |
79 | Remove
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/public/html/template-update.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Template
9 |
10 |
11 |
12 |
13 |
14 |
15 |
88 |
--------------------------------------------------------------------------------
/app/Global.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2014 Rui Sun
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 |
17 |
18 | import com.fasterxml.jackson.databind.ObjectMapper;
19 | import com.google.inject.AbstractModule;
20 | import com.google.inject.Guice;
21 | import com.google.inject.Injector;
22 | import com.images3.ImageS3;
23 | import com.images3.rest.exceptions.AmazonS3BucketAccessFailedExceptionMapper;
24 | import com.images3.rest.exceptions.DuplicateImagePlantNameExceptionMapper;
25 | import com.images3.rest.exceptions.DuplicateImageVersionExceptionMapper;
26 | import com.images3.rest.exceptions.DuplicateTemplateNameExceptionMapper;
27 | import com.images3.rest.exceptions.ExceptionMapper;
28 | import com.images3.rest.exceptions.IllegalImagePlantNameLengthExceptionMapper;
29 | import com.images3.rest.exceptions.IllegalImageVersionExceptionMapper;
30 | import com.images3.rest.exceptions.IllegalResizingDimensionsExceptionMapper;
31 | import com.images3.rest.exceptions.IllegalTemplateNameExceptionMapper;
32 | import com.images3.rest.exceptions.IllegalTemplateNameLengthExceptionMapper;
33 | import com.images3.rest.exceptions.NoSuchEntityFoundExceptionMapper;
34 | import com.images3.rest.exceptions.UnachievableTemplateExceptionMapper;
35 | import com.images3.rest.exceptions.UnremovableTemplateExceptionMapper;
36 | import com.images3.rest.exceptions.UnsupportedImageFormatExceptionMapper;
37 |
38 | import play.Application;
39 | import play.GlobalSettings;
40 | import play.Logger;
41 | import play.libs.F.Promise;
42 | import play.mvc.Result;
43 | import play.mvc.Http;
44 |
45 | public class Global extends GlobalSettings {
46 |
47 | private Injector injector;
48 | private ExceptionMapper exceptionHandler;
49 |
50 | public void onStart(Application app) {
51 | initExceptionHandlers();
52 | final ImageS3Provider imageS3Provider = new ImageS3Provider(
53 | app.getFile(app.configuration().getString("images3.conf")).getAbsolutePath(),
54 | app.getFile(app.configuration().getString("imageprocessor.conf")).getAbsolutePath(),
55 | app.getFile(app.configuration().getString("mongodb.conf")).getAbsolutePath()
56 | );
57 | injector = Guice.createInjector(new AbstractModule() {
58 |
59 | @Override
60 | protected void configure() {
61 | bind(ImageS3.class).toProvider(imageS3Provider).asEagerSingleton();
62 | bind(ObjectMapper.class).toProvider(new ObjectMapperProvider()).asEagerSingleton();;
63 | }
64 |
65 | });
66 | Logger.info("Application has started");
67 | }
68 |
69 | private void initExceptionHandlers() {
70 | exceptionHandler = new AmazonS3BucketAccessFailedExceptionMapper(null);
71 | exceptionHandler = new DuplicateImagePlantNameExceptionMapper(exceptionHandler);
72 | exceptionHandler = new DuplicateImageVersionExceptionMapper(exceptionHandler);
73 | exceptionHandler = new DuplicateTemplateNameExceptionMapper(exceptionHandler);
74 | exceptionHandler = new IllegalImagePlantNameLengthExceptionMapper(exceptionHandler);
75 | exceptionHandler = new IllegalResizingDimensionsExceptionMapper(exceptionHandler);
76 | exceptionHandler = new IllegalTemplateNameLengthExceptionMapper(exceptionHandler);
77 | exceptionHandler = new IllegalTemplateNameExceptionMapper(exceptionHandler);
78 | exceptionHandler = new NoSuchEntityFoundExceptionMapper(exceptionHandler);
79 | exceptionHandler = new UnachievableTemplateExceptionMapper(exceptionHandler);
80 | exceptionHandler = new UnremovableTemplateExceptionMapper(exceptionHandler);
81 | exceptionHandler = new UnsupportedImageFormatExceptionMapper(exceptionHandler);
82 | exceptionHandler = new IllegalImageVersionExceptionMapper(exceptionHandler);
83 | }
84 |
85 | public void onStop(Application app) {
86 | Logger.info("Application shutdown...");
87 | }
88 |
89 | public
T getControllerInstance(Class clazz) throws Exception {
90 | return injector.getInstance(clazz);
91 | }
92 |
93 | public Promise onError(Http.RequestHeader request, Throwable t) {
94 | Throwable excep = (t.getCause() == null ? t : t.getCause());
95 | return Promise.pure(exceptionHandler.toResult(excep));
96 | }
97 |
98 | //public Promise onHandlerNotFound(Http.RequestHeader request) {
99 | //return Promise.pure(Results.notFound("/404.html"));
100 | //}
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/app/com/images3/rest/controllers/TemplateController.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2014 Rui Sun
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.images3.rest.controllers;
17 |
18 | import java.io.IOException;
19 | import java.util.List;
20 |
21 | import org.gogoup.dddutils.pagination.PaginatedResult;
22 |
23 | import com.fasterxml.jackson.databind.ObjectMapper;
24 | import com.google.inject.Inject;
25 | import com.images3.ImageS3;
26 | import com.images3.TemplateAddRequest;
27 | import com.images3.common.TemplateIdentity;
28 | import com.images3.rest.models.PaginatedResultModel;
29 | import com.images3.rest.models.TemplateAddRequestModel;
30 | import com.images3.TemplateResponse;
31 |
32 | import play.mvc.Controller;
33 | import play.mvc.Result;
34 |
35 | public class TemplateController extends Controller {
36 |
37 | private ImageS3 imageS3;
38 | private ObjectMapper objectMapper;
39 |
40 | @Inject
41 | public TemplateController(ImageS3 imageS3, ObjectMapper objectMapper) {
42 | this.imageS3 = imageS3;
43 | this.objectMapper = objectMapper;
44 | }
45 |
46 | public Result addTemplate(String imagePlantId) throws IOException {
47 | TemplateAddRequestModel requestModel = objectMapper.readValue(
48 | request().body().asJson().toString(), TemplateAddRequestModel.class);
49 | TemplateAddRequest request = new TemplateAddRequest(
50 | new TemplateIdentity(imagePlantId, requestModel.getName()),
51 | requestModel.getResizingConfig());
52 | TemplateResponse response = imageS3.addTemplate(request);
53 | String respJson = objectMapper.writeValueAsString(response);
54 | return ok(respJson);
55 | }
56 |
57 | public Result archiveTemplate(String imagePlantId, String name) throws IOException {
58 | boolean isArchived = request().body().asJson().get("isArchived").asBoolean();
59 | TemplateIdentity id = new TemplateIdentity(imagePlantId, name);
60 | TemplateResponse response = imageS3.archiveTemplate(id, isArchived);
61 | String respJson = objectMapper.writeValueAsString(response);
62 | return ok(respJson);
63 | }
64 |
65 | public Result getTemplate(String imagePlantId, String name) throws IOException {
66 | TemplateResponse response = imageS3.getTemplate(new TemplateIdentity(imagePlantId, name));
67 | String respJson = objectMapper.writeValueAsString(response);
68 | return ok(respJson);
69 | }
70 |
71 | public Result deleteTemplate(String imagePlantId, String name) {
72 | imageS3.deleteTemplate(new TemplateIdentity(imagePlantId, name));
73 | return status(204);
74 | }
75 |
76 | public Result getTemplates(String id, String page, String archived) throws IOException {
77 | PaginatedResult> pages = null;
78 | if (null == archived || archived.trim().length() == 0) {
79 | pages = imageS3.getAllTemplates(id);
80 | } else if ("false".equalsIgnoreCase(archived)) {
81 | pages = imageS3.getActiveTempaltes(id);
82 | } else if ("true".equalsIgnoreCase(archived)) {
83 | pages = imageS3.getArchivedTemplates(id);
84 | } else {
85 | return internalServerError("'archived' must be true or false.");
86 | }
87 | if (page.equalsIgnoreCase("ALL")) {
88 | List templates = pages.getAllResults();
89 | PaginatedResultModel> response =
90 | new PaginatedResultModel>(null, null, templates);
91 | String respJson = objectMapper.writeValueAsString(response);
92 | return ok(respJson);
93 | }
94 | return getPaginatedResultResponse(pages, page);
95 | }
96 |
97 | private Result getPaginatedResultResponse(PaginatedResult> pages,
98 | String page) throws IOException {
99 | if (null == page
100 | || page.trim().length() == 0) {
101 | page = (String) pages.getFirstPageCursor();
102 | }
103 | List templates = pages.getResult(page);
104 | String nextPage = (String) pages.getNextPageCursor();
105 | String prevPage = (String) pages.getPrevPageCursor();
106 | PaginatedResultModel> response =
107 | new PaginatedResultModel>(prevPage, nextPage, templates);
108 | String respJson = objectMapper.writeValueAsString(response);
109 | return ok(respJson);
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/app/com/images3/rest/controllers/ImagePlantController.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2014 Rui Sun
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.images3.rest.controllers;
17 |
18 | import java.io.IOException;
19 | import java.util.Date;
20 | import java.util.List;
21 | import java.util.concurrent.TimeUnit;
22 |
23 | import org.gogoup.dddutils.pagination.PaginatedResult;
24 |
25 | import com.fasterxml.jackson.core.JsonProcessingException;
26 | import com.fasterxml.jackson.databind.ObjectMapper;
27 | import com.google.inject.Inject;
28 | import com.images3.ImagePlantAddRequest;
29 | import com.images3.ImagePlantResponse;
30 | import com.images3.ImagePlantUpdateRequest;
31 | import com.images3.ImageReportQueryRequest;
32 | import com.images3.ImageReportResponse;
33 | import com.images3.ImageS3;
34 | import com.images3.common.ImageMetricsType;
35 | import com.images3.common.TimeInterval;
36 | import com.images3.rest.models.PaginatedResultModel;
37 |
38 | import play.mvc.Controller;
39 | import play.mvc.Result;
40 |
41 | public class ImagePlantController extends Controller {
42 |
43 | private ImageS3 imageS3;
44 | private ObjectMapper objectMapper;
45 |
46 | @Inject
47 | public ImagePlantController(ImageS3 imageS3, ObjectMapper objectMapper) {
48 | this.imageS3 = imageS3;
49 | this.objectMapper = objectMapper;
50 | }
51 |
52 | public Result addImagePlant() throws IOException {
53 | ImagePlantAddRequest request = objectMapper.readValue(
54 | request().body().asJson().toString(), ImagePlantAddRequest.class);
55 | ImagePlantResponse response = imageS3.addImagePlant(request);
56 | String respJson = objectMapper.writeValueAsString(response);
57 | return ok(respJson);
58 | }
59 |
60 | public Result updateImagePlant(String id) throws IOException {
61 | ImagePlantUpdateRequest request = objectMapper.readValue(
62 | request().body().asJson().toString(), ImagePlantUpdateRequest.class);
63 | request = new ImagePlantUpdateRequest(id, request.getName(), request.getBucket());
64 | ImagePlantResponse response = imageS3.updateImagePlant(request);
65 | String respJson = objectMapper.writeValueAsString(response);
66 | return ok(respJson);
67 | }
68 |
69 | public Result deleteImagePlant(String id) {
70 | imageS3.deleteImagePlant(id);
71 | return status(204);
72 | }
73 |
74 | public Result getImagePlant(String id) throws IOException {
75 | ImagePlantResponse response = imageS3.getImagePlant(id);
76 | String respJson = objectMapper.writeValueAsString(response);
77 | return ok(respJson);
78 | }
79 |
80 | public Result getAllImagePlants(String page) throws IOException {
81 | PaginatedResult> pages = imageS3.getAllImagePlants();
82 | if (null == page
83 | || page.trim().length() == 0) {
84 | page = (String) pages.getFirstPageCursor();
85 | }
86 | List result = pages.getResult(page);
87 | String nextPage = (String) pages.getNextPageCursor();
88 | String prevPage = (String) pages.getPrevPageCursor();
89 | PaginatedResultModel> response =
90 | new PaginatedResultModel>(prevPage, nextPage, result);
91 | String respJson = objectMapper.writeValueAsString(response);
92 | return ok(respJson);
93 | }
94 |
95 | public Result getImageReport(String id, String templateName, Long startTime,
96 | Long length, String timeUnit, String types) throws JsonProcessingException {
97 | if (null != templateName
98 | && templateName.trim().length() == 0) {
99 | templateName = null;
100 | }
101 | TimeInterval interval =
102 | new TimeInterval(new Date(startTime), length, TimeUnit.valueOf(timeUnit));
103 | ImageReportQueryRequest request =
104 | new ImageReportQueryRequest(
105 | id, templateName, interval, getImageReportTypes(types));
106 | ImageReportResponse response = imageS3.getImageReport(request);
107 | String respJson = objectMapper.writeValueAsString(response);
108 | return ok(respJson);
109 | }
110 |
111 | private ImageMetricsType[] getImageReportTypes(String typeString) {
112 | String [] types = typeString.split(",");
113 | ImageMetricsType[] reportTypes = new ImageMetricsType[types.length];
114 | int index = 0;
115 | for (String type: types) {
116 | reportTypes[index++] = ImageMetricsType.valueOf(type.trim());
117 | }
118 | return reportTypes;
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/public/html/imageplant-create.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Create ImagePlant
9 |
10 |
11 |
12 |
13 |
14 |
15 |
96 |
97 |
--------------------------------------------------------------------------------
/app/com/images3/rest/controllers/ImageController.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2014 Rui Sun
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.images3.rest.controllers;
17 |
18 | import java.io.File;
19 | import java.io.IOException;
20 | import java.util.List;
21 |
22 | import org.gogoup.dddutils.pagination.PaginatedResult;
23 |
24 | import com.fasterxml.jackson.databind.ObjectMapper;
25 | import com.google.inject.Inject;
26 | import com.images3.ImageAddRequest;
27 | import com.images3.ImageResponse;
28 | import com.images3.ImageS3;
29 | import com.images3.common.ImageIdentity;
30 | import com.images3.common.TemplateIdentity;
31 | import com.images3.rest.models.PaginatedResultModel;
32 |
33 | import play.mvc.Controller;
34 | import play.mvc.Result;
35 |
36 | public class ImageController extends Controller {
37 |
38 | private ImageS3 imageS3;
39 | private ObjectMapper objectMapper;
40 |
41 | @Inject
42 | public ImageController(ImageS3 imageS3, ObjectMapper objectMapper) {
43 | this.imageS3 = imageS3;
44 | this.objectMapper = objectMapper;
45 | }
46 |
47 | public Result addImage(String imagePlantId) throws IOException {
48 | File imageContent = request().body().asRaw().asFile();
49 | ImageAddRequest request = new ImageAddRequest(imagePlantId, imageContent);
50 | ImageResponse response = imageS3.addImage(request);
51 | String respJson = objectMapper.writeValueAsString(response);
52 | return ok(respJson);
53 | }
54 |
55 | public Result getImageFile(String imagePlantId, String imageId, String template) throws IOException {
56 | if (isEmptyTemplate(template)) {
57 | return getImageFile(imagePlantId, imageId);
58 | } else {
59 | return getImageFileWithTemplate(imagePlantId, imageId, template);
60 | }
61 | }
62 |
63 | private Result getImageFile(String imagePlantId, String imageId) throws IOException {
64 | File content = imageS3.getImageContent(new ImageIdentity(imagePlantId, imageId));
65 | return ok(content, content.getName());
66 | }
67 |
68 | private Result getImageFileWithTemplate(String imagePlantId, String imageId, String templateName) throws IOException {
69 | File content = imageS3.getImageContent(new ImageIdentity(imagePlantId, imageId), templateName);
70 | return ok(content, content.getName());
71 | }
72 |
73 | public Result getImage(String imagePlantId, String imageId, String template) throws IOException {
74 | if (isEmptyTemplate(template)) {
75 | return getImage(imagePlantId, imageId);
76 | } else {
77 | return getImageWithTemplate(imagePlantId, imageId, template);
78 | }
79 | }
80 |
81 | private Result getImage(String imagePlantId, String imageId) throws IOException {
82 | ImageResponse response = imageS3.getImage(new ImageIdentity(imagePlantId, imageId));
83 | String respJson = objectMapper.writeValueAsString(response);
84 | return ok(respJson);
85 | }
86 |
87 | private Result getImageWithTemplate(String imagePlantId, String imageId, String templateName) throws IOException {
88 | ImageResponse response = imageS3.getImage(new ImageIdentity(imagePlantId, imageId), templateName);
89 | String respJson = objectMapper.writeValueAsString(response);
90 | return ok(respJson);
91 | }
92 |
93 | public Result getImages(String imagePlantId, String page, String template) throws IOException {
94 | if (isEmptyTemplate(template)) {
95 | return getImages(imagePlantId, page);
96 | } else {
97 | return getImagesByTemplate(imagePlantId, template, page);
98 | }
99 | }
100 |
101 | private Result getImages(String imagePlantId, String page) throws IOException {
102 | PaginatedResult> pages = imageS3.getImages(imagePlantId);
103 | return getPaginatedResultResponse(pages, page);
104 | }
105 |
106 | private Result getImagesByTemplate(String imagePlantId, String templateName, String page) throws IOException {
107 | PaginatedResult> pages =
108 | imageS3.getImages(new TemplateIdentity(imagePlantId, templateName));
109 | return getPaginatedResultResponse(pages, page);
110 | }
111 |
112 | public Result getVersioningImages(String imagePlantId, String imageId, String page) throws IOException {
113 | PaginatedResult> pages =
114 | imageS3.getVersioningImages(new ImageIdentity(imagePlantId, imageId));
115 | return getPaginatedResultResponse(pages, page);
116 | }
117 |
118 | private Result getPaginatedResultResponse(PaginatedResult> pages,
119 | String page) throws IOException {
120 | if (null == page
121 | || page.trim().length() == 0) {
122 | page = (String) pages.getFirstPageCursor();
123 | }
124 | List images = pages.getResult(page);
125 | String nextPage = (String) pages.getNextPageCursor();
126 | String prevPage = (String) pages.getPrevPageCursor();
127 | PaginatedResultModel> response =
128 | new PaginatedResultModel>(prevPage, nextPage, images);
129 | String respJson = objectMapper.writeValueAsString(response);
130 | return ok(respJson);
131 | }
132 |
133 | public Result deleteImage(String imagePlantId, String imageId) {
134 | imageS3.deleteImage(new ImageIdentity(imagePlantId, imageId));
135 | return status(204);
136 | }
137 |
138 | private boolean isEmptyTemplate(String template) {
139 | return (null == template || template.trim().length() == 0);
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/public/stylesheets/angular-busy.min.css:
--------------------------------------------------------------------------------
1 | .cg-busy{position:absolute;top:0;left:0;right:0;bottom:0;z-index:1001}.cg-busy-animation.ng-hide-add,.cg-busy-animation.ng-hide-remove{-webkit-transition:all .3s ease;-moz-transition:all .3s ease;-o-transition:all .3s ease;transition:all .3s ease;display:block!important}.cg-busy-animation.ng-hide-remove{opacity:0;-webkit-transform:translate(0px,-40px);-moz-transform:translate(0px,-40px);-ms-transform:translate(0px,-40px);-o-transform:translate(0px,-40px);transform:translate(0px,-40px)}.cg-busy-animation.ng-hide-add,.cg-busy-animation.ng-hide-remove.ng-hide-remove-active{opacity:1;-webkit-transform:translate(0px,0);-moz-transform:translate(0px,0);-ms-transform:translate(0px,0);-o-transform:translate(0px,0);transform:translate(0px,0)}.cg-busy-animation.ng-hide-add.ng-hide-add-active{opacity:0;-webkit-transform:translate(0px,-40px);-moz-transform:translate(0px,-40px);-ms-transform:translate(0px,-40px);-o-transform:translate(0px,-40px);transform:translate(0px,-40px)}.cg-busy-backdrop{background-color:#fff;opacity:.7}.cg-busy-backdrop-animation.ng-hide-add,.cg-busy-backdrop-animation.ng-hide-remove{-webkit-transition:opacity .3s ease;-moz-transition:opacity .3s ease;-o-transition:opacity .3s ease;transition:opacity .3s ease;display:block!important}.cg-busy-backdrop-animation.ng-hide{opacity:0}.cg-busy-default-wrapper{text-align:center}.cg-busy-default-sign{display:inline-block;position:relative;z-index:1002;padding-bottom:6px;color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);background-color:#e9eeee;border:1px solid #ddd;border-top-width:0;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px;border-top-left-radius:0;border-top-right-radius:0;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.cg-busy-default-text{margin:13px 12px 6px 49px;font-size:16px;color:#555;text-align:left;max-width:400px}.cg-busy-default-spinner{position:absolute;width:25px;height:25px;display:inline-block;top:12px;left:14px}.cg-busy-default-spinner div{width:12%;height:26%;background:#000;position:absolute;left:44.5%;top:37%;opacity:0;-webkit-animation:cg-busy-spinner-anim 1s linear infinite;-moz-animation:cg-busy-spinner-anim 1s linear infinite;-ms-animation:cg-busy-spinner-anim 1s linear infinite;-o-animation:cg-busy-spinner-anim 1s linear infinite;animation:cg-busy-spinner-anim 1s linear infinite;-webkit-border-radius:50px;-moz-border-radius:50px;border-radius:50px;-webkit-box-shadow:0 0 3px rgba(0,0,0,.2);-moz-box-shadow:0 0 3px rgba(0,0,0,.2);box-shadow:0 0 3px rgba(0,0,0,.2)}.cg-busy-default-spinner div.bar1{-webkit-transform:rotate(0deg) translate(0,-142%);-moz-transform:rotate(0deg) translate(0,-142%);-ms-transform:rotate(0deg) translate(0,-142%);-o-transform:rotate(0deg) translate(0,-142%);transform:rotate(0deg) translate(0,-142%);-webkit-animation-delay:0s;-moz-animation-delay:0s;-ms-animation-delay:0s;-o-animation-delay:0s;animation-delay:0s}.cg-busy-default-spinner div.bar2{-webkit-transform:rotate(30deg) translate(0,-142%);-moz-transform:rotate(30deg) translate(0,-142%);-ms-transform:rotate(30deg) translate(0,-142%);-o-transform:rotate(30deg) translate(0,-142%);transform:rotate(30deg) translate(0,-142%);-webkit-animation-delay:-.9167s;-moz-animation-delay:-.9167s;-ms-animation-delay:-.9167s;-o-animation-delay:-.9167s;animation-delay:-.9167s}.cg-busy-default-spinner div.bar3{-webkit-transform:rotate(60deg) translate(0,-142%);-moz-transform:rotate(60deg) translate(0,-142%);-ms-transform:rotate(60deg) translate(0,-142%);-o-transform:rotate(60deg) translate(0,-142%);transform:rotate(60deg) translate(0,-142%);-webkit-animation-delay:-.833s;-moz-animation-delay:-.833s;-ms-animation-delay:-.833s;-o-animation-delay:-.833s;animation-delay:-.833s}.cg-busy-default-spinner div.bar4{-webkit-transform:rotate(90deg) translate(0,-142%);-moz-transform:rotate(90deg) translate(0,-142%);-ms-transform:rotate(90deg) translate(0,-142%);-o-transform:rotate(90deg) translate(0,-142%);transform:rotate(90deg) translate(0,-142%);-webkit-animation-delay:-.75s;-moz-animation-delay:-.75s;-ms-animation-delay:-.75s;-o-animation-delay:-.75s;animation-delay:-.75s}.cg-busy-default-spinner div.bar5{-webkit-transform:rotate(120deg) translate(0,-142%);-moz-transform:rotate(120deg) translate(0,-142%);-ms-transform:rotate(120deg) translate(0,-142%);-o-transform:rotate(120deg) translate(0,-142%);transform:rotate(120deg) translate(0,-142%);-webkit-animation-delay:-.667s;-moz-animation-delay:-.667s;-ms-animation-delay:-.667s;-o-animation-delay:-.667s;animation-delay:-.667s}.cg-busy-default-spinner div.bar6{-webkit-transform:rotate(150deg) translate(0,-142%);-moz-transform:rotate(150deg) translate(0,-142%);-ms-transform:rotate(150deg) translate(0,-142%);-o-transform:rotate(150deg) translate(0,-142%);transform:rotate(150deg) translate(0,-142%);-webkit-animation-delay:-.5833s;-moz-animation-delay:-.5833s;-ms-animation-delay:-.5833s;-o-animation-delay:-.5833s;animation-delay:-.5833s}.cg-busy-default-spinner div.bar7{-webkit-transform:rotate(180deg) translate(0,-142%);-moz-transform:rotate(180deg) translate(0,-142%);-ms-transform:rotate(180deg) translate(0,-142%);-o-transform:rotate(180deg) translate(0,-142%);transform:rotate(180deg) translate(0,-142%);-webkit-animation-delay:-.5s;-moz-animation-delay:-.5s;-ms-animation-delay:-.5s;-o-animation-delay:-.5s;animation-delay:-.5s}.cg-busy-default-spinner div.bar8{-webkit-transform:rotate(210deg) translate(0,-142%);-moz-transform:rotate(210deg) translate(0,-142%);-ms-transform:rotate(210deg) translate(0,-142%);-o-transform:rotate(210deg) translate(0,-142%);transform:rotate(210deg) translate(0,-142%);-webkit-animation-delay:-.41667s;-moz-animation-delay:-.41667s;-ms-animation-delay:-.41667s;-o-animation-delay:-.41667s;animation-delay:-.41667s}.cg-busy-default-spinner div.bar9{-webkit-transform:rotate(240deg) translate(0,-142%);-moz-transform:rotate(240deg) translate(0,-142%);-ms-transform:rotate(240deg) translate(0,-142%);-o-transform:rotate(240deg) translate(0,-142%);transform:rotate(240deg) translate(0,-142%);-webkit-animation-delay:-.333s;-moz-animation-delay:-.333s;-ms-animation-delay:-.333s;-o-animation-delay:-.333s;animation-delay:-.333s}.cg-busy-default-spinner div.bar10{-webkit-transform:rotate(270deg) translate(0,-142%);-moz-transform:rotate(270deg) translate(0,-142%);-ms-transform:rotate(270deg) translate(0,-142%);-o-transform:rotate(270deg) translate(0,-142%);transform:rotate(270deg) translate(0,-142%);-webkit-animation-delay:-.25s;-moz-animation-delay:-.25s;-ms-animation-delay:-.25s;-o-animation-delay:-.25s;animation-delay:-.25s}.cg-busy-default-spinner div.bar11{-webkit-transform:rotate(300deg) translate(0,-142%);-moz-transform:rotate(300deg) translate(0,-142%);-ms-transform:rotate(300deg) translate(0,-142%);-o-transform:rotate(300deg) translate(0,-142%);transform:rotate(300deg) translate(0,-142%);-webkit-animation-delay:-.1667s;-moz-animation-delay:-.1667s;-ms-animation-delay:-.1667s;-o-animation-delay:-.1667s;animation-delay:-.1667s}.cg-busy-default-spinner div.bar12{-webkit-transform:rotate(330deg) translate(0,-142%);-moz-transform:rotate(330deg) translate(0,-142%);-ms-transform:rotate(330deg) translate(0,-142%);-o-transform:rotate(330deg) translate(0,-142%);transform:rotate(330deg) translate(0,-142%);-webkit-animation-delay:-.0833s;-moz-animation-delay:-.0833s;-ms-animation-delay:-.0833s;-o-animation-delay:-.0833s;animation-delay:-.0833s}@-webkit-keyframes cg-busy-spinner-anim{from{opacity:1}to{opacity:.25}}@-moz-keyframes cg-busy-spinner-anim{from{opacity:1}to{opacity:.25}}@keyframes cg-busy-spinner-anim{from{opacity:1}to{opacity:.25}}
--------------------------------------------------------------------------------
/public/stylesheets/bootstrap-theme.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.0.3 (http://getbootstrap.com)
3 | * Copyright 2014 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | .btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.list-group{border-radius:4px}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);border-color:#3278b3}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc}
--------------------------------------------------------------------------------
/public/javascripts/images3-controllers.js:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2014 Rui Sun
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 |
17 | var imageS3Controllers = angular.module('imageS3Controllers', ['imageS3Services']);
18 |
19 | imageS3Controllers.controller('ImagePlantController', ['$scope', '$state', '$stateParams', 'prompt', 'ImagePlants',
20 | function ($scope, $state, $stateParams, prompt, ImagePlants) {
21 |
22 | $scope.imagePlant = initialImagePlant($stateParams.imagePlantId);
23 | $scope.errorCode = 0;
24 | $scope.errorMessage = '';
25 | $scope.awsAPISecretKey = '*********';
26 |
27 | $scope.viewImagePlant = function(imagePlant) {
28 | currentImagePlantId = imagePlant.id;
29 | $state.go('imageplant.overview', {imagePlantId: imagePlant.id});
30 | }
31 |
32 | $scope.showImagePlants = function(pageId) {
33 | ImagePlants.getAll({pageId: pageId}, function(response) {
34 | $scope.imagePlants = response.results;
35 | $scope.page = response.page;
36 | })
37 | }
38 |
39 | $scope.showImagePlant = function() {
40 | ImagePlants.getById({id: $stateParams.imagePlantId}, function(response) {
41 | $scope.imagePlant = response;
42 | })
43 | }
44 |
45 | $scope.createImagePlant = function(imagePlant) {
46 | ImagePlants.create(imagePlant,
47 | function(response) {
48 | $state.go('imageplants', {});
49 | },
50 | function(error) {
51 | if (error.status >= 400
52 | && error.status <= 417) {
53 | var errorResp = error.data;
54 | $scope.errorCode = errorResp.code;
55 | $scope.errorMessage = errorResp.message;
56 | }
57 | }
58 | )
59 |
60 | }
61 |
62 | $scope.removeImagePlant = function(imagePlant) {
63 |
64 | prompt({
65 | "title": "Do you want to continue to remove this image plant?",
66 | "message": "Removing this image plant will remove all associated images and template completely!" +
67 | " There is no way to undo this.",
68 | "buttons": [
69 | {
70 | "label": "Continue",
71 | "primary": true
72 | },
73 | {
74 | "label": "Cancel",
75 | "cancel": true
76 | }
77 | ]
78 | }).then(function(result){
79 | if (result.primary) {
80 | $scope.myPromise = ImagePlants.remove({imagePlantId: imagePlant.id},
81 | function(response) {
82 | $state.go('imageplants', {});
83 | });
84 | }
85 | });
86 | }
87 |
88 | $scope.updateImagePlant = function(imagePlant) {
89 | var updateImagePlant = {};
90 | updateImagePlant.id = imagePlant.id;
91 | updateImagePlant.name = imagePlant.name;
92 | updateImagePlant.bucket = imagePlant.bucket;
93 | if ($scope.awsAPISecretKey != '*********') {
94 | updateImagePlant.bucket.secretKey = $scope.awsAPISecretKey;
95 | }
96 | ImagePlants.update({imagePlantId: imagePlant.id}, updateImagePlant,
97 | function(response) {
98 | prompt({
99 | "title": "Success",
100 | "message": "ImagePlant, '" + imagePlant.name + "' has been updated.",
101 | "buttons": [
102 | {
103 | "label": "Ok",
104 | "primary": true
105 | }
106 | ]
107 | }).then(function(result){
108 | $state.transitionTo($state.current, $stateParams, {
109 | reload: true,
110 | inherit: false,
111 | notify: true
112 | });
113 | });
114 |
115 | },
116 | function(error) {
117 | if (error.status >= 400
118 | && error.status <= 417) {
119 | var errorResp = error.data;
120 | $scope.errorCode = errorResp.code;
121 | $scope.errorMessage = errorResp.message;
122 | }
123 | }
124 | )
125 | }
126 |
127 | }
128 | ]);
129 |
130 | imageS3Controllers.controller('TemplateController', ['$scope', '$state', '$stateParams', 'prompt', 'Templates',
131 | function ($scope, $state, $stateParams, prompt, Templates) {
132 |
133 | $scope.template = initialTemplate($stateParams.imagePlantId);
134 | $scope.errorCode = 0;
135 | $scope.errorMessage = '';
136 |
137 | $scope.createTemplate = function (template) {
138 | Templates.create(
139 | {imagePlantId: $stateParams.imagePlantId},
140 | template,
141 | function(response) {
142 | $scope.viewTemplates($stateParams.page, $stateParams.archived);
143 | },
144 | function(error) {
145 | if (error.status >= 400
146 | && error.status <= 417) {
147 | var errorResp = error.data;
148 | $scope.errorCode = errorResp.code;
149 | $scope.errorMessage = errorResp.message;
150 | }
151 | }
152 | );
153 | }
154 |
155 | $scope.viewTemplate = function(template, page, archived) {
156 | $state.go('imageplant.template-update', {templateName: template, page:page, archived:archived}, {location: false});
157 | }
158 |
159 | $scope.loadTemplate = function(templateName) {
160 | Templates.getByName({id: $stateParams.imagePlantId, name: templateName},
161 | function(response) {
162 | $scope.template = response;
163 | })
164 | }
165 |
166 | $scope.viewTemplates = function(page, isArchived) {
167 | if ((typeof(isArchived) === 'undefined')) {
168 | isArchived = '';
169 | }
170 | if ((typeof(page) === 'undefined')) {
171 | page = '';
172 | }
173 | $state.go('imageplant.templates', {page: page, archived: isArchived});
174 | }
175 |
176 | $scope.loadTemplates = function (page, isArchived) {
177 | Templates.getByImagePlantId({id: $stateParams.imagePlantId, pageId: page, isArchived: isArchived}, function(response) {
178 | $scope.templates = response.results;
179 | $scope.page = response.page;
180 | })
181 | }
182 |
183 | $scope.viewCreateTemplate = function(page, archived) {
184 | $state.go('imageplant.template-create', {page: page, archived: archived}, {location: false});
185 | }
186 |
187 | $scope.removeTemplate = function(template) {
188 | prompt({
189 | "title": "Do you want to continue to remove this template?",
190 | "message": "After removing this template, there is no way to undo this.",
191 | "buttons": [
192 | {
193 | "label": "Continue",
194 | "primary": true
195 | },
196 | {
197 | "label": "Cancel",
198 | "cancel": true
199 | }
200 | ]
201 | }).then(function(result){
202 | if (result.primary) {
203 | Templates.remove({imagePlantId: template.id.imagePlantId, templateName: template.id.templateName},
204 | function(response) {
205 | $scope.viewTemplates($stateParams.page, $stateParams.archived);
206 | }
207 | )
208 | }
209 | });
210 |
211 | }
212 |
213 | $scope.updateTemplateAvailability = function(template) {
214 | var newTemplate = angular.copy(template);
215 | if (newTemplate.isArchived) {
216 | newTemplate.isArchived = false;
217 | Templates.update({imagePlantId: newTemplate.id.imagePlantId, templateName: newTemplate.id.templateName},
218 | newTemplate,
219 | function(data) {
220 | //console.log("HERE======>data: " + angular.toJson(data, true));
221 | $state.go('imageplant.template-update', {templateName: data.id.templateName});
222 | template.isArchived = data.isArchived;
223 | },
224 | function(error) {
225 | }
226 | )
227 | } else {
228 | newTemplate.isArchived = true;
229 | prompt({
230 | "title": "Do you want to continue to deactivate this template?",
231 | "message": "Deactivating this template will cause all associated images cannot be accessed.",
232 | "buttons": [
233 | {
234 | "label": "Continue",
235 | "primary": true
236 | },
237 | {
238 | "label": "Cancel",
239 | "cancel": true
240 | }
241 | ]
242 | }).then(function(result){
243 | if (result.primary) {
244 | Templates.update({imagePlantId: newTemplate.id.imagePlantId, templateName: newTemplate.id.templateName},
245 | newTemplate,
246 | function(data) {
247 | $state.go('imageplant.template-update', {templateName: data.id.templateName});
248 | template.isArchived = data.isArchived;
249 | },
250 | function(error) {
251 | }
252 | )
253 | }
254 | });
255 | }
256 | }
257 | }
258 | ]);
259 |
260 |
261 | imageS3Controllers.controller('ImageController', ['$scope', '$state', '$stateParams', '$modal', 'Images',
262 | function ($scope, $state, $stateParams, $modal, Images) {
263 | $scope.errorCode = 0;
264 | $scope.errorMessage = '';
265 | $scope.uploadStarted = false;
266 |
267 | $scope.viewImages = function(pageId, templateName) {
268 | $state.go('imageplant.images', {page: pageId, template: templateName});
269 | }
270 |
271 | $scope.loadImages = function () {
272 | if ((typeof($stateParams.template) === 'undefined')) {
273 | $stateParams.template = 'master';
274 | }
275 | if ((typeof($stateParams.page) === 'undefined')) {
276 | $stateParams.page = '';
277 | }
278 | Images.getByImagePlantId({id: $stateParams.imagePlantId, pageId: $stateParams.page, template: $stateParams.template}, function(response) {
279 | $scope.images = response.results;
280 | $scope.page = response.page;
281 | })
282 | }
283 |
284 | $scope.viewImageContent = function(imageId, template, currentPage) {
285 | $stateParams.imageId = imageId;
286 | $stateParams.template = template;
287 | var imageContentModal = $modal.open({
288 | url: '/images/:imageId?template',
289 | templateUrl: 'html/image-content.html',
290 | controller: 'ImageController',
291 | resolve: {
292 | $stateParams: function() {
293 | return $stateParams;
294 | }
295 | }
296 | });
297 | }
298 |
299 | $scope.viewUploadImage = function(pageId, templateName) {
300 | $state.go('imageplant.images-upload', {page: pageId, template: templateName}, {location: false});
301 | }
302 |
303 | $scope.loadImageContent = function(template) {
304 | if (! (typeof(template) === 'undefined')) {
305 | $stateParams.template = template;
306 | }
307 | if (typeof($stateParams.template) === 'undefined') {
308 | $stateParams.template = '';
309 | }
310 | $scope.uploadStarted = true;
311 | Images.getByVersion({id: $stateParams.imagePlantId, imageId: $stateParams.imageId, template: $stateParams.template},
312 | function(response) {
313 | $scope.uploadStarted = false;
314 | $scope.imageContent = '/rest/v1/imageplants/' + $stateParams.imagePlantId + '/imagefiles/' + $stateParams.imageId + '?template=' + $stateParams.template;
315 | $scope.currentTemplate = $stateParams.template;
316 | },
317 | function(error) {
318 | $scope.uploadStarted = false;
319 | }
320 | )
321 |
322 | }
323 |
324 | $scope.uploadImage = function($flow) {
325 | $flow.opts.testChunks = false;
326 | $flow.opts.simultaneousUploads = 1;
327 | $flow.opts.forceChunkSize = false;
328 | $flow.opts.method = "octet";
329 | $flow.opts.target = "/rest/v1/imageplants/" + $stateParams.imagePlantId + "/images";
330 | $flow.upload();
331 | $scope.uploadStarted = true;
332 | $flow.on('fileSuccess', function (file,message) {
333 | $scope.uploadStarted = false;
334 | $scope.viewImages($stateParams.page, $stateParams.template);
335 | });
336 | $flow.on('fileError', function (file,message) {
337 | $scope.uploadStarted = false;
338 | var data = angular.fromJson(message);
339 | $scope.errorCode = data.code;
340 | if (data.code == 400108) {
341 | $scope.errorMessage = "Unsupported image format found!";
342 | }
343 | });
344 | }
345 |
346 | }
347 | ]);
348 |
349 | var imageReportCounts = null;
350 | var imageReportSize = null;
351 |
352 | imageS3Controllers.controller('ImageReportController',
353 | ['$rootScope', '$scope', '$state', '$stateParams', '$timeout', 'ImagePlants',
354 | function ($rootScope, $scope, $state, $stateParams, $timeout, ImagePlants) {
355 |
356 | $scope.refreshImageCharts = function() {
357 | var imagePlantId = $stateParams.imagePlantId;
358 | imageReportCounts = null;
359 | imageReportSize = null;
360 | autoRefreshImageReport[imagePlantId] = true;
361 | var start = new Date().getTime() - (10 * 60 * 1000); //back 10 mins
362 | var refreshRate = 10 * 1000; //milliseconds
363 | (function tick() {
364 | ImagePlants.getImageReport(
365 | {
366 | id: $stateParams.imagePlantId,
367 | templateName: '',
368 | startTime: start,
369 | length: '10',
370 | timeUnit: 'MINUTES',
371 | types: 'COUNTS_INBOUND, COUNTS_OUTBOUND, SIZE_INBOUND, SIZE_OUTBOUND'
372 | },
373 | function(response) {
374 | if (!autoRefreshImageReport[imagePlantId]) {
375 | return;
376 | }
377 | var countData = generateImageReportMorrisData(
378 | response.times, response.values.COUNTS_INBOUND, response.values.COUNTS_OUTBOUND);
379 | drawImageReportCounts(countData);
380 | var sizeData = generateImageReportMorrisData(
381 | response.times, response.values.SIZE_INBOUND, response.values.SIZE_OUTBOUND);
382 | drawImageReportSize(sizeData);
383 | start = start + refreshRate;
384 | $timeout(tick, refreshRate);
385 | });
386 |
387 | })();
388 | }
389 |
390 | }
391 | ]);
392 |
393 | function generateImageReportMorrisData(times, inboundValues, outboundValues) {
394 | var data = [];
395 | for (i=0; i-1&&a.splice(c,1)}function h(a,b){setTimeout(a.bind(b),0)}function i(a){return j(arguments,function(b){b!==a&&j(b,function(b,c){a[c]=b})}),a}function j(a,b,c){if(a){var d;if("undefined"!=typeof a.length){for(d=0;d1&&"pending"===a.chunks[a.chunks.length-1].status()&&0===a.chunks[0].preprocessState?(a.chunks[a.chunks.length-1].send(),b=!0,!1):void 0}),b))return b;if(j(this.files,function(a){return a.paused||j(a.chunks,function(a){return"pending"===a.status()&&0===a.preprocessState?(a.send(),b=!0,!1):void 0}),b?!1:void 0}),b)return!0;var c=!1;return j(this.files,function(a){return a.isComplete()?void 0:(c=!0,!1)}),c||a||h(function(){this.fire("complete")},this),!1},assignBrowse:function(a,c,d,e){"undefined"==typeof a.length&&(a=[a]),j(a,function(a){var f;"INPUT"===a.tagName&&"file"===a.type?f=a:(f=b.createElement("input"),f.setAttribute("type","file"),i(f.style,{visibility:"hidden",position:"absolute"}),a.appendChild(f),a.addEventListener("click",function(){f.click()},!1)),this.opts.singleFile||d||f.setAttribute("multiple","multiple"),c&&f.setAttribute("webkitdirectory","webkitdirectory"),j(e,function(a,b){f.setAttribute(b,a)});var g=this;f.addEventListener("change",function(a){g.addFiles(a.target.files,a),a.target.value=""},!1)},this)},assignDrop:function(a){"undefined"==typeof a.length&&(a=[a]),j(a,function(a){a.addEventListener("dragover",this.preventEvent,!1),a.addEventListener("dragenter",this.preventEvent,!1),a.addEventListener("drop",this.onDrop,!1)},this)},unAssignDrop:function(a){"undefined"==typeof a.length&&(a=[a]),j(a,function(a){a.removeEventListener("dragover",this.preventEvent),a.removeEventListener("dragenter",this.preventEvent),a.removeEventListener("drop",this.onDrop)},this)},isUploading:function(){var a=!1;return j(this.files,function(b){return b.isUploading()?(a=!0,!1):void 0}),a},upload:function(){if(!this.isUploading()){this.fire("uploadStart");for(var a=!1,b=1;b<=this.opts.simultaneousUploads;b++)a=this.uploadNextChunk(!0)||a;a||h(function(){this.fire("complete")},this)}},resume:function(){j(this.files,function(a){a.resume()})},pause:function(){j(this.files,function(a){a.pause()})},cancel:function(){for(var a=this.files.length-1;a>=0;a--)this.files[a].cancel()},progress:function(){var a=0,b=0;return j(this.files,function(c){a+=c.progress()*c.size,b+=c.size}),b>0?a/b:0},addFile:function(a,b){this.addFiles([a],b)},addFiles:function(a,b){var c=[];j(a,function(a){if((a.size%4096!==0||"."!==a.name&&"."!==a.fileName)&&!this.getFromUniqueIdentifier(this.generateUniqueIdentifier(a))){var d=new e(this,a);this.fire("fileAdded",d,b)&&c.push(d)}},this),this.fire("filesAdded",c,b)&&j(c,function(a){this.opts.singleFile&&this.files.length>0&&this.removeFile(this.files[0]),this.files.push(a)},this),this.fire("filesSubmitted",c,b)},removeFile:function(a){for(var b=this.files.length-1;b>=0;b--)this.files[b]===a&&(this.files.splice(b,1),a.abort())},getFromUniqueIdentifier:function(a){var b=!1;return j(this.files,function(c){c.uniqueIdentifier===a&&(b=c)}),b},getSize:function(){var a=0;return j(this.files,function(b){a+=b.size}),a},sizeUploaded:function(){var a=0;return j(this.files,function(b){a+=b.sizeUploaded()}),a},timeRemaining:function(){var a=0,b=0;return j(this.files,function(c){c.paused||c.error||(a+=c.size-c.sizeUploaded(),b+=c.averageSpeed)}),a&&!b?Number.POSITIVE_INFINITY:a||b?Math.floor(a/b):0}},e.prototype={measureSpeed:function(){var a=Date.now()-this._lastProgressCallback;if(a){var b=this.flowObj.opts.speedSmoothingFactor,c=this.sizeUploaded();this.currentSpeed=Math.max((c-this._prevUploadedSize)/a*1e3,0),this.averageSpeed=b*this.currentSpeed+(1-b)*this.averageSpeed,this._prevUploadedSize=c}},chunkEvent:function(a,b){switch(a){case"progress":if(Date.now()-this._lastProgressCallbackc;c++)this.chunks.push(new f(this.flowObj,this,c))},progress:function(){if(this.error)return 1;if(1===this.chunks.length)return this._prevProgress=Math.max(this._prevProgress,this.chunks[0].progress()),this._prevProgress;var a=0;j(this.chunks,function(b){a+=b.progress()*(b.endByte-b.startByte)});var b=a/this.size;return this._prevProgress=Math.max(this._prevProgress,b>.999?1:b),this._prevProgress},isUploading:function(){var a=!1;return j(this.chunks,function(b){return"uploading"===b.status()?(a=!0,!1):void 0}),a},isComplete:function(){var a=!1;return j(this.chunks,function(b){var c=b.status();return"pending"===c||"uploading"===c||1===b.preprocessState?(a=!0,!1):void 0}),!a},sizeUploaded:function(){var a=0;return j(this.chunks,function(b){a+=b.sizeUploaded()}),a},timeRemaining:function(){if(this.paused||this.error)return 0;var a=this.size-this.sizeUploaded();return a&&!this.averageSpeed?Number.POSITIVE_INFINITY:a||this.averageSpeed?Math.floor(a/this.averageSpeed):0},getType:function(){return this.file.type&&this.file.type.split("/")[1]},getExtension:function(){return this.name.substr((~-this.name.lastIndexOf(".")>>>0)+2).toLowerCase()}},f.prototype={getParams:function(){return{flowChunkNumber:this.offset+1,flowChunkSize:this.flowObj.opts.chunkSize,flowCurrentChunkSize:this.endByte-this.startByte,flowTotalSize:this.fileObjSize,flowIdentifier:this.fileObj.uniqueIdentifier,flowFilename:this.fileObj.name,flowRelativePath:this.fileObj.relativePath,flowTotalChunks:this.fileObj.chunks.length}},getTarget:function(a){var b=this.flowObj.opts.target;return b+=b.indexOf("?")<0?"?":"&",b+a.join("&")},test:function(){this.xhr=new XMLHttpRequest,this.xhr.addEventListener("load",this.testHandler,!1),this.xhr.addEventListener("error",this.testHandler,!1);var a=this.prepareXhrRequest("GET");this.xhr.send(a)},preprocessFinished:function(){this.preprocessState=2,this.send()},send:function(){var a=this.flowObj.opts.preprocess;if("function"==typeof a)switch(this.preprocessState){case 0:return this.preprocessState=1,void a(this);case 1:return;case 2:}if(this.flowObj.opts.testChunks&&!this.tested)return void this.test();this.loaded=0,this.total=0,this.pendingRetry=!1;var b=this.fileObj.file.slice?"slice":this.fileObj.file.mozSlice?"mozSlice":this.fileObj.file.webkitSlice?"webkitSlice":"slice",c=this.fileObj.file[b](this.startByte,this.endByte);this.xhr=new XMLHttpRequest,this.xhr.upload.addEventListener("progress",this.progressHandler,!1),this.xhr.addEventListener("load",this.doneHandler,!1),this.xhr.addEventListener("error",this.doneHandler,!1);var d=this.prepareXhrRequest("POST",this.flowObj.opts.method,c);this.xhr.send(d)},abort:function(){var a=this.xhr;this.xhr=null,a&&a.abort()},status:function(){return this.pendingRetry?"uploading":this.xhr?this.xhr.readyState<4?"uploading":200==this.xhr.status?"success":this.flowObj.opts.permanentErrors.indexOf(this.xhr.status)>-1||this.retries>=this.flowObj.opts.maxChunkRetries?"error":(this.abort(),"pending"):"pending"},message:function(){return this.xhr?this.xhr.responseText:""},progress:function(){if(this.pendingRetry)return 0;var a=this.status();return"success"===a||"error"===a?1:"pending"===a?0:this.total>0?this.loaded/this.total:0},sizeUploaded:function(){var a=this.endByte-this.startByte;return"success"!==this.status()&&(a=this.progress()*a),a},prepareXhrRequest:function(a,b,c){var d=this.flowObj.opts.query;"function"==typeof d&&(d=d(this.fileObj,this)),d=i(this.getParams(),d);var e=this.flowObj.opts.target,f=null;if("GET"===a||"octet"===b){var g=[];j(d,function(a,b){g.push([encodeURIComponent(b),encodeURIComponent(a)].join("="))}),e=this.getTarget(g),f=c||null}else f=new FormData,j(d,function(a,b){f.append(b,a)}),f.append(this.flowObj.opts.fileParameterName,c);return this.xhr.open(a,e,!0),this.xhr.withCredentials=this.flowObj.opts.withCredentials,j(this.flowObj.opts.headers,function(a,b){this.xhr.setRequestHeader(b,a)},this),f}},d.extend=i,d.each=j,d.FlowFile=e,d.FlowChunk=f,d.version="2.5.0","object"==typeof module&&module&&"object"==typeof module.exports?module.exports=d:(a.Flow=d,"function"==typeof define&&define.amd&&define("flow",[],function(){return d}))}(window,document),angular.module("flow.provider",[]).provider("flowFactory",function(){"use strict";this.defaults={},this.factory=function(a){return new Flow(a)},this.events=[],this.on=function(a,b){this.events.push([a,b])},this.$get=function(){var a=this.factory,b=this.defaults,c=this.events;return{create:function(d){var e=a(angular.extend({},b,d));return angular.forEach(c,function(a){e.on(a[0],a[1])}),e}}}}),angular.module("flow.init",["flow.provider"]).controller("flowCtrl",["$scope","$attrs","$parse","flowFactory",function(a,b,c,d){var e=angular.extend({},a.$eval(b.flowInit)),f=d.create(e);f.on("catchAll",function(b){var c=Array.prototype.slice.call(arguments);c.shift();var d=a.$broadcast.apply(a,["flow::"+b,f].concat(c));return{progress:1,filesSubmitted:1,fileSuccess:1,fileError:1,complete:1}[b]&&a.$apply(),d.defaultPrevented?!1:void 0}),a.$flow=f,b.hasOwnProperty("flowName")&&(c(b.flowName).assign(a,f),a.$on("$destroy",function(){c(b.flowName).assign(a)}))}]).directive("flowInit",[function(){return{scope:!0,controller:"flowCtrl"}}]),angular.module("flow.btn",["flow.init"]).directive("flowBtn",[function(){return{restrict:"EA",scope:!1,require:"^flowInit",link:function(a,b,c){var d=c.hasOwnProperty("flowDirectory"),e=c.hasOwnProperty("flowSingleFile"),f=c.hasOwnProperty("flowAttrs")&&a.$eval(c.flowAttrs);a.$flow.assignBrowse(b,d,e,f)}}}]),angular.module("flow.dragEvents",["flow.init"]).directive("flowPreventDrop",function(){return{scope:!1,link:function(a,b){b.bind("drop dragover",function(a){a.preventDefault()})}}}).directive("flowDragEnter",["$timeout",function(a){return{scope:!1,link:function(b,c,d){function e(a){var b=!1,c=a.dataTransfer||a.originalEvent.dataTransfer;return angular.forEach(c&&c.types,function(a){"Files"===a&&(b=!0)}),b}var f,g=!1;c.bind("dragover",function(c){e(c)&&(g||(b.$apply(d.flowDragEnter),g=!0),a.cancel(f),c.preventDefault())}),c.bind("dragleave drop",function(){f=a(function(){b.$eval(d.flowDragLeave),f=null,g=!1},100)})}}}]),angular.module("flow.drop",["flow.init"]).directive("flowDrop",function(){return{scope:!1,require:"^flowInit",link:function(a,b,c){function d(){a.$flow.assignDrop(b)}function e(){a.$flow.unAssignDrop(b)}c.flowDropEnabled?a.$watch(c.flowDropEnabled,function(a){a?d():e()}):d()}}}),!function(a){"use strict";function b(a){return a.charAt(0).toUpperCase()+a.slice(1)}var c=a.module("flow.events",["flow.init"]),d={fileSuccess:["$file","$message"],fileProgress:["$file"],fileAdded:["$file","$event"],filesAdded:["$files","$event"],filesSubmitted:["$files","$event"],fileRetry:["$file"],fileError:["$file","$message"],uploadStart:[],complete:[],progress:[],error:["$message","$file"]};a.forEach(d,function(d,e){var f="flow"+b(e);"flowUploadStart"==f&&(f="flowUploadStarted"),c.directive(f,[function(){return{require:"^flowInit",controller:["$scope","$attrs",function(b,c){b.$on("flow::"+e,function(){var e=Array.prototype.slice.call(arguments),g=e.shift();if(b.$flow===e.shift()){var h={};a.forEach(d,function(a,b){h[a]=e[b]}),b.$eval(c[f],h)===!1&&g.preventDefault()}})}]}}])})}(angular),angular.module("flow.img",["flow.init"]).directive("flowImg",[function(){return{scope:!1,require:"^flowInit",link:function(a,b,c){var d=c.flowImg;a.$watch(d,function(b){if(b){var d=new FileReader;d.readAsDataURL(b.file),d.onload=function(b){a.$apply(function(){c.$set("src",b.target.result)})}}})}}}]),angular.module("flow.transfers",["flow.init"]).directive("flowTransfers",[function(){return{scope:!0,require:"^flowInit",link:function(a){a.transfers=a.$flow.files}}}]),angular.module("flow",["flow.provider","flow.init","flow.events","flow.btn","flow.drop","flow.transfers","flow.img","flow.dragEvents"]);
--------------------------------------------------------------------------------
/public/javascripts/angular-ui-router.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * State-based routing for AngularJS
3 | * @version v0.2.11
4 | * @link http://angular-ui.github.com/
5 | * @license MIT License, http://www.opensource.org/licenses/MIT
6 | */
7 | "undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return J(new(J(function(){},{prototype:a})),b)}function e(a){return I(arguments,function(b){b!==a&&I(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)})}),a}function f(a,b){var c=[];for(var d in a.path){if(a.path[d]!==b.path[d])break;c.push(a.path[d])}return c}function g(a){if(Object.keys)return Object.keys(a);var c=[];return b.forEach(a,function(a,b){c.push(b)}),c}function h(a,b){if(Array.prototype.indexOf)return a.indexOf(b,Number(arguments[2])||0);var c=a.length>>>0,d=Number(arguments[2])||0;for(d=0>d?Math.ceil(d):Math.floor(d),0>d&&(d+=c);c>d;d++)if(d in a&&a[d]===b)return d;return-1}function i(a,b,c,d){var e,i=f(c,d),j={},k=[];for(var l in i)if(i[l].params&&(e=g(i[l].params),e.length))for(var m in e)h(k,e[m])>=0||(k.push(e[m]),j[e[m]]=a[e[m]]);return J({},j,b)}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e "));if(o[c]=d,F(a))m.push(c,[function(){return b.get(a)}],h);else{var e=b.annotate(a);I(e,function(a){a!==c&&g.hasOwnProperty(a)&&k(g[a],a)}),m.push(c,a,e)}n.pop(),o[c]=f}}function l(a){return G(a)&&a.then&&a.$$promises}if(!G(g))throw new Error("'invocables' must be an object");var m=[],n=[],o={};return I(g,k),g=n=o=null,function(d,f,g){function h(){--s||(t||e(r,f.$$values),p.$$values=r,p.$$promises=!0,delete p.$$inheritedValues,o.resolve(r))}function k(a){p.$$failure=a,o.reject(a)}function n(c,e,f){function i(a){l.reject(a),k(a)}function j(){if(!D(p.$$failure))try{l.resolve(b.invoke(e,g,r)),l.promise.then(function(a){r[c]=a,h()},i)}catch(a){i(a)}}var l=a.defer(),m=0;I(f,function(a){q.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,q[a].then(function(b){r[a]=b,--m||j()},i))}),m||j(),q[c]=l.promise}if(l(d)&&g===c&&(g=f,f=d,d=null),d){if(!G(d))throw new Error("'locals' must be an object")}else d=i;if(f){if(!l(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=j;var o=a.defer(),p=o.promise,q=p.$$promises={},r=J({},d),s=1+m.length/3,t=!1;if(D(f.$$failure))return k(f.$$failure),p;f.$$inheritedValues&&e(r,f.$$inheritedValues),f.$$values?(t=e(r,f.$$values),p.$$inheritedValues=f.$$values,h()):(f.$$inheritedValues&&(p.$$inheritedValues=f.$$inheritedValues),J(q,f.$$promises),f.then(h,k));for(var u=0,v=m.length;v>u;u+=3)d.hasOwnProperty(m[u])?h():n(m[u],m[u+1],m[u+2]);return p}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function m(a,b,c){this.fromConfig=function(a,b,c){return D(a.template)?this.fromString(a.template,b):D(a.templateUrl)?this.fromUrl(a.templateUrl,b):D(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return E(a)?a(b):a},this.fromUrl=function(c,d){return E(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function n(a,d){function e(a){return D(a)?this.type.decode(a):p.$$getDefaultValue(this)}function f(b,c,d){if(!/^\w+(-+\w+)*$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(n[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");n[b]=J({type:c||new o,$value:e},d)}function g(a,b,c){var d=a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!b)return d;var e=c?"?":"";return d+e+"("+b+")"+e}function h(a){if(!d.params||!d.params[a])return{};var b=d.params[a];return G(b)?b:{value:b}}d=b.isObject(d)?d:{};var i,j=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,k="^",l=0,m=this.segments=[],n=this.params={};this.source=a;for(var q,r,s,t,u;(i=j.exec(a))&&(q=i[2]||i[3],r=i[4]||("*"==i[1]?".*":"[^/]*"),s=a.substring(l,i.index),t=this.$types[r]||new o({pattern:new RegExp(r)}),u=h(q),!(s.indexOf("?")>=0));)k+=g(s,t.$subPattern(),D(u.value)),f(q,t,u),m.push(s),l=j.lastIndex;s=a.substring(l);var v=s.indexOf("?");if(v>=0){var w=this.sourceSearch=s.substring(v);s=s.substring(0,v),this.sourcePath=a.substring(0,l+v),I(w.substring(1).split(/[&?]/),function(a){f(a,null,h(a))})}else this.sourcePath=a,this.sourceSearch="";k+=g(s)+(d.strict===!1?"/?":"")+"$",m.push(s),this.regexp=new RegExp(k,d.caseInsensitive?"i":c),this.prefix=m[0]}function o(a){J(this,a)}function p(){function a(){return{strict:f,caseInsensitive:e}}function b(a){return E(a)||H(a)&&E(a[a.length-1])}function c(){I(h,function(a){if(n.prototype.$types[a.name])throw new Error("A type named '"+a.name+"' has already been defined.");var c=new o(b(a.def)?d.invoke(a.def):a.def);n.prototype.$types[a.name]=c})}var d,e=!1,f=!0,g=!0,h=[],i={"int":{decode:function(a){return parseInt(a,10)},is:function(a){return D(a)?this.decode(a.toString())===a:!1},pattern:/\d+/},bool:{encode:function(a){return a?1:0},decode:function(a){return 0===parseInt(a,10)?!1:!0},is:function(a){return a===!0||a===!1},pattern:/0|1/},string:{pattern:/[^\/]*/},date:{equals:function(a,b){return a.toISOString()===b.toISOString()},decode:function(a){return new Date(a)},encode:function(a){return[a.getFullYear(),("0"+(a.getMonth()+1)).slice(-2),("0"+a.getDate()).slice(-2)].join("-")},pattern:/[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/}};p.$$getDefaultValue=function(a){if(!b(a.value))return a.value;if(!d)throw new Error("Injectable functions cannot be called at configuration time");return d.invoke(a.value)},this.caseInsensitive=function(a){e=a},this.strictMode=function(a){f=a},this.compile=function(b,c){return new n(b,J(a(),c))},this.isMatcher=function(a){if(!G(a))return!1;var b=!0;return I(n.prototype,function(c,d){E(c)&&(b=b&&D(a[d])&&E(a[d]))}),b},this.type=function(a,b){return D(b)?(h.push({name:a,def:b}),g||c(),this):n.prototype.$types[a]},this.$get=["$injector",function(a){return d=a,g=!1,n.prototype.$types={},c(),I(i,function(a,b){n.prototype.$types[b]||(n.prototype.$types[b]=new o(a))}),this}]}function q(a,b){function d(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function e(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function f(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return D(d)?d:!0}function g(b,c,d,e){function f(a,b,c){return"/"===m?a:b?m.slice(0,-1)+a:c?m.slice(1)+a:a}function g(a){function c(a){var c=a(d,b);return c?(F(c)&&b.replace().url(c),!0):!1}if(!a||!a.defaultPrevented){var e,f=i.length;for(e=0;f>e;e++)if(c(i[e]))return;j&&c(j)}}function l(){return h=h||c.$on("$locationChangeSuccess",g)}var m=e.baseHref(),n=b.url();return k||l(),{sync:function(){g()},listen:function(){return l()},update:function(a){return a?void(n=b.url()):void(b.url()!==n&&(b.url(n),b.replace()))},push:function(a,c,d){b.url(a.format(c||{})),d&&d.replace&&b.replace()},href:function(c,d,e){if(!c.validates(d))return null;var g=a.html5Mode(),h=c.format(d);if(e=e||{},g||null===h||(h="#"+a.hashPrefix()+h),h=f(h,g,e.absolute),!e.absolute||!h)return h;var i=!g&&h?"/":"",j=b.port();return j=80===j||443===j?"":":"+j,[b.protocol(),"://",b.host(),j,i,h].join("")}}}var h,i=[],j=null,k=!1;this.rule=function(a){if(!E(a))throw new Error("'rule' must be a function");return i.push(a),this},this.otherwise=function(a){if(F(a)){var b=a;a=function(){return b}}else if(!E(a))throw new Error("'rule' must be a function");return j=a,this},this.when=function(a,c){var g,h=F(c);if(F(a)&&(a=b.compile(a)),!h&&!E(c)&&!H(c))throw new Error("invalid 'handler' in when()");var i={matcher:function(a,c){return h&&(g=b.compile(c),c=["$match",function(a){return g.format(a)}]),J(function(b,d){return f(b,c,a.exec(d.path(),d.search()))},{prefix:F(a.prefix)?a.prefix:""})},regex:function(a,b){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(g=b,b=["$match",function(a){return e(g,a)}]),J(function(c,d){return f(c,b,a.exec(d.path()))},{prefix:d(a)})}},j={matcher:b.isMatcher(a),regex:a instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](a,c));throw new Error("invalid 'what' in when()")},this.deferIntercept=function(a){a===c&&(a=!0),k=a},this.$get=g,g.$inject=["$location","$rootScope","$injector","$browser"]}function r(a,e){function f(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function h(a,b){if(!a)return c;var d=F(a),e=d?a:a.name,g=f(e);if(g){if(!b)throw new Error("No reference point given for path '"+e+"'");for(var h=e.split("."),i=0,j=h.length,k=b;j>i;i++)if(""!==h[i]||0!==i){if("^"!==h[i])break;if(!k.parent)throw new Error("Path '"+e+"' not valid for state '"+b.name+"'");k=k.parent}else k=b;h=h.slice(i).join("."),e=k.name+(k.name&&h?".":"")+h}var l=v[e];return!l||!d&&(d||l!==a&&l.self!==a)?c:l}function l(a,b){w[a]||(w[a]=[]),w[a].push(b)}function m(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!F(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(v.hasOwnProperty(c))throw new Error("State '"+c+"'' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):F(b.parent)?b.parent:"";if(e&&!v[e])return l(e,b.self);for(var f in y)E(y[f])&&(b[f]=y[f](b,y.$delegates[f]));if(v[c]=b,!b[x]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){u.$current.navigable==b&&j(a,c)||u.transitionTo(b,a,{location:!1})}]),w[c])for(var g=0;g-1}function o(a){var b=a.split("."),c=u.$current.name.split(".");if("**"===b[0]&&(c=c.slice(c.indexOf(b[1])),c.unshift("**")),"**"===b[b.length-1]&&(c.splice(c.indexOf(b[b.length-2])+1,Number.MAX_VALUE),c.push("**")),b.length!=c.length)return!1;for(var d=0,e=b.length;e>d;d++)"*"===b[d]&&(c[d]="*");return c.join("")===b.join("")}function p(a,b){return F(a)&&!D(b)?y[a]:E(b)&&F(a)?(y[a]&&!y.$delegates[a]&&(y.$delegates[a]=y[a]),y[a]=b,this):this}function q(a,b){return G(a)?b=a:b.name=a,m(b),this}function r(a,e,f,l,m,p,q){function r(b,c,d,f){var g=a.$broadcast("$stateNotFound",b,c,d);if(g.defaultPrevented)return q.update(),A;if(!g.retry)return null;if(f.$retry)return q.update(),B;var h=u.transition=e.when(g.retry);return h.then(function(){return h!==u.transition?y:(b.options.$retry=!0,u.transitionTo(b.to,b.toParams,b.options))},function(){return A}),q.update(),h}function w(a,c,d,h,i){var j=d?c:k(g(a.params),c),n={$stateParams:j};i.resolve=m.resolve(a.resolve,n,i.resolve,a);var o=[i.resolve.then(function(a){i.globals=a})];return h&&o.push(h),I(a.views,function(c,d){var e=c.resolve&&c.resolve!==a.resolve?c.resolve:{};e.$template=[function(){return f.load(d,{view:c,locals:n,params:j})||""}],o.push(m.resolve(e,n,i.resolve,a).then(function(f){if(E(c.controllerProvider)||H(c.controllerProvider)){var g=b.extend({},e,n);f.$$controller=l.invoke(c.controllerProvider,null,g)}else f.$$controller=c.controller;f.$$state=a,f.$$controllerAs=c.controllerAs,i[d]=f}))}),e.all(o).then(function(){return i})}var y=e.reject(new Error("transition superseded")),z=e.reject(new Error("transition prevented")),A=e.reject(new Error("transition aborted")),B=e.reject(new Error("transition failed"));return t.locals={resolve:null,globals:{$stateParams:{}}},u={params:{},current:t.self,$current:t,transition:null},u.reload=function(){u.transitionTo(u.current,p,{reload:!0,inherit:!1,notify:!1})},u.go=function(a,b,c){return u.transitionTo(a,b,J({inherit:!0,relative:u.$current},c))},u.transitionTo=function(b,c,f){c=c||{},f=J({location:!0,inherit:!1,relative:null,notify:!0,reload:!1,$retry:!1},f||{});var m,n=u.$current,o=u.params,v=n.path,A=h(b,f.relative);if(!D(A)){var B={to:b,toParams:c,options:f},C=r(B,n.self,o,f);if(C)return C;if(b=B.to,c=B.toParams,f=B.options,A=h(b,f.relative),!D(A)){if(!f.relative)throw new Error("No such state '"+b+"'");throw new Error("Could not resolve '"+b+"' from state '"+f.relative+"'")}}if(A[x])throw new Error("Cannot transition to abstract state '"+b+"'");f.inherit&&(c=i(p,c||{},u.$current,A)),b=A;var E=b.path,F=0,G=E[F],H=t.locals,I=[];if(!f.reload)for(;G&&G===v[F]&&j(c,o,G.ownParams);)H=I[F]=G.locals,F++,G=E[F];if(s(b,n,H,f))return b.self.reloadOnSearch!==!1&&q.update(),u.transition=null,e.when(u.current);if(c=k(g(b.params),c||{}),f.notify&&a.$broadcast("$stateChangeStart",b.self,c,n.self,o).defaultPrevented)return q.update(),z;for(var L=e.when(H),M=F;M=F;d--)g=v[d],g.self.onExit&&l.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=F;d=0?c:c+"@"+(b?b.state.name:"")}function x(a,b){var c,d=a.match(/^\s*({[^}]*})\s*$/);if(d&&(a=b+"("+d[1]+")"),c=a.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/),!c||4!==c.length)throw new Error("Invalid state ref '"+a+"'");return{state:c[1],paramExpr:c[3]||null}}function y(a){var b=a.parent().inheritedData("$uiView");return b&&b.state&&b.state.name?b.state:void 0}function z(a,c){var d=["location","inherit","reload"];return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(e,f,g,h){var i=x(g.uiSref,a.current.name),j=null,k=y(f)||a.$current,l="FORM"===f[0].nodeName,m=l?"action":"href",n=!0,o={relative:k,inherit:!0},p=e.$eval(g.uiSrefOpts)||{};b.forEach(d,function(a){a in p&&(o[a]=p[a])});var q=function(b){if(b&&(j=b),n){var c=a.href(i.state,j,o),d=h[1]||h[0];return d&&d.$$setStateInfo(i.state,j),null===c?(n=!1,!1):void(f[0][m]=c)}};i.paramExpr&&(e.$watch(i.paramExpr,function(a){a!==j&&q(a)},!0),j=e.$eval(i.paramExpr)),q(),l||f.bind("click",function(b){var d=b.which||b.button;if(!(d>1||b.ctrlKey||b.metaKey||b.shiftKey||f.attr("target"))){var e=c(function(){a.go(i.state,j,o)});b.preventDefault(),b.preventDefault=function(){c.cancel(e)}}})}}}function A(a,b,c){return{restrict:"A",controller:["$scope","$element","$attrs",function(d,e,f){function g(){h()?e.addClass(m):e.removeClass(m)}function h(){return"undefined"!=typeof f.uiSrefActiveEq?a.$current.self===k&&i():a.includes(k.name)&&i()}function i(){return!l||j(l,b)}var k,l,m;m=c(f.uiSrefActiveEq||f.uiSrefActive||"",!1)(d),this.$$setStateInfo=function(b,c){k=a.get(b,y(e)),l=c,g()},d.$on("$stateChangeSuccess",g)}]}}function B(a){return function(b){return a.is(b)}}function C(a){return function(b){return a.includes(b)}}var D=b.isDefined,E=b.isFunction,F=b.isString,G=b.isObject,H=b.isArray,I=b.forEach,J=b.extend,K=b.copy;b.module("ui.router.util",["ng"]),b.module("ui.router.router",["ui.router.util"]),b.module("ui.router.state",["ui.router.router","ui.router.util"]),b.module("ui.router",["ui.router.state"]),b.module("ui.router.compat",["ui.router"]),l.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",l),m.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",m),n.prototype.concat=function(a,b){return new n(this.sourcePath+a+this.sourceSearch,b)},n.prototype.toString=function(){return this.source},n.prototype.exec=function(a,b){var c=this.regexp.exec(a);if(!c)return null;b=b||{};var d,e,f,g=this.parameters(),h=g.length,i=this.segments.length-1,j={};if(i!==c.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(d=0;i>d;d++)f=g[d],e=this.params[f],j[f]=e.$value(c[d+1]);for(;h>d;d++)f=g[d],e=this.params[f],j[f]=e.$value(b[f]);return j},n.prototype.parameters=function(a){return D(a)?this.params[a]||null:g(this.params)},n.prototype.validates=function(a){var b,c,d=!0,e=this;return I(a,function(a,f){e.params[f]&&(c=e.params[f],b=!a&&D(c.value),d=d&&(b||c.type.is(a)))}),d},n.prototype.format=function(a){var b=this.segments,c=this.parameters();if(!a)return b.join("").replace("//","/");var d,e,f,g,h,i,j=b.length-1,k=c.length,l=b[0];if(!this.validates(a))return null;for(d=0;j>d;d++)g=c[d],f=a[g],h=this.params[g],(D(f)||"/"!==b[d]&&"/"!==b[d+1])&&(null!=f&&(l+=encodeURIComponent(h.type.encode(f))),l+=b[d+1]);for(;k>d;d++)g=c[d],f=a[g],null!=f&&(i=H(f),i&&(f=f.map(encodeURIComponent).join("&"+g+"=")),l+=(e?"&":"?")+g+"="+(i?f:encodeURIComponent(f)),e=!0);return l},n.prototype.$types={},o.prototype.is=function(){return!0},o.prototype.encode=function(a){return a},o.prototype.decode=function(a){return a},o.prototype.equals=function(a,b){return a==b},o.prototype.$subPattern=function(){var a=this.pattern.toString();return a.substr(1,a.length-2)},o.prototype.pattern=/.*/,b.module("ui.router.util").provider("$urlMatcherFactory",p),q.$inject=["$locationProvider","$urlMatcherFactoryProvider"],b.module("ui.router.router").provider("$urlRouter",q),r.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider"],b.module("ui.router.state").value("$stateParams",{}).provider("$state",r),s.$inject=[],b.module("ui.router.state").provider("$view",s),b.module("ui.router.state").provider("$uiViewScroll",t),u.$inject=["$state","$injector","$uiViewScroll"],v.$inject=["$compile","$controller","$state"],b.module("ui.router.state").directive("uiView",u),b.module("ui.router.state").directive("uiView",v),z.$inject=["$state","$timeout"],A.$inject=["$state","$stateParams","$interpolate"],b.module("ui.router.state").directive("uiSref",z).directive("uiSrefActive",A).directive("uiSrefActiveEq",A),B.$inject=["$state"],C.$inject=["$state"],b.module("ui.router.state").filter("isState",B).filter("includedByState",C)}(window,window.angular);
--------------------------------------------------------------------------------