├── conf ├── imagecontent.properties ├── imageprocessor.properties ├── mongodb.properties ├── application.conf └── routes ├── project ├── activator-sbt-idea-shim.sbt ├── activator-sbt-eclipse-shim.sbt ├── build.properties └── plugins.sbt ├── public ├── images │ ├── icon_26.png │ └── loading.gif ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── html │ ├── custom-busy-template.html │ ├── image-report.html │ ├── 404.html │ ├── imageplant-overview.html │ ├── image-content.html │ ├── index.html │ ├── imageplant.html │ ├── imageplant-list.html │ ├── image-upload.html │ ├── template-list.html │ ├── image-list.html │ ├── template-create.html │ ├── imageplant-update.html │ ├── template-update.html │ └── imageplant-create.html ├── stylesheets │ ├── morris.css │ ├── images3.css │ ├── dashboard.css │ ├── angular-busy.min.css │ └── bootstrap-theme.min.css └── javascripts │ ├── images3-utils.js │ ├── angular-prompt.min.js │ ├── angular-busy.min.js │ ├── images3-app.js │ ├── images3-services.js │ ├── images3-controllers.js │ ├── ng-flow-standalone.min.js │ └── angular-ui-router.min.js ├── app ├── com │ └── images3 │ │ └── rest │ │ ├── models │ │ ├── TemplateIdentityModel.java │ │ ├── TemplateResponseModel.java │ │ ├── AmazonS3BucketModel.java │ │ ├── TemplateAddRequestModel.java │ │ ├── ImagePlantUpdateRequestModel.java │ │ ├── ImagePlantAddRequestModel.java │ │ ├── ResizingConfigModel.java │ │ └── PaginatedResultModel.java │ │ ├── exceptions │ │ ├── PreciseExceptionMapper.java │ │ ├── ExceptionMapper.java │ │ ├── UnsupportedImageFormatExceptionMapper.java │ │ ├── UnremovableTemplateExceptionMapper.java │ │ ├── UnachievableTemplateExceptionMapper.java │ │ ├── IllegalImageVersionExceptionMapper.java │ │ ├── DuplicateTemplateNameExceptionMapper.java │ │ ├── NoSuchEntityFoundExceptionMapper.java │ │ ├── DuplicateImageVersionExceptionMapper.java │ │ ├── DuplicateImagePlantNameExceptionMapper.java │ │ ├── IllegalTemplateNameExceptionMapper.java │ │ ├── AmazonS3BucketAccessFailedExceptionMapper.java │ │ ├── IllegalTemplateNameLengthExceptionMapper.java │ │ ├── IllegalImagePlantNameLengthExceptionMapper.java │ │ ├── IllegalResizingDimensionsExceptionMapper.java │ │ └── ErrorResponse.java │ │ └── controllers │ │ ├── TemplateController.java │ │ ├── ImagePlantController.java │ │ └── ImageController.java ├── ImageS3Provider.java ├── ObjectMapperProvider.java └── Global.java ├── .gitignore └── README.md /conf/imagecontent.properties: -------------------------------------------------------------------------------- 1 | imagecontent.download.dir = /images3/images -------------------------------------------------------------------------------- /conf/imageprocessor.properties: -------------------------------------------------------------------------------- 1 | image.processing.tempdir = /images3/images -------------------------------------------------------------------------------- /project/activator-sbt-idea-shim.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.2") -------------------------------------------------------------------------------- /public/images/icon_26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/images3/images3-play/HEAD/public/images/icon_26.png -------------------------------------------------------------------------------- /public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/images3/images3-play/HEAD/public/images/loading.gif -------------------------------------------------------------------------------- /project/activator-sbt-eclipse-shim.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0") -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/images3/images3-play/HEAD/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/images3/images3-play/HEAD/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/images3/images3-play/HEAD/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Wed Aug 13 21:06:39 EDT 2014 3 | template.uuid=7ee88409-6a2c-4287-bf0f-6b4043de2317 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 5 | 6 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.1") 7 | -------------------------------------------------------------------------------- /public/html/custom-busy-template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
{{$message}}
4 | 5 |
-------------------------------------------------------------------------------- /conf/mongodb.properties: -------------------------------------------------------------------------------- 1 | mongodb.url = 127.0.0.1 2 | mongodb.port = 27017 3 | mongodb.username = 4 | mongodb.password = 5 | 6 | #The following properties are used to specify the size of each page 7 | # for 'Image Plant', 'Image', 'Template' and 'Image Report' 8 | 9 | imageplant.page.size = 10 10 | image.page.size = 15 11 | template.page.size = 10 12 | imagemetricsservice.page.size = 50 -------------------------------------------------------------------------------- /public/stylesheets/morris.css: -------------------------------------------------------------------------------- 1 | .morris-hover{position:absolute;z-index:1000}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255,255,255,0.8);border:solid 2px rgba(230,230,230,0.8);font-family:sans-serif;font-size:12px;text-align:center}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0} 2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0} 3 | -------------------------------------------------------------------------------- /public/html/image-report.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Image (count)
5 |
6 |
7 |
8 |
Image (bytes)
9 |
10 |
11 |
12 |
13 | 14 | -------------------------------------------------------------------------------- /public/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ImageS3 Management (404) 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

The page you requested was not found!

14 |
15 |
16 | 17 | -------------------------------------------------------------------------------- /public/html/imageplant-overview.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |

Overview

9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /conf/application.conf: -------------------------------------------------------------------------------- 1 | # Strongly recommend to change application secret by setup environment variable 'APPLICATION_SECRET' 2 | # Generate secret: [app] $ play-generate-secret 3 | application.secret="changeme" 4 | application.secret=${?APPLICATION_SECRET} 5 | 6 | # Logger 7 | # ~~~~~ 8 | # You can also configure logback (http://logback.qos.ch/), 9 | # by providing an application-logger.xml file in the conf directory. 10 | 11 | # Root logger: 12 | logger.root=ERROR 13 | 14 | # Logger used by the framework: 15 | logger.play=INFO 16 | 17 | # Logger provided to your application: 18 | logger.application=DEBUG 19 | 20 | 21 | # ImageS3 configure files 22 | images3.conf = conf/imagecontent.properties 23 | imageprocessor.conf = conf/imageprocessor.properties 24 | mongodb.conf = conf/mongodb.properties 25 | 26 | 27 | -------------------------------------------------------------------------------- /public/html/image-content.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/stylesheets/images3.css: -------------------------------------------------------------------------------- 1 | .logo-images3 { 2 | background: url(/images/icon_26.png) no-repeat center center; 3 | background-size: 26px auto; 4 | display: inline-block; 5 | height: 26px; 6 | width: 26px; 7 | margin-right: 8px; 8 | vertical-align: -7px; 9 | } 10 | 11 | .brand { 12 | display: block; 13 | margin-top: 12px; 14 | margin-left: 4px; 15 | font-size: 18px; 16 | color: #fff; 17 | font-weight: normal; 18 | } 19 | 20 | div a:hover { 21 | text-decoration: none; 22 | color: #fff; 23 | } 24 | 25 | .sidebar-fixed { 26 | position: absolute; 27 | } 28 | 29 | .sidebar-fixed ul li { 30 | border-bottom: 1px solid #e7e7e7; 31 | } 32 | 33 | .sidebar-brand { 34 | padding-top: 5px; 35 | padding-bottom: 20px; 36 | } 37 | 38 | .sidebar-fixed ul li a { 39 | padding-top: 15px; 40 | padding-bottom: 15px; 41 | } 42 | 43 | .valign-bottom { 44 | position: absolute; 45 | bottom:0; 46 | right:0; 47 | } 48 | 49 | div.busy { 50 | opacity: 0.5; 51 | background: #000; 52 | width: 100%; 53 | height: 100%; 54 | z-index: 10; 55 | top: 0; 56 | left: 0; 57 | position: fixed; 58 | } -------------------------------------------------------------------------------- /app/com/images3/rest/models/TemplateIdentityModel.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.models; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | 20 | public abstract class TemplateIdentityModel { 21 | 22 | TemplateIdentityModel(@JsonProperty("imagePlantId") String imagePlantId, 23 | @JsonProperty("templateName") String templateName) {} 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/com/images3/rest/models/TemplateResponseModel.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.models; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | 20 | public abstract class TemplateResponseModel { 21 | 22 | @JsonProperty("isArchived") abstract public boolean isArchived(); 23 | @JsonProperty("isRemovable") abstract public boolean isRemovable(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/com/images3/rest/models/AmazonS3BucketModel.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.models; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | 20 | public abstract class AmazonS3BucketModel { 21 | 22 | AmazonS3BucketModel(@JsonProperty("accessKey") String accessKey, 23 | @JsonProperty("secretKey") String secretKey, 24 | @JsonProperty("name") String name) {} 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/com/images3/rest/models/TemplateAddRequestModel.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.models; 17 | 18 | import com.images3.common.ResizingConfig; 19 | 20 | public class TemplateAddRequestModel { 21 | 22 | private String name; 23 | private ResizingConfig resizingConfig; 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | public ResizingConfig getResizingConfig() { 29 | return resizingConfig; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/com/images3/rest/models/ImagePlantUpdateRequestModel.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.models; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | import com.images3.common.AmazonS3Bucket; 20 | public abstract class ImagePlantUpdateRequestModel { 21 | 22 | ImagePlantUpdateRequestModel(@JsonProperty("id") String id, 23 | @JsonProperty("name") String name, 24 | @JsonProperty("bucket") AmazonS3Bucket bucket) {} 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/com/images3/rest/models/ImagePlantAddRequestModel.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.models; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | import com.images3.common.AmazonS3Bucket; 20 | import com.images3.common.ResizingConfig; 21 | 22 | public abstract class ImagePlantAddRequestModel { 23 | 24 | ImagePlantAddRequestModel(@JsonProperty("name") String name, 25 | @JsonProperty("bucket") AmazonS3Bucket bucket, 26 | @JsonProperty("resizingConfig") ResizingConfig resizingConfig) {} 27 | 28 | } 29 | -------------------------------------------------------------------------------- /public/javascripts/images3-utils.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 | function initialImagePlant() { 21 | return { 22 | name: "", 23 | bucket: { 24 | accessKey: "", 25 | secretKey: "", 26 | name: "" 27 | }, 28 | resizingConfig: { 29 | unit: "PERCENT", 30 | width: 0, 31 | height: 0, 32 | isKeepProportions: true 33 | } 34 | }; 35 | } 36 | 37 | function initialTemplate(imagePlantId) { 38 | return { 39 | name: "", 40 | resizingConfig: { 41 | unit: "PIXEL", 42 | width: 0, 43 | height: 0, 44 | isKeepProportions: true 45 | } 46 | }; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /app/com/images3/rest/models/ResizingConfigModel.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.models; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | import com.images3.common.ResizingUnit; 20 | 21 | public abstract class ResizingConfigModel { 22 | 23 | ResizingConfigModel(@JsonProperty("unit") ResizingUnit unit, 24 | @JsonProperty("width") int width, 25 | @JsonProperty("height") int height, 26 | @JsonProperty("isKeepProportions") boolean isKeepProportions) {} 27 | 28 | @JsonProperty("isKeepProportions") abstract public boolean isKeepProportions(); 29 | } 30 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/PreciseExceptionMapper.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.exceptions; 17 | 18 | public abstract class PreciseExceptionMapper extends ExceptionMapper { 19 | 20 | private Class exceptionClass; 21 | 22 | public PreciseExceptionMapper(Class exceptionClass) { 23 | this(exceptionClass, null); 24 | } 25 | 26 | public PreciseExceptionMapper(Class exceptionClass, ExceptionMapper successor) { 27 | super(successor); 28 | this.exceptionClass = exceptionClass; 29 | } 30 | 31 | @Override 32 | protected boolean isMatch(Throwable t) { 33 | return (exceptionClass.equals(t.getClass())); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/com/images3/rest/models/PaginatedResultModel.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.models; 17 | 18 | import java.util.LinkedHashMap; 19 | import java.util.Map; 20 | 21 | public class PaginatedResultModel { 22 | 23 | private Map page; 24 | private T results; 25 | 26 | public PaginatedResultModel(String prev, String next, T results) { 27 | this.page = new LinkedHashMap(2); 28 | this.page.put("prev", prev); 29 | this.page.put("next", next); 30 | this.results = results; 31 | } 32 | 33 | public Map getPage() { 34 | return page; 35 | } 36 | 37 | public T getResults() { 38 | return results; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/target 3 | target 4 | tmp 5 | .history 6 | dist 7 | /.idea 8 | /*.iml 9 | /out 10 | /.idea_modules 11 | /.classpath 12 | /.project 13 | /RUNNING_PID 14 | /.settings 15 | 16 | 17 | ### PlayFramework template 18 | # Ignore Play! working directory # 19 | bin/ 20 | /db 21 | .eclipse 22 | /lib/ 23 | /logs/ 24 | /modules 25 | /project/project 26 | /project/target 27 | /target 28 | tmp/ 29 | test-result 30 | server.pid 31 | *.iml 32 | *.eml 33 | /dist/ 34 | .cache 35 | 36 | 37 | 38 | ### JetBrains template 39 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion 40 | 41 | *.iml 42 | 43 | ## Directory-based project format: 44 | .idea/ 45 | # if you remove the above rule, at least ignore the following: 46 | 47 | # User-specific stuff: 48 | # .idea/workspace.xml 49 | # .idea/tasks.xml 50 | # .idea/dictionaries 51 | 52 | # Sensitive or high-churn files: 53 | # .idea/dataSources.ids 54 | # .idea/dataSources.xml 55 | # .idea/sqlDataSources.xml 56 | # .idea/dynamic.xml 57 | # .idea/uiDesigner.xml 58 | 59 | # Gradle: 60 | # .idea/gradle.xml 61 | # .idea/libraries 62 | 63 | # Mongo Explorer plugin: 64 | # .idea/mongoSettings.xml 65 | 66 | ## File-based project format: 67 | *.ipr 68 | *.iws 69 | 70 | ## Plugin-specific files: 71 | 72 | # IntelliJ 73 | /out/ 74 | 75 | # mpeltonen/sbt-idea plugin 76 | .idea_modules/ 77 | 78 | # JIRA plugin 79 | atlassian-ide-plugin.xml 80 | 81 | # Crashlytics plugin (for Android Studio and IntelliJ) 82 | com_crashlytics_export_strings.xml 83 | crashlytics.properties 84 | crashlytics-build.properties 85 | 86 | 87 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/ExceptionMapper.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.exceptions; 17 | import play.mvc.Result; 18 | import play.mvc.Results; 19 | 20 | 21 | public abstract class ExceptionMapper { 22 | 23 | private ExceptionMapper successor; 24 | 25 | public ExceptionMapper(ExceptionMapper successor) { 26 | this.successor = successor; 27 | } 28 | 29 | public Result toResult(Throwable t) { 30 | if (isMatch(t)) { 31 | return getResult(t); 32 | } 33 | if (null == successor) { 34 | return Results.internalServerError(); 35 | } 36 | return successor.toResult(t); 37 | } 38 | 39 | protected abstract boolean isMatch(Throwable t); 40 | 41 | protected abstract Result getResult(Throwable t); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/UnsupportedImageFormatExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.UnsupportedImageFormatException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class UnsupportedImageFormatExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public UnsupportedImageFormatExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(UnsupportedImageFormatException.class, successor); 33 | } 34 | 35 | @Override 36 | protected Result getResult(Throwable t) { 37 | UnsupportedImageFormatException exception = (UnsupportedImageFormatException) t; 38 | Map details = new HashMap(); 39 | ErrorResponse response = new ErrorResponse( 40 | ErrorResponse.UNSUPPORTED_IMAGE_FORMAT, 41 | details, 42 | exception.getMessage()); 43 | return Results.badRequest(Json.toJson(response)); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/UnremovableTemplateExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.UnremovableTemplateException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class UnremovableTemplateExceptionMapper extends PreciseExceptionMapper { 28 | 29 | public UnremovableTemplateExceptionMapper( 30 | ExceptionMapper successor) { 31 | super(UnremovableTemplateException.class, successor); 32 | } 33 | 34 | @Override 35 | protected Result getResult(Throwable t) { 36 | UnremovableTemplateException exception = (UnremovableTemplateException) t; 37 | Map details = new HashMap(); 38 | details.put("id", exception.getId()); 39 | ErrorResponse response = new ErrorResponse( 40 | ErrorResponse.UNREMOVABLE_TEMPLATE, 41 | details, 42 | exception.getMessage()); 43 | return Results.badRequest(Json.toJson(response)); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/UnachievableTemplateExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.UnachievableTemplateException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class UnachievableTemplateExceptionMapper extends PreciseExceptionMapper { 28 | 29 | public UnachievableTemplateExceptionMapper( 30 | ExceptionMapper successor) { 31 | super(UnachievableTemplateException.class, successor); 32 | } 33 | 34 | @Override 35 | protected Result getResult(Throwable t) { 36 | UnachievableTemplateException exception = (UnachievableTemplateException) t; 37 | Map details = new HashMap(); 38 | details.put("id", exception.getId()); 39 | ErrorResponse response = new ErrorResponse( 40 | ErrorResponse.UNARCHIVABLE_TEMPLATE, 41 | details, 42 | exception.getMessage()); 43 | return Results.badRequest(Json.toJson(response)); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/IllegalImageVersionExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.IllegalImageVersionException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class IllegalImageVersionExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public IllegalImageVersionExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(IllegalImageVersionException.class, successor); 33 | } 34 | 35 | @Override 36 | protected Result getResult(Throwable t) { 37 | IllegalImageVersionException exception = (IllegalImageVersionException) t; 38 | Map details = new HashMap(); 39 | details.put("version", exception.getVersion()); 40 | ErrorResponse response = new ErrorResponse( 41 | ErrorResponse.ILLEGAL_IMAGE_VERSIONG, 42 | details, 43 | exception.getMessage()); 44 | return Results.badRequest(Json.toJson(response)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/DuplicateTemplateNameExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.DuplicateTemplateNameException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class DuplicateTemplateNameExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public DuplicateTemplateNameExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(DuplicateTemplateNameException.class, successor); 33 | } 34 | 35 | @Override 36 | protected Result getResult(Throwable t) { 37 | DuplicateTemplateNameException exception = (DuplicateTemplateNameException) t; 38 | Map details = new HashMap(); 39 | details.put("name", exception.getName()); 40 | ErrorResponse response = new ErrorResponse( 41 | ErrorResponse.DUPLICATE_TEMPALTE_NAME, 42 | details, 43 | exception.getMessage()); 44 | return Results.badRequest(Json.toJson(response)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/NoSuchEntityFoundExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.NoSuchEntityFoundException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class NoSuchEntityFoundExceptionMapper extends PreciseExceptionMapper { 28 | 29 | public NoSuchEntityFoundExceptionMapper( 30 | ExceptionMapper successor) { 31 | super(NoSuchEntityFoundException.class, successor); 32 | } 33 | 34 | @Override 35 | protected Result getResult(Throwable t) { 36 | NoSuchEntityFoundException exception = (NoSuchEntityFoundException) t; 37 | Map details = new HashMap(); 38 | details.put("entity", exception.getEntity()); 39 | details.put("id", exception.getId()); 40 | ErrorResponse response = new ErrorResponse( 41 | ErrorResponse.NO_SUCH_ENTITY, 42 | details, 43 | exception.getMessage()); 44 | return Results.notFound(Json.toJson(response)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/DuplicateImageVersionExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.DuplicateImageVersionException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class DuplicateImageVersionExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public DuplicateImageVersionExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(DuplicateImageVersionException.class, successor); 33 | } 34 | 35 | @Override 36 | protected Result getResult(Throwable t) { 37 | DuplicateImageVersionException exception = (DuplicateImageVersionException) t; 38 | Map details = new HashMap(); 39 | details.put("version", exception.getVersion()); 40 | ErrorResponse response = new ErrorResponse( 41 | ErrorResponse.DUPLICATE_IMAGE_VERSION, 42 | details, 43 | exception.getMessage()); 44 | return Results.badRequest(Json.toJson(response)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/DuplicateImagePlantNameExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.DuplicateImagePlantNameException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class DuplicateImagePlantNameExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public DuplicateImagePlantNameExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(DuplicateImagePlantNameException.class, successor); 33 | } 34 | 35 | @Override 36 | protected Result getResult(Throwable t) { 37 | DuplicateImagePlantNameException exception = (DuplicateImagePlantNameException) t; 38 | Map details = new HashMap(); 39 | details.put("name", exception.getName()); 40 | ErrorResponse response = new ErrorResponse( 41 | ErrorResponse.DUPLICATE_IMAGEPLANT_NAME, 42 | details, 43 | exception.getMessage()); 44 | return Results.badRequest(Json.toJson(response)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/IllegalTemplateNameExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.IllegalTemplateNameException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class IllegalTemplateNameExceptionMapper extends PreciseExceptionMapper { 28 | 29 | public IllegalTemplateNameExceptionMapper( 30 | ExceptionMapper successor) { 31 | super(IllegalTemplateNameException.class, successor); 32 | } 33 | 34 | @Override 35 | protected Result getResult(Throwable t) { 36 | IllegalTemplateNameException exception = (IllegalTemplateNameException) t; 37 | Map details = new HashMap(); 38 | details.put("name", exception.getName()); 39 | details.put("pattern", exception.getPattern()); 40 | ErrorResponse response = new ErrorResponse( 41 | ErrorResponse.ILLEGAL_TEMPLATE_NAME, 42 | details, 43 | exception.getMessage()); 44 | return Results.badRequest(Json.toJson(response)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /public/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ImageS3 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/AmazonS3BucketAccessFailedExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.AmazonS3BucketAccessFailedException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class AmazonS3BucketAccessFailedExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public AmazonS3BucketAccessFailedExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(AmazonS3BucketAccessFailedException.class, successor); 33 | 34 | } 35 | 36 | @Override 37 | protected Result getResult(Throwable t) { 38 | AmazonS3BucketAccessFailedException exception = (AmazonS3BucketAccessFailedException) t; 39 | Map details = new HashMap(); 40 | details.put("bucket", exception.getBucket()); 41 | ErrorResponse response = new ErrorResponse( 42 | ErrorResponse.AMAZONS3_BUCKET_ACCESS_FAILED, 43 | details, 44 | exception.getMessage()); 45 | return Results.unauthorized(Json.toJson(response)); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/IllegalTemplateNameLengthExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.IllegalTemplateNameLengthException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class IllegalTemplateNameLengthExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public IllegalTemplateNameLengthExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(IllegalTemplateNameLengthException.class, successor); 33 | } 34 | 35 | @Override 36 | protected Result getResult(Throwable t) { 37 | IllegalTemplateNameLengthException exception = (IllegalTemplateNameLengthException) t; 38 | Map details = new HashMap(); 39 | details.put("name", exception.getName()); 40 | details.put("minLength", exception.getMinLength()); 41 | details.put("maxLength", exception.getMaxLength()); 42 | ErrorResponse response = new ErrorResponse( 43 | ErrorResponse.ILLEGAL_TEMPLATE_NAME_LENGTH, 44 | details, 45 | exception.getMessage()); 46 | return Results.badRequest(Json.toJson(response)); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/IllegalImagePlantNameLengthExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.IllegalImagePlantNameLengthException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class IllegalImagePlantNameLengthExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public IllegalImagePlantNameLengthExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(IllegalImagePlantNameLengthException.class, successor); 33 | } 34 | 35 | @Override 36 | protected Result getResult(Throwable t) { 37 | IllegalImagePlantNameLengthException exception = (IllegalImagePlantNameLengthException) t; 38 | Map details = new HashMap(); 39 | details.put("name", exception.getName()); 40 | details.put("minLength", exception.getMinLength()); 41 | details.put("maxLength", exception.getMaxLength()); 42 | ErrorResponse response = new ErrorResponse( 43 | ErrorResponse.ILLEGAL_IMAGEPLANT_NAME_LENGTH, 44 | details, 45 | exception.getMessage()); 46 | return Results.badRequest(Json.toJson(response)); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/IllegalResizingDimensionsExceptionMapper.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.exceptions; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.images3.exceptions.IllegalResizingDimensionsException; 22 | 23 | import play.libs.Json; 24 | import play.mvc.Result; 25 | import play.mvc.Results; 26 | 27 | public class IllegalResizingDimensionsExceptionMapper extends 28 | PreciseExceptionMapper { 29 | 30 | public IllegalResizingDimensionsExceptionMapper( 31 | ExceptionMapper successor) { 32 | super(IllegalResizingDimensionsException.class, successor); 33 | } 34 | 35 | @Override 36 | protected Result getResult(Throwable t) { 37 | IllegalResizingDimensionsException exception = (IllegalResizingDimensionsException) t; 38 | Map details = new HashMap(); 39 | details.put("minimum", exception.getMinimum()); 40 | details.put("maximum", exception.getMaximum()); 41 | details.put("unit", exception.getUnit().toString()); 42 | ErrorResponse response = new ErrorResponse( 43 | ErrorResponse.ILLEGAL_RESIZING_DIMENSIONS, 44 | details, 45 | exception.getMessage()); 46 | 47 | return Results.badRequest(Json.toJson(response)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /public/stylesheets/dashboard.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Base structure 3 | */ 4 | 5 | /* Move down content because we have a fixed navbar that is 50px tall */ 6 | body { 7 | padding-top: 50px; 8 | } 9 | 10 | 11 | /* 12 | * Global add-ons 13 | */ 14 | 15 | .sub-header { 16 | padding-bottom: 10px; 17 | border-bottom: 1px solid #eee; 18 | } 19 | 20 | /* 21 | * Top navigation 22 | * Hide default border to remove 1px line. 23 | */ 24 | .navbar-fixed-top { 25 | border: 0; 26 | } 27 | 28 | /* 29 | * Sidebar 30 | */ 31 | 32 | /* Hide for mobile, show later */ 33 | .sidebar { 34 | display: none; 35 | } 36 | @media (min-width: 768px) { 37 | .sidebar { 38 | position: fixed; 39 | top: 51px; 40 | bottom: 0; 41 | left: 0; 42 | z-index: 1000; 43 | display: block; 44 | padding: 20px; 45 | overflow-x: hidden; 46 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ 47 | background-color: #f5f5f5; 48 | border-right: 1px solid #eee; 49 | } 50 | } 51 | 52 | /* Sidebar navigation */ 53 | .nav-sidebar { 54 | margin-right: -21px; /* 20px padding + 1px border */ 55 | margin-bottom: 20px; 56 | margin-left: -20px; 57 | } 58 | .nav-sidebar > li > a { 59 | padding-right: 20px; 60 | padding-left: 20px; 61 | } 62 | .nav-sidebar > .active > a, 63 | .nav-sidebar > .active > a:hover, 64 | .nav-sidebar > .active > a:focus { 65 | color: #fff; 66 | background-color: #428bca; 67 | } 68 | 69 | 70 | /* 71 | * Main content 72 | */ 73 | 74 | .main { 75 | padding: 20px; 76 | } 77 | @media (min-width: 768px) { 78 | .main { 79 | padding-right: 40px; 80 | padding-left: 40px; 81 | } 82 | } 83 | .main .page-header { 84 | margin-top: 0; 85 | } 86 | 87 | 88 | /* 89 | * Placeholder dashboard ideas 90 | */ 91 | 92 | .placeholders { 93 | margin-bottom: 30px; 94 | text-align: center; 95 | } 96 | .placeholders h4 { 97 | margin-bottom: 0; 98 | } 99 | .placeholder { 100 | margin-bottom: 20px; 101 | } 102 | .placeholder img { 103 | display: inline-block; 104 | border-radius: 50%; 105 | } 106 | -------------------------------------------------------------------------------- /public/html/imageplant.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 8 |
9 |
10 |
11 |
12 |
13 | 44 |
45 |
46 |
47 |
48 |
-------------------------------------------------------------------------------- /app/ImageS3Provider.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 | import com.google.inject.Provider; 17 | import com.images3.ImageS3; 18 | import com.images3.ImageS3Server; 19 | import com.images3.data.impl.ImageContentAccessProvider; 20 | import com.images3.data.impl.ImageProcessorProvider; 21 | import com.images3.data.impl.MongoDBAccessProvider; 22 | 23 | 24 | public class ImageS3Provider implements Provider { 25 | 26 | private ImageContentAccessProvider imageContentAccessProvider; 27 | private ImageProcessorProvider imageProcessorProvider; 28 | private MongoDBAccessProvider accessProvider; 29 | 30 | public ImageS3Provider(String imgContentConf, String imgProcessorConf, String mongodbConf) { 31 | imageContentAccessProvider = new ImageContentAccessProvider(imgContentConf); 32 | imageProcessorProvider = new ImageProcessorProvider(imgProcessorConf); 33 | accessProvider = new MongoDBAccessProvider(mongodbConf); 34 | } 35 | 36 | @Override 37 | public ImageS3 get() { 38 | return new ImageS3Server.Builder() 39 | .setImageContentAccess(imageContentAccessProvider.getImageContentAccess()) 40 | .setImageProcessor(imageProcessorProvider.getImageProcessor()) 41 | .setImageAccess(accessProvider.getImageAccess()) 42 | .setImagePlantAccess(accessProvider.getImagePlantAccess()) 43 | .setTempalteAccess(accessProvider.getTemplateAccess()) 44 | .setImageMetricsService(accessProvider.getImageMetricsService()) 45 | .build(); 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/com/images3/rest/exceptions/ErrorResponse.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.exceptions; 17 | import java.util.Collections; 18 | import java.util.Map; 19 | 20 | 21 | public class ErrorResponse { 22 | 23 | public static final int NO_SUCH_ENTITY = 400100; 24 | public static final int DUPLICATE_IMAGEPLANT_NAME = 400101; 25 | public static final int DUPLICATE_IMAGE_VERSION = 400102; 26 | public static final int DUPLICATE_TEMPALTE_NAME = 400103; 27 | public static final int ILLEGAL_IMAGEPLANT_NAME_LENGTH = 400104; 28 | public static final int ILLEGAL_RESIZING_DIMENSIONS = 400105; 29 | public static final int ILLEGAL_TEMPLATE_NAME = 400106; 30 | public static final int ILLEGAL_TEMPLATE_NAME_LENGTH = 400107; 31 | public static final int UNSUPPORTED_IMAGE_FORMAT = 400108; 32 | public static final int UNREMOVABLE_TEMPLATE = 400109; 33 | public static final int AMAZONS3_BUCKET_ACCESS_FAILED = 400110; 34 | public static final int UNARCHIVABLE_TEMPLATE = 400111; 35 | public static final int ILLEGAL_IMAGE_VERSIONG = 400112; 36 | 37 | private int code; 38 | private Map details; 39 | private String message; 40 | 41 | public ErrorResponse(int code, Map details, String message) { 42 | this.code = code; 43 | this.details = Collections.unmodifiableMap(details); 44 | this.message = message; 45 | } 46 | 47 | public int getCode() { 48 | return code; 49 | } 50 | public Map getDetails() { 51 | return details; 52 | } 53 | public String getMessage() { 54 | return message; 55 | } 56 | 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /app/ObjectMapperProvider.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 | import com.fasterxml.jackson.databind.ObjectMapper; 18 | import com.google.inject.Provider; 19 | import com.images3.common.AmazonS3Bucket; 20 | import com.images3.common.ResizingConfig; 21 | import com.images3.common.TemplateIdentity; 22 | import com.images3.ImagePlantAddRequest; 23 | import com.images3.ImagePlantUpdateRequest; 24 | import com.images3.TemplateResponse; 25 | import com.images3.rest.models.AmazonS3BucketModel; 26 | import com.images3.rest.models.ImagePlantAddRequestModel; 27 | import com.images3.rest.models.ImagePlantUpdateRequestModel; 28 | import com.images3.rest.models.ResizingConfigModel; 29 | import com.images3.rest.models.TemplateIdentityModel; 30 | import com.images3.rest.models.TemplateResponseModel; 31 | 32 | 33 | public class ObjectMapperProvider implements Provider { 34 | 35 | @Override 36 | public ObjectMapper get() { 37 | ObjectMapper mapper = new ObjectMapper(); 38 | mapper.configure(com.fasterxml.jackson.databind.SerializationFeature. 39 | WRITE_DATES_AS_TIMESTAMPS , false); 40 | mapper.addMixInAnnotations(ImagePlantAddRequest.class, ImagePlantAddRequestModel.class); 41 | mapper.addMixInAnnotations(ImagePlantUpdateRequest.class, ImagePlantUpdateRequestModel.class); 42 | mapper.addMixInAnnotations(AmazonS3Bucket.class, AmazonS3BucketModel.class); 43 | mapper.addMixInAnnotations(ResizingConfig.class, ResizingConfigModel.class); 44 | mapper.addMixInAnnotations(TemplateIdentity.class, TemplateIdentityModel.class); 45 | mapper.addMixInAnnotations(TemplateResponse.class, TemplateResponseModel.class); 46 | return mapper; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /public/html/imageplant-list.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |

ImagePlants

9 |
10 |
11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
NameBucketCreated TimeTemplatesImages
{{imagePlant.name}}{{imagePlant.bucket.name}}{{imagePlant.creationTime | date:'medium'}}{{imagePlant.numberOfTemplates}}{{imagePlant.numberOfImages}}
42 |
43 |
44 |
45 |
46 | 52 |
53 |
54 |
-------------------------------------------------------------------------------- /public/html/image-upload.html: -------------------------------------------------------------------------------- 1 |
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 | 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 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
NameWidthHeightUnitKeep ProportionsArchivedRemovable
{{template.id.templateName}}{{template.resizingConfig.width}}{{template.resizingConfig.height}}{{template.resizingConfig.unit}}{{template.resizingConfig.isKeepProportions}}{{template.isArchived}}{{template.isRemovable}}
46 |
47 |
48 |
49 |
50 | 56 |
57 |
58 |
-------------------------------------------------------------------------------- /public/html/image-list.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |

Images

9 |
10 |
11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
IdWidthHeightFormatSize (bytes)Uploaded Time
{{image.id.imageId}}{{image.metadata.dimension.width}}px{{image.metadata.dimension.height}}px{{image.metadata.format}}{{image.metadata.size}}{{image.dateTime | date:'medium'}}
46 |
47 |
48 |
49 |
50 | 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
')}]); -------------------------------------------------------------------------------- /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 |
16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 | {{errorMessage}} 25 |
26 |
27 |
28 |
29 | 30 |
31 | 35 |
36 |
37 |
38 | 39 |
40 | 41 |
42 |
43 |
44 | 45 |
46 | 47 |
48 |
49 | {{errorMessage}} 50 |
51 |
52 |
53 |
54 |
55 | 58 |
59 |
60 |
61 |
62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 |
71 |
72 |
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 |
17 |
18 |
19 |
20 | 21 |
22 |

{{imagePlant.id}}

23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 | {{errorMessage}} 32 |
33 |
34 |
35 | 36 |
37 |

{{imagePlant.creationTime | date:'medium'}}

38 |
39 |
40 |
41 |
42 | 43 |
44 | 45 |
46 |
47 |
48 | 49 |
50 | 51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 |
59 | {{errorMessage}} 60 |
61 |
62 |
63 |
64 |
65 | 66 |
67 |
68 |
69 |
70 |
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 | 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 |
16 |
17 |
18 |
19 | 20 |
21 |

{{template.id.templateName}}

22 |
23 |
24 |
25 | 26 |
27 |

{{template.resizingConfig.unit}}

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

{{template.resizingConfig.width}}

34 |
35 |
36 |
37 | 38 |
39 |

{{template.resizingConfig.height}}

40 |
41 |
42 |
43 | 44 |
45 |

{{template.resizingConfig.isKeepProportions}}

46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | WARNING 54 |
This template still CANNOT be accessed. you need to activate it first.
55 | WARNING 56 |
Deactivating this template will cause all associated images cannot be accessed.
57 |
Master template cannot be deactivated
58 |
59 | 60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | INFO 71 |
This template can be removed, if no associated images exists.
72 |
73 | 74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | 83 |
84 |
85 |
86 |
87 |
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 |
16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 | {{errorMessage}} 25 |
26 |
27 |
28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 | 42 |
43 | 44 |
45 |
46 | {{errorMessage}} 47 |
48 |
49 |
50 |
51 | 52 |
53 | 57 |
58 |
59 |
60 | 61 |
62 | 63 |
64 |
65 |
66 | 67 |
68 | 69 |
70 |
71 | {{errorMessage}} 72 |
73 |
74 |
75 |
76 |
77 | 80 |
81 |
82 |
83 |
84 |
85 |
86 | 87 |
88 |
89 | 90 |
91 |
92 |
93 | 94 |
95 |
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); --------------------------------------------------------------------------------