From 766cfc05ca19b2ac5004c1ab2ce105a96a1296e2 Mon Sep 17 00:00:00 2001 From: Patrick Huang Date: Fri, 26 Jun 2015 12:04:56 +1000 Subject: [PATCH] Squashed commit of the following: commit 71d284ba8f988efaf6f15f6ceb86b18fcb562bd4 Author: Patrick Huang Date: Thu Jun 18 16:42:49 2015 +1000 minor change commit 5e952150d6c358aca438fa274c35c62933c05936 Merge: 772525a b817e15 Author: Patrick Huang Date: Thu Jun 18 13:55:21 2015 +1000 Merge branch 'integration/master' into resteasy3 Conflicts: zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml zanata-war/src/test/java/org/zanata/rest/RestLimitingSynchronousDispatcherTest.java zanata-war/src/test/java/org/zanata/rest/editor/dto/TransUnitTest.java commit 772525afbbfc13d199f2fc204184d29d8d49dfaa Author: Patrick Huang Date: Thu Jun 18 13:49:01 2015 +1000 use jboss module instead of packaging the jar (javax.inject-api) commit 11e8fca4df8ba81eab500251d3ec22ef6b78abe9 Author: Patrick Huang Date: Thu Jun 18 11:59:46 2015 +1000 fix functional test commit 1c34b8beee15681af8f0bd0e3679a83b983c9975 Author: Patrick Huang Date: Wed Jun 17 14:15:11 2015 +1000 fix all static analysis error and added enforcer rule for stax-api conflict commit 459b17f7be51c1ae46ff96f58e8784728ebbb076 Author: Patrick Huang Date: Wed Jun 17 12:54:03 2015 +1000 fix duplicate class finder error commit 9c0b497bba438f52125146cea35c0b92c3927136 Author: Patrick Huang Date: Wed Jun 17 12:08:52 2015 +1000 fix dependency check commit 20f4ce8b7eaf8d29045117675694be024aa9562a Author: Patrick Huang Date: Wed Jun 17 11:36:11 2015 +1000 override built-in RESTEasy Jackson provider to NOT use JAXB annotation commit 5a15bcca4324e03c4a8e9e9a8607cc8a08e74c7d Author: Patrick Huang Date: Tue Jun 16 15:01:46 2015 +1000 fix integration test commit f1a74e9950335af483ac4ae54bdd0e5198004601 Author: Carlos A. Munoz Date: Thu Jun 11 11:48:47 2015 +1000 Fix a failing test. commit efabd4e03c445d5b5a72ead88c2a5e1248cf9b83 Author: Carlos A. Munoz Date: Fri Jun 5 11:03:50 2015 +1000 Fixes for Resteasy3 Disble Java EE modules from EAP/Wildfly Enable Providers via Seam2 Adjust Arquillian packaging. commit 2b08324f1a978c513315be5518e2aee08b911af9 Author: Sean Flanigan Date: Fri May 29 14:51:58 2015 +1000 Make enforcer happy commit f1e89355ca30510a169c2403bc557404d099bdcd Author: Sean Flanigan Date: Fri May 29 13:08:31 2015 +1000 Upgrade to RESTEasy 3 --- pom.xml | 23 +- zanata-war/pom.xml | 18 +- .../RestLimitingSynchronousDispatcher.java | 4 +- .../rest/ZanataJacksonJsonProvider.java | 68 +++ .../rest/ZanataRestSecurityInterceptor.java | 38 +- .../rest/ZanataRestVersionInterceptor.java | 54 +- .../zanata/rest/ZanataResteasyBootstrap.java | 15 +- .../org/zanata/rest/service/RestUtils.java | 19 +- .../seam/resteasy/AbstractResource.java | 152 +++++ .../org/zanata/seam/resteasy/Application.java | 211 +++++++ .../seam/resteasy/EntityHomeWrapper.java | 133 ++++ .../zanata/seam/resteasy/ResourceHome.java | 385 ++++++++++++ .../zanata/seam/resteasy/ResourceQuery.java | 157 +++++ .../seam/resteasy/ResteasyBootstrap.java | 569 ++++++++++++++++++ .../ResteasyContextInjectionInterceptor.java | 69 +++ .../resteasy/ResteasyResourceAdapter.java | 220 +++++++ .../resteasy/SeamResteasyProviderFactory.java | 54 ++ .../resteasy/SeamResteasyResourceFactory.java | 109 ++++ .../zanata/seam/resteasy/package-info.java | 6 + .../main/java/org/zanata/util/HttpUtil.java | 15 +- .../META-INF/seam-deployment.properties | 6 +- .../src/main/resources/messages_nl.properties | 4 +- .../src/main/resources/messages_tr.properties | 2 +- .../org/zanata/seam/resteasy/resteasy-2.3.xsd | 204 +++++++ .../WEB-INF/jboss-deployment-structure.xml | 7 +- zanata-war/src/main/webapp/app/config.json | 2 +- zanata-war/src/main/webapp/app/css/app.css | 2 +- zanata-war/src/main/webapp/app/js/app.js | 2 +- zanata-war/src/main/webapp/app/js/libs.js | 2 +- .../src/main/webapp/app/js/templates.js | 2 +- .../java/org/zanata/MockResourceFactory.java | 7 +- .../test/java/org/zanata/ZanataRestTest.java | 2 +- .../org/zanata/arquillian/Deployments.java | 12 +- ...RestLimitingSynchronousDispatcherTest.java | 10 +- .../service/ProjectVersionServiceTest.java | 9 +- .../editor/service/StatisticsServiceTest.java | 1 - 36 files changed, 2484 insertions(+), 109 deletions(-) create mode 100644 zanata-war/src/main/java/org/zanata/rest/ZanataJacksonJsonProvider.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/AbstractResource.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/Application.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/EntityHomeWrapper.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/ResourceHome.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/ResourceQuery.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyBootstrap.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyContextInjectionInterceptor.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyResourceAdapter.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/SeamResteasyProviderFactory.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/SeamResteasyResourceFactory.java create mode 100644 zanata-war/src/main/java/org/zanata/seam/resteasy/package-info.java create mode 100644 zanata-war/src/main/resources/org/zanata/seam/resteasy/resteasy-2.3.xsd diff --git a/pom.xml b/pom.xml index d6d96e8099..24344bc930 100644 --- a/pom.xml +++ b/pom.xml @@ -97,7 +97,7 @@ 5.1.26 compile - 2.3.7.Final + 3.0.11.Final ${project.build.directory}/cargo/installs @@ -187,6 +187,12 @@ provided + + com.sun.xml.fastinfoset + FastInfoset + 1.2.12 + + org.jboss.seam jboss-seam @@ -248,6 +254,12 @@ org.apache.httpcomponents httpclient 4.4 + + + commons-logging + commons-logging + + @@ -494,6 +506,12 @@ org.zanata zanata-adapter-xliff ${zanata.common.version} + + + javax.xml.bind + jsr173_api + + @@ -1244,6 +1262,9 @@ org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec + + + javax.xml.bind:jsr173_api diff --git a/zanata-war/pom.xml b/zanata-war/pom.xml index b9d7bff60f..2d4f9ce149 100644 --- a/zanata-war/pom.xml +++ b/zanata-war/pom.xml @@ -154,8 +154,6 @@ org.hibernate:hibernate-testing org.jboss.arquillian.junit:arquillian-junit-container org.jboss.arquillian.protocol:arquillian-protocol-servlet - - org.jboss.resteasy:resteasy-jackson-provider org.codehaus.jackson:jackson-xc org.jboss.seam:jboss-seam-debug @@ -1409,10 +1407,6 @@ - - org.jboss.seam - jboss-seam-resteasy - org.jboss.resteasy resteasy-jaxrs @@ -1455,6 +1449,10 @@ resteasy-multipart-provider ${resteasy.scope} + + commons-logging + commons-logging + javax.servlet servlet-api @@ -2148,18 +2146,10 @@ org.apache.httpcomponents httpcore - 4.2.4 org.apache.httpcomponents httpclient - 4.2.5 - - - commons-logging - commons-logging - - diff --git a/zanata-war/src/main/java/org/zanata/rest/RestLimitingSynchronousDispatcher.java b/zanata-war/src/main/java/org/zanata/rest/RestLimitingSynchronousDispatcher.java index 3624f11c9a..b70687bd03 100644 --- a/zanata-war/src/main/java/org/zanata/rest/RestLimitingSynchronousDispatcher.java +++ b/zanata-war/src/main/java/org/zanata/rest/RestLimitingSynchronousDispatcher.java @@ -31,7 +31,6 @@ import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.UnhandledException; -import org.jboss.seam.resteasy.SeamResteasyProviderFactory; import org.jboss.seam.security.management.JpaIdentityStore; import org.jboss.seam.web.ServletContexts; import org.zanata.dao.AccountDAO; @@ -42,6 +41,7 @@ import com.google.common.base.Throwables; import lombok.extern.slf4j.Slf4j; +import org.zanata.seam.resteasy.SeamResteasyProviderFactory; import org.zanata.security.SecurityFunctions; import org.zanata.util.HttpUtil; import org.zanata.util.ServiceLocator; @@ -100,7 +100,7 @@ public void invoke(final HttpRequest request, final HttpResponse response) { } if(!SecurityFunctions.canAccessRestPath(authenticatedUser, - request.getHttpMethod(), request.getPreprocessedPath())) { + request.getHttpMethod(), request.getUri().getMatchingPath())) { /** * Not using response.sendError because the app server will generate diff --git a/zanata-war/src/main/java/org/zanata/rest/ZanataJacksonJsonProvider.java b/zanata-war/src/main/java/org/zanata/rest/ZanataJacksonJsonProvider.java new file mode 100644 index 0000000000..82d0827d73 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/rest/ZanataJacksonJsonProvider.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.rest; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.ext.Provider; + +import org.codehaus.jackson.jaxrs.Annotations; +import org.codehaus.jackson.jaxrs.JacksonJsonProvider; +import org.jboss.resteasy.annotations.providers.NoJackson; +import org.jboss.resteasy.util.FindAnnotation; + +/** + * ResteasyJacksonProvider will use JAXB annotation as well as Jackson. This is + * different from RESTEasy 2 which only use Jackson annotations. We need to + * override this to make our REST api backward compatible. + * + * @author Patrick Huang pahuang@redhat.com + */ +@Provider +@Consumes({ "application/*+json", "text/json" }) +@Produces({ "application/*+json", "text/json" }) +public class ZanataJacksonJsonProvider extends JacksonJsonProvider { + public ZanataJacksonJsonProvider() { + super(Annotations.JACKSON); + } + + @Override + public boolean isReadable(Class aClass, Type type, + Annotation[] annotations, MediaType mediaType) { + if (FindAnnotation + .findAnnotation(aClass, annotations, NoJackson.class) != null) + return false; + return super.isReadable(aClass, type, annotations, mediaType); + } + + @Override + public boolean isWriteable(Class aClass, Type type, + Annotation[] annotations, MediaType mediaType) { + if (FindAnnotation + .findAnnotation(aClass, annotations, NoJackson.class) != null) + return false; + return super.isWriteable(aClass, type, annotations, mediaType); + } +} diff --git a/zanata-war/src/main/java/org/zanata/rest/ZanataRestSecurityInterceptor.java b/zanata-war/src/main/java/org/zanata/rest/ZanataRestSecurityInterceptor.java index 65d7575457..16d79531dc 100644 --- a/zanata-war/src/main/java/org/zanata/rest/ZanataRestSecurityInterceptor.java +++ b/zanata-war/src/main/java/org/zanata/rest/ZanataRestSecurityInterceptor.java @@ -1,48 +1,44 @@ package org.zanata.rest; -import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.Provider; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.jboss.resteasy.annotations.interception.SecurityPrecedence; import org.jboss.resteasy.annotations.interception.ServerInterceptor; -import org.jboss.resteasy.core.ResourceMethod; -import org.jboss.resteasy.core.ServerResponse; -import org.jboss.resteasy.spi.Failure; -import org.jboss.resteasy.spi.HttpRequest; -import org.jboss.resteasy.spi.interception.PreProcessInterceptor; import org.zanata.security.SecurityFunctions; import org.zanata.security.ZanataIdentity; import org.zanata.util.HttpUtil; +import java.io.IOException; + +@Provider @SecurityPrecedence -@ServerInterceptor @Slf4j -public class ZanataRestSecurityInterceptor implements PreProcessInterceptor { +public class ZanataRestSecurityInterceptor implements ContainerRequestFilter { @Override - public ServerResponse - preProcess(HttpRequest request, ResourceMethod method) - throws Failure, WebApplicationException { - - String username = HttpUtil.getUsername(request); - String apiKey = HttpUtil.getApiKey(request); - if (StringUtils.isNotEmpty(username)|| StringUtils.isNotEmpty(apiKey)) { + public void filter(ContainerRequestContext context) + throws IOException { + String username = HttpUtil.getUsername(context.getHeaders()); + String apiKey = HttpUtil.getApiKey(context.getHeaders()); + if (StringUtils.isNotEmpty(username) || StringUtils.isNotEmpty(apiKey)) { ZanataIdentity.instance().getCredentials().setUsername(username); ZanataIdentity.instance().setApiKey(apiKey); ZanataIdentity.instance().tryLogin(); if (!SecurityFunctions.canAccessRestPath(ZanataIdentity.instance(), - request.getHttpMethod(), request.getPreprocessedPath())) { + context.getMethod(), context.getUriInfo().getPath())) { log.info(InvalidApiKeyUtil.getMessage(username, apiKey)); - return ServerResponse.copyIfNotServerResponse(Response.status( - Status.UNAUTHORIZED).entity( - InvalidApiKeyUtil.getMessage(username, apiKey)) - .build()); + context.abortWith(Response.status(Status.UNAUTHORIZED) + .entity(InvalidApiKeyUtil.getMessage(username, apiKey)) + .build()); } } - return null; + } } diff --git a/zanata-war/src/main/java/org/zanata/rest/ZanataRestVersionInterceptor.java b/zanata-war/src/main/java/org/zanata/rest/ZanataRestVersionInterceptor.java index 158e465020..cebcefdfbd 100644 --- a/zanata-war/src/main/java/org/zanata/rest/ZanataRestVersionInterceptor.java +++ b/zanata-war/src/main/java/org/zanata/rest/ZanataRestVersionInterceptor.java @@ -1,46 +1,50 @@ package org.zanata.rest; -import static org.jboss.seam.ScopeType.APPLICATION; - +import javax.ws.rs.ConstrainedTo; +import javax.ws.rs.RuntimeType; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.Provider; +import javax.ws.rs.ext.ReaderInterceptor; +import javax.ws.rs.ext.ReaderInterceptorContext; import org.jboss.resteasy.annotations.interception.HeaderDecoratorPrecedence; import org.jboss.resteasy.annotations.interception.ServerInterceptor; -import org.jboss.resteasy.core.ResourceMethod; -import org.jboss.resteasy.core.ServerResponse; -import org.jboss.resteasy.spi.Failure; -import org.jboss.resteasy.spi.HttpRequest; -import org.jboss.resteasy.spi.interception.PreProcessInterceptor; +import org.zanata.rest.service.RestUtils; import org.zanata.service.impl.VersionManager; import org.zanata.util.ServiceLocator; import org.zanata.util.VersionUtility; -@ServerInterceptor +import java.io.IOException; + +@ConstrainedTo(RuntimeType.SERVER) +@Provider @HeaderDecoratorPrecedence -public class ZanataRestVersionInterceptor implements PreProcessInterceptor { +public class ZanataRestVersionInterceptor implements ReaderInterceptor { + @Override - public ServerResponse - preProcess(HttpRequest request, ResourceMethod method) - throws Failure, WebApplicationException { + public Object aroundReadFrom(ReaderInterceptorContext context) + throws IOException, WebApplicationException { + MultivaluedMap headers = context.getHeaders(); String clientApiVer = - request.getHttpHeaders().getRequestHeaders() - .getFirst(RestConstant.HEADER_VERSION_NO); + headers.getFirst(RestConstant.HEADER_VERSION_NO); String serverApiVer = VersionUtility.getAPIVersionInfo().getVersionNo(); VersionManager verManager = ServiceLocator.instance().getInstance(VersionManager.class); - return verManager.checkVersion(clientApiVer, serverApiVer) ? null - : ServerResponse - .copyIfNotServerResponse(Response - .status(Status.PRECONDITION_FAILED) - .entity("Client API Version '" - + clientApiVer - + "' and Server API Version '" - + serverApiVer - + "' do not match. Please update your Zanata client") - .build()); + // NB checkVersion doesn't actually reject outdated versions yet + return verManager.checkVersion(clientApiVer, serverApiVer) ? + context.proceed() : + RestUtils.copyIfNotServerResponse(Response + .status(Status.PRECONDITION_FAILED) + .entity("Client API Version '" + + clientApiVer + + "' and Server API Version '" + + serverApiVer + + + "' do not match. Please update your Zanata client") + .build()); } - } diff --git a/zanata-war/src/main/java/org/zanata/rest/ZanataResteasyBootstrap.java b/zanata-war/src/main/java/org/zanata/rest/ZanataResteasyBootstrap.java index 63a2fa7516..ed70b31323 100644 --- a/zanata-war/src/main/java/org/zanata/rest/ZanataResteasyBootstrap.java +++ b/zanata-war/src/main/java/org/zanata/rest/ZanataResteasyBootstrap.java @@ -15,8 +15,8 @@ import org.jboss.seam.annotations.Startup; import org.jboss.seam.deployment.AnnotationDeploymentHandler; import org.jboss.seam.deployment.HotDeploymentStrategy; -import org.jboss.seam.resteasy.ResteasyBootstrap; -import org.jboss.seam.resteasy.SeamResteasyProviderFactory; +import org.zanata.seam.resteasy.ResteasyBootstrap; +import org.zanata.seam.resteasy.SeamResteasyProviderFactory; @Name("org.jboss.seam.resteasy.bootstrap") @Scope(ScopeType.APPLICATION) @@ -47,17 +47,6 @@ public void registerHotDeployedClasses() { } } - @Override - protected void initDispatcher() { - super.initDispatcher(); - getDispatcher().getProviderFactory() - .getServerPreProcessInterceptorRegistry() - .register(ZanataRestSecurityInterceptor.class); - getDispatcher().getProviderFactory() - .getServerPreProcessInterceptorRegistry() - .register(ZanataRestVersionInterceptor.class); - } - @Override protected Dispatcher createDispatcher( SeamResteasyProviderFactory providerFactory) { diff --git a/zanata-war/src/main/java/org/zanata/rest/service/RestUtils.java b/zanata-war/src/main/java/org/zanata/rest/service/RestUtils.java index 1c51bc6d86..c3f96dffbb 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/RestUtils.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/RestUtils.java @@ -11,12 +11,14 @@ import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.MessageBodyReader; +import org.jboss.resteasy.core.Headers; +import org.jboss.resteasy.core.ServerResponse; import org.jboss.resteasy.spi.NoLogWebApplicationException; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; -import org.jboss.seam.resteasy.SeamResteasyProviderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.zanata.seam.resteasy.SeamResteasyProviderFactory; @Name("restUtils") public class RestUtils { @@ -25,6 +27,19 @@ public class RestUtils { @In Validator validator; + public static ServerResponse copyIfNotServerResponse(Response response) { + if (response instanceof ServerResponse) { + return (ServerResponse) response; + } + Object entity = response.getEntity(); + int status = response.getStatus(); + Headers metadata = new Headers<>(); + if (response.getMetadata() != null) { + metadata.putAll(response.getMetadata()); + } + return new ServerResponse(entity, status, metadata); + } + /** * Validate Hibernate Validator based constraints. * @@ -76,7 +91,7 @@ public T unmarshall(Class entityClass, InputStream is, requestHeaders, is); } catch (Exception e) { log.debug("Bad Request: Unable to read request body:", e); - throw new NoLogWebApplicationException(Response + throw new NoLogWebApplicationException(e, Response .status(Status.BAD_REQUEST) .entity("Unable to read request body: " + e.getMessage()) .build()); diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/AbstractResource.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/AbstractResource.java new file mode 100644 index 0000000000..ab1edc59c3 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/AbstractResource.java @@ -0,0 +1,152 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; + +/** + * Resource class used by ResourceHome and ResourceQuery components. Contains + * information about path, media types and entity class the component operates + * on. + * + * @param + * entity class + * @author Jozef Hartinger + */ +public abstract class AbstractResource { + + @Context + private HttpHeaders httpHeaders; + + private String path = null; + private MediaType[] mediaTypes = null; + private Class entityClass = null; + + public AbstractResource() { + mediaTypes = new MediaType[] { MediaType.APPLICATION_XML_TYPE }; + } + + public String[] getMediaTypes() { + String[] mediaTypes = new String[this.mediaTypes.length]; + for (int i = 0; i < mediaTypes.length; i++) { + mediaTypes[i] = this.mediaTypes[i].toString(); + } + return mediaTypes; + } + + public void setMediaTypes(String[] mediaTypes) { + this.mediaTypes = new MediaType[mediaTypes.length]; + for (int i = 0; i < mediaTypes.length; i++) { + this.mediaTypes[i] = MediaType.valueOf(mediaTypes[i]); + } + } + + public void setEntityClass(Class entityClass) { + this.entityClass = entityClass; + } + + /** + * Retrieve entity class. If not set, type parameters of a superclass are + * examined. + * + * @return entity class + */ + public Class getEntityClass() { + if (entityClass == null) { + Type superclass = this.getClass().getGenericSuperclass(); + if (superclass instanceof ParameterizedType) { + ParameterizedType parameterizedSuperclass = + (ParameterizedType) superclass; + if (parameterizedSuperclass.getActualTypeArguments().length > 0) { + return (Class) parameterizedSuperclass + .getActualTypeArguments()[0]; + } + } + throw new RuntimeException("Unable to determine entity class."); + } else { + return entityClass; + } + } + + /** + * Retrieve a suffix of this resource's URI. See + * {@link #setPath(String path)} + * + * @return path + * @see javax.ws.rs.Path + */ + public String getPath() { + return path; + } + + /** + * Set the path this resource will operate on. This method is intended to be + * used only by Seam to create a resource configured in component + * descriptor. To specify the path in other cases, use @Path annotation. See + * + * @param path + */ + public void setPath(String path) { + this.path = path; + } + + /** + * Select a media type that will be used for marshalling entity. Media type + * is selected from the intersection of media types supported by this + * resource and media types accepted by client. + * + * @return selected media type, returns null if no suitable media type is + * found + */ + protected MediaType selectResponseMediaType() { + for (MediaType acceptedMediaType : httpHeaders + .getAcceptableMediaTypes()) { + for (MediaType availableMediaType : mediaTypes) { + if (acceptedMediaType.isCompatible(availableMediaType)) + return availableMediaType; + } + } + return null; + } + + /** + * Check if media type passed as parameter is supported by this resource. + * + * @param mediaType + * @return true if and only if the media type is accepted by this resource + */ + public boolean isMediaTypeCompatible(MediaType mediaType) { + for (MediaType availableMediaType : mediaTypes) { + if (availableMediaType.isCompatible(mediaType)) { + return true; + } + } + return false; + } +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/Application.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/Application.java new file mode 100644 index 0000000000..09a40ea88a --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/Application.java @@ -0,0 +1,211 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import org.jboss.seam.ScopeType; +import org.jboss.seam.Component; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.annotations.Install; + +import javax.ws.rs.core.MediaType; +import java.util.*; + +/** + * RESTEasy and JAX-RS configuration, override in components.xml to customize + * RESTful request processing and RESTEasy settings. + * + * @author Christian Bauer + */ +@Name("org.jboss.seam.resteasy.application") +@Scope(ScopeType.APPLICATION) +@Install(precedence = Install.BUILT_IN) +@AutoCreate +public class Application extends javax.ws.rs.core.Application { + + final private Map, Set> providerClasses = + new HashMap, Set>(); + final private Map, Set> resourceClasses = + new HashMap, Set>(); + + private List providerClassNames = new ArrayList(); + private List resourceClassNames = new ArrayList(); + + private Map mediaTypeMappings = + new HashMap(); + private Map languageMappings = + new HashMap(); + + private boolean scanProviders = true; + private boolean scanResources = true; + private boolean useBuiltinProviders = true; + private boolean destroySessionAfterRequest = true; + + private String resourcePathPrefix = "/rest"; + private boolean stripSeamResourcePath = true; + + public Application() { + super(); + } + + public Set> getProviderClasses() { + return providerClasses.keySet(); + } + + @Override + public Set> getClasses() { + return resourceClasses.keySet(); + } + + public void addProviderClass(Class clazz) { + providerClasses.put(clazz, null); + } + + public void addProviderClass(Class clazz, Component component) { + Set components = providerClasses.get(clazz); + if (components == null) { + components = new HashSet(); + providerClasses.put(clazz, components); + } + components.add(component); + } + + public void removeProviderClass(Class clazz) { + providerClasses.remove(clazz); + } + + public void addResourceClass(Class clazz) { + resourceClasses.put(clazz, null); + } + + public void addResourceClass(Class clazz, Set newComponents) { + Set components = resourceClasses.get(clazz); + if (components == null) { + components = new HashSet(); + resourceClasses.put(clazz, components); + } + components.addAll(newComponents); + } + + public void removeResourceClass(Class clazz) { + resourceClasses.remove(clazz); + } + + public Set getProviderClassComponent(Class clazz) { + return providerClasses.get(clazz) != null ? providerClasses.get(clazz) + : null; + } + + public Set getResourceClassComponent(Class clazz) { + return resourceClasses.get(clazz) != null ? resourceClasses.get(clazz) + : null; + } + + public Map getMediaTypeMappings() { + Map extMap = new HashMap(); + for (String ext : mediaTypeMappings.keySet()) { + String value = mediaTypeMappings.get(ext); + extMap.put(ext, MediaType.valueOf(value)); + } + return extMap; + } + + public void setMediaTypeMappings(Map mediaTypeMappings) { + this.mediaTypeMappings = mediaTypeMappings; + } + + public Map getLanguageMappings() { + return languageMappings; + } + + public void setLanguageMappings(Map languageMappings) { + this.languageMappings = languageMappings; + } + + public List getProviderClassNames() { + return providerClassNames; + } + + public void setProviderClassNames(List providerClassNames) { + this.providerClassNames = providerClassNames; + } + + public List getResourceClassNames() { + return resourceClassNames; + } + + public void setResourceClassNames(List resourceClassNames) { + this.resourceClassNames = resourceClassNames; + } + + public boolean isScanProviders() { + return scanProviders; + } + + public void setScanProviders(boolean scanProviders) { + this.scanProviders = scanProviders; + } + + public boolean isScanResources() { + return scanResources; + } + + public void setScanResources(boolean scanResources) { + this.scanResources = scanResources; + } + + public boolean isUseBuiltinProviders() { + return useBuiltinProviders; + } + + public void setUseBuiltinProviders(boolean useBuiltinProviders) { + this.useBuiltinProviders = useBuiltinProviders; + } + + public boolean isDestroySessionAfterRequest() { + return destroySessionAfterRequest; + } + + public void + setDestroySessionAfterRequest(boolean destroySessionAfterRequest) { + this.destroySessionAfterRequest = destroySessionAfterRequest; + } + + public String getResourcePathPrefix() { + return resourcePathPrefix; + } + + public void setResourcePathPrefix(String resourcePathPrefix) { + this.resourcePathPrefix = resourcePathPrefix; + } + + public boolean isStripSeamResourcePath() { + return stripSeamResourcePath; + } + + public void setStripSeamResourcePath(boolean stripSeamResourcePath) { + this.stripSeamResourcePath = stripSeamResourcePath; + } +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/EntityHomeWrapper.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/EntityHomeWrapper.java new file mode 100644 index 0000000000..7320435e5b --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/EntityHomeWrapper.java @@ -0,0 +1,133 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import org.jboss.seam.framework.EntityHome; +import org.jboss.seam.framework.HibernateEntityHome; +import org.jboss.seam.framework.Home; + +/** + * This class provides an unified interface for accessing EntityHome and + * HibernateEntityHome instances. + * + * @author Jozef Hartinger + * @see org.jboss.seam.framework.EntityHome + * @see org.jboss.seam.framework.HibernateEntityHome + */ +public class EntityHomeWrapper { + private Home home = null; + private boolean hibernate; + + /** + * EntityHome or HibernateEntityHome instance is expected. + */ + public EntityHomeWrapper(Home entityHome) { + if (entityHome instanceof EntityHome) { + hibernate = false; + } else if (entityHome instanceof HibernateEntityHome) { + hibernate = true; + } else { + throw new IllegalArgumentException( + "You must use either EntityHome or HibernateEntityHome instance."); + } + this.home = entityHome; + } + + public Object getId() { + return home.getId(); + } + + public void setId(Object id) { + home.setId(id); + } + + public T getInstance() { + return home.getInstance(); + } + + public void setInstance(T instance) { + home.setInstance(instance); + } + + public T find() { + if (hibernate) { + return getHibernateEntityHome().find(); + } else { + return getEntityHome().find(); + } + } + + public void persist() { + if (hibernate) { + getHibernateEntityHome().persist(); + } else { + getEntityHome().persist(); + } + } + + public void remove() { + if (hibernate) { + getHibernateEntityHome().remove(); + } else { + getEntityHome().remove(); + } + } + + /** + * Merge the state of the given entity with the current persistence context. + */ + public void merge(T object) { + if (hibernate) { + getHibernateEntityHome().getSession().merge(object); + getHibernateEntityHome().update(); + } else { + getEntityHome().getEntityManager().merge(object); + getEntityHome().update(); + } + } + + public Class getEntityClass() { + return home.getEntityClass(); + } + + private EntityHome getEntityHome() { + return (EntityHome) home; + } + + private HibernateEntityHome getHibernateEntityHome() { + return (HibernateEntityHome) home; + } + + /** + * Return the underlying EntityHome or HibernateEntityHome instance. + */ + public Home unwrap() { + if (hibernate) { + return getHibernateEntityHome(); + } else { + return getEntityHome(); + } + } + +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/ResourceHome.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResourceHome.java new file mode 100644 index 0000000000..c668ca236b --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResourceHome.java @@ -0,0 +1,385 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import java.io.InputStream; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.annotation.Annotation; +import java.net.URI; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.PathParam; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.MessageBodyReader; + +import org.jboss.resteasy.core.StringParameterInjector; +import org.jboss.seam.Entity; +import org.jboss.seam.annotations.Create; +import org.jboss.seam.framework.Home; + +import static javax.ws.rs.core.Response.Status.UNSUPPORTED_MEDIA_TYPE; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; + +/** + * This component exposes EntityHome and HibernateEntityHome components as a + * REST resource. + * + * @param + * Entity class + * @param + * Entity id class + * @author Jozef Hartinger + */ +// Empty @Path because it's ignored by second-stage bootstrap if not subclassed +// or in components.xml +// but we need it as a marker so we'll find components.xml declarations during +// first stage of bootstrap. +@Path("") +public class ResourceHome extends AbstractResource { + private EntityHomeWrapper entityHome = null; + + @Context + private UriInfo uriInfo; + @Context + private HttpHeaders headers; + @HeaderParam("Content-Type") + private MediaType requestContentType; + + private Class entityIdClass = null; + private boolean readonly; + + private static final PathParamAnnotation pathParamAnnotation = + new PathParamAnnotation(); + + /** + * Called at component instantiation. EntityHome component must be set in + * order for component to be created. + */ + @Create + public void create() { + setEntityHome(getEntityHome()); + if (entityHome == null) { + throw new IllegalStateException("entityHome is not set"); + } + } + + /** + * Called by RESTEasy when HTTP GET request is received. String form of + * entity identifier is passed as a parameter. Returns a response containing + * database entity. + * + * @param rawId + * String form of entity identifier + * @return response + * @see #getEntity + */ + @Path("/{id}") + @GET + public Response getResource(@PathParam("id") String rawId) { + MediaType selectedMediaType = selectResponseMediaType(); + if (selectedMediaType == null) { + return Response.status(UNSUPPORTED_MEDIA_TYPE).build(); + } + + T2 id = unmarshallId(rawId); + T entity = getEntity(id); + + return Response.ok(new GenericEntity(entity, getEntityClass()) { + }, selectedMediaType).build(); + } + + /** + * Retrieve an entity identified by id parameter. + * + * @param id + * entity identifier + * @return entity database entity + */ + public T getEntity(T2 id) { + entityHome.setId(id); + return entityHome.find(); + + } + + /** + * Called by RESTEasy when HTTP POST request is received. Persists received + * entity and returns 201 HTTP status code with location header set to new + * URI if operation succeeds. + * + * @param messageBody + * HTTP request body + * @return response + * @see #createEntity + */ + @POST + public Response createResource(InputStream messageBody) { + if (readonly) { + return Response.status(405).build(); + } + + // check if we accept this content type + if (!isMediaTypeCompatible(requestContentType)) { + return Response.status(UNSUPPORTED_MEDIA_TYPE).build(); + } + + T entity = unmarshallEntity(messageBody); + + T2 id = createEntity(entity); + + URI uri = uriInfo.getAbsolutePathBuilder().path(id.toString()).build(); + return Response.created(uri).build(); + } + + /** + * Store entity passed as a parameter in the database. + * + * @param entity + * Object to be persisted + * @return id identifier assigned to the entity + */ + public T2 createEntity(T entity) { + entityHome.setInstance(entity); + entityHome.persist(); + return (T2) entityHome.getId(); + } + + /** + * Called by RESTEasy when HTTP PUT request is received. Merges the state of + * the database entity with the received representation. + * + * @param rawId + * String form of entity identifier + * @param messageBody + * HTTP request body + * @return response + * @see #updateEntity + */ + @Path("/{id}") + @PUT + public Response updateResource(@PathParam("id") String rawId, + InputStream messageBody) { + if (readonly) { + return Response.status(405).build(); + } + + // check if we accept this content type + if (!isMediaTypeCompatible(requestContentType)) { + return Response.status(UNSUPPORTED_MEDIA_TYPE).build(); + } + + T entity = unmarshallEntity(messageBody); + T2 id = unmarshallId(rawId); + + // check representation id - we don't allow renaming + Object storedId = Entity.forBean(entity).getIdentifier(entity); + if (!id.equals(storedId)) { + return Response.status(BAD_REQUEST).build(); + } + + updateEntity(entity, id); + return Response.noContent().build(); + } + + /** + * Merge the state of the database entity with the entity passed as a + * parameter. Override to customize the update strategy - for instance to + * update specific fields only instead of a full merge. + * + * @param entity + */ + public void updateEntity(T entity, T2 id) { + entityHome.merge(entity); + } + + /** + * Called by RESTEasy when HTTP DELETE request is received. Deletes a + * database entity. + * + * @param rawId + * String form of entity identifier + * @return response + * @see #deleteEntity + */ + @Path("/{id}") + @DELETE + public Response deleteResource(@PathParam("id") String rawId) { + if (readonly) { + return Response.status(405).build(); + } + + T2 id = unmarshallId(rawId); + deleteEntity(id); + return Response.noContent().build(); + } + + /** + * Delete database entity. + * + * @param id + * entity identifier + */ + public void deleteEntity(T2 id) { + getEntity(id); + entityHome.remove(); + } + + /** + * Convert HTTP request body into entity class instance. + * + * @param is + * HTTP request body + * @return entity + */ + private T unmarshallEntity(InputStream is) { + Class entityClass = getEntityClass(); + MessageBodyReader reader = + SeamResteasyProviderFactory.getInstance().getMessageBodyReader( + entityClass, entityClass, entityClass.getAnnotations(), + requestContentType); + if (reader == null) { + throw new RuntimeException( + "Unable to find MessageBodyReader for content type " + + requestContentType); + } + T entity; + try { + entity = + reader.readFrom(entityClass, entityClass, + entityClass.getAnnotations(), requestContentType, + headers.getRequestHeaders(), is); + } catch (Exception e) { + throw new RuntimeException("Unable to unmarshall request body"); + } + return entity; + } + + /** + * Converts String form of entity identifier to it's natural type. + * + * @param id + * String form of entity identifier + * @return entity identifier + */ + private T2 unmarshallId(String id) { + StringParameterInjector injector = + new StringParameterInjector(getEntityIdClass(), + getEntityIdClass(), "id", PathParam.class, null, null, + new Annotation[] { pathParamAnnotation }, + SeamResteasyProviderFactory.getInstance()); + return (T2) injector.extractValue(id); + } + + /** + * EntityHome component getter. Override this method to set the EntityHome + * this resource will operate on. You can use either EntityHome or + * HibernateEntityHome instance. + * + * @return entity home + */ + public Home getEntityHome() { + return (entityHome == null) ? null : entityHome.unwrap(); + } + + /** + * EntityHome component setter + * + * @param entityHome + */ + public void setEntityHome(Home entityHome) { + this.entityHome = new EntityHomeWrapper(entityHome); + } + + @Override + public Class getEntityClass() { + return entityHome.getEntityClass(); + } + + public boolean isReadonly() { + return readonly; + } + + /** + * If set to read-only mode, this resource will only response to GET + * requests. HTTP 415 status code (method not allowed) will returned in all + * other cases. + * + * @param readonly + */ + public void setReadonly(boolean readonly) { + this.readonly = readonly; + } + + /** + * Retrieve entity identifier's class. If not set, type parameters of a + * superclass are examined. + * + * @return class of entity identifier + */ + public Class getEntityIdClass() { + if (entityIdClass == null) { + Type superclass = this.getClass().getGenericSuperclass(); + if (superclass instanceof ParameterizedType) { + ParameterizedType parameterizedSuperclass = + (ParameterizedType) superclass; + if (parameterizedSuperclass.getActualTypeArguments().length == 2) { + return (Class) parameterizedSuperclass + .getActualTypeArguments()[1]; + } + } + throw new RuntimeException("Unable to determine entity id class."); + } else { + return entityIdClass; + } + } + + public void setEntityIdClass(Class entityIdClass) { + this.entityIdClass = entityIdClass; + } + + /** + * Annotation implementation (@PathParam("id")) for providing RESTEasy with + * metadata. + */ + static class PathParamAnnotation implements PathParam { + + public String value() { + return "id"; + } + + public Class annotationType() { + return PathParam.class; + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/ResourceQuery.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResourceQuery.java new file mode 100644 index 0000000000..826a413207 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResourceQuery.java @@ -0,0 +1,157 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.annotations.providers.jaxb.Wrapped; +import org.jboss.seam.annotations.Create; +import org.jboss.seam.framework.EntityQuery; +import org.jboss.seam.framework.Query; + +import static javax.ws.rs.core.Response.Status.UNSUPPORTED_MEDIA_TYPE; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; + +/** + * This component exposes EntityQuery component as a REST resource responding to + * HTTP GET request. + * + * @param + * entity type + * @author Jozef Hartinger + */ +// Empty @Path because it's ignored by second-stage bootstrap if not subclassed +// or in components.xml +// but we need it as a marker so we'll find components.xml declarations during +// first stage of bootstrap. +@Path("") +public class ResourceQuery extends AbstractResource { + + private Query entityQuery = null; + + /** + * Called at component instantiation. + */ + @Create + public void create() { + this.entityQuery = getEntityQuery(); + if (entityQuery == null) { + this.entityQuery = createEntityQuery(); + } + } + + /** + * Called by RESTEasy to respond for an HTTP GET request. Retrieves a list + * of entities matching criteria set by query parameters from database and + * returns it wrapped in Response instance. + * + * @param start + * first entity in the list + * @param show + * maximum size of the list + * @return representation of a list of database entries + * @see #getEntityList + */ + @GET + @Wrapped + public Response getResourceList( + @QueryParam("start") @DefaultValue("0") int start, + @QueryParam("show") @DefaultValue("25") int show) { + MediaType selectedMediaType = selectResponseMediaType(); + if (selectedMediaType == null) { + return Response.status(UNSUPPORTED_MEDIA_TYPE).build(); + } + + if ((start < 0) || (show < 0)) { + return Response.status(BAD_REQUEST).build(); + } + + final List result = getEntityList(start, show); + // create a proper response type + Type responseType = new ParameterizedType() { + + public Type getRawType() { + return result.getClass(); + } + + public Type getOwnerType() { + return null; + } + + public Type[] getActualTypeArguments() { + return new Type[] { getEntityClass() }; + } + }; + return Response.ok(new GenericEntity(result, responseType) { + }, selectedMediaType).build(); + } + + /** + * Retrieve a list of database entities. + * + * @param start + * first entity in the list + * @param show + * maximum size of the list, 0 for unlimited + * @return list of database entries + */ + public List getEntityList(int start, int show) { + entityQuery.setFirstResult(start); + // set 0 for unlimited + if (show > 0) { + entityQuery.setMaxResults(show); + } + return entityQuery.getResultList(); + } + + /** + * EntityQuery getter + * + * @return EntityQuery instance + */ + public Query getEntityQuery() { + return entityQuery; + } + + public void setEntityQuery(Query query) { + this.entityQuery = query; + } + + public Query createEntityQuery() { + Query entityQuery = new EntityQuery(); + entityQuery.setEjbql("select entity from " + getEntityClass().getName() + + " entity"); + return entityQuery; + } +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyBootstrap.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyBootstrap.java new file mode 100644 index 0000000000..45d3778c14 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyBootstrap.java @@ -0,0 +1,569 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import static org.jboss.seam.annotations.Install.BUILT_IN; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.ext.Providers; + +import org.jboss.resteasy.core.Dispatcher; +import org.jboss.resteasy.core.SynchronousDispatcher; +import org.jboss.resteasy.core.ThreadLocalResteasyProviderFactory; +import org.jboss.resteasy.plugins.providers.RegisterBuiltin; +import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory; +import org.jboss.resteasy.spi.Registry; +import org.jboss.resteasy.spi.ResourceFactory; +import org.jboss.resteasy.spi.StringConverter; +import org.jboss.seam.Component; +import org.jboss.seam.ScopeType; +import org.jboss.seam.Seam; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.Create; +import org.jboss.seam.annotations.Factory; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Install; +import org.jboss.seam.annotations.JndiName; +import org.jboss.seam.annotations.Logger; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.annotations.Startup; +import org.jboss.seam.contexts.Contexts; +import org.jboss.seam.core.Init; +import org.jboss.seam.deployment.AnnotationDeploymentHandler; +import org.jboss.seam.deployment.DeploymentStrategy; +import org.jboss.seam.log.Log; +import org.jboss.seam.util.EJB; +import org.jboss.seam.util.Reflections; + +/** + * Detects (through scanning and configuration) JAX-RS resources and providers, + * then registers them with RESTEasy. + *

+ * This class is a factory for org.jboss.seam.resteasy.dispatcher and + * it has been designed for extension. Alternatively, you can ignore what this + * class is doing and provide a different + * org.jboss.seam.resteasy.dispatcher yourself without extending this + * class. + *

+ *

+ * The main methods of this class are registerProviders() and + * registerResources(). These methods call out to the individual + * fine-grained registration procedures, which you can override if a different + * registration strategy for a particular type/component is desired. + *

+ * + * @author Christian Bauer + */ +@Name("org.jboss.seam.resteasy.bootstrap") +@Scope(ScopeType.APPLICATION) +@Startup +@AutoCreate +@Install(precedence = BUILT_IN, + classDependencies = "org.jboss.resteasy.spi.ResteasyProviderFactory") +public class ResteasyBootstrap { + + @Logger + Log log; + + @In + protected Application application; + + // The job of this class is to initialize and configure the RESTEasy + // Dispatcher instance + protected Dispatcher dispatcher; + + @Factory("org.jboss.seam.resteasy.dispatcher") + public Dispatcher getDispatcher() { + return dispatcher; + } + + @Create + public void init() { + log.info("bootstrapping JAX-RS application"); + + // Custom ResteasyProviderFactory that understands Seam component lookup + // at runtime + SeamResteasyProviderFactory providerFactory = createProviderFactory(); + dispatcher = createDispatcher(providerFactory); + initDispatcher(); + + // Always use the "deployment sensitive" factory - that means it is + // handled through ThreadLocal, not static + // TODO: How does that actually work? It's never used because the + // dispatcher is created with the original one + SeamResteasyProviderFactory + .setInstance(new ThreadLocalResteasyProviderFactory( + providerFactory)); + + // Put Providers, Registry and Dispatcher into RESTEasy context. + dispatcher.getDefaultContextObjects().put(Providers.class, + providerFactory); + dispatcher.getDefaultContextObjects().put(Registry.class, + dispatcher.getRegistry()); + dispatcher.getDefaultContextObjects().put(Dispatcher.class, dispatcher); + Map contextDataMap = SeamResteasyProviderFactory.getContextDataMap(); + contextDataMap.putAll(dispatcher.getDefaultContextObjects()); + + // Seam can scan the classes for us, we just have to list them in + // META-INF/seam-deployment.properties + DeploymentStrategy deployment = + (DeploymentStrategy) Component + .getInstance("deploymentStrategy"); + AnnotationDeploymentHandler handler = + (AnnotationDeploymentHandler) deployment + .getDeploymentHandlers().get( + AnnotationDeploymentHandler.NAME); + + Collection> providers = findProviders(handler); + Collection> resources = findResources(handler); + Collection seamComponents = findSeamComponents(); + + registerProviders(seamComponents, providers); + registerResources(seamComponents, resources); + } + + protected SeamResteasyProviderFactory createProviderFactory() { + return new SeamResteasyProviderFactory(); + } + + protected Dispatcher createDispatcher( + SeamResteasyProviderFactory providerFactory) { + return new SynchronousDispatcher(providerFactory); + } + + protected void initDispatcher() { + assert application.getLanguageMappings().isEmpty(); + assert application.getMediaTypeMappings().isEmpty(); + // not supported, but we don't use them + // getDispatcher().setLanguageMappings(application.getLanguageMappings()); + // getDispatcher().setMediaTypeMappings(application.getMediaTypeMappings()); + } + + protected Collection> findProviders( + AnnotationDeploymentHandler handler) { + return findTypes( + handler, + application.isScanProviders(), + javax.ws.rs.ext.Provider.class.getName(), + application.getProviderClassNames()); + } + + protected Collection> findResources( + AnnotationDeploymentHandler handler) { + return findTypes( + handler, + application.isScanResources(), + javax.ws.rs.Path.class.getName(), + application.getResourceClassNames()); + } + + protected Collection> findTypes( + AnnotationDeploymentHandler handler, + boolean scanClasspathForAnnotations, + String annotationFQName, Collection includeTypeNames) { + + Collection> types = new HashSet(); + + if (scanClasspathForAnnotations) { + Collection> annotatedTypes = + handler.getClassMap().get(annotationFQName); + if (annotatedTypes != null) + types.addAll(annotatedTypes); + } + + try { + for (String s : new HashSet(includeTypeNames)) { + types.add(Reflections.classForName(s)); + } + } catch (ClassNotFoundException ex) { + log.error("error loading JAX-RS type: " + ex.getMessage(), ex); + } + + return types; + } + + protected Collection findSeamComponents() { + // Iterate through all variables in the application context that end + // with ".component" + log.debug("discovering all Seam components"); + Collection seamComponents = new HashSet(); + String[] applicationContextNames = + Contexts.getApplicationContext().getNames(); + for (String applicationContextName : applicationContextNames) { + if (applicationContextName.endsWith(".component")) { + Component seamComponent = + (Component) Component.getInstance( + applicationContextName, ScopeType.APPLICATION); + seamComponents.add(seamComponent); + } + } + return seamComponents; + } + + protected void registerProviders(Collection seamComponents, + Collection> providerClasses) { + + // RESTEasy built-in providers first + if (application.isUseBuiltinProviders()) { + log.info("registering built-in RESTEasy providers"); + RegisterBuiltin.register(getDispatcher().getProviderFactory()); + } + + Set handledProviders = new HashSet(); // Stuff we don't want to + // examine twice + + /* + * TODO: Retracted due to missing RESTEasy SPI for external provider + * metadata, see https://jira.jboss.org/jira/browse/JBSEAM-4247 for + * (Component seamComponent : seamComponents) { // The component can + * have one (not many) @Provider annotated business interface Class + * providerInterface = + * getAnnotatedInterface(javax.ws.rs.ext.Provider.class, seamComponent); + * + * // How we register it depends on the component type switch + * (seamComponent.getType()) { + * + * // TODO: We don't support EJB Seam components as providers + * + * case JAVA_BEAN: + * + * // We are only interested in components that have a @Provider + * annotation on iface or bean if (providerInterface == null && + * !seamComponent + * .getBeanClass().isAnnotationPresent(javax.ws.rs.ext.Provider.class)) + * { break; } + * + * // They also have to be in the right scope, otherwise we can't handle + * their lifecylce (yet) switch (seamComponent.getScope()) { case + * APPLICATION: + * + * // StringConverter is a special case if + * (StringConverter.class.isAssignableFrom + * (seamComponent.getBeanClass())) { + * getDispatcher().getProviderFactory().addStringConverter( + * (StringConverter) Component.getInstance(seamComponent.getName()) ); } + * else { registerSeamComponentProvider(seamComponent); } break; + * + * default: throw new RuntimeException( "Provider Seam component '" + + * seamComponent.getName() + "' must be scoped " + "APPLICATION" ); } + * break; } + * + * // We simply add everything we have seen so far... it's not really + * necessary but it doesn't hurt (does it?) + * handledProviders.add(seamComponent.getBeanClass()); + * handledProviders.addAll(seamComponent.getBusinessInterfaces()); } + */ + + for (Class providerClass : providerClasses) { + + // An @Provider annotated type may: + + // - have been already handled as a Seam component in the previous + // routine + if (handledProviders.contains(providerClass)) + continue; + + // - be a RESTEasy built-in provider + if (providerClass.getName().startsWith( + "org.jboss.resteasy.plugins.providers")) + continue; + + // - be an interface, which we don't care about if we don't have an + // implementation + if (providerClass.isInterface()) + continue; + + // - be just plain RESTEasy, no Seam component lookup or lifecycle + if (StringConverter.class.isAssignableFrom(providerClass)) { + log.debug( + "registering provider as RESTEasy StringConverter: {0}", + providerClass); + // getDispatcher().getProviderFactory().addStringConverter((Class) providerClass); + throw new RuntimeException( + "StringConverter support not implemented"); + } else { + log.debug("registering provider as plain JAX-RS type: {0}", + providerClass); + getDispatcher().getProviderFactory().registerProvider( + providerClass); + } + } + } + + protected void registerResources(Collection seamComponents, + Collection> resourceClasses) { + + Set handledResources = new HashSet(); // Stuff we don't want to + // examine twice + // These classes themselves should not be registered at all + // Configured ResourceHome and ResourceQuery components will be + // registered later + handledResources.add(ResourceHome.class); + handledResources.add(ResourceQuery.class); + + for (Component seamComponent : seamComponents) { + + // A bean class of type (not subtypes) ResourceHome or ResourceQuery + // annotated with @Path, then + // it's a Seam component resource we need to register with getPath() + // on the instance, it has been + // configured in components.xml + if (seamComponent.getBeanClass().equals(ResourceHome.class) || + seamComponent.getBeanClass().equals(ResourceQuery.class)) { + registerHomeQueryResources(seamComponent); + continue; + } + + // The component can have one (not many) @Path annotated business + // interface + Class resourceInterface = + getAnnotatedInterface(javax.ws.rs.Path.class, + seamComponent); + + // How we register it depends on the component type + switch (seamComponent.getType()) { + case STATELESS_SESSION_BEAN: + // EJB seam component resources must be @Path annotated on one + // of their business interfaces + if (resourceInterface != null) { + // TODO: Do we have to consider the scope? It should be + // stateless, right? + registerInterfaceSeamComponentResource(seamComponent, + resourceInterface); + } + break; + case STATEFUL_SESSION_BEAN: + // EJB seam component resources must be @Path annotated on one + // of their business interfaces + if (resourceInterface != null) { + log.error( + "Not implemented: Stateful EJB Seam component resource: " + + seamComponent); + // TODO: + // registerStatefulEJBSeamComponentResource(seamComponent); + } + break; + case JAVA_BEAN: + + // We are only interested in components that have a @Path + // annotation on iface or bean + if (resourceInterface == null + && + !seamComponent.getBeanClass().isAnnotationPresent( + javax.ws.rs.Path.class)) { + break; + } + + // They also have to be in the right scope, otherwise we can't + // handle their lifecylce (yet) + switch (seamComponent.getScope()) { + case EVENT: + case APPLICATION: + case STATELESS: + case SESSION: + if (resourceInterface != null) { + registerInterfaceSeamComponentResource( + seamComponent, + resourceInterface); + } else if (seamComponent.getBeanClass() + .isAnnotationPresent( + javax.ws.rs.Path.class)) { + registerSeamComponentResource(seamComponent); + } + break; + default: + throw new RuntimeException( + "Resource Seam component '" + + seamComponent.getName() + + "' must be scoped either " + + "EVENT, APPLICATION, STATELESS, or SESSION"); + } + break; + } + + // We simply add everything we have seen so far... it's not really + // necessary but it doesn't hurt (does it?) + handledResources.add(seamComponent.getBeanClass()); + handledResources.addAll(seamComponent.getBusinessInterfaces()); + + } + + for (Class resourceClass : resourceClasses) { + // An @Path annotated type may: + + // - have been already handled as a Seam component in the previous + // routine + if (handledResources.contains(resourceClass)) + continue; + + // - be an interface, which we don't care about if we don't have an + // implementation + if (resourceClass.isInterface()) + continue; + + // - be a @Stateless EJB implementation class that was listed in + // components.xml + if (resourceClass.isAnnotationPresent(EJB.STATELESS)) { + registerStatelessEJBResource(resourceClass); + } else if (resourceClass.isAnnotationPresent(EJB.STATEFUL)) { + // - be a @Stateful EJB implementation class that was listed in + // components.xml + throw new RuntimeException( + "Only stateless EJBs can be JAX-RS resources, remove from configuration: " + + resourceClass.getName()); + } else { + // - just be a regular JAX-RS lifecycle instance that can + // created/destroyed by RESTEasy + registerPlainResource(resourceClass); + } + } + + } + + protected void registerHomeQueryResources(Component seamComponent) { + // We can always instantiate this safely here because it can't have + // dependencies! + AbstractResource instance = + (AbstractResource) seamComponent.newInstance(); + String path = instance.getPath(); + if (path != null) { + if (!path.startsWith("/")) { + path = "/" + path; + } + + log.debug( + "registering resource, configured ResourceHome/Query on path {1}, as Seam component: {0}", + seamComponent.getName(), + path + ); + + ResourceFactory factory = new SeamResteasyResourceFactory( + seamComponent.getBeanClass(), + seamComponent, + getDispatcher().getProviderFactory() + ); + + getDispatcher().getRegistry().addResourceFactory(factory, path); + } else { + log.error( + "Unable to register {0} resource on null path, check components.xml", + seamComponent.getName()); + } + } + + protected void registerSeamComponentResource(Component seamComponent) { + log.debug("registering resource as Seam component: {0}", + seamComponent.getName()); + + ResourceFactory factory = new SeamResteasyResourceFactory( + seamComponent.getBeanClass(), + seamComponent, + getDispatcher().getProviderFactory() + ); + + getDispatcher().getRegistry().addResourceFactory(factory); + } + + protected void registerInterfaceSeamComponentResource( + Component seamComponent, Class resourceInterface) { + log.debug( + "registering resource, annotated interface {1}, as Seam component: {0}", + seamComponent.getName(), + resourceInterface.getName() + ); + + ResourceFactory factory = new SeamResteasyResourceFactory( + resourceInterface, + seamComponent, + getDispatcher().getProviderFactory() + ); + + getDispatcher().getRegistry().addResourceFactory(factory); + } + + protected void registerStatelessEJBResource(Class ejbImplementationClass) { + String jndiName = getJndiName(ejbImplementationClass); + + log.debug( + "registering resource, stateless EJB implementation {1}, as RESTEasy JNDI resource name: {0}", + jndiName, + ejbImplementationClass.getName() + ); + getDispatcher().getRegistry().addJndiResource(jndiName); + } + + protected void registerPlainResource(Class plainResourceClass) { + log.debug("registering resource, event-scoped JAX-RS lifecycle: {0}", + plainResourceClass.getName()); + getDispatcher().getRegistry().addResourceFactory( + new POJOResourceFactory(plainResourceClass)); + } + + protected void registerSeamComponentProvider(Component seamComponent) { + log.debug("registering provider as Seam component: {0}", + seamComponent.getName()); + getDispatcher().getProviderFactory().registerProviderInstance( + Component.getInstance(seamComponent.getName()) + ); + } + + protected Class getAnnotatedInterface( + Class annotation, Component seamComponent) { + Class resourceInterface = null; + for (Class anInterface : seamComponent.getBusinessInterfaces()) { + if (anInterface.isAnnotationPresent(annotation)) { + if (resourceInterface != null) { + throw new IllegalStateException( + "Only one business interface can be annotated " + + annotation + ": " + seamComponent); + } + resourceInterface = anInterface; + } + } + return resourceInterface; + } + + protected String getJndiName(Class beanClass) { + if (beanClass.isAnnotationPresent(JndiName.class)) { + return beanClass.getAnnotation(JndiName.class).value(); + } else { + String jndiPattern = Init.instance().getJndiPattern(); + if (jndiPattern == null) { + throw new IllegalArgumentException( + "You must specify org.jboss.seam.core.init.jndiPattern or use @JndiName: " + + beanClass.getName()); + } + return jndiPattern + .replace("#{ejbName}", Seam.getEjbName(beanClass)); + } + } + +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyContextInjectionInterceptor.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyContextInjectionInterceptor.java new file mode 100644 index 0000000000..4d07b79194 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyContextInjectionInterceptor.java @@ -0,0 +1,69 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.PropertyInjector; +import org.jboss.seam.Component; +import org.jboss.seam.annotations.intercept.Interceptor; +import org.jboss.seam.intercept.AbstractInterceptor; +import org.jboss.seam.intercept.InvocationContext; + +/** + * Runs after Seam injection and provides JAX RS @Context handling, required for + * field injection on the actual bean (not proxy) instance. + * + * @author Christian Bauer + */ +@Interceptor(stateless = true) +public class ResteasyContextInjectionInterceptor extends AbstractInterceptor { + + public static final String RE_HTTP_REQUEST_VAR = + "org.jboss.resteasy.spi.HttpRequest"; + public static final String RE_HTTP_RESPONSE_VAR = + "org.jboss.resteasy.spi.HttpResponse"; + + private final PropertyInjector propertyInjector; + + public ResteasyContextInjectionInterceptor( + PropertyInjector propertyInjector) { + this.propertyInjector = propertyInjector; + } + + public Object aroundInvoke(InvocationContext ic) throws Exception { + HttpRequest request = + (HttpRequest) Component.getInstance(RE_HTTP_REQUEST_VAR); + HttpResponse response = + (HttpResponse) Component.getInstance(RE_HTTP_RESPONSE_VAR); + + propertyInjector.inject(request, response, ic.getTarget()); + + return ic.proceed(); + } + + public boolean isInterceptorEnabled() { + return true; + } +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyResourceAdapter.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyResourceAdapter.java new file mode 100644 index 0000000000..b5efc08795 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/ResteasyResourceAdapter.java @@ -0,0 +1,220 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import static org.jboss.seam.annotations.Install.BUILT_IN; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.SecurityContext; + +import org.jboss.resteasy.core.Dispatcher; +import org.jboss.resteasy.core.SynchronousDispatcher; +import org.jboss.resteasy.core.ThreadLocalResteasyProviderFactory; +import org.jboss.resteasy.plugins.server.servlet.HttpServletInputMessage; +import org.jboss.resteasy.plugins.server.servlet.HttpServletResponseWrapper; +import org.jboss.resteasy.plugins.server.servlet.ServletSecurityContext; +import org.jboss.resteasy.plugins.server.servlet.ServletUtil; +import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.ResteasyUriInfo; +import org.jboss.seam.Component; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.Create; +import org.jboss.seam.annotations.Install; +import org.jboss.seam.annotations.Logger; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.annotations.intercept.BypassInterceptors; +import org.jboss.seam.log.Log; +import org.jboss.seam.servlet.ContextualHttpServletRequest; +import org.jboss.seam.web.AbstractResource; +import org.jboss.seam.web.Session; + +/** + * Accepts incoming HTTP requests through the SeamResourceServlet and + * dispatches the call to RESTEasy. Wraps the call in Seam contexts. + * + * @author Christian Bauer + */ +@Scope(ScopeType.APPLICATION) +@Name("org.jboss.seam.resteasy.resourceAdapter") +@BypassInterceptors +@Install(precedence = BUILT_IN) +public class ResteasyResourceAdapter extends AbstractResource { + + @Logger + Log log; + + protected Dispatcher dispatcher; + protected Application application; + + @Create + public void init() { + // No injection, so lookup on first request + dispatcher = + (Dispatcher) Component + .getInstance("org.jboss.seam.resteasy.dispatcher"); + application = (Application) Component.getInstance(Application.class); + if (dispatcher == null) { + throw new IllegalStateException( + "ReasteasyDispatcher not available, make sure RESTEasy and all required JARs are on your classpath"); + } + } + + @Override + public String getResourcePath() { + return application.getResourcePathPrefix(); + } + + @Override + public void getResource(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + try { + log.debug("processing REST request"); + + // TODO: As far as I can tell from tracing RE code: All this + // thread-local stuff has no effect because + // the "default" provider factory is always used. But we do it + // anyway, just to mimic the servlet handler + // in RE... + + // Wrap in RESTEasy thread-local factory handling + ThreadLocalResteasyProviderFactory.push(dispatcher + .getProviderFactory()); + + // Wrap in RESTEasy contexts (this also puts stuff in a + // thread-local) + SeamResteasyProviderFactory + .pushContext(HttpServletRequest.class, request); + SeamResteasyProviderFactory.pushContext(HttpServletResponse.class, + response); + SeamResteasyProviderFactory.pushContext(SecurityContext.class, + new ServletSecurityContext(request)); + + // Wrap in Seam contexts + new ContextualHttpServletRequest(request) { + @Override + public void process() throws ServletException, IOException { + try { + ResteasyHttpHeaders headers = + ServletUtil.extractHttpHeaders(request); + ResteasyUriInfo uriInfo = + extractUriInfo(request, + application.getResourcePathPrefix()); + + HttpResponse theResponse = + new HttpServletResponseWrapper( + response, + dispatcher.getProviderFactory() + ); + + // TODO: This requires a SynchronousDispatcher + HttpRequest in = new HttpServletInputMessage( + request, + response, + null, + null, + headers, + uriInfo, + request.getMethod().toUpperCase(), + (SynchronousDispatcher) dispatcher + ); + // HttpRequest in = new HttpServletInputMessage( + // request, + // theResponse, + // headers, + // uriInfo, + // request.getMethod().toUpperCase(), + // (SynchronousDispatcher) dispatcher + // ); + + dispatcher.invoke(in, theResponse); + } finally { + /* + * Prevent anemic sessions clog up the server + * + * session.isNew() check - do not close non-anemic + * sessions established by the view layer (JSF) which + * are reused by the JAX-RS requests (so that the + * requests do not have to be re-authorized) + */ + if (application.isDestroySessionAfterRequest() + && request.getSession().isNew()) { + log.debug( + "Destroying HttpSession after REST request"); + Session.instance().invalidate(); + } + } + } + }.run(); + + } finally { + // Clean up the thread-locals + SeamResteasyProviderFactory.clearContextData(); + ThreadLocalResteasyProviderFactory.pop(); + log.debug("completed processing of REST request"); + } + } + + protected ResteasyUriInfo extractUriInfo(HttpServletRequest request, + String pathPrefix) { + try { + // Append a slash if there isn't one + if (!pathPrefix.startsWith("/")) { + pathPrefix = "/" + pathPrefix; + } + + // Get the full path of the current request + URL requestURL = new URL(request.getRequestURL().toString()); + String requestPath = requestURL.getPath(); + + // Find the 'servlet mapping prefix' for RESTEasy (in our case: + // /seam/resource/rest) + String mappingPrefix = + requestPath.substring(0, requestPath.indexOf(pathPrefix) + + pathPrefix.length()); + + // Still is //seam/resource/rest, so cut off the context + mappingPrefix = + mappingPrefix.substring(request.getContextPath().length()); + + log.debug("Using request mapping prefix: " + mappingPrefix); + + // This is the prefix used by RESTEasy to resolve resources and + // generate URIs with + return ServletUtil.extractUriInfo(request, mappingPrefix); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/SeamResteasyProviderFactory.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/SeamResteasyProviderFactory.java new file mode 100644 index 0000000000..719e3ce0c4 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/SeamResteasyProviderFactory.java @@ -0,0 +1,54 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import org.jboss.resteasy.spi.ResteasyProviderFactory; + +/** + * TODO: We need to significantly extend and change that class so we can lookup + * provider instances through Seam at runtime. The original class has only been + * designed for registration of "singleton" providers during startup. See + * comment about the TL handling in ResteasyResourceAdapter.java. + * + * @author Christian Bauer + */ +public class SeamResteasyProviderFactory extends ResteasyProviderFactory { + + public static void setInstance(ResteasyProviderFactory factory) { + ResteasyProviderFactory.setInstance(factory); + } + + public static ResteasyProviderFactory getInstance() { + // workaround for https://issues.jboss.org/browse/RESTEASY-1119 + // can this cause a memory leak? + // TODO remove when https://issues.jboss.org/browse/RESTEASY-1119 + // fix is released + ResteasyProviderFactory factory = + ResteasyProviderFactory.getInstance(); + ResteasyProviderFactory.pushContext(javax.ws.rs.ext.Providers.class, + factory); + return factory; + } + +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/SeamResteasyResourceFactory.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/SeamResteasyResourceFactory.java new file mode 100644 index 0000000000..fcf880d10b --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/SeamResteasyResourceFactory.java @@ -0,0 +1,109 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.seam.resteasy; + +import org.jboss.resteasy.spi.ResourceFactory; +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.jboss.resteasy.core.PropertyInjectorImpl; + +import org.jboss.seam.contexts.Contexts; +import org.jboss.seam.Component; +import org.jboss.seam.log.Log; +import org.jboss.seam.log.Logging; + +/** + * Looks up Seam component in Seam contexts when a JAX-RS resource is requested. + * + * @author Christian Bauer + */ +public class SeamResteasyResourceFactory implements ResourceFactory { + Log log = Logging.getLog(SeamResteasyResourceFactory.class); + + private final Class resourceType; + private final Component seamComponent; + private final ResteasyProviderFactory providerFactory; + + public SeamResteasyResourceFactory(Class resourceType, + Component seamComponent, ResteasyProviderFactory providerFactory) { + this.resourceType = resourceType; + this.seamComponent = seamComponent; + this.providerFactory = providerFactory; + } + + @Override + public Class getScannableClass() { + return resourceType; + } + + @Override + public void registered(ResteasyProviderFactory factory) { + // Wrap the Resteasy PropertyInjectorImpl in a Seam interceptor (for + // @Context injection) + seamComponent.addInterceptor( + new ResteasyContextInjectionInterceptor( + new PropertyInjectorImpl(getScannableClass(), + providerFactory) + ) + ); + + // NOTE: Adding an interceptor to Component at this stage means that the + // interceptor is + // always executed last in the chain. The sorting of interceptors of a + // Component occurs + // only when the Component metadata is instantiated. This is OK in this + // case, as the + // JAX RS @Context injection can occur last after all other interceptors + // executed. + + } + + @Override + public Object createResource(HttpRequest request, HttpResponse response, + ResteasyProviderFactory factory) { + // Push this onto event context so we have it available in + // ResteasyContextInjectionInterceptor + Contexts.getEventContext().set( + ResteasyContextInjectionInterceptor.RE_HTTP_REQUEST_VAR, + request); + Contexts.getEventContext().set( + ResteasyContextInjectionInterceptor.RE_HTTP_RESPONSE_VAR, + response); + log.debug( + "creating RESTEasy resource instance by looking up Seam component: " + + seamComponent.getName()); + return Component.getInstance(seamComponent.getName()); + } + + @Override + public void requestFinished(HttpRequest request, HttpResponse response, + Object resource) { + } + + @Override + public void unregistered() { + } + +} diff --git a/zanata-war/src/main/java/org/zanata/seam/resteasy/package-info.java b/zanata-war/src/main/java/org/zanata/seam/resteasy/package-info.java new file mode 100644 index 0000000000..2598c0ba8e --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/seam/resteasy/package-info.java @@ -0,0 +1,6 @@ +// Implementation copied from Seam 2.3.1, commit f3077fe + +@Namespace("http://jboss.org/schema/seam/resteasy") +package org.zanata.seam.resteasy; + +import org.jboss.seam.annotations.Namespace; diff --git a/zanata-war/src/main/java/org/zanata/util/HttpUtil.java b/zanata-war/src/main/java/org/zanata/util/HttpUtil.java index 95a6eb111d..f2ce7be909 100644 --- a/zanata-war/src/main/java/org/zanata/util/HttpUtil.java +++ b/zanata-war/src/main/java/org/zanata/util/HttpUtil.java @@ -23,6 +23,7 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.MultivaluedMap; import org.apache.commons.lang.StringUtils; import org.jboss.resteasy.spi.HttpRequest; @@ -53,8 +54,11 @@ public final class HttpUtil { .getProperty("ZANATA_PROXY_HEADER"); public static String getApiKey(HttpRequest request) { - return request.getHttpHeaders().getRequestHeaders() - .getFirst(X_AUTH_TOKEN_HEADER); + return getApiKey(request.getMutableHeaders()); + } + + public static String getApiKey(MultivaluedMap headers) { + return headers.getFirst(X_AUTH_TOKEN_HEADER); } @VisibleForTesting @@ -63,8 +67,11 @@ static void refreshProxyHeader() { } public static String getUsername(HttpRequest request) { - return request.getHttpHeaders().getRequestHeaders() - .getFirst(X_AUTH_USER_HEADER); + return getUsername(request.getMutableHeaders()); + } + + public static String getUsername(MultivaluedMap headers) { + return headers.getFirst(X_AUTH_USER_HEADER); } /** diff --git a/zanata-war/src/main/resources/META-INF/seam-deployment.properties b/zanata-war/src/main/resources/META-INF/seam-deployment.properties index 26ddfd0625..a7f5a09dd7 100644 --- a/zanata-war/src/main/resources/META-INF/seam-deployment.properties +++ b/zanata-war/src/main/resources/META-INF/seam-deployment.properties @@ -1,2 +1,6 @@ # A colon-separated list of annotation types to handle -org.jboss.seam.deployment.annotationTypes=org.zanata.webtrans.server.ActionHandlerFor:org.hibernate.search.annotations.Indexed +org.jboss.seam.deployment.annotationTypes=\ + org.zanata.webtrans.server.ActionHandlerFor:\ + org.hibernate.search.annotations.Indexed:\ + javax.ws.rs.ext.Provider:\ + javax.ws.rs.Path diff --git a/zanata-war/src/main/resources/messages_nl.properties b/zanata-war/src/main/resources/messages_nl.properties index b19f9b587e..5fa7fb036f 100644 --- a/zanata-war/src/main/resources/messages_nl.properties +++ b/zanata-war/src/main/resources/messages_nl.properties @@ -123,7 +123,7 @@ jsf.dashboard.settings.removeAccount.label=Account verwijderen jsf.dashboard.settings.addAccount.label=Ander account toevoegen jsf.dashboard.settings.mergeAccounts.label=Account samenvoegen jsf.dashboard.settings.mergeAccount.info.label=Gebruik dit om andere Zanata-accounts, die je mogelijk hebt aangemaakt tijdens aanmaken van een verbonden account, samen te voegen. -jsf.dashboard.settings.mergeAccount.warning.label=Dit zal dat account samenvoegen met het account waarmee je momenteel bent ingelogd. +jsf.dashboard.settings.mergeAccount.warning.label=Dit zal dat account samenvoegen met het account waarmee je momenteel bent ingelogd. jsf.dashboard.settings.profileSettings.label=Profielinstellingen jsf.dashboard.settings.usernameCannotBeChanged.message=Gebruikersnaam kan niet worden veranderd jsf.dashboard.settings.updateProfile.label=Profiel bijwerken @@ -137,7 +137,7 @@ jsf.dashboard.settings.clientConfigHelp.message=Hulp\: Configureren van de clien jsf.dashboard.settings.mavenClientConfigHelp.message=Hulp\: Configureren van de Maven-plugin jsf.dashboard.settings.profileUpdated.message=Het profiel is bijgewerkt jsf.dashboard.settings.removeIdentity.confirm.message=Weet je zeker dat je dit verbonden account wilt verwijderen? -jsf.dashboard.settings.apiKeyNeeded.message=De CLI-client heeft een een API-sleutel en configuratie nodig. +jsf.dashboard.settings.apiKeyNeeded.message=De CLI-client heeft een een API-sleutel en configuratie nodig. jsf.EditHomePage=Bewerken homepagina jsf.tooltip.MoreActions=Meer akties jsf.label.review=Review diff --git a/zanata-war/src/main/resources/messages_tr.properties b/zanata-war/src/main/resources/messages_tr.properties index 92c073be7b..f08512c459 100644 --- a/zanata-war/src/main/resources/messages_tr.properties +++ b/zanata-war/src/main/resources/messages_tr.properties @@ -108,7 +108,7 @@ jsf.dashboard.settings.languageTeams.label=Dil Tak\u0131mlar\u0131 jsf.dashboard.settings.apiKey.label=API Anahtar\u0131 jsf.dashboard.settings.generateNewApiKey.label=Yeni API Anahtar\u0131 yarat jsf.dashboard.settings.joinLangTeam.message=Dil tak\u0131m\u0131na kat\u0131l -jsf.dashboard.settings.clientConfigHelp.message=Yard\u0131m\: \u0130stemci +jsf.dashboard.settings.clientConfigHelp.message=Yard\u0131m\: \u0130stemci jsf.dashboard.settings.profileUpdated.message=Profiliniz g\u00FCncellendi jsf.EditHomePage=Ana Sayfay\u0131 D\u00FCzenle jsf.tooltip.MoreActions=Daha fazla Etkinlik diff --git a/zanata-war/src/main/resources/org/zanata/seam/resteasy/resteasy-2.3.xsd b/zanata-war/src/main/resources/org/zanata/seam/resteasy/resteasy-2.3.xsd new file mode 100644 index 0000000000..bcc8e96720 --- /dev/null +++ b/zanata-war/src/main/resources/org/zanata/seam/resteasy/resteasy-2.3.xsd @@ -0,0 +1,204 @@ + + + + + + + + + An implementation of JAX-RS Application with additional properties for RESTEasy. + + + + + + + + List of provider classes. + + + + + + + List of resource classes. + + + + + + + Maps media type URI extensions to Accept header, see RESTEasy documentation and JAX-RS (JSR 311). + + + + + + + Maps language URI extension to Accept header, see RESTEasy documentation and JAX-RS (JSR 311). + + + + + + + + + + + + + Allows you to expose an entity home component as a + REST resource. + + + + + + + + + + + + + + + + Allows you to expose an entity query component as a + REST resource. + + + + + + + + + + + + + + + + + Enable automatic discovery of classes annoated with JAX-RS @Provider, defaults to 'true'. + + + + + + + Enable automatic discovery of classes annoated with JAX-RS @Path, defaults to 'true'. + + + + + + + Enable RESTEasy built-in providers, defaults to 'true'. + + + + + + + Destroy the HttpSession after a REST request if it was created for that request (it is a + new session), defaults to 'true'. + + + + + + + Append this prefix to any request path, after the SeamResourceServlet + url-pattern prefix (configured in web.xml). Defaults to "/rest". + + + + + + + (DEPRECATED) Has no effect, remove from configuration. + + + + + + + + + + Location of the resource. For example /user + + + + + + + Media type this resource will operate on. Defaults to application/xml. + + + + + + + + + + Entity identifier class. + + + + + + + EntityHome component that will be used for operating database. + + + + + + + Disable "write" operations on this resource. Resource will only allow GET + method. + + + + + + + + + + Entity class. + + + + + + + EntityQuery component that will be used for + listing operation (GET on path). If not set, it will be created + automatically. + + + + + diff --git a/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml b/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml index c230bd3e92..2d2547b92f 100644 --- a/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml +++ b/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-deployment-structure.xml @@ -3,12 +3,15 @@ - + + + @@ -17,6 +20,7 @@ + @@ -27,6 +31,7 @@ + diff --git a/zanata-war/src/main/webapp/app/config.json b/zanata-war/src/main/webapp/app/config.json index 0baa22b643..d8820da5dc 100644 --- a/zanata-war/src/main/webapp/app/config.json +++ b/zanata-war/src/main/webapp/app/config.json @@ -1,4 +1,4 @@ { - + "appPath": "/app" } diff --git a/zanata-war/src/main/webapp/app/css/app.css b/zanata-war/src/main/webapp/app/css/app.css index d275e8b360..ae5177f4e7 100644 --- a/zanata-war/src/main/webapp/app/css/app.css +++ b/zanata-war/src/main/webapp/app/css/app.css @@ -1 +1 @@ -.u-cf:before,.u-cf:after{content:" ";display:table}.u-cf:after{clear:both}.u-nbfc{overflow:hidden!important}.u-nbfcAlt{display:table-cell!important;width:10000px!important}.u-floatLeft{float:left!important}.u-floatRight{float:right!important}@media (min-width:0) and (max-width:740px){.u-sm-floatNone{clear:both;float:none!important}}.u-bgFaint{opacity:.9}.u-bgHigh{background-color:#f5f6f7}.u-bgHigher{background-color:#fbfcfc}.u-bgHighest{background-color:#fff}.u-bgLow{background-color:#e5e8e9}.u-bgLower{background-color:#dee1e2}.u-bgLowest{background-color:#d7d9da}.u-bgPop{border:1px solid rgba(32,113,138,.12);border-bottom-width:2px}.u-bgNeutral{background-color:#ECEFF0}.u-bgPrimary{background-color:#03A6D7}.u-bgSecondary{background-color:#20718A}.u-bgDanger{background-color:#ffd8d8}.u-bgWarning{background-color:#fec}.u-bgUnsure{background-color:#fdfce8}.u-bgSuccess{background-color:#def4e5}.u-bgHighlight{background-color:#cdedf7}.u-block{display:block!important}.u-hidden{display:none!important}.u-hiddenVisually{position:absolute!important;overflow:hidden!important;width:1px!important;height:1px!important;padding:0!important;border:0!important;clip:rect(1px,1px,1px,1px)!important}.u-inline{display:inline!important}.u-inlineBlock{display:inline-block!important;max-width:100%}.u-table{display:table!important}.u-tableCell{display:table-cell!important}.u-tableRow{display:table-row!important}.u-round{border-radius:1000px!important}.u-round,.u-roundish{overflow:hidden!important}.u-roundish{border-radius:.5625rem!important}.u-rounded{border-radius:1.125rem!important;overflow:hidden!important}@media (min-width:0) and (max-width:740px){.u-sm-hidden{display:none!important}}@media (min-width:741px) and (max-width:960px){.u-md-hidden{display:none!important}}@media (max-width:960px){.u-ltemd-hidden{display:none!important}}@media (min-width:741px){.u-gtemd-hidden{display:none!important}}@media (min-width:961px) and (max-width:1270px){.u-lg-hidden{display:none!important}}@media (min-width:961px){.u-gtelg-hidden{display:none!important}}.u-dlUnstyled>dd{margin-left:0}.u-dlInline dt,.u-dlInline dd{display:inline-block;margin-right:.1875rem;margin-left:0}.u-listUnstyled{margin-bottom:0;padding-left:0;list-style:none}.u-listUnstyled>li{margin-left:0;list-style:none}.u-listInline{margin-left:-.1875rem;padding-left:0;list-style:none}.u-listInline>li{display:inline-block;padding-right:.1875rem;padding-left:.1875rem}.u-listHorizontal{font-size:0;list-style:none}.u-listHorizontal>li{font-size:1rem;display:inline-block;vertical-align:top}.u-posAbsolute,.u-posAbsoluteCenter{position:absolute!important}.u-posAbsoluteCenter{bottom:0!important;left:0!important;margin:auto!important;right:0!important;top:0!important}.u-posFixed{position:fixed!important;-webkit-backface-visibility:hidden;backface-visibility:hidden}.u-posRelative{position:relative!important}.u-posStatic{position:static!important}.u-posAbsoluteLeft{left:0!important}.u-posAbsoluteLeft,.u-posAbsoluteRight{bottom:0!important;position:absolute!important;top:0!important}.u-posAbsoluteRight{right:0!important}.u-posCenterCenter{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.u-sizeFit{display:block!important;float:left!important;width:auto!important}.u-sizeFitAlt{float:right!important}.u-sizeFitAlt,.u-sizeFill{display:block!important;width:auto!important}.u-sizeFill{overflow:hidden!important}.u-sizeFillAlt{display:table-cell!important;max-width:100%!important;width:10000px!important}.u-sizeFull{box-sizing:border-box!important;display:block!important;width:100%!important}.u-size1of12{width:8.333333333333332%!important}.u-size1of10{width:10%!important}.u-size1of8{width:12.5%!important}.u-size1of6,.u-size2of12{width:16.666666666666664%!important}.u-size1of5,.u-size2of10{width:20%!important}.u-size1of4,.u-size2of8,.u-size3of12{width:25%!important}.u-size3of10{width:30%!important}.u-size1of3,.u-size2of6,.u-size4of12{width:33.33333333333333%!important}.u-size3of8{width:37.5%!important}.u-size2of5,.u-size4of10{width:40%!important}.u-size5of12{width:41.66666666666667%!important}.u-size1of2,.u-size2of4,.u-size3of6,.u-size4of8,.u-size5of10,.u-size6of12{width:50%!important}.u-size7of12{width:58.333333333333336%!important}.u-size3of5,.u-size6of10{width:60%!important}.u-size5of8{width:62.5%!important}.u-size2of3,.u-size4of6,.u-size8of12{width:66.66666666666666%!important}.u-size7of10{width:70%!important}.u-size3of4,.u-size6of8,.u-size9of12{width:75%!important}.u-size4of5,.u-size8of10{width:80%!important}.u-size5of6,.u-size10of12{width:83.33333333333334%!important}.u-size7of8{width:87.5%!important}.u-size9of10{width:90%!important}.u-size11of12{width:91.66666666666666%!important}@media (min-width:0) and (max-width:740px){.u-sm-sizeFit{display:block!important;float:left!important;width:auto!important}.u-sm-sizeFitAlt{float:right!important}.u-sm-sizeFitAlt,.u-sm-sizeFill{display:block!important;width:auto!important}.u-sm-sizeFill{overflow:hidden!important}.u-sm-sizeFillAlt{display:table-cell!important;max-width:100%!important;width:10000px!important}.u-sm-sizeFull{box-sizing:border-box!important;display:block!important;width:100%!important}.u-sm-size1of12{width:8.333333333333332%!important}.u-sm-size1of10{width:10%!important}.u-sm-size1of8{width:12.5%!important}.u-sm-size1of6,.u-sm-size2of12{width:16.666666666666664%!important}.u-sm-size1of5,.u-sm-size2of10{width:20%!important}.u-sm-size1of4,.u-sm-size2of8,.u-sm-size3of12{width:25%!important}.u-sm-size3of10{width:30%!important}.u-sm-size1of3,.u-sm-size2of6,.u-sm-size4of12{width:33.33333333333333%!important}.u-sm-size3of8{width:37.5%!important}.u-sm-size2of5,.u-sm-size4of10{width:40%!important}.u-sm-size5of12{width:41.66666666666667%!important}.u-sm-size1of2,.u-sm-size2of4,.u-sm-size3of6,.u-sm-size4of8,.u-sm-size5of10,.u-sm-size6of12{width:50%!important}.u-sm-size7of12{width:58.333333333333336%!important}.u-sm-size3of5,.u-sm-size6of10{width:60%!important}.u-sm-size5of8{width:62.5%!important}.u-sm-size2of3,.u-sm-size4of6,.u-sm-size8of12{width:66.66666666666666%!important}.u-sm-size7of10{width:70%!important}.u-sm-size3of4,.u-sm-size6of8,.u-sm-size9of12{width:75%!important}.u-sm-size4of5,.u-sm-size8of10{width:80%!important}.u-sm-size5of6,.u-sm-size10of12{width:83.33333333333334%!important}.u-sm-size7of8{width:87.5%!important}.u-sm-size9of10{width:90%!important}.u-sm-size11of12{width:91.66666666666666%!important}}@media (min-width:741px) and (max-width:960px){.u-md-sizeFit{display:block!important;float:left!important;width:auto!important}.u-md-sizeFitAlt{float:right!important}.u-md-sizeFitAlt,.u-md-sizeFill{display:block!important;width:auto!important}.u-md-sizeFill{overflow:hidden!important}.u-md-sizeFillAlt{display:table-cell!important;max-width:100%!important;width:10000px!important}.u-md-sizeFull{box-sizing:border-box!important;display:block!important;width:100%!important}.u-md-size1of12{width:8.333333333333332%!important}.u-md-size1of10{width:10%!important}.u-md-size1of8{width:12.5%!important}.u-md-size1of6,.u-md-size2of12{width:16.666666666666664%!important}.u-md-size1of5,.u-md-size2of10{width:20%!important}.u-md-size1of4,.u-md-size2of8,.u-md-size3of12{width:25%!important}.u-md-size3of10{width:30%!important}.u-md-size1of3,.u-md-size2of6,.u-md-size4of12{width:33.33333333333333%!important}.u-md-size3of8{width:37.5%!important}.u-md-size2of5,.u-md-size4of10{width:40%!important}.u-md-size5of12{width:41.66666666666667%!important}.u-md-size1of2,.u-md-size2of4,.u-md-size3of6,.u-md-size4of8,.u-md-size5of10,.u-md-size6of12{width:50%!important}.u-md-size7of12{width:58.333333333333336%!important}.u-md-size3of5,.u-md-size6of10{width:60%!important}.u-md-size5of8{width:62.5%!important}.u-md-size2of3,.u-md-size4of6,.u-md-size8of12{width:66.66666666666666%!important}.u-md-size7of10{width:70%!important}.u-md-size3of4,.u-md-size6of8,.u-md-size9of12{width:75%!important}.u-md-size4of5,.u-md-size8of10{width:80%!important}.u-md-size5of6,.u-md-size10of12{width:83.33333333333334%!important}.u-md-size7of8{width:87.5%!important}.u-md-size9of10{width:90%!important}.u-md-size11of12{width:91.66666666666666%!important}}@media (min-width:961px) and (max-width:1270px){.u-lg-sizeFit{display:block!important;float:left!important;width:auto!important}.u-lg-sizeFitAlt{float:right!important}.u-lg-sizeFitAlt,.u-lg-sizeFill{display:block!important;width:auto!important}.u-lg-sizeFill{overflow:hidden!important}.u-lg-sizeFillAlt{display:table-cell!important;max-width:100%!important;width:10000px!important}.u-lg-sizeFull{box-sizing:border-box!important;display:block!important;width:100%!important}.u-lg-size1of12{width:8.333333333333332%!important}.u-lg-size1of10{width:10%!important}.u-lg-size1of8{width:12.5%!important}.u-lg-size1of6,.u-lg-size2of12{width:16.666666666666664%!important}.u-lg-size1of5,.u-lg-size2of10{width:20%!important}.u-lg-size1of4,.u-lg-size2of8,.u-lg-size3of12{width:25%!important}.u-lg-size3of10{width:30%!important}.u-lg-size1of3,.u-lg-size2of6,.u-lg-size4of12{width:33.33333333333333%!important}.u-lg-size3of8{width:37.5%!important}.u-lg-size2of5,.u-lg-size4of10{width:40%!important}.u-lg-size5of12{width:41.66666666666667%!important}.u-lg-size1of2,.u-lg-size2of4,.u-lg-size3of6,.u-lg-size4of8,.u-lg-size5of10,.u-lg-size6of12{width:50%!important}.u-lg-size7of12{width:58.333333333333336%!important}.u-lg-size3of5,.u-lg-size6of10{width:60%!important}.u-lg-size5of8{width:62.5%!important}.u-lg-size2of3,.u-lg-size4of6,.u-lg-size8of12{width:66.66666666666666%!important}.u-lg-size7of10{width:70%!important}.u-lg-size3of4,.u-lg-size6of8,.u-lg-size9of12{width:75%!important}.u-lg-size4of5,.u-lg-size8of10{width:80%!important}.u-lg-size5of6,.u-lg-size10of12{width:83.33333333333334%!important}.u-lg-size7of8{width:87.5%!important}.u-lg-size9of10{width:90%!important}.u-lg-size11of12{width:91.66666666666666%!important}}.u-sizeWidth-1-4{width:.375rem!important}.u-sizeWidth-1-2{width:.75rem!important}.u-sizeWidth-3-4{width:1.125rem!important}.u-sizeWidth-1{width:1.5rem!important}.u-sizeWidth-1_1-2{width:2.25rem!important}.u-sizeWidth-2{width:3rem!important}.u-sizeWidth-6{width:9rem!important}.u-sizeHeight-1-4{height:.375rem!important}.u-sizeHeight-1-2{height:.75rem!important}.u-sizeHeight-3-4{height:1.125rem!important}.u-sizeHeight-1{height:1.5rem!important}.u-sizeHeight-1_1-4{height:1.875rem!important}.u-sizeHeight-1_1-2{height:2.25rem!important}.u-sizeHeight-2{height:3rem!important}.u-sizeLineHeight-1-4{line-height:.375rem!important}.u-sizeLineHeight-1-2{line-height:.75rem!important}.u-sizeLineHeight-3-4{line-height:1.125rem!important}.u-sizeLineHeight-1{line-height:1.5rem!important}.u-sizeLineHeight-1_1-4{line-height:1.875rem!important}.u-sizeLineHeight-1_1-2{line-height:2.25rem!important}.u-sizeLineHeight-2{line-height:3rem!important}.u-sM-1-8{margin:.1875rem!important}.u-sM-1-4{margin:.375rem!important}.u-sM-1-2{margin:.75rem!important}.u-sM-3-4{margin:1.125rem!important}.u-sM-1{margin:1.5rem!important}.u-sM-1_1-2{margin:2.25rem!important}.u-sM-2{margin:3rem!important}.u-sMT-1-8{margin-top:.1875rem!important}.u-sMT-1-4{margin-top:.375rem!important}.u-sMT-1-2{margin-top:.75rem!important}.u-sMT-3-4{margin-top:1.125rem!important}.u-sMT-1{margin-top:1.5rem!important}.u-sMT-1_1-2{margin-top:2.25rem!important}.u-sMT-2{margin-top:3rem!important}.u-sMR-1-4{margin-right:.375rem!important}.u-sMR-1-2{margin-right:.75rem!important}.u-sMR-3-4{margin-right:1.125rem!important}.u-sMR-1{margin-right:1.5rem!important}.u-sMR-1_1-2{margin-right:2.25rem!important}.u-sMR-2{margin-right:3rem!important}.u-sMB-1-4{margin-bottom:.375rem!important}.u-sMB-1-2{margin-bottom:.75rem!important}.u-sMB-3-4{margin-bottom:1.125rem!important}.u-sMB-1{margin-bottom:1.5rem!important}.u-sMB-1_1-2{margin-bottom:2.25rem!important}.u-sMB-2{margin-bottom:3rem!important}.u-sML-1-4{margin-left:.375rem!important}.u-sML-1-2{margin-left:.75rem!important}.u-sML-3-4{margin-left:1.125rem!important}.u-sML-1{margin-left:1.5rem!important}.u-sML-1_1-2{margin-left:2.25rem!important}.u-sML-2{margin-left:3rem!important}.u-sMV-1-4{margin-top:.375rem!important;margin-bottom:.375rem!important}.u-sMV-1-2{margin-top:.75rem!important;margin-bottom:.75rem!important}.u-sMV-3-4{margin-top:1.125rem!important;margin-bottom:1.125rem!important}.u-sMV-1{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.u-sMV-1_1-2{margin-top:2.25rem!important;margin-bottom:2.25rem!important}.u-sMV-2{margin-top:3rem!important;margin-bottom:3rem!important}.u-sMH-1-4{margin-left:.375rem!important;margin-right:.375rem!important}.u-sMH-1-2{margin-left:.75rem!important;margin-right:.75rem!important}.u-sMH-3-4{margin-left:1.125rem!important;margin-right:1.125rem!important}.u-sMH-1{margin-left:1.5rem!important;margin-right:1.5rem!important}.u-sMH-1_1-2{margin-left:2.25rem!important;margin-right:2.25rem!important}.u-sMH-2{margin-left:3rem!important;margin-right:3rem!important}.u-sP-1-4{padding:.375rem!important}.u-sP-1-2{padding:.75rem!important}.u-sP-3-4{padding:1.125rem!important}.u-sP-1{padding:1.5rem!important}.u-sP-1_1-2{padding:2.25rem!important}.u-sP-2{padding:3rem!important}.u-sPT-1-4{padding-top:.375rem!important}.u-sPT-1-2{padding-top:.75rem!important}.u-sPT-3-4{padding-top:1.125rem!important}.u-sPT-1{padding-top:1.5rem!important}.u-sPT-1_1-2{padding-top:2.25rem!important}.u-sPT-2{padding-top:3rem!important}.u-sPR-1-4{padding-right:.375rem!important}.u-sPR-1-2{padding-right:.75rem!important}.u-sPR-3-4{padding-right:1.125rem!important}.u-sPR-1{padding-right:1.5rem!important}.u-sPR-1_1-2{padding-right:2.25rem!important}.u-sPR-2{padding-right:3rem!important}.u-sPB-1-4{padding-bottom:.375rem!important}.u-sPB-1-2{padding-bottom:.75rem!important}.u-sPB-3-4{padding-bottom:1.125rem!important}.u-sPB-1{padding-bottom:1.5rem!important}.u-sPB-1_1-2{padding-bottom:2.25rem!important}.u-sPB-2{padding-bottom:3rem!important}.u-sPL-1-4{padding-left:.375rem!important}.u-sPL-1-2{padding-left:.75rem!important}.u-sPL-3-4{padding-left:1.125rem!important}.u-sPL-1{padding-left:1.5rem!important}.u-sPL-1_1-2{padding-left:2.25rem!important}.u-sPL-2{padding-left:3rem!important}.u-sPV-1-4{padding-top:.375rem!important;padding-bottom:.375rem!important}.u-sPV-1-2{padding-top:.75rem!important;padding-bottom:.75rem!important}.u-sPV-3-4{padding-top:1.125rem!important;padding-bottom:1.125rem!important}.u-sPV-1{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.u-sPV-1_1-2{padding-top:2.25rem!important;padding-bottom:2.25rem!important}.u-sPV-2{padding-top:3rem!important;padding-bottom:3rem!important}.u-sPH-1-4{padding-left:.375rem!important;padding-right:.375rem!important}.u-sPH-1-2{padding-left:.75rem!important;padding-right:.75rem!important}.u-sPH-3-4{padding-left:1.125rem!important;padding-right:1.125rem!important}.u-sPH-1{padding-left:1.5rem!important;padding-right:1.5rem!important}.u-sPH-1_1-2{padding-left:2.25rem!important;padding-right:2.25rem!important}.u-sPH-2{padding-left:3rem!important;padding-right:3rem!important}.u-textBreak{word-wrap:break-word!important}.u-textCenter{text-align:center!important}.u-textLeft{text-align:left!important}.u-textRight{text-align:right!important}.u-textInheritColor{color:inherit!important}.u-textKern{text-rendering:optimizeLegibility;-webkit-font-feature-settings:"kern" 1;-moz-font-feature-settings:"kern" 1;font-feature-settings:"kern" 1;-webkit-font-kerning:normal;-moz-font-kerning:normal;font-kerning:normal}.u-textNoWrap,.u-textTruncate{white-space:nowrap!important}.u-textTruncate{max-width:100%;overflow:hidden!important;text-overflow:ellipsis!important;word-wrap:normal!important}.u-textUpper{text-transform:uppercase}.u-textLower{text-transform:lowercase}.u-textCapitalize{text-transform:capitalize}.u-textInvert{color:#f2f2f2}.u-textMuted{opacity:.6}.u-textEmpty{color:#90b8c5;font-weight:600}.u-textMicro{font-size:.75rem}.u-textMini,.u-textMeta{font-size:.875rem}.u-textMeta{color:#90b8c5}.u-textLead{font-size:1.5rem;font-weight:300}.u-textPrimary{color:#03A6D7}.u-textSecondary{color:#20718A}.u-textHighlight{color:#03A6D7}.u-textSuccess{color:#5CCA7B}.u-textUnsure{color:#E9DF1B}.u-textNeutral{color:#bcd4dc}.u-textWarning{color:#FFA800}.u-textDanger{color:#FF3B3D}.u-textPilcrow:before{content:'\00b6';color:#bcd4dc;padding:0 .1875rem}.u-textTab{position:relative;display:inline-block;width:1.2em;text-align:center}.u-textTab:before{content:'\21E5';color:#bcd4dc}.u-textSpace{position:relative}.u-textSpace:before{position:absolute;content:'.';color:#bcd4dc}.Grid{display:block;font-size:0;margin:0;padding:0;text-align:left}.Grid--alignCenter{text-align:center}.Grid--alignRight{text-align:right}.Grid--alignMiddle>.Grid-cell{vertical-align:middle}.Grid--alignBottom>.Grid-cell{vertical-align:bottom}.Grid--withGutter{margin:0 -10px}.Grid--withGutter>.Grid-cell{padding:0 10px}.Grid-cell{box-sizing:border-box;display:inline-block;font-size:1rem;margin:0;padding:0;text-align:left;vertical-align:top;width:100%}.Grid-cell--center{display:block;margin:0 auto}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}hr{box-sizing:content-box;height:0}pre{overflow:auto}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}::-webkit-input-placeholder{color:#a9bfc6}:-moz-placeholder,::-moz-placeholder{color:#a9bfc6}:-ms-input-placeholder{color:#a9bfc6}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:none}button{background:0 0;border:0;padding:0;text-align:inherit}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button:hover,button:active{outline:none}fieldset{border:0;margin:0;padding:0}input[type="text"],input[type="password"],input[type="date"],input[type="datetime"],input[type="email"],input[type="number"],input[type="search"],input[type="tel"],input[type="time"],input[type="url"],textarea{-webkit-appearance:none;outline:none;box-sizing:border-box}@-ms-viewport{width:device-width}@viewport{width:device-width}html{font-family:'Source Sans Pro',"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif;font-size:16px;line-height:1.5;box-sizing:border-box;color:#444c54}*,*:before,*:after{box-sizing:inherit}body{min-width:320px;background:#ECEFF0}:active,:hover{outline:none}h1,h2,h3,h4,h5,h6{font-size:16px;margin:0;color:#20718A}a{color:#03A6D7;text-decoration:none;cursor:pointer}a:hover{color:#0395c2}a:active{color:#0285ac}ol,ul{list-style:none;margin:0;padding:0}ul:empty,ol:empty{display:none}img{max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}figcaption{color:gray}hr{width:100%;border-bottom:1px solid;border-color:rgba(32,113,138,.12);margin:rhythm(1)0;background:0 0}hr,iframe{border:0}[tabindex="-1"]:focus{outline:none!important}code{font-family:Monaco,Courier,monospace;margin:0;padding:0 .1875rem}code,kbd,pre{font-size:.8125rem;font-weight:400;color:#4d4d4d}pre,samp{font-family:Monaco,Courier,monospace;padding:0 .1875rem}samp{font-size:.8125rem;font-weight:400;color:#4d4d4d}samp,blockquote,dl,dd,p,pre{margin:0}kbd{margin:0 .1875rem;padding:.1875rem .5625rem;border:1px solid rgba(65,105,136,.07);border-bottom:3px solid rgba(65,105,136,.2);border-radius:.375rem;background-color:#fff;background-clip:padding;white-space:nowrap;display:inline-block;text-transform:uppercase;font-family:'Source Sans Pro',"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif}.ButtonGroup{display:block;font-size:0;margin:0;list-style:none;padding:0}.ButtonGroup-item{display:block;font-size:1rem}.ButtonGroup-item>.Button{display:block;width:100%}.ButtonGroup-item>.Button:hover,.ButtonGroup-item>.Button:focus,.ButtonGroup-item>.Button:active,.ButtonGroup-item>.Button.is-pressed{z-index:1}.ButtonGroup--hz>.ButtonGroup-item{display:inline-block}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item{margin-top:0}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item:first-child{margin-top:0}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item:not(:first-child):not(:last-child)>.Button{border-radius:0}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item:first-child:not(:only-child)>.Button{border-bottom-left-radius:0;border-bottom-right-radius:0}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item:last-child:not(:only-child)>.Button{border-top-left-radius:0;border-top-right-radius:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item{margin-left:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item:first-child{margin-left:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item:not(:first-child):not(:last-child)>.Button{border-radius:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item:first-child:not(:only-child)>.Button{border-bottom-right-radius:0;border-top-right-radius:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item:last-child:not(:only-child)>.Button{border-bottom-left-radius:0;border-top-left-radius:0}.ButtonGroup--hz>.ButtonGroup-item{vertical-align:middle}.ButtonGroup--round .ButtonGroup-item:first-child .Button{border-top-left-radius:100px;border-bottom-left-radius:100px}.ButtonGroup--round .ButtonGroup-item:last-child .Button{border-top-right-radius:100px;border-bottom-right-radius:100px}.Button{-webkit-appearance:none;background:0 0;border-color:currentcolor;border-style:solid;border-width:0;box-sizing:border-box;color:transparent;cursor:pointer;display:inline-block;font:inherit;line-height:normal;margin:0;padding:.1875rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:normal}.Button::-moz-focus-inner{border:0;padding:0}.Button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.Button:hover,.Button:focus,.Button:active{text-decoration:none}.Button:disabled,.Button.is-disabled{cursor:default;opacity:.6}.Button{-webkit-transition:all .25s cubic-bezier(.075,.82,.165,1);transition:all .25s cubic-bezier(.075,.82,.165,1);min-height:1.875rem;text-shadow:1px 1px 0 rgba(0,0,0,.15)}.Button:disabled,.Button.is-disabled{pointer-events:none}.Button:hover,.Button:active,.Button.is-active{outline:inherit}.Button--default{background-color:#deeaed}.Button--default:hover{background-color:#c8d3d5}.Button--default:active,.Button--default.is-active{background-color:#bdc7c9}.Button--primary{color:#fff;background-color:#03A6D7}.Button--primary:hover{background-color:#0395c2}.Button--primary:active,.Button--primary.is-active{background-color:#038db7}.Button--secondary{color:#fff;background-color:#20718A}.Button--secondary:hover{background-color:#1d667c}.Button--secondary:active,.Button--secondary.is-active{background-color:#1b6075}.Button--highlight{color:#fff;background-color:#03A6D7}.Button--highlight:hover{background-color:#0395c2}.Button--highlight:active,.Button--highlight.is-active{background-color:#038db7}.Button--success{color:#fff;background-color:#5CCA7B}.Button--success:hover{background-color:#53b66f}.Button--success:active,.Button--success.is-active{background-color:#4eac69}.Button--unsure{color:#fff;background-color:#E9DF1B}.Button--unsure:hover{background-color:#d2c918}.Button--unsure:active,.Button--unsure.is-active{background-color:#c6be17}.Button--neutral{color:#fff;background-color:#90b8c5}.Button--neutral:hover{background-color:#82a6b1}.Button--neutral:active,.Button--neutral.is-active{background-color:#7a9ca7}.Button--warning{color:#fff;background-color:#FFA800}.Button--warning:hover{background-color:#e69700}.Button--warning:active,.Button--warning.is-active{background-color:#d98f00}.Button--danger{color:#fff;background-color:#FF3B3D}.Button--danger:hover{background-color:#e63537}.Button--danger:active,.Button--danger.is-active{background-color:#d93234}.Button--invisible{background-color:transparent;color:#90b8c5}.Button--invisible:hover{background-color:#edf4f6;color:#639cad}.Button--invisible:active,.Button--invisible.is-active{color:#20718A;background-color:#e4eef1}.Button--snug{padding-left:.375rem;padding-right:.375rem}.Button--small{min-height:1.5rem;padding:.1875rem .75rem;font-size:.875rem}.InputGroup{position:relative;display:table;border-collapse:separate}.InputGroup-input,.InputGroup-addon,.InputGroup-button{display:table-cell;-webkit-transition:.2s cubic-bezier(.26,.47,.36,.94);transition:.2s cubic-bezier(.26,.47,.36,.94)}.InputGroup-input,.InputGroup-addon{background-color:transparent;padding:0 .375rem}.InputGroup-input{border:none;width:100%;color:#20718A}.InputGroup-addon{color:#639cad;text-align:center;width:1%;white-space:nowrap;vertical-align:middle}.InputGroup-addon:before{content:' '}.InputGroup.is-focused .InputGroup-input,.InputGroup.is-focused .InputGroup-addon{background-color:#fff}.InputGroup--bordered .InputGroup-input,.InputGroup--bordered .InputGroup-addon,.InputGroup--outlined .InputGroup-input,.InputGroup--outlined .InputGroup-addon{border-top:1px solid #a6c6d0;border-bottom:1px solid #a6c6d0}.InputGroup--bordered .InputGroup-input{border-left:1px solid #a6c6d0;border-right:1px solid #a6c6d0}.InputGroup--outlined .InputGroup-input:first-child,.InputGroup--outlined .InputGroup-addon:first-child{border-left:1px solid #a6c6d0}.InputGroup--outlined .InputGroup-input:last-child,.InputGroup--outlined .InputGroup-addon:last-child{border-right:1px solid #a6c6d0}.InputGroup--bordered.is-focused .InputGroup-input,.InputGroup--bordered.is-focused .InputGroup-addon,.InputGroup--outlined.is-focused .InputGroup-input,.InputGroup--outlined.is-focused .InputGroup-addon{border-color:#639cad}.InputGroup--rounded .InputGroup-input:first-child,.InputGroup--rounded .InputGroup-addon:first-child{border-bottom-left-radius:1.5rem;border-top-left-radius:1.5rem;padding-left:.5625rem}.InputGroup--rounded .InputGroup-input:last-child,.InputGroup--rounded .InputGroup-addon:last-child{border-bottom-right-radius:1.5rem;border-top-right-radius:1.5rem;padding-right:.5625rem}.Dropdown{position:relative;z-index:100;display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.Dropdown.is-active{z-index:900}.Dropdown-toggleIcon{display:inline-block;-webkit-transition:all .2s cubic-bezier(.175,.885,.32,1.275);transition:all .2s cubic-bezier(.175,.885,.32,1.275);text-align:center}.Dropdown.is-active .Dropdown-toggleIcon{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.Dropdown-content{position:absolute;z-index:800;top:100%;left:0;visibility:hidden;float:left;overflow:auto;overflow-x:hidden;overflow-y:auto;max-height:25.5rem;min-width:100%;margin:0;padding:0;-webkit-transition:all .2s cubic-bezier(.175,.885,.32,1.275);transition:all .2s cubic-bezier(.175,.885,.32,1.275);-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);text-align:left;opacity:0;background-color:#fff;background-clip:padding-box;box-shadow:0 0 1.5rem rgba(0,0,0,.1)}.Dropdown-content--Bordered{border:1px solid rgba(32,113,138,.12);border-bottom-width:2px}.Dropdown.is-active>.Dropdown-content{visibility:visible;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);opacity:1}.Dropdown-title{font-size:.875rem;padding:.1875rem .375rem;background-color:transparent;border-bottom:1px solid rgba(32,113,138,.12);color:#79aab9}.Dropdown-item{display:block;line-height:1.3125rem;padding:.375rem;-webkit-transition:all .2s ease-out;transition:all .2s ease-out;color:#03A6D7}.Dropdown-item:hover{color:#fff;background-color:#03A6D7}.Dropdown--right .Dropdown-content{left:auto;right:0}.Header{background-color:#03A6D7;position:fixed;z-index:100;width:100%;top:0;box-shadow:0 .375rem 1.5rem rgba(0,0,0,.1)}.Header-item{height:3rem;display:inline-block;padding-top:.75rem;padding-bottom:.75rem}.Header-avatar{margin-top:.5625rem;margin-bottom:.5625rem;width:1.875rem;height:1.875rem;display:inline-block}.h1,.h2,.h3,.h4,.h5,.h6{font-family:'Source Sans Pro',"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif;font-weight:600}.h1{font-size:36px}.h2{font-size:28px}.h3{font-size:24px}.h4{font-size:20px}.h5{font-size:18px}.h6{font-size:16px}.Heading--panel{font-size:1rem;font-weight:400;margin:0;text-transform:uppercase}.Icon{text-align:center;display:inline-block;-webkit-transition:.25s all cubic-bezier(.175,.885,.32,1.275);transition:.25s all cubic-bezier(.175,.885,.32,1.275)}.Icon.is-rotated{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.Icon-item{vertical-align:-25%;display:inline-block;width:1.5rem;height:1.5rem;fill:currentColor!important}.Icon--xlg .Icon-item{width:3rem;height:3rem}.Icon--lg .Icon-item{width:2.25rem;height:2.25rem}.Icon--sm .Icon-item{vertical-align:-15%;width:1.125rem;height:1.125rem}.Icon--xsm .Icon-item{vertical-align:-10%;width:.9375rem;height:.9375rem}.Icon--circle{position:relative;padding:.75rem}.Icon--circle:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;border:2px solid;opacity:.2;border-radius:3rem}.Icon--circle.Icon--lg{border-width:3px}.Icon--circle.Icon--xlg{border-width:3px;padding:1.125rem}.Icon--circle.Icon--lg{padding:.9375rem}.Icon--circle.Icon--sm{border-width:1px;padding:.5625rem}.Icon--circle.Icon--xsm{border-width:1px;padding:.375rem}.Icon--stroked .Icon-item{fill:none;stroke:currentColor;stroke-width:3}.Icon--loader .Icon-item{position:relative}.Icon--loader-dot{position:absolute;left:0;top:36.1%;display:inline-block;width:27.8%;height:27.8%;-webkit-animation:bouncedelay .9s infinite cubic-bezier(.175,.885,.32,1.275);animation:bouncedelay .9s infinite cubic-bezier(.175,.885,.32,1.275);border-radius:3rem;background-color:currentColor;-webkit-animation-fill-mode:both;animation-fill-mode:both}.Icon--loader-dot:nth-of-type(2){left:36.1%;-webkit-animation-delay:.15s;animation-delay:.15s}.Icon--loader-dot:nth-of-type(3){right:0;left:auto;-webkit-animation-delay:.3s;animation-delay:.3s}@-webkit-keyframes bouncedelay{0%,90%,100%{-webkit-transform:scale(0,0);transform:scale(0,0);opacity:.2}40%{-webkit-transform:scale(1,1);transform:scale(1,1);opacity:1}}@keyframes bouncedelay{0%,90%,100%{-webkit-transform:scale(0,0);transform:scale(0,0);opacity:.2}40%{-webkit-transform:scale(1,1);transform:scale(1,1);opacity:1}}.Link{color:#03A6D7;text-decoration:none;cursor:pointer}.Link:hover{color:#0395c2}.Link:active{color:#0285ac}.Link--invert{opacity:.8}.Link--invert,.Link--invert:hover{color:#fff!important}.Link--invert:hover{opacity:1}.Link--invert:active,.Link--invert.is-active{opacity:.6}.Link--success{color:#5CCA7B!important}.Link--success:hover{color:#408d56 !important}.Link--success:active,.Link--success.is-active{color:#255131 !important}.Link--unsure{color:#E9DF1B!important}.Link--unsure:hover{color:#a39c13 !important}.Link--unsure:active,.Link--unsure.is-active{color:#5d590b !important}.Link--neutral{color:#bcd4dc !important}.Link--neutral:hover{color:#84949a !important}.Link--neutral:active,.Link--neutral.is-active{color:#4b5558 !important}.Link--warning{color:#FFA800!important}.Link--warning:hover{color:#b37600 !important}.Link--warning:active,.Link--warning.is-active{color:#664300 !important}.Link--danger{color:#FF3B3D!important}.Link--danger:hover{color:#b3292b !important}.Link--danger:active,.Link--danger.is-active{color:#661818 !important}.Difference ins,.Difference del{padding:0 1px;border-radius:2px}.Difference ins{background-color:#c9eed3;text-decoration:none}.Difference del{background-color:#ffe0e0;text-decoration:none}.LogoLoader{position:relative;display:inline-block;margin-top:-.09375rem;width:2.4375rem;height:2.4375rem;color:#fff;border-radius:100px}.LogoLoader--inverted{color:#03A6D7}.LogoLoader-logo{-webkit-transition:all .25s cubic-bezier(.175,.885,.32,1.275);transition:all .25s cubic-bezier(.175,.885,.32,1.275);fill:currentColor}.LogoLoader-logo,.LogoLoader-svg{position:absolute;top:0;left:0;right:0;bottom:0}.LogoLoader-svg{overflow:visible}.LogoLoader path{-webkit-transform-origin:50% 50% 0;-ms-transform-origin:50% 50% 0;transform-origin:50% 50% 0}.LogoLoader:hover .LogoLoader-z{-webkit-animation:pop .3s cubic-bezier(.175,.885,.32,1.275);animation:pop .3s cubic-bezier(.175,.885,.32,1.275);-webkit-animation-iteration-count:2;animation-iteration-count:2;-webkit-animation-direction:alternate;animation-direction:alternate}.LogoLoader-z{-webkit-transform:scale(1,1);-ms-transform:scale(1,1);transform:scale(1,1);-webkit-transition:all .25s cubic-bezier(.175,.885,.32,1.275);transition:all .25s cubic-bezier(.175,.885,.32,1.275)}.LogoLoader .LogoLoader-logo{-webkit-transform-origin:50% 50% 0;-ms-transform-origin:50% 50% 0;transform-origin:50% 50% 0}.LogoLoader .LogoLoader-circle,.LogoLoader .LogoLoader-circlePulse{-webkit-transform:scale(1,1);-ms-transform:scale(1,1);transform:scale(1,1)}.LogoLoader.is-loading .LogoLoader-z,.LogoLoader.is-loading .LogoLoader-circle{-webkit-animation:pulseBegin 1s infinite linear;animation:pulseBegin 1s infinite linear}.LogoLoader.is-loading .LogoLoader-circle,.LogoLoader.is-loading .LogoLoader-circlePulse{-webkit-animation-delay:.1s;animation-delay:.1s}.LogoLoader.is-loading .LogoLoader-circlePulse{-webkit-animation:pulse 1s infinite linear;animation:pulse 1s infinite linear}@-webkit-keyframes pulseBegin{0%{-webkit-transform:scale(1);transform:scale(1)}20%{-webkit-transform:scale(.6);transform:scale(.6)}40%{-webkit-transform:scale(1.2);transform:scale(1.2)}60%,100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes pulseBegin{0%{-webkit-transform:scale(1);transform:scale(1)}20%{-webkit-transform:scale(.6);transform:scale(.6)}40%{-webkit-transform:scale(1.2);transform:scale(1.2)}60%,100%{-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes pulse{0%{opacity:0}0%,20%{-webkit-transform:scale(.6);transform:scale(.6)}40%{-webkit-transform:scale(1.2);transform:scale(1.2);opacity:.5}60%{-webkit-transform:scale(1.6);transform:scale(1.6);opacity:.7}100%{-webkit-transform:scale(2);transform:scale(2);opacity:0}}@keyframes pulse{0%{opacity:0}0%,20%{-webkit-transform:scale(.6);transform:scale(.6)}40%{-webkit-transform:scale(1.2);transform:scale(1.2);opacity:.5}60%{-webkit-transform:scale(1.6);transform:scale(1.6);opacity:.7}100%{-webkit-transform:scale(2);transform:scale(2);opacity:0}}@-webkit-keyframes pop{to{-webkit-transform:rotate(15deg)scale(1.1,1.1);transform:rotate(15deg)scale(1.1,1.1)}}@keyframes pop{to{-webkit-transform:rotate(15deg)scale(1.1,1.1);transform:rotate(15deg)scale(1.1,1.1)}}.Progressbar{position:relative;width:100%;height:.75rem;margin:0;background-color:#bcd4dc}.Progressbar--sm{height:.375rem}.Progressbar--lg{height:1.5rem}.Progressbar-item{position:absolute;left:0;z-index:100;margin:0;padding:0;width:100%;height:100%;list-style:none}.Progressbar-approved{background-color:#03A6D7;z-index:200}.Progressbar-translated{background-color:#5CCA7B}.Progressbar-needsWork{background-color:#E9DF1B}.Progressbar-untranslated{background-color:#bcd4dc}.Modal{position:fixed;z-index:1000;top:0;right:0;bottom:0;left:0;display:block;visibility:hidden;overflow:auto;overflow-y:scroll;width:100%;height:100%;margin:0 auto;padding:4.5rem 1.5rem 1.5rem;-webkit-transition:all .15s linear;transition:all .15s linear;opacity:0;background-color:rgba(236,239,240,.95);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:75rem;perspective:75rem;-webkit-overflow-scrolling:touch}.Modal.is-active{visibility:visible;opacity:1}.Modal-dialog{position:relative;width:90%;top:50%;left:50%;max-height:100%;min-width:300px;max-width:45rem;-webkit-transition:all .25s cubic-bezier(.175,.885,.32,1.1);transition:all .25s cubic-bezier(.175,.885,.32,1.1);-webkit-transform:translateX(-50%)translateY(100%);-ms-transform:translateX(-50%)translateY(100%);transform:translateX(-50%)translateY(100%);-webkit-transform-origin:0;-ms-transform-origin:0;transform-origin:0;background-color:#f7f9f9}.Modal.is-active .Modal-dialog{-webkit-transform:translateX(-50%)translateY(-50%);-ms-transform:translateX(-50%)translateY(-50%);transform:translateX(-50%)translateY(-50%)}.Modal-header{position:fixed;top:-3rem;left:0;right:0;border:1px solid rgba(32,113,138,.12);background-color:#fff;z-index:100}.Modal-title{font-size:1.375rem;font-weight:300;line-height:1.5rem;margin:0;padding:.75rem 2.25rem .75rem 1.5rem}.Modal-close{position:absolute;top:0;right:0;width:3rem;height:3rem;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;text-align:center;border-left:1px solid rgba(32,113,138,.12)}.Modal-close:hover{background-color:rgba(32,113,138,.05)}.Modal-close:active{background-color:rgba(32,113,138,.1)}.Modal-content{position:relative;max-height:100%;overflow:auto;background-clip:padding-box;border:1px solid rgba(32,113,138,.12);border-top-color:transparent;border-bottom-width:2px}.Modal-container.is-modal{overflow:hidden;height:100%}.Toggle,.Toggle-label{cursor:pointer}.Toggle{position:relative;display:inline-block;min-width:2.0625rem;margin:0 .09375rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:top}.Toggle-checkbox{position:absolute;cursor:none;top:0;left:0;width:100%;height:100%;opacity:0}.Toggle-label{font-weight:600;display:block;position:relative;padding:0 .5625rem;-webkit-transition:all .25s cubic-bezier(.075,.82,.165,1);transition:all .25s cubic-bezier(.075,.82,.165,1);text-align:center;color:currentColor}.Toggle-fakeCheckbox{opacity:1;position:absolute;top:0;left:0;width:100%;height:100%;content:'';background-color:transparent}.Toggle:hover>.Toggle-fakeCheckbox,.Toggle-checkbox:focus~.Toggle-fakeCheckbox{background-color:currentColor;opacity:.2}.Toggle-checkbox:checked~.Toggle-label,.Toggle.is-active~.Toggle-label{color:#fff}.Toggle-checkbox:checked~.Toggle-fakeCheckbox,.Toggle.is-active>.Toggle-fakeCheckbox{background-color:currentColor}.Toggle:hover>.Toggle-checkbox:checked~.Toggle-fakeCheckbox,.Toggle-checkbox:checked:focus~.Toggle-fakeCheckbox,.Toggle.is-active:hover>.Toggle-fakeCheckbox{opacity:.8}.Switch{padding-left:2.25rem;position:relative}.Switch-checkbox{position:absolute;margin-left:-9999px;visibility:hidden}.Switch-label{cursor:pointer}.Switch-label:before,.Switch-label:after{content:'';position:absolute;left:0;top:50%;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);border-radius:1.125rem;-webkit-transition:all .2s ease-out;transition:all .2s ease-out}.Switch-label:before{width:1.875rem;height:1.125rem;background-color:#d2e3e8}.Switch-label:after{width:1.125rem;height:1.125rem;background-color:#79aab9;border:1px solid transparent;-webkit-transform:translateY(-50%)scale(.8,.8);-ms-transform:translateY(-50%)scale(.8,.8);transform:translateY(-50%)scale(.8,.8)}.Switch-labelText{color:#79aab9;font-size:.875rem}.Switch-checkbox:checked~.Switch-label:before{background-color:#20718A}.Switch-checkbox:checked~.Switch-label:after{background-color:#fff;-webkit-transform:translateY(-50%)translateX(66%)scale(.8,.8);-ms-transform:translateY(-50%)translateX(66%)scale(.8,.8);transform:translateY(-50%)translateX(66%)scale(.8,.8)}.Switch-checkbox:checked~.Switch-label .Switch-labelText{color:#20718A}.TransUnit{position:relative;margin:0;padding:0;cursor:text;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-bottom:1px solid rgba(32,113,138,.12)}.TransUnit:focus{outline:0;box-shadow:none}.TransUnit.is-focused{opacity:1!important;background-color:#fff}.TransUnit:before,.TransUnit:after{position:absolute;left:0;visibility:hidden;width:100%;height:1.5rem;content:'';-webkit-transform:scaleY(0);-ms-transform:scaleY(0);transform:scaleY(0);opacity:0;background-color:#fff;background-clip:padding-box}.TransUnit:before{box-shadow:0 -.375rem .75rem rgba(0,0,0,.04);-webkit-transform-origin:bottom;-ms-transform-origin:bottom;transform-origin:bottom;top:-1.125rem}.TransUnit:after{box-shadow:0 .375rem .75rem rgba(0,0,0,.04);-webkit-transform-origin:top;-ms-transform-origin:top;transform-origin:top;bottom:-1.125rem}.TransUnit.is-focused:before,.TransUnit.is-focused:after{visibility:visible;-webkit-transform:scaleY(1);-ms-transform:scaleY(1);transform:scaleY(1);opacity:1}.TransUnit.is-first{border-top:1px solid rgba(32,113,138,.12)}.TransUnit-container.is-unit-selected .TransUnit{opacity:.8}.TransUnit-panel{padding-top:1.125rem;padding-bottom:1.125rem;padding-left:1.5rem;list-style:none;vertical-align:top}.TransUnit-panel,.TransUnit-item{position:relative}.TransUnit-source{cursor:pointer;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.TransUnit.is-focused .TransUnit-source{cursor:text}.TransUnit-translation{cursor:text;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.TransUnit-panelHeader,.TransUnit-panelFooter,.TransUnit-itemHeader{position:absolute;z-index:200;right:0;left:0;width:100%;padding-right:1.125rem;padding-left:1.125rem;cursor:default}.TransUnit-panelHeader,.TransUnit-panelFooter{visibility:hidden;opacity:0}.TransUnit.is-focused .TransUnit-panelHeader,.TransUnit.is-focused .TransUnit-panelFooter{visibility:visible;opacity:1}.TransUnit-panelHeader{top:-1.125rem}.TransUnit-itemHeader{top:0}.TransUnit-panelFooter{bottom:-1.125rem}.TransUnit-heading{font-size:1rem;font-weight:400;line-height:2.25rem;display:inline-block;float:left;margin:0;vertical-align:top;text-transform:uppercase}.TransUnit-text{font-family:'Source Sans Pro',"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif;font-size:16px;line-height:1.5;overflow:hidden;width:100%;min-height:1.5rem;margin:0;padding:1.125rem;resize:none;-webkit-transition:height .1s cubic-bezier(.075,.82,.165,1);transition:height .1s cubic-bezier(.075,.82,.165,1);white-space:pre-wrap;word-wrap:break-word;-moz-tab-size:8;tab-size:8;color:#444c54;border:none;background-color:transparent;box-shadow:none;-webkit-appearance:none}.TransUnit-text:focus{border:none;outline:none;background-color:transparent}.TransUnit-text span{font-weight:400!important;font-style:normal!important}.TransUnit-status,.TransUnit-status:before,.TransUnit-status:after{position:absolute;z-index:200;left:0;width:1.5rem;background-color:#bcd4dc}.TransUnit-status{top:-1;bottom:-1}.TransUnit-status.is-loading,.TransUnit-status.is-loading:before{background-image:-webkit-repeating-linear-gradient(45deg,transparent,transparent .375rem,rgba(255,255,255,.5).375rem,rgba(255,255,255,.5).75rem);background-image:repeating-linear-gradient(45deg,transparent,transparent .375rem,rgba(255,255,255,.5).375rem,rgba(255,255,255,.5).75rem);background-size:100% 1000px;-webkit-animation:loading 7s linear;animation:loading 7s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.TransUnit-status:before{top:-1.125rem;height:100%;padding-top:1.125rem;padding-bottom:1.125rem;box-sizing:content-box;z-index:300;visibility:hidden;content:'';-webkit-transform:scaleY(.65);-ms-transform:scaleY(.65);transform:scaleY(.65)}.TransUnit-status:after{-webkit-transition:none;transition:none;z-index:400;top:0;box-sizing:content-box;width:1.3125rem;height:100%;content:'';background-color:#fff;opacity:.6}.TransUnit.is-focused .TransUnit-status:before{visibility:visible;-webkit-transform:scaleY(1);-ms-transform:scaleY(1);transform:scaleY(1)}.TransUnit.is-focused .TransUnit-status:after{top:-1.125rem;padding-top:1.125rem;padding-bottom:1.125rem;opacity:.8}.TransUnit-metaData{position:relative;right:0;left:0;height:100%;text-align:center}.TransUnit-metaDataItem{line-height:1.125rem}.TransUnit-metaDataButton{font-size:.75rem;line-height:.6000000000000001rem;opacity:.8}.TransUnit-metaDataButton:hover{opacity:1}.TransUnit-metaDataComments,.TransUnit-metaDataErrors{position:absolute;right:0;left:0}.TransUnit-metaDataComments{top:1.125rem}.TransUnit-metaDataErrors{top:2.625rem}.TransUnit-metaDataErrorsIcon{text-shadow:0 0 2px #fff}@media (min-width:0) and (max-width:740px){.TransUnit-Heading,.TransUnit-panelFooterLeftNav{padding-left:1.5rem}}@media (min-width:741px){.TransUnit,.TransUnit-item{display:table;width:100%;table-layout:fixed}.TransUnit-panel{display:table-cell;width:50%;padding-left:0}.TransUnit-source{padding-right:.75rem}.TransUnit-translation{padding-left:.75rem}.TransUnit-panelHeader--source,.TransUnit-panelFooter--source{padding-right:1.875rem;right:auto}.TransUnit-panelHeader--translation,.TransUnit-panelFooter--translation{padding-left:1.875rem;left:auto}.TransUnit-status,.TransUnit-status:before,.TransUnit-status:after{left:50%;margin-left:-.75rem}}.TransUnit--highlight .TransUnit-status,.TransUnit--highlight .TransUnit-status:before{background-color:#03A6D7}.TransUnit--success .TransUnit-status,.TransUnit--success .TransUnit-status:before{background-color:#5CCA7B}.TransUnit--unsure .TransUnit-status,.TransUnit--unsure .TransUnit-status:before{background-color:#E9DF1B}.TransUnit--warning .TransUnit-status,.TransUnit--warning .TransUnit-status:before{background-color:#FFA800}.TransUnit--danger .TransUnit-status,.TransUnit--danger .TransUnit-status:before{background-color:#FF3B3D}.TransUnit--secondary .TransUnit-status,.TransUnit--secondary .TransUnit-status:before{background-color:#20718A}.TransUnit--suggestion:hover{background-color:#fbfcfc}.TransUnit--suggestion .TransUnit-panel{padding-top:.75rem;padding-bottom:.75rem}.TransUnit--suggestion .TransUnit-item,.TransUnit--suggestion .TransUnit-details{padding-left:1.125rem;padding-right:1.125rem}.TransUnit--suggestion .TransUnit-itemHeader{margin-top:-1.125rem}.TransUnit--suggestion .TransUnit-item:nth-last-child(n+3):first-child,.TransUnit--suggestion .TransUnit-item:nth-last-child(n+3):first-child~.TransUnit-item{margin-top:1.5rem}.TransUnit--suggestion .TransUnit-details{margin-top:.375rem}@-webkit-keyframes loading{0%{background-position:0 0}100%{background-position:0 1000px}}@keyframes loading{0%{background-position:0 0}100%{background-position:0 1000px}}.Resizer{position:absolute;z-index:1000}.Resizer--vertical{cursor:ew-resize;width:12px;top:0;bottom:0}.Resizer--horizontal{height:12px;left:0;right:0;cursor:ns-resize}.Editor{overflow:hidden}.Editor-header,.Editor-loader{-webkit-transition:.2s all cubic-bezier(.175,.885,.32,1.275);transition:.2s all cubic-bezier(.175,.885,.32,1.275)}.Editor-header.is-minimised{-webkit-transform:translateY(-3rem);-ms-transform:translateY(-3rem);transform:translateY(-3rem)}.Editor-header.is-minimised .Editor-mainNav{visibility:hidden}.Editor-loader{position:absolute;z-index:900;top:0;left:50%;-webkit-transform:translate(-50%,.375rem);-ms-transform:translate(-50%,.375rem);transform:translate(-50%,.375rem)}.Editor-loader.is-minimised{-webkit-transform:translateX(-50%)scale(.75,.75);-ms-transform:translateX(-50%)scale(.75,.75);transform:translateX(-50%)scale(.75,.75)}.Editor-mainNav{height:3rem}.Editor-content{position:absolute;left:0;right:0;top:5.625rem;bottom:0;overflow:auto;overflow-y:scroll;width:100%;-webkit-overlow-scrolling:touch}.Editor-content.is-maximised{top:2.625rem}.Editor-translationsWrapper{height:100%}.Editor-translations{min-height:100%;padding:3rem 0;background:-webkit-linear-gradient(left,#d7e5ea ,#d7e5ea);background:linear-gradient(left,#d7e5ea ,#d7e5ea);background-position:left center;background-size:1.5rem 100%;background-repeat:no-repeat}.Editor-currentDoc,.Editor-currentLang{max-width:5.25rem}.Editor-suggestions{position:fixed;z-index:200;right:0;bottom:0;left:0;overflow:hidden;box-shadow:0 -.1875rem 1.5rem rgba(0,0,0,.1)}.Editor-suggestionsHeader{position:absolute;top:0;left:0;right:0;box-shadow:0 1px 1px rgba(0,0,0,.1);z-index:300;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.Editor-suggestionsBody{position:absolute;top:2.25rem;left:0;right:0;bottom:0;overflow:scroll;overflow-x:hidden;overflow-y:scroll}.Editor-suggestions.is-search-active .Editor-suggestionsBody{top:4.5rem}.Editor-suggestionsSearch{clear:both}@media (min-width:0) and (max-width:740px){.Editor-currentProject{max-width:5.25rem}}@media (min-width:741px){.Editor-translations{background:-webkit-linear-gradient(left,#d7e5ea ,#d7e5ea),-webkit-linear-gradient(left,#ECEFF0,#ECEFF0);background:linear-gradient(to right,#d7e5ea ,#d7e5ea),linear-gradient(to right,#ECEFF0,#ECEFF0);background-position:center,right;background-size:1.5rem 100%,50% 100%;background-repeat:no-repeat}}.fade{-webkit-transition:opacity .25s easein;transition:opacity .25s easein;opacity:0}.fade.in{opacity:1}[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none!important} \ No newline at end of file +.u-cf:before,.u-cf:after{content:" ";display:table}.u-cf:after{clear:both}.u-nbfc{overflow:hidden!important}.u-nbfcAlt{display:table-cell!important;width:10000px!important}.u-floatLeft{float:left!important}.u-floatRight{float:right!important}@media (min-width:0) and (max-width:740px){.u-sm-floatNone{clear:both;float:none!important}}.u-bgFaint{opacity:.9}.u-bgHigh{background-color:#f5f6f7}.u-bgHigher{background-color:#fbfcfc}.u-bgHighest{background-color:#fff}.u-bgLow{background-color:#e5e8e9}.u-bgLower{background-color:#dee1e2}.u-bgLowest{background-color:#d7d9da}.u-bgPop{border:1px solid rgba(32,113,138,.12);border-bottom-width:2px}.u-bgNeutral{background-color:#ECEFF0}.u-bgPrimary{background-color:#03A6D7}.u-bgSecondary{background-color:#20718A}.u-bgDanger{background-color:#ffd8d8}.u-bgWarning{background-color:#fec}.u-bgUnsure{background-color:#fdfce8}.u-bgSuccess{background-color:#def4e5}.u-bgHighlight{background-color:#cdedf7}.u-block{display:block!important}.u-hidden{display:none!important}.u-hiddenVisually{position:absolute!important;overflow:hidden!important;width:1px!important;height:1px!important;padding:0!important;border:0!important;clip:rect(1px,1px,1px,1px)!important}.u-inline{display:inline!important}.u-inlineBlock{display:inline-block!important;max-width:100%}.u-table{display:table!important}.u-tableCell{display:table-cell!important}.u-tableRow{display:table-row!important}.u-round{border-radius:1000px!important}.u-round,.u-roundish{overflow:hidden!important}.u-roundish{border-radius:.5625rem!important}.u-rounded{border-radius:1.125rem!important;overflow:hidden!important}@media (min-width:0) and (max-width:740px){.u-sm-hidden{display:none!important}}@media (min-width:741px) and (max-width:960px){.u-md-hidden{display:none!important}}@media (max-width:960px){.u-ltemd-hidden{display:none!important}}@media (min-width:741px){.u-gtemd-hidden{display:none!important}}@media (min-width:961px) and (max-width:1270px){.u-lg-hidden{display:none!important}}@media (min-width:961px){.u-gtelg-hidden{display:none!important}}.u-dlUnstyled>dd{margin-left:0}.u-dlInline dt,.u-dlInline dd{display:inline-block;margin-right:.1875rem;margin-left:0}.u-listUnstyled{margin-bottom:0;padding-left:0;list-style:none}.u-listUnstyled>li{margin-left:0;list-style:none}.u-listInline{margin-left:-.1875rem;padding-left:0;list-style:none}.u-listInline>li{display:inline-block;padding-right:.1875rem;padding-left:.1875rem}.u-listHorizontal{font-size:0;list-style:none}.u-listHorizontal>li{font-size:1rem;display:inline-block;vertical-align:top}.u-posAbsolute,.u-posAbsoluteCenter{position:absolute!important}.u-posAbsoluteCenter{bottom:0!important;left:0!important;margin:auto!important;right:0!important;top:0!important}.u-posFixed{position:fixed!important;-webkit-backface-visibility:hidden;backface-visibility:hidden}.u-posRelative{position:relative!important}.u-posStatic{position:static!important}.u-posAbsoluteLeft{left:0!important}.u-posAbsoluteLeft,.u-posAbsoluteRight{bottom:0!important;position:absolute!important;top:0!important}.u-posAbsoluteRight{right:0!important}.u-posCenterCenter{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.u-sizeFit{display:block!important;float:left!important;width:auto!important}.u-sizeFitAlt{float:right!important}.u-sizeFitAlt,.u-sizeFill{display:block!important;width:auto!important}.u-sizeFill{overflow:hidden!important}.u-sizeFillAlt{display:table-cell!important;max-width:100%!important;width:10000px!important}.u-sizeFull{box-sizing:border-box!important;display:block!important;width:100%!important}.u-size1of12{width:8.333333333333332%!important}.u-size1of10{width:10%!important}.u-size1of8{width:12.5%!important}.u-size1of6,.u-size2of12{width:16.666666666666664%!important}.u-size1of5,.u-size2of10{width:20%!important}.u-size1of4,.u-size2of8,.u-size3of12{width:25%!important}.u-size3of10{width:30%!important}.u-size1of3,.u-size2of6,.u-size4of12{width:33.33333333333333%!important}.u-size3of8{width:37.5%!important}.u-size2of5,.u-size4of10{width:40%!important}.u-size5of12{width:41.66666666666667%!important}.u-size1of2,.u-size2of4,.u-size3of6,.u-size4of8,.u-size5of10,.u-size6of12{width:50%!important}.u-size7of12{width:58.333333333333336%!important}.u-size3of5,.u-size6of10{width:60%!important}.u-size5of8{width:62.5%!important}.u-size2of3,.u-size4of6,.u-size8of12{width:66.66666666666666%!important}.u-size7of10{width:70%!important}.u-size3of4,.u-size6of8,.u-size9of12{width:75%!important}.u-size4of5,.u-size8of10{width:80%!important}.u-size5of6,.u-size10of12{width:83.33333333333334%!important}.u-size7of8{width:87.5%!important}.u-size9of10{width:90%!important}.u-size11of12{width:91.66666666666666%!important}@media (min-width:0) and (max-width:740px){.u-sm-sizeFit{display:block!important;float:left!important;width:auto!important}.u-sm-sizeFitAlt{float:right!important}.u-sm-sizeFitAlt,.u-sm-sizeFill{display:block!important;width:auto!important}.u-sm-sizeFill{overflow:hidden!important}.u-sm-sizeFillAlt{display:table-cell!important;max-width:100%!important;width:10000px!important}.u-sm-sizeFull{box-sizing:border-box!important;display:block!important;width:100%!important}.u-sm-size1of12{width:8.333333333333332%!important}.u-sm-size1of10{width:10%!important}.u-sm-size1of8{width:12.5%!important}.u-sm-size1of6,.u-sm-size2of12{width:16.666666666666664%!important}.u-sm-size1of5,.u-sm-size2of10{width:20%!important}.u-sm-size1of4,.u-sm-size2of8,.u-sm-size3of12{width:25%!important}.u-sm-size3of10{width:30%!important}.u-sm-size1of3,.u-sm-size2of6,.u-sm-size4of12{width:33.33333333333333%!important}.u-sm-size3of8{width:37.5%!important}.u-sm-size2of5,.u-sm-size4of10{width:40%!important}.u-sm-size5of12{width:41.66666666666667%!important}.u-sm-size1of2,.u-sm-size2of4,.u-sm-size3of6,.u-sm-size4of8,.u-sm-size5of10,.u-sm-size6of12{width:50%!important}.u-sm-size7of12{width:58.333333333333336%!important}.u-sm-size3of5,.u-sm-size6of10{width:60%!important}.u-sm-size5of8{width:62.5%!important}.u-sm-size2of3,.u-sm-size4of6,.u-sm-size8of12{width:66.66666666666666%!important}.u-sm-size7of10{width:70%!important}.u-sm-size3of4,.u-sm-size6of8,.u-sm-size9of12{width:75%!important}.u-sm-size4of5,.u-sm-size8of10{width:80%!important}.u-sm-size5of6,.u-sm-size10of12{width:83.33333333333334%!important}.u-sm-size7of8{width:87.5%!important}.u-sm-size9of10{width:90%!important}.u-sm-size11of12{width:91.66666666666666%!important}}@media (min-width:741px) and (max-width:960px){.u-md-sizeFit{display:block!important;float:left!important;width:auto!important}.u-md-sizeFitAlt{float:right!important}.u-md-sizeFitAlt,.u-md-sizeFill{display:block!important;width:auto!important}.u-md-sizeFill{overflow:hidden!important}.u-md-sizeFillAlt{display:table-cell!important;max-width:100%!important;width:10000px!important}.u-md-sizeFull{box-sizing:border-box!important;display:block!important;width:100%!important}.u-md-size1of12{width:8.333333333333332%!important}.u-md-size1of10{width:10%!important}.u-md-size1of8{width:12.5%!important}.u-md-size1of6,.u-md-size2of12{width:16.666666666666664%!important}.u-md-size1of5,.u-md-size2of10{width:20%!important}.u-md-size1of4,.u-md-size2of8,.u-md-size3of12{width:25%!important}.u-md-size3of10{width:30%!important}.u-md-size1of3,.u-md-size2of6,.u-md-size4of12{width:33.33333333333333%!important}.u-md-size3of8{width:37.5%!important}.u-md-size2of5,.u-md-size4of10{width:40%!important}.u-md-size5of12{width:41.66666666666667%!important}.u-md-size1of2,.u-md-size2of4,.u-md-size3of6,.u-md-size4of8,.u-md-size5of10,.u-md-size6of12{width:50%!important}.u-md-size7of12{width:58.333333333333336%!important}.u-md-size3of5,.u-md-size6of10{width:60%!important}.u-md-size5of8{width:62.5%!important}.u-md-size2of3,.u-md-size4of6,.u-md-size8of12{width:66.66666666666666%!important}.u-md-size7of10{width:70%!important}.u-md-size3of4,.u-md-size6of8,.u-md-size9of12{width:75%!important}.u-md-size4of5,.u-md-size8of10{width:80%!important}.u-md-size5of6,.u-md-size10of12{width:83.33333333333334%!important}.u-md-size7of8{width:87.5%!important}.u-md-size9of10{width:90%!important}.u-md-size11of12{width:91.66666666666666%!important}}@media (min-width:961px) and (max-width:1270px){.u-lg-sizeFit{display:block!important;float:left!important;width:auto!important}.u-lg-sizeFitAlt{float:right!important}.u-lg-sizeFitAlt,.u-lg-sizeFill{display:block!important;width:auto!important}.u-lg-sizeFill{overflow:hidden!important}.u-lg-sizeFillAlt{display:table-cell!important;max-width:100%!important;width:10000px!important}.u-lg-sizeFull{box-sizing:border-box!important;display:block!important;width:100%!important}.u-lg-size1of12{width:8.333333333333332%!important}.u-lg-size1of10{width:10%!important}.u-lg-size1of8{width:12.5%!important}.u-lg-size1of6,.u-lg-size2of12{width:16.666666666666664%!important}.u-lg-size1of5,.u-lg-size2of10{width:20%!important}.u-lg-size1of4,.u-lg-size2of8,.u-lg-size3of12{width:25%!important}.u-lg-size3of10{width:30%!important}.u-lg-size1of3,.u-lg-size2of6,.u-lg-size4of12{width:33.33333333333333%!important}.u-lg-size3of8{width:37.5%!important}.u-lg-size2of5,.u-lg-size4of10{width:40%!important}.u-lg-size5of12{width:41.66666666666667%!important}.u-lg-size1of2,.u-lg-size2of4,.u-lg-size3of6,.u-lg-size4of8,.u-lg-size5of10,.u-lg-size6of12{width:50%!important}.u-lg-size7of12{width:58.333333333333336%!important}.u-lg-size3of5,.u-lg-size6of10{width:60%!important}.u-lg-size5of8{width:62.5%!important}.u-lg-size2of3,.u-lg-size4of6,.u-lg-size8of12{width:66.66666666666666%!important}.u-lg-size7of10{width:70%!important}.u-lg-size3of4,.u-lg-size6of8,.u-lg-size9of12{width:75%!important}.u-lg-size4of5,.u-lg-size8of10{width:80%!important}.u-lg-size5of6,.u-lg-size10of12{width:83.33333333333334%!important}.u-lg-size7of8{width:87.5%!important}.u-lg-size9of10{width:90%!important}.u-lg-size11of12{width:91.66666666666666%!important}}.u-sizeWidth-1-4{width:.375rem!important}.u-sizeWidth-1-2{width:.75rem!important}.u-sizeWidth-3-4{width:1.125rem!important}.u-sizeWidth-1{width:1.5rem!important}.u-sizeWidth-1_1-2{width:2.25rem!important}.u-sizeWidth-2{width:3rem!important}.u-sizeWidth-6{width:9rem!important}.u-sizeHeight-1-4{height:.375rem!important}.u-sizeHeight-1-2{height:.75rem!important}.u-sizeHeight-3-4{height:1.125rem!important}.u-sizeHeight-1{height:1.5rem!important}.u-sizeHeight-1_1-4{height:1.875rem!important}.u-sizeHeight-1_1-2{height:2.25rem!important}.u-sizeHeight-2{height:3rem!important}.u-sizeLineHeight-1-4{line-height:.375rem!important}.u-sizeLineHeight-1-2{line-height:.75rem!important}.u-sizeLineHeight-3-4{line-height:1.125rem!important}.u-sizeLineHeight-1{line-height:1.5rem!important}.u-sizeLineHeight-1_1-4{line-height:1.875rem!important}.u-sizeLineHeight-1_1-2{line-height:2.25rem!important}.u-sizeLineHeight-2{line-height:3rem!important}.u-sM-1-8{margin:.1875rem!important}.u-sM-1-4{margin:.375rem!important}.u-sM-1-2{margin:.75rem!important}.u-sM-3-4{margin:1.125rem!important}.u-sM-1{margin:1.5rem!important}.u-sM-1_1-2{margin:2.25rem!important}.u-sM-2{margin:3rem!important}.u-sMT-1-8{margin-top:.1875rem!important}.u-sMT-1-4{margin-top:.375rem!important}.u-sMT-1-2{margin-top:.75rem!important}.u-sMT-3-4{margin-top:1.125rem!important}.u-sMT-1{margin-top:1.5rem!important}.u-sMT-1_1-2{margin-top:2.25rem!important}.u-sMT-2{margin-top:3rem!important}.u-sMR-1-4{margin-right:.375rem!important}.u-sMR-1-2{margin-right:.75rem!important}.u-sMR-3-4{margin-right:1.125rem!important}.u-sMR-1{margin-right:1.5rem!important}.u-sMR-1_1-2{margin-right:2.25rem!important}.u-sMR-2{margin-right:3rem!important}.u-sMB-1-4{margin-bottom:.375rem!important}.u-sMB-1-2{margin-bottom:.75rem!important}.u-sMB-3-4{margin-bottom:1.125rem!important}.u-sMB-1{margin-bottom:1.5rem!important}.u-sMB-1_1-2{margin-bottom:2.25rem!important}.u-sMB-2{margin-bottom:3rem!important}.u-sML-1-4{margin-left:.375rem!important}.u-sML-1-2{margin-left:.75rem!important}.u-sML-3-4{margin-left:1.125rem!important}.u-sML-1{margin-left:1.5rem!important}.u-sML-1_1-2{margin-left:2.25rem!important}.u-sML-2{margin-left:3rem!important}.u-sMV-1-4{margin-top:.375rem!important;margin-bottom:.375rem!important}.u-sMV-1-2{margin-top:.75rem!important;margin-bottom:.75rem!important}.u-sMV-3-4{margin-top:1.125rem!important;margin-bottom:1.125rem!important}.u-sMV-1{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.u-sMV-1_1-2{margin-top:2.25rem!important;margin-bottom:2.25rem!important}.u-sMV-2{margin-top:3rem!important;margin-bottom:3rem!important}.u-sMH-1-4{margin-left:.375rem!important;margin-right:.375rem!important}.u-sMH-1-2{margin-left:.75rem!important;margin-right:.75rem!important}.u-sMH-3-4{margin-left:1.125rem!important;margin-right:1.125rem!important}.u-sMH-1{margin-left:1.5rem!important;margin-right:1.5rem!important}.u-sMH-1_1-2{margin-left:2.25rem!important;margin-right:2.25rem!important}.u-sMH-2{margin-left:3rem!important;margin-right:3rem!important}.u-sP-1-4{padding:.375rem!important}.u-sP-1-2{padding:.75rem!important}.u-sP-3-4{padding:1.125rem!important}.u-sP-1{padding:1.5rem!important}.u-sP-1_1-2{padding:2.25rem!important}.u-sP-2{padding:3rem!important}.u-sPT-1-4{padding-top:.375rem!important}.u-sPT-1-2{padding-top:.75rem!important}.u-sPT-3-4{padding-top:1.125rem!important}.u-sPT-1{padding-top:1.5rem!important}.u-sPT-1_1-2{padding-top:2.25rem!important}.u-sPT-2{padding-top:3rem!important}.u-sPR-1-4{padding-right:.375rem!important}.u-sPR-1-2{padding-right:.75rem!important}.u-sPR-3-4{padding-right:1.125rem!important}.u-sPR-1{padding-right:1.5rem!important}.u-sPR-1_1-2{padding-right:2.25rem!important}.u-sPR-2{padding-right:3rem!important}.u-sPB-1-4{padding-bottom:.375rem!important}.u-sPB-1-2{padding-bottom:.75rem!important}.u-sPB-3-4{padding-bottom:1.125rem!important}.u-sPB-1{padding-bottom:1.5rem!important}.u-sPB-1_1-2{padding-bottom:2.25rem!important}.u-sPB-2{padding-bottom:3rem!important}.u-sPL-1-4{padding-left:.375rem!important}.u-sPL-1-2{padding-left:.75rem!important}.u-sPL-3-4{padding-left:1.125rem!important}.u-sPL-1{padding-left:1.5rem!important}.u-sPL-1_1-2{padding-left:2.25rem!important}.u-sPL-2{padding-left:3rem!important}.u-sPV-1-4{padding-top:.375rem!important;padding-bottom:.375rem!important}.u-sPV-1-2{padding-top:.75rem!important;padding-bottom:.75rem!important}.u-sPV-3-4{padding-top:1.125rem!important;padding-bottom:1.125rem!important}.u-sPV-1{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.u-sPV-1_1-2{padding-top:2.25rem!important;padding-bottom:2.25rem!important}.u-sPV-2{padding-top:3rem!important;padding-bottom:3rem!important}.u-sPH-1-4{padding-left:.375rem!important;padding-right:.375rem!important}.u-sPH-1-2{padding-left:.75rem!important;padding-right:.75rem!important}.u-sPH-3-4{padding-left:1.125rem!important;padding-right:1.125rem!important}.u-sPH-1{padding-left:1.5rem!important;padding-right:1.5rem!important}.u-sPH-1_1-2{padding-left:2.25rem!important;padding-right:2.25rem!important}.u-sPH-2{padding-left:3rem!important;padding-right:3rem!important}.u-textBreak{word-wrap:break-word!important}.u-textCenter{text-align:center!important}.u-textLeft{text-align:left!important}.u-textRight{text-align:right!important}.u-textInheritColor{color:inherit!important}.u-textKern{text-rendering:optimizeLegibility;-webkit-font-feature-settings:"kern" 1;-moz-font-feature-settings:"kern" 1;font-feature-settings:"kern" 1;-webkit-font-kerning:normal;-moz-font-kerning:normal;font-kerning:normal}.u-textNoWrap,.u-textTruncate{white-space:nowrap!important}.u-textTruncate{max-width:100%;overflow:hidden!important;text-overflow:ellipsis!important;word-wrap:normal!important}.u-textUpper{text-transform:uppercase}.u-textLower{text-transform:lowercase}.u-textCapitalize{text-transform:capitalize}.u-textInvert{color:#f2f2f2}.u-textMuted{opacity:.6}.u-textEmpty{color:#90b8c5;font-weight:600}.u-textMicro{font-size:.75rem}.u-textMini,.u-textMeta{font-size:.875rem}.u-textMeta{color:#90b8c5}.u-textLead{font-size:1.5rem;font-weight:300}.u-textPrimary{color:#03A6D7}.u-textSecondary{color:#20718A}.u-textHighlight{color:#03A6D7}.u-textSuccess{color:#5CCA7B}.u-textUnsure{color:#E9DF1B}.u-textNeutral{color:#bcd4dc}.u-textWarning{color:#FFA800}.u-textDanger{color:#FF3B3D}.u-textPilcrow:before{content:'\00b6';color:#bcd4dc;padding:0 .1875rem}.u-textTab{position:relative;display:inline-block;width:1.2em;text-align:center}.u-textTab:before{content:'\21E5';color:#bcd4dc}.u-textSpace{position:relative}.u-textSpace:before{position:absolute;content:'.';color:#bcd4dc}.Grid{display:block;font-size:0;margin:0;padding:0;text-align:left}.Grid--alignCenter{text-align:center}.Grid--alignRight{text-align:right}.Grid--alignMiddle>.Grid-cell{vertical-align:middle}.Grid--alignBottom>.Grid-cell{vertical-align:bottom}.Grid--withGutter{margin:0 -10px}.Grid--withGutter>.Grid-cell{padding:0 10px}.Grid-cell{box-sizing:border-box;display:inline-block;font-size:1rem;margin:0;padding:0;text-align:left;vertical-align:top;width:100%}.Grid-cell--center{display:block;margin:0 auto}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}hr{box-sizing:content-box;height:0}pre{overflow:auto}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}::-webkit-input-placeholder{color:#a9bfc6}:-moz-placeholder,::-moz-placeholder{color:#a9bfc6}:-ms-input-placeholder{color:#a9bfc6}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:none}button{background:0 0;border:0;padding:0;text-align:inherit}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button:hover,button:active{outline:none}fieldset{border:0;margin:0;padding:0}input[type="text"],input[type="password"],input[type="date"],input[type="datetime"],input[type="email"],input[type="number"],input[type="search"],input[type="tel"],input[type="time"],input[type="url"],textarea{-webkit-appearance:none;outline:none;box-sizing:border-box}@-ms-viewport{width:device-width}@viewport{width:device-width}html{font-family:'Source Sans Pro',"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif;font-size:16px;line-height:1.5;box-sizing:border-box;color:#444c54}*,*:before,*:after{box-sizing:inherit}body{min-width:320px;background:#ECEFF0}:active,:hover{outline:none}h1,h2,h3,h4,h5,h6{font-size:16px;margin:0;color:#20718A}a{color:#03A6D7;text-decoration:none;cursor:pointer}a:hover{color:#0395c2}a:active{color:#0285ac}ol,ul{list-style:none;margin:0;padding:0}ul:empty,ol:empty{display:none}img{max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}figcaption{color:gray}hr{width:100%;border-bottom:1px solid;border-color:rgba(32,113,138,.12);margin:rhythm(1)0;background:0 0}hr,iframe{border:0}[tabindex="-1"]:focus{outline:none!important}code{font-family:Monaco,Courier,monospace;margin:0;padding:0 .1875rem}code,kbd,pre{font-size:.8125rem;font-weight:400;color:#4d4d4d}pre,samp{font-family:Monaco,Courier,monospace;padding:0 .1875rem}samp{font-size:.8125rem;font-weight:400;color:#4d4d4d}samp,blockquote,dl,dd,p,pre{margin:0}kbd{margin:0 .1875rem;padding:.1875rem .5625rem;border:1px solid rgba(65,105,136,.07);border-bottom:3px solid rgba(65,105,136,.2);border-radius:.375rem;background-color:#fff;background-clip:padding;white-space:nowrap;display:inline-block;text-transform:uppercase;font-family:'Source Sans Pro',"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif}.ButtonGroup{display:block;font-size:0;margin:0;list-style:none;padding:0}.ButtonGroup-item{display:block;font-size:1rem}.ButtonGroup-item>.Button{display:block;width:100%}.ButtonGroup-item>.Button:hover,.ButtonGroup-item>.Button:focus,.ButtonGroup-item>.Button:active,.ButtonGroup-item>.Button.is-pressed{z-index:1}.ButtonGroup--hz>.ButtonGroup-item{display:inline-block}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item{margin-top:0}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item:first-child{margin-top:0}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item:not(:first-child):not(:last-child)>.Button{border-radius:0}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item:first-child:not(:only-child)>.Button{border-bottom-left-radius:0;border-bottom-right-radius:0}.ButtonGroup--borderCollapse:not(.ButtonGroup--hz)>.ButtonGroup-item:last-child:not(:only-child)>.Button{border-top-left-radius:0;border-top-right-radius:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item{margin-left:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item:first-child{margin-left:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item:not(:first-child):not(:last-child)>.Button{border-radius:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item:first-child:not(:only-child)>.Button{border-bottom-right-radius:0;border-top-right-radius:0}.ButtonGroup--borderCollapse.ButtonGroup--hz>.ButtonGroup-item:last-child:not(:only-child)>.Button{border-bottom-left-radius:0;border-top-left-radius:0}.ButtonGroup--hz>.ButtonGroup-item{vertical-align:middle}.ButtonGroup--round .ButtonGroup-item:first-child .Button{border-top-left-radius:100px;border-bottom-left-radius:100px}.ButtonGroup--round .ButtonGroup-item:last-child .Button{border-top-right-radius:100px;border-bottom-right-radius:100px}.Button{-webkit-appearance:none;background:0 0;border-color:currentcolor;border-style:solid;border-width:0;box-sizing:border-box;color:transparent;cursor:pointer;display:inline-block;font:inherit;line-height:normal;margin:0;padding:.1875rem .75rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:normal}.Button::-moz-focus-inner{border:0;padding:0}.Button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.Button:hover,.Button:focus,.Button:active{text-decoration:none}.Button:disabled,.Button.is-disabled{cursor:default;opacity:.6}.Button{-webkit-transition:all .25s cubic-bezier(.075,.82,.165,1);transition:all .25s cubic-bezier(.075,.82,.165,1);min-height:1.875rem;text-shadow:1px 1px 0 rgba(0,0,0,.15)}.Button:disabled,.Button.is-disabled{pointer-events:none}.Button:hover,.Button:active,.Button.is-active{outline:inherit}.Button--default{background-color:#deeaed}.Button--default:hover{background-color:#c8d3d5}.Button--default:active,.Button--default.is-active{background-color:#bdc7c9}.Button--primary{color:#fff;background-color:#03A6D7}.Button--primary:hover{background-color:#0395c2}.Button--primary:active,.Button--primary.is-active{background-color:#038db7}.Button--secondary{color:#fff;background-color:#20718A}.Button--secondary:hover{background-color:#1d667c}.Button--secondary:active,.Button--secondary.is-active{background-color:#1b6075}.Button--highlight{color:#fff;background-color:#03A6D7}.Button--highlight:hover{background-color:#0395c2}.Button--highlight:active,.Button--highlight.is-active{background-color:#038db7}.Button--success{color:#fff;background-color:#5CCA7B}.Button--success:hover{background-color:#53b66f}.Button--success:active,.Button--success.is-active{background-color:#4eac69}.Button--unsure{color:#fff;background-color:#E9DF1B}.Button--unsure:hover{background-color:#d2c918}.Button--unsure:active,.Button--unsure.is-active{background-color:#c6be17}.Button--neutral{color:#fff;background-color:#90b8c5}.Button--neutral:hover{background-color:#82a6b1}.Button--neutral:active,.Button--neutral.is-active{background-color:#7a9ca7}.Button--warning{color:#fff;background-color:#FFA800}.Button--warning:hover{background-color:#e69700}.Button--warning:active,.Button--warning.is-active{background-color:#d98f00}.Button--danger{color:#fff;background-color:#FF3B3D}.Button--danger:hover{background-color:#e63537}.Button--danger:active,.Button--danger.is-active{background-color:#d93234}.Button--invisible{background-color:transparent;color:#90b8c5}.Button--invisible:hover{background-color:#edf4f6;color:#639cad}.Button--invisible:active,.Button--invisible.is-active{color:#20718A;background-color:#e4eef1}.Button--snug{padding-left:.375rem;padding-right:.375rem}.Button--small{min-height:1.5rem;padding:.1875rem .75rem;font-size:.875rem}.InputGroup{position:relative;display:table;border-collapse:separate}.InputGroup-input,.InputGroup-addon,.InputGroup-button{display:table-cell;-webkit-transition:.2s cubic-bezier(.26,.47,.36,.94);transition:.2s cubic-bezier(.26,.47,.36,.94)}.InputGroup-input,.InputGroup-addon{background-color:transparent;padding:0 .375rem}.InputGroup-input{border:none;width:100%;color:#20718A}.InputGroup-addon{color:#639cad;text-align:center;width:1%;white-space:nowrap;vertical-align:middle}.InputGroup-addon:before{content:' '}.InputGroup.is-focused .InputGroup-input,.InputGroup.is-focused .InputGroup-addon{background-color:#fff}.InputGroup--bordered .InputGroup-input,.InputGroup--bordered .InputGroup-addon,.InputGroup--outlined .InputGroup-input,.InputGroup--outlined .InputGroup-addon{border-top:1px solid #a6c6d0;border-bottom:1px solid #a6c6d0}.InputGroup--bordered .InputGroup-input{border-left:1px solid #a6c6d0;border-right:1px solid #a6c6d0}.InputGroup--outlined .InputGroup-input:first-child,.InputGroup--outlined .InputGroup-addon:first-child{border-left:1px solid #a6c6d0}.InputGroup--outlined .InputGroup-input:last-child,.InputGroup--outlined .InputGroup-addon:last-child{border-right:1px solid #a6c6d0}.InputGroup--bordered.is-focused .InputGroup-input,.InputGroup--bordered.is-focused .InputGroup-addon,.InputGroup--outlined.is-focused .InputGroup-input,.InputGroup--outlined.is-focused .InputGroup-addon{border-color:#639cad}.InputGroup--rounded .InputGroup-input:first-child,.InputGroup--rounded .InputGroup-addon:first-child{border-bottom-left-radius:1.5rem;border-top-left-radius:1.5rem;padding-left:.5625rem}.InputGroup--rounded .InputGroup-input:last-child,.InputGroup--rounded .InputGroup-addon:last-child{border-bottom-right-radius:1.5rem;border-top-right-radius:1.5rem;padding-right:.5625rem}.Dropdown{position:relative;z-index:100;display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.Dropdown.is-active{z-index:900}.Dropdown-toggleIcon{display:inline-block;-webkit-transition:all .2s cubic-bezier(.175,.885,.32,1.275);transition:all .2s cubic-bezier(.175,.885,.32,1.275);text-align:center}.Dropdown.is-active .Dropdown-toggleIcon{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.Dropdown-content{position:absolute;z-index:800;top:100%;left:0;visibility:hidden;float:left;overflow:auto;overflow-x:hidden;overflow-y:auto;max-height:25.5rem;min-width:100%;margin:0;padding:0;-webkit-transition:all .2s cubic-bezier(.175,.885,.32,1.275);transition:all .2s cubic-bezier(.175,.885,.32,1.275);-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);text-align:left;opacity:0;background-color:#fff;background-clip:padding-box;box-shadow:0 0 1.5rem rgba(0,0,0,.1)}.Dropdown-content--Bordered{border:1px solid rgba(32,113,138,.12);border-bottom-width:2px}.Dropdown.is-active>.Dropdown-content{visibility:visible;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);opacity:1}.Dropdown-title{font-size:.875rem;padding:.1875rem .375rem;background-color:transparent;border-bottom:1px solid rgba(32,113,138,.12);color:#79aab9}.Dropdown-item{display:block;line-height:1.3125rem;padding:.375rem;-webkit-transition:all .2s ease-out;transition:all .2s ease-out;color:#03A6D7}.Dropdown-item:hover{color:#fff;background-color:#03A6D7}.Dropdown--right .Dropdown-content{left:auto;right:0}.Header{background-color:#03A6D7;position:fixed;z-index:100;width:100%;top:0;box-shadow:0 .375rem 1.5rem rgba(0,0,0,.1)}.Header-item{height:3rem;display:inline-block;padding-top:.75rem;padding-bottom:.75rem}.Header-avatar{margin-top:.5625rem;margin-bottom:.5625rem;width:1.875rem;height:1.875rem;display:inline-block}.h1,.h2,.h3,.h4,.h5,.h6{font-family:'Source Sans Pro',"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif;font-weight:600}.h1{font-size:36px}.h2{font-size:28px}.h3{font-size:24px}.h4{font-size:20px}.h5{font-size:18px}.h6{font-size:16px}.Heading--panel{font-size:1rem;font-weight:400;margin:0;text-transform:uppercase}.Icon{text-align:center;display:inline-block;-webkit-transition:.25s all cubic-bezier(.175,.885,.32,1.275);transition:.25s all cubic-bezier(.175,.885,.32,1.275)}.Icon.is-rotated{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.Icon-item{vertical-align:-25%;display:inline-block;width:1.5rem;height:1.5rem;fill:currentColor!important}.Icon--xlg .Icon-item{width:3rem;height:3rem}.Icon--lg .Icon-item{width:2.25rem;height:2.25rem}.Icon--sm .Icon-item{vertical-align:-15%;width:1.125rem;height:1.125rem}.Icon--xsm .Icon-item{vertical-align:-10%;width:.9375rem;height:.9375rem}.Icon--circle{position:relative;padding:.75rem}.Icon--circle:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;border:2px solid;opacity:.2;border-radius:3rem}.Icon--circle.Icon--lg{border-width:3px}.Icon--circle.Icon--xlg{border-width:3px;padding:1.125rem}.Icon--circle.Icon--lg{padding:.9375rem}.Icon--circle.Icon--sm{border-width:1px;padding:.5625rem}.Icon--circle.Icon--xsm{border-width:1px;padding:.375rem}.Icon--stroked .Icon-item{fill:none;stroke:currentColor;stroke-width:3}.Icon--loader .Icon-item{position:relative}.Icon--loader-dot{position:absolute;left:0;top:36.1%;display:inline-block;width:27.8%;height:27.8%;-webkit-animation:bouncedelay .9s infinite cubic-bezier(.175,.885,.32,1.275);animation:bouncedelay .9s infinite cubic-bezier(.175,.885,.32,1.275);border-radius:3rem;background-color:currentColor;-webkit-animation-fill-mode:both;animation-fill-mode:both}.Icon--loader-dot:nth-of-type(2){left:36.1%;-webkit-animation-delay:.15s;animation-delay:.15s}.Icon--loader-dot:nth-of-type(3){right:0;left:auto;-webkit-animation-delay:.3s;animation-delay:.3s}@-webkit-keyframes bouncedelay{0%,90%,100%{-webkit-transform:scale(0,0);transform:scale(0,0);opacity:.2}40%{-webkit-transform:scale(1,1);transform:scale(1,1);opacity:1}}@keyframes bouncedelay{0%,90%,100%{-webkit-transform:scale(0,0);transform:scale(0,0);opacity:.2}40%{-webkit-transform:scale(1,1);transform:scale(1,1);opacity:1}}.Link{color:#03A6D7;text-decoration:none;cursor:pointer}.Link:hover{color:#0395c2}.Link:active{color:#0285ac}.Link--invert{opacity:.8}.Link--invert,.Link--invert:hover{color:#fff!important}.Link--invert:hover{opacity:1}.Link--invert:active,.Link--invert.is-active{opacity:.6}.Link--success{color:#5CCA7B!important}.Link--success:hover{color:#408d56 !important}.Link--success:active,.Link--success.is-active{color:#255131 !important}.Link--unsure{color:#E9DF1B!important}.Link--unsure:hover{color:#a39c13 !important}.Link--unsure:active,.Link--unsure.is-active{color:#5d590b !important}.Link--neutral{color:#bcd4dc !important}.Link--neutral:hover{color:#84949a !important}.Link--neutral:active,.Link--neutral.is-active{color:#4b5558 !important}.Link--warning{color:#FFA800!important}.Link--warning:hover{color:#b37600 !important}.Link--warning:active,.Link--warning.is-active{color:#664300 !important}.Link--danger{color:#FF3B3D!important}.Link--danger:hover{color:#b3292b !important}.Link--danger:active,.Link--danger.is-active{color:#661818 !important}.Difference ins,.Difference del{padding:0 1px;border-radius:2px}.Difference ins{background-color:#c9eed3;text-decoration:none}.Difference del{background-color:#ffe0e0;text-decoration:none}.LogoLoader{position:relative;display:inline-block;margin-top:-.09375rem;width:2.4375rem;height:2.4375rem;color:#fff;border-radius:100px}.LogoLoader--inverted{color:#03A6D7}.LogoLoader-logo{-webkit-transition:all .25s cubic-bezier(.175,.885,.32,1.275);transition:all .25s cubic-bezier(.175,.885,.32,1.275);fill:currentColor}.LogoLoader-logo,.LogoLoader-svg{position:absolute;top:0;left:0;right:0;bottom:0}.LogoLoader-svg{overflow:visible}.LogoLoader path{-webkit-transform-origin:50% 50% 0;-ms-transform-origin:50% 50% 0;transform-origin:50% 50% 0}.LogoLoader:hover .LogoLoader-z{-webkit-animation:pop .3s cubic-bezier(.175,.885,.32,1.275);animation:pop .3s cubic-bezier(.175,.885,.32,1.275);-webkit-animation-iteration-count:2;animation-iteration-count:2;-webkit-animation-direction:alternate;animation-direction:alternate}.LogoLoader-z{-webkit-transform:scale(1,1);-ms-transform:scale(1,1);transform:scale(1,1);-webkit-transition:all .25s cubic-bezier(.175,.885,.32,1.275);transition:all .25s cubic-bezier(.175,.885,.32,1.275)}.LogoLoader .LogoLoader-logo{-webkit-transform-origin:50% 50% 0;-ms-transform-origin:50% 50% 0;transform-origin:50% 50% 0}.LogoLoader .LogoLoader-circle,.LogoLoader .LogoLoader-circlePulse{-webkit-transform:scale(1,1);-ms-transform:scale(1,1);transform:scale(1,1)}.LogoLoader.is-loading .LogoLoader-z,.LogoLoader.is-loading .LogoLoader-circle{-webkit-animation:pulseBegin 1s infinite linear;animation:pulseBegin 1s infinite linear}.LogoLoader.is-loading .LogoLoader-circle,.LogoLoader.is-loading .LogoLoader-circlePulse{-webkit-animation-delay:.1s;animation-delay:.1s}.LogoLoader.is-loading .LogoLoader-circlePulse{-webkit-animation:pulse 1s infinite linear;animation:pulse 1s infinite linear}@-webkit-keyframes pulseBegin{0%{-webkit-transform:scale(1);transform:scale(1)}20%{-webkit-transform:scale(.6);transform:scale(.6)}40%{-webkit-transform:scale(1.2);transform:scale(1.2)}60%,100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes pulseBegin{0%{-webkit-transform:scale(1);transform:scale(1)}20%{-webkit-transform:scale(.6);transform:scale(.6)}40%{-webkit-transform:scale(1.2);transform:scale(1.2)}60%,100%{-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes pulse{0%{opacity:0}0%,20%{-webkit-transform:scale(.6);transform:scale(.6)}40%{-webkit-transform:scale(1.2);transform:scale(1.2);opacity:.5}60%{-webkit-transform:scale(1.6);transform:scale(1.6);opacity:.7}100%{-webkit-transform:scale(2);transform:scale(2);opacity:0}}@keyframes pulse{0%{opacity:0}0%,20%{-webkit-transform:scale(.6);transform:scale(.6)}40%{-webkit-transform:scale(1.2);transform:scale(1.2);opacity:.5}60%{-webkit-transform:scale(1.6);transform:scale(1.6);opacity:.7}100%{-webkit-transform:scale(2);transform:scale(2);opacity:0}}@-webkit-keyframes pop{to{-webkit-transform:rotate(15deg)scale(1.1,1.1);transform:rotate(15deg)scale(1.1,1.1)}}@keyframes pop{to{-webkit-transform:rotate(15deg)scale(1.1,1.1);transform:rotate(15deg)scale(1.1,1.1)}}.Progressbar{position:relative;width:100%;height:.75rem;margin:0;background-color:#bcd4dc}.Progressbar--sm{height:.375rem}.Progressbar--lg{height:1.5rem}.Progressbar-item{position:absolute;left:0;z-index:100;margin:0;padding:0;width:100%;height:100%;list-style:none}.Progressbar-approved{background-color:#03A6D7;z-index:200}.Progressbar-translated{background-color:#5CCA7B}.Progressbar-needsWork{background-color:#E9DF1B}.Progressbar-untranslated{background-color:#bcd4dc}.Modal{position:fixed;z-index:1000;top:0;right:0;bottom:0;left:0;display:block;visibility:hidden;overflow:auto;overflow-y:scroll;width:100%;height:100%;margin:0 auto;padding:4.5rem 1.5rem 1.5rem;-webkit-transition:all .15s linear;transition:all .15s linear;opacity:0;background-color:rgba(236,239,240,.95);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:75rem;perspective:75rem;-webkit-overflow-scrolling:touch}.Modal.is-active{visibility:visible;opacity:1}.Modal-dialog{position:relative;width:90%;top:50%;left:50%;max-height:100%;min-width:300px;max-width:45rem;-webkit-transition:all .25s cubic-bezier(.175,.885,.32,1.1);transition:all .25s cubic-bezier(.175,.885,.32,1.1);-webkit-transform:translateX(-50%)translateY(100%);-ms-transform:translateX(-50%)translateY(100%);transform:translateX(-50%)translateY(100%);-webkit-transform-origin:0;-ms-transform-origin:0;transform-origin:0;background-color:#f7f9f9}.Modal.is-active .Modal-dialog{-webkit-transform:translateX(-50%)translateY(-50%);-ms-transform:translateX(-50%)translateY(-50%);transform:translateX(-50%)translateY(-50%)}.Modal-header{position:fixed;top:-3rem;left:0;right:0;border:1px solid rgba(32,113,138,.12);background-color:#fff;z-index:100}.Modal-title{font-size:1.375rem;font-weight:300;line-height:1.5rem;margin:0;padding:.75rem 2.25rem .75rem 1.5rem}.Modal-close{position:absolute;top:0;right:0;width:3rem;height:3rem;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;text-align:center;border-left:1px solid rgba(32,113,138,.12)}.Modal-close:hover{background-color:rgba(32,113,138,.05)}.Modal-close:active{background-color:rgba(32,113,138,.1)}.Modal-content{position:relative;max-height:100%;overflow:auto;background-clip:padding-box;border:1px solid rgba(32,113,138,.12);border-top-color:transparent;border-bottom-width:2px}.Modal-container.is-modal{overflow:hidden;height:100%}.Toggle,.Toggle-label{cursor:pointer}.Toggle{position:relative;display:inline-block;min-width:2.0625rem;margin:0 .09375rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:top}.Toggle-checkbox{position:absolute;cursor:none;top:0;left:0;width:100%;height:100%;opacity:0}.Toggle-label{font-weight:600;display:block;position:relative;padding:0 .5625rem;-webkit-transition:all .25s cubic-bezier(.075,.82,.165,1);transition:all .25s cubic-bezier(.075,.82,.165,1);text-align:center;color:currentColor}.Toggle-fakeCheckbox{opacity:1;position:absolute;top:0;left:0;width:100%;height:100%;content:'';background-color:transparent}.Toggle:hover>.Toggle-fakeCheckbox,.Toggle-checkbox:focus~.Toggle-fakeCheckbox{background-color:currentColor;opacity:.2}.Toggle-checkbox:checked~.Toggle-label,.Toggle.is-active~.Toggle-label{color:#fff}.Toggle-checkbox:checked~.Toggle-fakeCheckbox,.Toggle.is-active>.Toggle-fakeCheckbox{background-color:currentColor}.Toggle:hover>.Toggle-checkbox:checked~.Toggle-fakeCheckbox,.Toggle-checkbox:checked:focus~.Toggle-fakeCheckbox,.Toggle.is-active:hover>.Toggle-fakeCheckbox{opacity:.8}.Switch{padding-left:2.25rem;position:relative}.Switch-checkbox{position:absolute;margin-left:-9999px;visibility:hidden}.Switch-label{cursor:pointer}.Switch-label:before,.Switch-label:after{content:'';position:absolute;left:0;top:50%;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);border-radius:1.125rem;-webkit-transition:all .2s ease-out;transition:all .2s ease-out}.Switch-label:before{width:1.875rem;height:1.125rem;background-color:#d2e3e8}.Switch-label:after{width:1.125rem;height:1.125rem;background-color:#79aab9;border:1px solid transparent;-webkit-transform:translateY(-50%)scale(.8,.8);-ms-transform:translateY(-50%)scale(.8,.8);transform:translateY(-50%)scale(.8,.8)}.Switch-labelText{color:#79aab9;font-size:.875rem}.Switch-checkbox:checked~.Switch-label:before{background-color:#20718A}.Switch-checkbox:checked~.Switch-label:after{background-color:#fff;-webkit-transform:translateY(-50%)translateX(66%)scale(.8,.8);-ms-transform:translateY(-50%)translateX(66%)scale(.8,.8);transform:translateY(-50%)translateX(66%)scale(.8,.8)}.Switch-checkbox:checked~.Switch-label .Switch-labelText{color:#20718A}.TransUnit{position:relative;margin:0;padding:0;cursor:text;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-bottom:1px solid rgba(32,113,138,.12)}.TransUnit:focus{outline:0;box-shadow:none}.TransUnit.is-focused{opacity:1!important;background-color:#fff}.TransUnit:before,.TransUnit:after{position:absolute;left:0;visibility:hidden;width:100%;height:1.5rem;content:'';-webkit-transform:scaleY(0);-ms-transform:scaleY(0);transform:scaleY(0);opacity:0;background-color:#fff;background-clip:padding-box}.TransUnit:before{box-shadow:0 -.375rem .75rem rgba(0,0,0,.04);-webkit-transform-origin:bottom;-ms-transform-origin:bottom;transform-origin:bottom;top:-1.125rem}.TransUnit:after{box-shadow:0 .375rem .75rem rgba(0,0,0,.04);-webkit-transform-origin:top;-ms-transform-origin:top;transform-origin:top;bottom:-1.125rem}.TransUnit.is-focused:before,.TransUnit.is-focused:after{visibility:visible;-webkit-transform:scaleY(1);-ms-transform:scaleY(1);transform:scaleY(1);opacity:1}.TransUnit.is-first{border-top:1px solid rgba(32,113,138,.12)}.TransUnit-container.is-unit-selected .TransUnit{opacity:.8}.TransUnit-panel{padding-top:1.125rem;padding-bottom:1.125rem;padding-left:1.5rem;list-style:none;vertical-align:top}.TransUnit-panel,.TransUnit-item{position:relative}.TransUnit-source{cursor:pointer;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.TransUnit.is-focused .TransUnit-source{cursor:text}.TransUnit-translation{cursor:text;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.TransUnit-panelHeader,.TransUnit-panelFooter,.TransUnit-itemHeader{position:absolute;z-index:200;right:0;left:0;width:100%;padding-right:1.125rem;padding-left:1.125rem;cursor:default}.TransUnit-panelHeader,.TransUnit-panelFooter{visibility:hidden;opacity:0}.TransUnit.is-focused .TransUnit-panelHeader,.TransUnit.is-focused .TransUnit-panelFooter{visibility:visible;opacity:1}.TransUnit-panelHeader{top:-1.125rem}.TransUnit-itemHeader{top:0}.TransUnit-panelFooter{bottom:-1.125rem}.TransUnit-heading{font-size:1rem;font-weight:400;line-height:2.25rem;display:inline-block;float:left;margin:0;vertical-align:top;text-transform:uppercase}.TransUnit-text{font-family:'Source Sans Pro',"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif;font-size:16px;line-height:1.5;overflow:hidden;width:100%;min-height:1.5rem;margin:0;padding:1.125rem;resize:none;-webkit-transition:height .1s cubic-bezier(.075,.82,.165,1);transition:height .1s cubic-bezier(.075,.82,.165,1);white-space:pre-wrap;word-wrap:break-word;-moz-tab-size:8;tab-size:8;color:#444c54;border:none;background-color:transparent;box-shadow:none;-webkit-appearance:none}.TransUnit-text:focus{border:none;outline:none;background-color:transparent}.TransUnit-text span{font-weight:400!important;font-style:normal!important}.TransUnit-status,.TransUnit-status:before,.TransUnit-status:after{position:absolute;z-index:200;left:0;width:1.5rem;background-color:#bcd4dc}.TransUnit-status{top:-1;bottom:-1}.TransUnit-status.is-loading,.TransUnit-status.is-loading:before{background-image:-webkit-repeating-linear-gradient(45deg,transparent,transparent .375rem,rgba(255,255,255,.5).375rem,rgba(255,255,255,.5).75rem);background-image:repeating-linear-gradient(45deg,transparent,transparent .375rem,rgba(255,255,255,.5).375rem,rgba(255,255,255,.5).75rem);background-size:100% 1000px;-webkit-animation:loading 7s linear;animation:loading 7s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.TransUnit-status:before{top:-1.125rem;height:100%;padding-top:1.125rem;padding-bottom:1.125rem;box-sizing:content-box;z-index:300;visibility:hidden;content:'';-webkit-transform:scaleY(.65);-ms-transform:scaleY(.65);transform:scaleY(.65)}.TransUnit-status:after{-webkit-transition:none;transition:none;z-index:400;top:0;box-sizing:content-box;width:1.3125rem;height:100%;content:'';background-color:#fff;opacity:.6}.TransUnit.is-focused .TransUnit-status:before{visibility:visible;-webkit-transform:scaleY(1);-ms-transform:scaleY(1);transform:scaleY(1)}.TransUnit.is-focused .TransUnit-status:after{top:-1.125rem;padding-top:1.125rem;padding-bottom:1.125rem;opacity:.8}.TransUnit-metaData{position:relative;right:0;left:0;height:100%;text-align:center}.TransUnit-metaDataItem{line-height:1.125rem}.TransUnit-metaDataButton{font-size:.75rem;line-height:.6000000000000001rem;opacity:.8}.TransUnit-metaDataButton:hover{opacity:1}.TransUnit-metaDataComments,.TransUnit-metaDataErrors{position:absolute;right:0;left:0}.TransUnit-metaDataComments{top:1.125rem}.TransUnit-metaDataErrors{top:2.625rem}.TransUnit-metaDataErrorsIcon{text-shadow:0 0 2px #fff}@media (min-width:0) and (max-width:740px){.TransUnit-Heading,.TransUnit-panelFooterLeftNav{padding-left:1.5rem}}@media (min-width:741px){.TransUnit,.TransUnit-item{display:table;width:100%;table-layout:fixed}.TransUnit-panel{display:table-cell;width:50%;padding-left:0}.TransUnit-source{padding-right:.75rem}.TransUnit-translation{padding-left:.75rem}.TransUnit-panelHeader--source,.TransUnit-panelFooter--source{padding-right:1.875rem;right:auto}.TransUnit-panelHeader--translation,.TransUnit-panelFooter--translation{padding-left:1.875rem;left:auto}.TransUnit-status,.TransUnit-status:before,.TransUnit-status:after{left:50%;margin-left:-.75rem}}.TransUnit--highlight .TransUnit-status,.TransUnit--highlight .TransUnit-status:before{background-color:#03A6D7}.TransUnit--success .TransUnit-status,.TransUnit--success .TransUnit-status:before{background-color:#5CCA7B}.TransUnit--unsure .TransUnit-status,.TransUnit--unsure .TransUnit-status:before{background-color:#E9DF1B}.TransUnit--warning .TransUnit-status,.TransUnit--warning .TransUnit-status:before{background-color:#FFA800}.TransUnit--danger .TransUnit-status,.TransUnit--danger .TransUnit-status:before{background-color:#FF3B3D}.TransUnit--secondary .TransUnit-status,.TransUnit--secondary .TransUnit-status:before{background-color:#20718A}.TransUnit--suggestion:hover{background-color:#fbfcfc}.TransUnit--suggestion .TransUnit-panel{padding-top:.75rem;padding-bottom:.75rem}.TransUnit--suggestion .TransUnit-item,.TransUnit--suggestion .TransUnit-details{padding-left:1.125rem;padding-right:1.125rem}.TransUnit--suggestion .TransUnit-itemHeader{margin-top:-1.125rem}.TransUnit--suggestion .TransUnit-item:nth-last-child(n+3):first-child,.TransUnit--suggestion .TransUnit-item:nth-last-child(n+3):first-child~.TransUnit-item{margin-top:1.5rem}.TransUnit--suggestion .TransUnit-details{margin-top:.375rem}@-webkit-keyframes loading{0%{background-position:0 0}100%{background-position:0 1000px}}@keyframes loading{0%{background-position:0 0}100%{background-position:0 1000px}}.Resizer{position:absolute;z-index:1000}.Resizer--vertical{cursor:ew-resize;width:12px;top:0;bottom:0}.Resizer--horizontal{height:12px;left:0;right:0;cursor:ns-resize}.Editor{overflow:hidden}.Editor-header,.Editor-loader{-webkit-transition:.2s all cubic-bezier(.175,.885,.32,1.275);transition:.2s all cubic-bezier(.175,.885,.32,1.275)}.Editor-header.is-minimised{-webkit-transform:translateY(-3rem);-ms-transform:translateY(-3rem);transform:translateY(-3rem)}.Editor-header.is-minimised .Editor-mainNav{visibility:hidden}.Editor-loader{position:absolute;z-index:900;top:0;left:50%;-webkit-transform:translate(-50%,.375rem);-ms-transform:translate(-50%,.375rem);transform:translate(-50%,.375rem)}.Editor-loader.is-minimised{-webkit-transform:translateX(-50%)scale(.75,.75);-ms-transform:translateX(-50%)scale(.75,.75);transform:translateX(-50%)scale(.75,.75)}.Editor-mainNav{height:3rem}.Editor-content{position:absolute;left:0;right:0;top:5.625rem;bottom:0;overflow:auto;overflow-y:scroll;width:100%;-webkit-overlow-scrolling:touch}.Editor-content.is-maximised{top:2.625rem}.Editor-translationsWrapper{height:100%}.Editor-translations{min-height:100%;padding:3rem 0;background:-webkit-linear-gradient(left,#d7e5ea ,#d7e5ea);background:linear-gradient(left,#d7e5ea ,#d7e5ea);background-position:left center;background-size:1.5rem 100%;background-repeat:no-repeat}.Editor-currentDoc,.Editor-currentLang{max-width:5.25rem}.Editor-suggestions{position:fixed;z-index:200;right:0;bottom:0;left:0;overflow:hidden;box-shadow:0 -.1875rem 1.5rem rgba(0,0,0,.1)}.Editor-suggestionsHeader{position:absolute;top:0;left:0;right:0;box-shadow:0 1px 1px rgba(0,0,0,.1);z-index:300;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.Editor-suggestionsBody{position:absolute;top:2.25rem;left:0;right:0;bottom:0;overflow:scroll;overflow-x:hidden;overflow-y:scroll}.Editor-suggestions.is-search-active .Editor-suggestionsBody{top:4.5rem}.Editor-suggestionsSearch{clear:both}@media (min-width:0) and (max-width:740px){.Editor-currentProject{max-width:5.25rem}}@media (min-width:741px){.Editor-translations{background:-webkit-linear-gradient(left,#d7e5ea ,#d7e5ea),-webkit-linear-gradient(left,#ECEFF0,#ECEFF0);background:linear-gradient(to right,#d7e5ea ,#d7e5ea),linear-gradient(to right,#ECEFF0,#ECEFF0);background-position:center,right;background-size:1.5rem 100%,50% 100%;background-repeat:no-repeat}}.fade{-webkit-transition:opacity .25s easein;transition:opacity .25s easein;opacity:0}.fade.in{opacity:1}[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none!important} diff --git a/zanata-war/src/main/webapp/app/js/app.js b/zanata-war/src/main/webapp/app/js/app.js index 09788d81ad..58d006bd8b 100644 --- a/zanata-war/src/main/webapp/app/js/app.js +++ b/zanata-war/src/main/webapp/app/js/app.js @@ -1,3 +1,3 @@ !function(){"use strict";angular.module("app",["ngResource","ngAnimate","ui.router","templates","cfp.hotkeys","focusOn","monospaced.elastic","gettext","diff-match-patch"])}(),function(){"use strict";function t(t,e,n,o){var r=function(t,e){return{request:function(t){return e.$broadcast("loadingStart"),t},requestError:function(n){return e.$broadcast("loadingStop"),console.error("Request error due to ",n),t.reject(n)},response:function(n){return e.$broadcast("loadingStop"),n||t.when(n)},responseError:function(n){return e.$broadcast("loadingStop"),401===n.status?console.error("Unauthorized access. Please login"):404===n.status?console.error("Service end point not found- ",n.config.url):console.error("Error in response ",n),t.reject(n)}}};r.$inject=["$q","$rootScope"],n.interceptors.push(r),e.otherwise("/"),t.state("editor",{url:"/:projectSlug/:versionSlug/translate",templateUrl:"editor/editor.html",controller:"EditorCtrl as editor",resolve:{url:["UrlService",function(t){return t.init()}]}}).state("editor.selectedContext",{url:"/:docId/:localeId",views:{"editor-content":{templateUrl:"editor/editor-content.html",controller:"EditorContentCtrl as editorContent"},"editor-suggestions":{templateUrl:"editor/editor-suggestions.html",controller:"EditorSuggestionsCtrl as editorSuggestions"},"editor-details":{templateUrl:"editor/editor-details.html",controller:"EditorDetailsCtrl as editorDetails"}}}).state("editor.selectedContext.tu",{url:"/?id&selected?states",reloadOnSearch:!1}),o.includeCheatSheet=!1}t.$inject=["$stateProvider","$urlRouterProvider","$httpProvider","hotkeysProvider"],angular.module("app").config(t)}(),function(){"use strict";angular.module("app").constant("_",window._).constant("str",window._.string).constant("Mousetrap",window.Mousetrap).constant("PRODUCTION",!0)}(),function(){"use strict";function t(t,e,n,o,r,i,a,s){function c(){return o.getAllLocales()}function u(){return e.getMyInfo().then(function(t){d.myInfo=t,d.myInfo.locale=o.DEFAULT_LOCALE,d.myInfo.gravatarUrl=n.gravatarUrl(d.myInfo.gravatarHash,72)},function(t){r.displayError("Error loading my info: "+t)})}function l(){o.getUILocaleList().then(function(t){for(var e in t.locales){var n={localeId:t.locales[e],name:""};d.uiLocaleList.push(n)}d.myInfo.locale=o.getLocaleByLocaleId(d.uiLocaleList,o.DEFAULT_LOCALE.localeId),d.myInfo.locale||(d.myInfo.locale=o.DEFAULT_LOCALE)},function(t){r.displayInfo("Error loading UI locale. Default to '"+o.DEFAULT_LOCALE.name+"': "+t),d.myInfo.locale=o.DEFAULT_LOCALE})}var d=this;d.PRODUCTION=s,d.settings=e.settings,d.uiLocaleList=[o.DEFAULT_LOCALE],n.init().then(c).then(u).then(l),d.onChangeUILocale=function(t){d.myInfo.locale=t;var e=d.myInfo.locale.localeId;a.startsWith(e,o.DEFAULT_LOCALE.localeId,!0)?i.setCurrentLanguage(o.DEFAULT_LOCALE.localeId):i.loadRemote(n.uiTranslationURL(e)).then(function(){i.setCurrentLanguage(e)},function(t){r.displayInfo("Error loading UI locale. Default to '"+o.DEFAULT_LOCALE.name+"': "+t),i.setCurrentLanguage(o.DEFAULT_LOCALE),d.myInfo.locale=o.DEFAULT_LOCALE})},d.dashboardPage=function(){return n.DASHBOARD_PAGE}}t.$inject=["$scope","UserService","UrlService","LocaleService","MessageHandler","gettextCatalog","StringUtil","PRODUCTION"],angular.module("app").controller("AppCtrl",t)}(),function(){"use strict";function t(t,e,n,o,r,i,a,s,c,u,l){function d(){v=r.readValue("status"),u.isUndefined(v)||(v=v.split(","),v=u.transform(v,function(t,e){return e=l.getServerId(e),t.push(e)})),I={status:v}}function f(t,e){var n,o,r,c,l=N.phrases;o=u.findIndex(l,function(t){return t.id===e.currentId}),r=Math.min(o+1,l.length-1),c=l[r].id,c!==e.currentId?i.emitEvent(i.EVENT.SELECT_TRANS_UNIT,{id:c,updateURL:!0,focus:!0},null):(n=l[o],i.emitEvent(i.EVENT.SAVE_TRANSLATION,{phrase:n,status:s.getSaveButtonStatus(n),locale:a.localeId,docId:a.docId}))}function S(t,e){var n,o,r,c,l=N.phrases;o=u.findIndex(l,function(t){return t.id===e.currentId}),r=Math.max(o-1,0),c=l[r].id,c!==e.currentId?i.emitEvent(i.EVENT.SELECT_TRANS_UNIT,{id:c,updateURL:!0,focus:!0},null):(n=l[o],i.emitEvent(i.EVENT.SAVE_TRANSLATION,{phrase:n,status:s.getSaveButtonStatus(n),locale:a.localeId,docId:a.docId}))}function g(t,e){var n,o,r=N.phrases,a=l.getStatusInfo(v);n=u.findIndex(r,function(t){return t.id===e.currentId});for(var s=n+1;sm&&(e.maxPageIndex=t%m!==0?e.maxPageIndex+=1:e.maxPageIndex),e.maxPageIndex=e.maxPageIndex-1<0?0:e.maxPageIndex-1,T(e.currentPageIndex)})}function T(t){var o=t*m;n.fetchAllPhrase(e.context,I,o,m).then(h)}function h(t){N.phrases=t}var v,I,m=50,N=this;return d(),N.phrases=[],e.updateContext(a.projectSlug,a.versionSlug,o.decodeDocId(a.docId),a.localeId),p(),t.$on(i.EVENT.FILTER_TRANS_UNIT,function(t,e){if(e.status.all===!0)c.search("status",null);else{var n=[];u.forEach(e.status,function(t,e){t&&n.push(e)}),c.search("status",n.join(","))}d(),p()}),t.$on(i.EVENT.GOTO_FIRST_PAGE,function(){e.currentPageIndex>0&&(e.currentPageIndex=0,E(e.currentPageIndex))}),t.$on(i.EVENT.GOTO_PREV_PAGE,function(){e.currentPageIndex>0&&(e.currentPageIndex-=1,E(e.currentPageIndex))}),t.$on(i.EVENT.GOTO_NEXT_PAGE,function(){e.currentPageIndex-1?!1:"INPUT"===t.tagName||"SELECT"===t.tagName||"TEXTAREA"===t.tagName||t.isContentEditable}function v(t){m(A.filter.status)?I(t):(A.filter.status.all=!1,t&&S.emitEvent(S.EVENT.FILTER_TRANS_UNIT,A.filter))}function I(t){A.filter.status.all=!0,A.filter.status.approved=!1,A.filter.status.translated=!1,A.filter.status.needsWork=!1,A.filter.status.untranslated=!1,t&&S.emitEvent(S.EVENT.FILTER_TRANS_UNIT,A.filter)}function m(t){return t.approved===t.translated&&t.translated===t.needsWork&&t.needsWork===t.untranslated}function N(){O()&&l.go("editor.selectedContext",{docId:A.context.docId,localeId:A.context.localeId})}function O(){return A.context.docId&&A.context.localeId}function _(t,e,o,r){n.getStatistics(t,e,o,r).then(function(t){A.wordStatistic=s.getWordStatistic(t),A.messageStatistic=s.getMsgStatistic(t)},function(t){d.displayError("Error loading statistic: "+t)})}var A=this;A.pageNumber=1,A.showCheatsheet=!1,A.shortcuts=E.mapValues(E.values(g.SHORTCUTS),function(t){var e=E.flatten(t.keyCombos,"combo");return{combos:E.map(e,function(t){return g.symbolizeKey(t)}),description:t.keyCombos[0].description}}),A.filter={status:{all:!0,approved:!1,translated:!1,needsWork:!1,untranslated:!1}},T(),p.bind("?",function(e){var n=e.srcElement;A.showCheatsheet||h(n)||(A.toggleKeyboardShortcutsModal(),t.$digest())},"keyup"),A.context=i.initContext(u.projectSlug,u.versionSlug,n.decodeDocId(u.docId),o.DEFAULT_LOCALE,o.DEFAULT_LOCALE.localeId,"READ_WRITE"),A.toggleKeyboardShortcutsModal=function(){A.showCheatsheet=!A.showCheatsheet};var C=a.SETTING.SHOW_SUGGESTIONS;t.showSuggestions=a.subscribe(C,function(e){t.showSuggestions=e}),A.toggleSuggestionPanel=function(){a.update(C,!t.showSuggestions)},A.versionPage=function(){return c.PROJECT_PAGE(A.context.projectSlug,A.context.versionSlug)},A.encodeDocId=function(t){return n.encodeDocId(t)},r.getProjectInfo(u.projectSlug).then(function(t){A.projectInfo=t},function(t){d.displayError("Error getting project information:"+t)}),o.getSupportedLocales(A.context.projectSlug,A.context.versionSlug).then(function(t){if(A.locales=t,!A.locales||A.locales.length<=0)d.displayError("No supported locales in "+A.context.projectSlug+" : "+A.context.versionSlug);else{var e=l.params.localeId,n=A.context;e?(n.localeId=e,o.containsLocale(A.locales,e)||(n.localeId=A.locales[0].localeId)):(n.localeId=A.locales[0].localeId,N())}},function(t){d.displayError("Error getting locale list: "+t)}),n.findAll(A.context.projectSlug,A.context.versionSlug).then(function(t){if(A.documents=t,!A.documents||A.documents.length<=0)d.displayError("No documents in "+A.context.projectSlug+" : "+A.context.versionSlug);else{var e=l.params.docId,o=A.context;e?(o.docId=n.decodeDocId(e),n.containsDoc(A.documents,o.docId)||(o.docId=A.documents[0].name)):(o.docId=A.documents[0].name,N())}},function(t){d.displayError("Error getting document list: "+t)}),f.$on(S.EVENT.SELECT_TRANS_UNIT,function(t,e){A.unitSelected=e.id,A.focused=e.focus}),f.$on(S.EVENT.CANCEL_EDIT,function(){A.unitSelected=!1,A.focused=!1}),f.$on(S.EVENT.REFRESH_STATISTIC,function(t,e){_(e.projectSlug,e.versionSlug,e.docId,e.localeId),A.context.docId=e.docId,A.context.localeId=e.localeId}),A.pageNumber=function(){return 0===i.maxPageIndex?i.currentPageIndex+1:i.currentPageIndex+1+" of "+(i.maxPageIndex+1)},A.getLocaleName=function(t){return o.getName(t)},A.firstPage=function(){S.emitEvent(S.EVENT.GOTO_FIRST_PAGE)},A.lastPage=function(){S.emitEvent(S.EVENT.GOTO_LAST_PAGE)},A.nextPage=function(){S.emitEvent(S.EVENT.GOTO_NEXT_PAGE)},A.previousPage=function(){S.emitEvent(S.EVENT.GOTO_PREV_PAGE)},A.resetFilter=function(){I(!0)},A.updateFilter=function(){v(!0)},this.settings=e.settings.editor,g.enableEditorKeys()}t.$inject=["$scope","UserService","DocumentService","LocaleService","ProjectService","EditorService","SettingsService","StatisticUtil","UrlService","$stateParams","$state","MessageHandler","$rootScope","EventService","EditorShortcuts","_","Mousetrap"],angular.module("app").controller("EditorCtrl",t)}(),function(){"use strict";function t(){var t=this;return t}angular.module("app").controller("EditorDetailsCtrl",t)}(),function(){"use strict";function t(t,e,n,o,r,i,a,s,c,u){function l(t,e){return a.hasTranslationChanged(t)||t.status!==e}function d(t){var a=n.cloneDeep(S.context),l=g[t],d=e(o.TRANSLATION_URL,{},{update:{method:"PUT",params:{localeId:l.locale}}}),f={id:l.phrase.id,revision:l.phrase.revision||0,content:l.phrase.newTranslations[0],contents:l.phrase.newTranslations,status:u.getServerId(l.status.ID),plural:l.phrase.plural};d.update(f).$promise.then(function(t){var e=l.phrase.status.ID;i.onTransUnitUpdated(a,f.id,l.locale,t.revision,t.status,l.phrase),s.updateStatistic(a.projectSlug,a.versionSlug,l.docId,l.locale,e,u.getId(t.status),l.phrase.wordCount),r.emitEvent(r.EVENT.SAVE_COMPLETED,l.phrase)},function(t){c.displayWarning("Update translation failed for "+f.id+" -"+t),i.onTransUnitUpdateFailed(f.id),r.emitEvent(r.EVENT.SAVE_COMPLETED,l.phrase)}),delete g[t]}function f(t,e){return n.isEmpty(n.compact(t.newTranslations))?u.getStatusInfo("UNTRANSLATED"):e}var S=this,g={};return S.context={},S.currentPageIndex=0,S.maxPageIndex=0,S.initContext=function(t,e,n,o,r,i){return S.context={projectSlug:t,versionSlug:e,docId:n,srcLocale:o,localeId:r,mode:i},S.context},S.updateContext=function(t,e,n,o){S.context.projectSlug!==t&&(S.context.projectSlug=t),S.context.versionSlug!==e&&(S.context.versionSlug=e),S.context.docId!==n&&(S.context.docId=n),S.context.localeId!==o&&(S.context.localeId=o)},t.$on(r.EVENT.SAVE_TRANSLATION,function(t,e){var o=e.phrase,i=e.status;if(l(o,i)){if(n.has(g,o.id)){var a=g[o.id];a.phrase=o,a.status=i}else i=f(o,i),g[o.id]={phrase:o,status:i,locale:e.locale,docId:e.docId};r.emitEvent(r.EVENT.SAVE_INITIATED,e),d(o.id)}}),S}t.$inject=["$rootScope","$resource","_","UrlService","EventService","PhraseService","PhraseUtil","DocumentService","MessageHandler","TransStatusService"],angular.module("app").factory("EditorService",t)}(),function(){"use strict";function t(t,e,n,o,r,i,a,s,c){function u(e){m.selectedTUCtrl&&(e.preventDefault(),t.emitEvent(t.EVENT.COPY_FROM_SOURCE,{phrase:m.selectedTUCtrl.getPhrase()}))}function l(e){m.selectedTUCtrl&&(e.preventDefault(),e.stopPropagation(),t.emitEvent(t.EVENT.GOTO_NEXT_ROW,v()))}function d(e){m.selectedTUCtrl&&(e.preventDefault(),e.stopPropagation(),t.emitEvent(t.EVENT.GOTO_PREVIOUS_ROW,v()))}function f(e){if(e.preventDefault(),e.stopPropagation(),N)m.cancelSaveAsModeIfOn(),m.selectedTUCtrl&&m.selectedTUCtrl.focusTranslation();else if(m.selectedTUCtrl){var n=m.selectedTUCtrl.getPhrase();r.hasTranslationChanged(n)?t.emitEvent(t.EVENT.UNDO_EDIT,n):t.emitEvent(t.EVENT.CANCEL_EDIT,n)}}function S(n){if(m.selectedTUCtrl){n.preventDefault();var o=m.selectedTUCtrl.getPhrase();t.emitEvent(t.EVENT.SAVE_TRANSLATION,{phrase:o,status:r.getSaveButtonStatus(o),locale:e.localeId,docId:e.docId})}}function g(e){e.preventDefault(),m.cancelSaveAsModeIfOn();var n=m.selectedTUCtrl.getPhrase();n&&(t.emitEvent(t.EVENT.TOGGLE_SAVE_OPTIONS,{id:n.id,open:!0}),I(n,"n","needsWork"),I(n,"t","translated"),I(n,"a","approved"))}function E(e){return function(n){n.preventDefault(),t.emitEvent(t.EVENT.COPY_FROM_SUGGESTION_N,e-1)}}function p(t,e,n,o,r){return this.defaultKey=t,this.keyCombos=[T(t,n,r,e)],o&&(this.otherKeys=o instanceof Array?o:[o],this.keyCombos.push(T(this.otherKeys,"",r,e))),this}function T(t,e,n,o){var r={allowIn:["TEXTAREA"],callback:o};return r.combo=t,e&&(r.description=e),n&&(r.action=n),r}function h(t){o.get(t.defaultKey)||n.forEach(t.keyCombos,function(t){o.add(t)})}function v(){return{currentId:m.selectedTUCtrl.getPhrase().id}}function I(t,e,n){var r=i.getStatusInfo(n);return o.add({combo:e,description:s.sprintf("Save as %s",n),allowIn:["INPUT","TEXTAREA"],action:"keydown",callback:function(e){m.saveTranslationCallBack(e,t,r)}})}var m=this,N=!1;return m.selectedTUCtrl=null,m.SHORTCUTS={COPY_SOURCE:new p("alt+c",u,"Copy source as translation","alt+g"),COPY_SUGGESTION_1:new p("mod+alt+1",E(1),"Copy first suggestion as translation"),COPY_SUGGESTION_2:new p("mod+alt+2",E(2),"Copy second suggestion as translation"),COPY_SUGGESTION_3:new p("mod+alt+3",E(3),"Copy third suggestion as translation"),COPY_SUGGESTION_4:new p("mod+alt+4",E(4),"Copy fourth suggestion as translation"),CANCEL_EDIT:new p("esc",f,"Cancel edit"),SAVE_AS_CURRENT_BUTTON_OPTION:new p("mod+s",S,"Save"),SAVE_AS_MODE:new p("mod+shift+s",g,"Save as…"),SAVE_AS_NEEDSWORK:{keyCombos:[{combo:"mod+shift+s n",description:"Save as needs work"}]},SAVE_AS_TRANSLATED:{keyCombos:[{combo:"mod+shift+s t",description:"Save as translated"}]},SAVE_AS_APPROVED:{keyCombos:[{combo:"mod+shift+s a",description:"Save as approved"}]},GOTO_NEXT_ROW_FAST:new p("mod+enter",l,"Save (if changed) and go to next string",["alt+k","alt+down"]),GOTO_PREVIOUS_ROW:new p("mod+shift+enter",d,"Save (if changed) and go to previous string",["alt+j","alt+up"])},m.enableEditorKeys=function(){o.get(m.SHORTCUTS.COPY_SOURCE.defaultKey)||n.forOwn(m.SHORTCUTS,function(t){t instanceof p&&h(t)})},m.disableEditorKeys=function(){n.forOwn(m.SHORTCUTS,function(t){n.forEach(t.keyCombos,function(t){m.deleteKeys(t.combo,t.action)})})},m.saveTranslationCallBack=function(n,o,r){N=!0,n.preventDefault(),n.stopPropagation(),t.emitEvent(t.EVENT.SAVE_TRANSLATION,{phrase:o,status:r,locale:e.localeId,docId:e.docId}),m.cancelSaveAsModeIfOn()},m.cancelSaveAsModeIfOn=function(){N&&m.selectedTUCtrl&&(N=!1,m.deleteKeys(["n","t","a"]),t.emitEvent(t.EVENT.TOGGLE_SAVE_OPTIONS,{id:m.selectedTUCtrl.getPhrase().id,open:!1}))},m.deleteKeys=function(t,e){var r=t instanceof Array?t:[t];e=e||"keydown",n.forEach(r,function(t){o.del(t),a.unbind(t,e)})},m.symbolizeKey=function(t){var e={command:"⌘",shift:"⇧",left:"←",right:"→",up:"↑",down:"↓","return":"↩",backspace:"⌫"};t=t.split("+");for(var n=0;n=0?"command":"ctrl"),t[n]=e[t[n]]||t[n];return t.join(" + ")},m}t.$inject=["EventService","$stateParams","_","hotkeys","PhraseUtil","TransStatusService","Mousetrap","str","$window"],angular.module("app").factory("EditorShortcuts",t)}(),function(){"use strict";function t(t,e,n,o,r,i,a,s,c){function u(e){t.isTextSearch=e,t.isPhraseSearch=!e}function l(e){t.suggestions=e}function d(){t.search.isVisible=!1,u(!1),S()}function f(e,n){t.search.input.text="",t.search.isVisible=!0,!n&&e&&t.focusSearch(e),h.searchForText(),g()}function S(){t.searchStrings=o.getSearchStrings(),t.search.isLoading=o.isLoading(),l(o.getResults())}function g(){t.searchStrings=r.getSearchStrings(),t.search.isLoading=r.isLoading(),l(r.getResults())}function E(t){t&&i.emitEvent(i.EVENT.COPY_FROM_SUGGESTION,{suggestion:t})}var p=n.SETTING.SHOW_SUGGESTIONS,T=n.SETTING.SUGGESTIONS_SHOW_DIFFERENCE,h=this;return t.suggestions=[],t.hasSuggestions=!1,t.$watch("suggestions.length",function(e){t.hasSuggestions=0!==e}),t.searchStrings=[],t.hasSearch=!1,t.$watch("searchStrings.length",function(e){t.hasSearch=0!==e}),t.isTransUnitSelected=!1,t.isTextSearch=!1,t.isPhraseSearch=!0,t.search={isVisible:!1,isLoading:!1,input:{text:"",focused:!1}},t.$watch("search.input.text",function(){h.searchForText()}),t.show=n.subscribe(p,function(e){t.show=e,e&&(t.isTransUnitSelected?S():t.search.isVisible||f(null,!0))}),t.diff=n.subscribe(T,function(e){t.diff=e}),t.focusSearch=function(t){t&&t.preventDefault(),c("searchSugInput")},h.closeSuggestions=function(){n.update(p,!1),i.emitEvent(i.EVENT.SUGGESTIONS_SEARCH_TOGGLE,!1)},h.clearSearchResults=function(e,n){t.search.input.text="",!n&&e&&t.focusSearch(e)},h.searchForText=function(){var e=t.search.input.text;e.length>0&&(t.search.isLoading=!0),u(!0),i.emitEvent(i.EVENT.REQUEST_TEXT_SUGGESTIONS,e)},h.toggleSearch=function(){t.search.isVisible?i.emitEvent(i.EVENT.SUGGESTIONS_SEARCH_TOGGLE,!1):i.emitEvent(i.EVENT.SUGGESTIONS_SEARCH_TOGGLE,!0)},t.show&&!t.isTransUnitSelected&&f(),a.$on(i.EVENT.SELECT_TRANS_UNIT,function(){""===t.search.input.text&&t.search.isVisible&&i.emitEvent(i.EVENT.SUGGESTIONS_SEARCH_TOGGLE,!1),t.isTransUnitSelected=!0}),a.$on(i.EVENT.CANCEL_EDIT,function(){t.isTransUnitSelected=!1,t.show&&!t.search.isVisible&&f(null,!0)}),a.$on(i.EVENT.SUGGESTIONS_SEARCH_TOGGLE,function(t,e){e?f(t):d(t)}),a.$on("PhraseSuggestionsService:updated",function(){t.isPhraseSearch&&S()}),a.$on("TextSuggestionsService:updated",function(){t.isTextSearch&&g()}),a.$on(i.EVENT.COPY_FROM_SUGGESTION_N,function(e,n){t.show?(E(t.suggestions[n]),t.$broadcast("EditorSuggestionsCtrl:nth-suggestion-copied",n)):E(o.getResults()[n])}),h}t.$inject=["$scope","_","SettingsService","PhraseSuggestionsService","TextSuggestionsService","EventService","$rootScope","$timeout","focus"],angular.module("app").controller("EditorSuggestionsCtrl",t)}(),function(){"use strict";function t(){return{restrict:"A",link:function(t,e,n){return t.$on("blurOn",function(t,o){return o===n.blurOn?e[0].blur():void 0})}}}angular.module("app").directive("blurOn",t)}(),function(){"use strict";function t(t){return{restrict:"A",scope:{callback:"&clickElsewhere"},link:function(e,n){var o=function(t){n[0].contains(t.target)||e.$apply(e.callback(t))};t.on("click",o),e.$on("$destroy",function(){t.off("click",o)})}}}t.$inject=["$document"],angular.module("app").directive("clickElsewhere",t)}(),function(){"use strict";function t(t,e,n,o,r,i,a,s){function c(t,e){return t+"-"+e}function u(t,e,n,o){var i=r.getWordStatistic(t),a=r.getMsgStatistic(t);if(i){o=parseInt(o);var s=parseInt(i[e])-o;i[e]=0>s?0:s,i[n]=parseInt(i[n])+o}if(a){var c=parseInt(a[e])-1;a[e]=0>c?0:c,a[n]=parseInt(a[n])+1}}var l=this,d={};return l.findAll=function(t,o){var r=e(n.DOCUMENT_LIST_URL,{},{query:{method:"GET",params:{projectSlug:t,versionSlug:o},isArray:!0}});return r.query().$promise},l.getStatistics=function(o,r,i,u){if(i&&u){var f=c(i,u);if(a.has(d,f))return t.when(d[f]);var S=l.encodeDocId(i),g=e(n.DOC_STATISTIC_URL,{},{query:{method:"GET",params:{projectSlug:o,versionSlug:r,docId:S,localeId:u},isArray:!0}});return g.query().$promise.then(function(t){return a.forEach(t,function(t){t[s.getId("needswork")]=t.needReview||0}),d[f]=t,d[f]})}},l.encodeDocId=function(t){return t?t.replace(/\//g,","):t},l.decodeDocId=function(t){return t?t.replace(/\,/g,"/"):t},l.containsDoc=function(t,e){return a.any(t,function(t){return o.equals(t.name,e,!0)})},l.updateStatistic=function(t,e,n,o,r,s,l){var f=c(n,o);a.has(d,f)&&(u(d[f],r,s,l),i.emitEvent(i.EVENT.REFRESH_STATISTIC,{projectSlug:t,versionSlug:e,docId:n,localeId:o}))},l}t.$inject=["$q","$resource","UrlService","StringUtil","StatisticUtil","EventService","_","TransStatusService"],angular.module("app").factory("DocumentService",t)}(),function(){"use strict";function t(t,e,n,o,r,i,a){var s,c=this,u=t.$new(),l=o.openClass,d=angular.noop,f=e.onToggle?n(e.onToggle):angular.noop;this.init=function(o){c.$element=o,e.isOpen&&(s=n(e.isOpen),d=s.assign,t.$watch(s,function(t){u.isOpen=!!t}))},this.toggle=function(t){return u.isOpen=arguments.length?!!t:!u.isOpen,u.isOpen},this.isOpen=function(){return u.isOpen},u.getToggleElement=function(){return c.toggleElement},u.focusToggleElement=function(){c.toggleElement&&c.toggleElement[0].focus()},u.$watch("isOpen",function(e,n){i[e?"addClass":"removeClass"](c.$element,l),e?(a(function(){u.focusToggleElement()}),r.open(u)):r.close(u),d(t,e),angular.isDefined(e)&&e!==n&&f(t,{open:!!e})}),t.$on("$locationChangeSuccess",function(){u.isOpen=!1}),t.$on("$destroy",function(){u.$destroy()}),t.$on("openDropdown",function(){u.isOpen=!0}),t.$on("closeDropdown",function(){u.isOpen=!1})}t.$inject=["$scope","$attrs","$parse","dropdownConfig","DropdownService","$animate","$timeout"],angular.module("app").controller("DropdownCtrl",t)}(),function(){"use strict";function t(t){var e=null,n=this;n.open=function(n){e||(t.bind("click",o),t.bind("keydown",r)),e&&e!==n&&(e.isOpen=!1),e=n},n.close=function(n){e===n&&(e=null,t.unbind("click",o),t.unbind("keydown",r))};var o=function(t){if(e){var n=e.getToggleElement();t&&n&&n[0].contains(t.target)||e.$apply(function(){e.isOpen=!1})}},r=function(t){27===t.which&&(e.focusToggleElement(),o())}}t.$inject=["$document"],angular.module("app").service("DropdownService",t)}(),function(){"use strict";var t={openClass:"is-active"};angular.module("app").constant("dropdownConfig",t)}(),function(){"use strict";function t(){return{restrict:"EA",controller:"DropdownCtrl",link:function(t,e,n,o){o.init(e)}}}function e(){return{restrict:"A",require:"?^dropdown",scope:{callback:"&onCloseDropdown"},link:function(t,e,n,o){o.onCloseDropdown=t.callback}}}function n(){return{restrict:"EA",require:"?^dropdown",link:function(t,e,n,o){if(o){o.toggleElement=e;var r=function(r){r.preventDefault(),r.stopPropagation(),e.hasClass("disabled")||n.disabled||t.$apply(function(){o.toggle()})};e.bind("click",r),e.attr({"aria-haspopup":!0,"aria-expanded":!1}),t.$watch(o.isOpen,function(n){e.attr("aria-expanded",!!n),o.onCloseDropdown&&!n&&t.$applyAsync(o.onCloseDropdown)}),t.$on("$destroy",function(){e.unbind("click",r)})}}}}angular.module("app").directive("dropdown",t).directive("onCloseDropdown",e).directive("dropdownToggle",n)}(),function(){"use strict";function t(t){var e=this;return e.EVENT={LOADING_START:"loadingStart",LOADING_STOP:"loadingStop",SELECT_TRANS_UNIT:"selectTransUnit",COPY_FROM_SOURCE:"copyFromSource",COPY_FROM_SUGGESTION:"copyFromSuggestion",COPY_FROM_SUGGESTION_N:"copyFromSuggestionN",UNDO_EDIT:"undoEdit",CANCEL_EDIT:"cancelEdit",FOCUS_TRANSLATION:"focusTranslation",SAVE_TRANSLATION:"saveTranslation",SAVE_INITIATED:"saveInitiated",SAVE_COMPLETED:"saveCompleted",TRANSLATION_TEXT_MODIFIED:"translationTextModified",REFRESH_STATISTIC:"refreshStatistic",GOTO_PREV_PAGE:"gotoPreviousPage",GOTO_NEXT_PAGE:"gotoNextPage",GOTO_FIRST_PAGE:"gotoFirstPage",GOTO_LAST_PAGE:"gotoLastPage",GOTO_NEXT_ROW:"gotoNextRow",GOTO_PREVIOUS_ROW:"gotoPreviousRow",GOTO_NEXT_UNTRANSLATED:"gotoNextUntranslated",TOGGLE_SAVE_OPTIONS:"openSaveOptions",FILTER_TRANS_UNIT:"filterTransUnit",PHRASE_SUGGESTION_COUNT:"phraseSuggestionCount",REQUEST_PHRASE_SUGGESTIONS:"requestPhraseSuggestions",REQUEST_TEXT_SUGGESTIONS:"requestTextSuggestions",SUGGESTIONS_SEARCH_TOGGLE:"suggestionsSearchToggle",USER_SETTING_CHANGED:"userSettingChanged"},e.broadcastEvent=function(e,n,o){o=o||t,o.$broadcast(e,n)},e.emitEvent=function(e,n,o){o=o||t,o.$emit(e,n)},e}t.$inject=["$rootScope"],angular.module("app").factory("EventService",t)}(),function(){"use strict";function t(t){return{restrict:"E",required:["name"],scope:{name:"@",title:"@",size:"@"},link:function(e,n){var o="",r="",i="";n.addClass("Icon"),e.title&&(i=""+e.title+""),"loader"===e.name?(n.addClass("Icon--loader"),r='',n.html(t.trustAsHtml(r))):(o=''+i+"",n.html(t.trustAsHtml(o)))}}}t.$inject=["$sce"],angular.module("app").directive("icon",t)}(),function(){"use strict";function t(t,e,n,o,r){function i(e,n){var r=o(t.LOCALE_LIST_URL,{},{query:{method:"GET",params:{projectSlug:e,versionSlug:n},isArray:!0}});return r.query().$promise}function a(){var e=o(t.ALL_LOCALE_URL,{},{query:{method:"GET",isArray:!0}});return e.query().$promise.then(function(t){d=n.cleanResourceList(t)})}function s(){var e=o(t.uiTranslationListURL,{},{query:{method:"GET"}});return e.query().$promise}function c(t,n){return t?r.find(t,function(t){return e.equals(t.localeId,n,!0)}):void 0}function u(t,n){return r.any(t,function(t){return e.equals(t.localeId,n,!0)})}function l(t){var e=c(d,t);return e?e.name:t}var d=[];return{getSupportedLocales:i,getUILocaleList:s,getLocaleByLocaleId:c,getAllLocales:a,containsLocale:u,getName:l,DEFAULT_LOCALE:{localeId:"en-US",name:"English"}}}t.$inject=["UrlService","StringUtil","FilterUtil","$resource","_"],angular.module("app").factory("LocaleService",t)}(),function(){"use strict";function t(t){return{restrict:"EA",scope:{loading:"=",inverted:"="},link:function(e){e.classes="",e.$on(t.EVENT.LOADING_START,function(){e.classes+=" is-loading"}),e.$on(t.EVENT.LOADING_STOP,function(){e.classes=e.classes.replace("is-loading","")}),e.$watch("inverted",function(t){t?e.classes+=" LogoLoader--inverted":e.classes=e.classes.replace("LogoLoader--inverted","")})},templateUrl:"components/logo-loader/logo-loader.html"}}t.$inject=["EventService"],angular.module("app").directive("logoLoader",t)}(),function(){"use strict";function t(){return{displayError:function(t){console.error(t)},displayWarning:function(t){console.warn(t)},displayInfo:function(t){console.info(t)}}}angular.module("app").factory("MessageHandler",t)}(),function(){"use strict";function t(){}angular.module("app").factory("NotificationService",t)}(),function(){"use strict";function t(t,e,n,o,r,i){function a(t,e,n,o){return t+"-"+e+"-"+n+"-"+o}var s=this,c={},u={};return s.getStates=function(s,u,l,d){var f=a(s,u,l,d);if(i.has(c,f))return t.when(c[f]);var S=r.encodeDocId(l),g={query:{method:"GET",params:{projectSlug:s,versionSlug:u,docId:S,localeId:d},isArray:!0}},E=e(o.TRANSLATION_STATUS_URL,{},g);return E.query().$promise.then(function(t){return t=n.cleanResourceList(t),c[f]=t,c[f]})},s.getTransUnits=function(r,a){function s(t){t=n.cleanResourceMap(t);for(var e in t)u[e][a]=t[e][a],l[e]=u[e];return l}function c(t){t=n.cleanResourceMap(t);for(var e in t)u[e]=t[e],l[e]=u[e];return l}var l={},d=[],f=[];if(r.forEach(function(t){i.has(u,t)?u[t][a]?l[t]=u[t]:f.push(t):d.push(t)}),i.isEmpty(d)&&i.isEmpty(f))return t.when(l);var S,g;return i.isEmpty(d)||(S=e(o.TEXT_FLOWS_URL,{},{query:{method:"GET",params:{localeId:a,ids:d.join(",")}}})),i.isEmpty(f)||(g=e(o.TRANSLATION_URL,{},{query:{method:"GET",params:{localeId:a,ids:f.join(",")}}})),S&&g?S.query().$promise.then(c).then(g.query().$promise.then(s)):S?S.query().$promise.then(c):g?g.query().$promise.then(s):void 0},s.onTransUnitUpdated=function(t,e,n,o,r,s){var l=a(t.projectSlug,t.versionSlug,t.docId,n),d=i.find(c[l],function(t){return t.id===e});d&&(d.state=r);var f=u[e][n];f||(f={}),f.revision=parseInt(o),f.state=r,f.contents=s.newTranslations.slice()},s}t.$inject=["$q","$resource","FilterUtil","UrlService","DocumentService","_"],angular.module("app").factory("PhraseCache",t)}(),function(){"use strict";function t(t,e,n,o,r){function i(t,e){return o.find(e,function(e){return e.id===t})}function a(e,n){return n&&(e=t.filterResources(e,["status"],n)),o.map(e,function(t){return t.id})}var s={};return s.phrases=[],s.getPhraseCount=function(t,n){return e.getStates(t.projectSlug,t.versionSlug,t.docId,t.localeId).then(function(t){var e=a(t,n.status);return e.length})},s.fetchAllPhrase=function(t,r,i,c){function u(t){var n=a(t,r.status);return isNaN(i)||(n=isNaN(c)?n.slice(i):n.slice(i,i+c)),e.getTransUnits(n,S).then(l).then(f)}function l(t){return o.map(t,function(t,e){var o=t.source,r=t[S];return{id:parseInt(e),sources:o.plural?o.contents:[o.content],translations:d(o,r),newTranslations:d(o,r),plural:o.plural,status:n.getStatusInfo(r?r.state:"untranslated"),revision:r?parseInt(r.revision):0,wordCount:parseInt(o.wordCount)}})}function d(t,e){return t.plural?e&&e.contents?e.contents.slice():[]:e?[e.content]:[]}function f(n){return e.getStates(t.projectSlug,t.versionSlug,t.docId,S).then(function(t){return s.phrases=o.sortBy(n,function(e){var r=o.findIndex(t,function(t){return t.id===e.id});return r>=0?r:n.length}),s.phrases})}var S=t.localeId;return e.getStates(t.projectSlug,t.versionSlug,t.docId,S).then(u)},s.onTransUnitUpdated=function(t,o,r,a,c,u){e.onTransUnitUpdated(t,o,r,a,c,u);var l=i(o,s.phrases);l&&(l.translations=u.newTranslations.slice(),l.revision=a,l.status=n.getStatusInfo(c))},s.onTransUnitUpdateFailed=function(t){var e=i(t,s.phrases);e&&(e.newTranslations=e.translations.slice())},s.findNextId=function(t){return e.getStates(r.projectSlug,r.versionSlug,r.docId,r.localeId).then(function(e){var n,r;return n=o.findIndex(e,function(e){return e.id===t}),r=n+1=0?n-1:0,e[r].id})},s.findNextStatus=function(t,i){return e.getStates(r.projectSlug,r.versionSlug,r.docId,r.localeId).then(function(e){var r,a,s=n.getStatusInfo(i);r=o.findIndex(e,function(e){return e.id===t});for(var c=r+1;c '},newline:{regex:/\n/g,template:'\n'},tab:{regex:/\t/g,template:' '}};return{restrict:"A",required:["ngBind"],scope:{ngBind:"="},link:function(n,o){n.$watch("ngBind",function(n){n=t(n,e.space),n=t(n,e.newline),n=t(n,e.tab),o.html(n)})}}}angular.module("app").directive("renderWhitespaceCharacters",t)}(),function(){"use strict";function t(t,e,n,o){function r(r,i,a){function s(){t.update(t.SETTING.SUGGESTIONS_PANEL_HEIGHT,r.actualPosition)}function c(t){if(t)r.actualPosition=r.position,r.actualHeight=r.height;else{var e=r.actualPosition;r.position=e,r.actualPosition=0,r.actualHeight=0}setTimeout(S)}function u(t){"vertical"===a.resizer?l(t.pageX):d(e.innerHeight-t.pageY)}function l(t){var n=t,o=angular.element(document.querySelector(a.resizerLeft)),s=angular.element(document.querySelector(a.resizerRight)),c=E(a.resizerMax,e.innerHeight),u=a.resizerMin||parseInt(a.resizerWidth);r.actualPosition=n,n=g(n,c,u),i.css({left:n-r.actualHeight/2+"px"}),o.css({width:n+"px"}),s.css({left:n+"px"})}function d(t){var n=t,o=angular.element(document.querySelector(a.resizerTop)),s=angular.element(document.querySelector(a.resizerBottom)),c=E(a.resizerMax,e.innerHeight),u=a.resizerMin||r.actualHeight;r.actualPosition=n,n=g(n,c,u),i.css({bottom:n-r.actualHeight/2+"px"}),o.css({bottom:n+"px"}),s.css({height:n+"px"})}function f(){n.unbind("mousemove",u),n.unbind("mouseup",f),s()}function S(){"vertical"===a.resizer?l(r.actualPosition):d(r.actualPosition)}function g(t,e,n){return e&&t>e?e:n>t?n:t}function E(t,e){return/[0-9]*\.?[0-9]+%/.test(t)?Math.round(e*(parseInt(t.replace("%",""))/100)):parseInt(t)}r.height=parseInt(a.resizerHeight),r.actualHeight=r.height,r.position=E(t.get(t.SETTING.SUGGESTIONS_PANEL_HEIGHT),e.innerHeight),r.actualPosition=r.position,r.show=t.subscribe(t.SETTING.SHOW_SUGGESTIONS,function(t){r.show=t,c(t)}),c(r.show),i.addClass("Resizer"),"vertical"===a.resizer?(i.addClass("Resizer--vertical"),o(function(){l(r.actualPosition)})):(i.addClass("Resizer--horizontal"),o(function(){d(r.actualPosition)})),i.on("mousedown",function(t){t.preventDefault(),n.on("mousemove",u),n.on("mouseup",f)}),angular.element(e).bind("resize",function(){o.cancel(r.resizing),r.resizing=o(S)})}return{link:r}}t.$inject=["SettingsService","$window","$document","$timeout"],angular.module("app").directive("resizer",t)}(),function(){"use strict";function t(){var t=this;t.init=function(){var e=t.container[0],n=t.child[0],o=n.offsetWidth-e.offsetWidth;t.width=o/2}}angular.module("app").controller("ScrollbarWidthCtrl",t)}(),function(){"use strict";function t(){return{restrict:"A",controller:"ScrollbarWidthCtrl as scrollbarWidthCtrl",link:function(t,e,n,o){o.init(e)}}}function e(){return{restrict:"A",require:"?^scrollbarWidth",link:function(t,e,n,o){o&&e.css(n.scrollbarWidthElement,o.width)}}}function n(){return{restrict:"A",require:"?^scrollbarWidth",link:function(t,e,n,o){o&&(o.container=e)}}}function o(){return{restrict:"A",require:"?^scrollbarWidth",link:function(t,e,n,o){o&&(o.child=e)}}}angular.module("app").directive("scrollbarWidth",t).directive("scrollbarWidthElement",e).directive("scrollbarWidthContainer",n).directive("scrollbarWidthChild",o)}(),function(){"use strict";function t(t,e,n,o){function r(e,n){c(n);var r={};r[e]=n,o.extend(f,r),t.emitEvent(t.EVENT.USER_SETTING_CHANGED,{setting:e,value:n})}function i(t){o.each(t,function(t,e){r(e,t)})}function a(t){return o.has(f,t)?f[t]:void console.error("Tried to look up setting with unrecognized key: %s",t)}function s(e,o){return n.$on(t.EVENT.USER_SETTING_CHANGED,function(t,n){n.setting===e&&o(n.value)}),a(e)}function c(t){switch(typeof t){case"boolean":case"number":case"string":break;default:throw new Error('Invalid type for setting value: "'+typeof t+'".')}}var u=this;u.SETTING={SUGGESTIONS_AUTOFILL_ON_ROW_SELECT:"suggestionsAutofillOnRowSelect",SUGGESTIONS_SHOW_DIFFERENCE:"suggestionsShowDifference",SHOW_SUGGESTIONS:"showSuggestions",SUGGESTIONS_PANEL_HEIGHT:"suggestionsPanelHeight"};var l=u.SETTING,d={};d[l.SUGGESTIONS_AUTOFILL_ON_ROW_SELECT]=!0,d[l.SUGGESTIONS_SHOW_DIFFERENCE]=!1,d[l.SHOW_SUGGESTIONS]=!0,d[l.SUGGESTIONS_PANEL_HEIGHT]="30%";var f=o.clone(d);return{SETTING:l,update:r,updateAll:i,get:a,subscribe:s}}t.$inject=["EventService","$q","$rootScope","_"],angular.module("app").factory("SettingsService",t)}(),function(){"use strict";function t(t,e,n,o,r){function i(){return g}function a(){return E?E.sources:[]}function s(){return p}function c(t){m=t,T||u()}function u(){var t=v+f,e=t-Date.now(),n=e>0?e:f;T=o(function(){return T=null,h>=S?void u():void l()},n)}function l(){if(null!==m){var t=m;m=null,o.cancel(T),T=null,d(t)}}function d(t){E=t;var e=Date.now();v=e,h++,n.getSuggestionsForPhrase(t).then(function(t){e>I&&(I=e,p=t)},function(t){console.error("Error searching for phrase ",t)})["finally"](function(){h--,r.$broadcast("PhraseSuggestionsService:updated"),S>h&&l()})}var f=300,S=3,g=!1,E=null,p=[],T=null,h=0,v=Date.now(),I=Date.now(),m=null;return r.$on(e.EVENT.REQUEST_PHRASE_SUGGESTIONS,function(t,e){var n=e.phrase;if(!(m&&m.id===n.id||!m&&0===h&&E&&E.id===n.id)){if(h>=S)return void c(n);var o=v+f;if(Date.now()99.99&&100>t?"99.99":t>=99.9&&100>t?"99.9":Math.round(t)!==t?t.toFixed(1):t},i.topMatch=function(){return n.suggestion.matchDetails[0]},i.showSuggestionCopied=function(){i.copyButtonText="Copied",i.copyButtonDisabled=!0,r(function(){i.copyButtonDisabled=!1,i.copyButtonText="Copy Translation"},500)},i.copySuggestion=function(){i.showSuggestionCopied(),t.emitEvent(t.EVENT.COPY_FROM_SUGGESTION,{suggestion:n.suggestion})},n.$on("EditorSuggestionsCtrl:nth-suggestion-copied",function(t,e){e===n.index&&i.showSuggestionCopied()}),n.detail=i.topMatch(),n.user=n.detail.lastModifiedBy||"Annoymous",n.remaining=n.suggestion.matchDetails.length-1,n.isTextFlow="LOCAL_PROJECT"===n.detail.type,i}t.$inject=["EventService","$rootScope","$scope","_","$timeout"],angular.module("app").controller("SuggestionCtrl",t)}(),function(){"use strict";function t(t,e,n,o,r){function i(t){return s([t])}function a(t){return s(t.sources).then(function(n){return e.emitEvent(e.EVENT.PHRASE_SUGGESTION_COUNT,{id:t.id,count:n.length}),n})}function s(e){var o=t.context.srcLocale.localeId,i=t.context.localeId,a={query:{method:"POST",params:{from:o,to:i,searchType:"FUZZY_PLURAL"},isArray:!0}},s=r(n.SUGGESTIONS_URL,{},a);return s.query({},e).$promise.then(c)}function c(t){return o.chain(t).map(l).map(u).sortBy(["similarityPercent","bestMatchScore","bestMatchModificationDate","relevanceScore"]).reverse().value()}function u(t){var e,n,r=t.matchDetails[0];return"LOCAL_PROJECT"===r.type&&(e=r.lastModifiedDate,n="Translated"===r.contentState?0:1),"IMPORTED_TM"===r.type&&(e=r.lastChanged,n=2),o.assign({},t,{bestMatchScore:n,bestMatchModificationDate:e})}function l(t){var e=o.sortBy(t.matchDetails,d);return o.assign({},t,{matchDetails:e})}function d(t){if("IMPORTED_TM"===t.type)return"3"+t.lastChanged;if("LOCAL_PROJECT"===t.type){if("Translated"===t.contentState)return"2"+t.lastModifiedDate;if("Approved"===t.contentState)return"1"+t.lastModifiedDate}return"9"}return{getSuggestionsForPhrase:a,getSuggestionsForText:i}}t.$inject=["EditorService","EventService","UrlService","_","$resource"],angular.module("app").factory("SuggestionsService",t)}(),function(){"use strict";function t(t,e,n,o,r){function i(){return T>0}function a(){return g?[g]:[]}function s(){return E}function c(t){I=t,p||u()}function u(){var t=h+f,e=t-Date.now(),n=e>0?e:f;p=r(function(){return p=null,T>=S?void u():void l()},n)}function l(){if(null!==I){var t=I;I=null,r.cancel(p),p=null,d(t)}}function d(t){g=t;var e=Date.now();h=e,T++,n.getSuggestionsForText(t).then(function(t){e>v&&(v=e,E=t)},function(t){console.error("Error searching for text ",t)})["finally"](function(){T--,o.$broadcast("TextSuggestionsService:updated"),S>T&&l()})}var f=300,S=3,g=null,E=[],p=null,T=0,h=Date.now(),v=Date.now(),I=null;return o.$on(e.EVENT.REQUEST_TEXT_SUGGESTIONS,function(t,e){if(!(I&&I===e||!I&&0===T&&g===e)){if(""===e)return g=e,E=[],I=null,r.cancel(p),p=null,h=Date.now,v=Date.now(),void o.$broadcast("TextSuggestionsService:updated");if(T>=S)return void c(e);var n=h+f;if(Date.now()')}}}angular.module("app").directive("toggleCheckbox",t)}(),function(){"use strict";function t(t){function e(t){return t=angular.uppercase(t),t&&"NEW"!==t?"NEEDREVIEW"===t&&(t="NEEDSWORK"):t="UNTRANSLATED",t}function n(t){return t=angular.lowercase(t),t&&"untranslated"!==t?"needswork"===t?"NeedReview":t.charAt(0).toUpperCase()+t.slice(1).toLowerCase():"New"}var o=this,r={UNTRANSLATED:{ID:"untranslated",NAME:"Untranslated",CSSCLASS:"neutral"},NEEDSWORK:{ID:"needswork",NAME:"Needs Work",CSSCLASS:"unsure"},TRANSLATED:{ID:"translated",NAME:"Translated",CSSCLASS:"success"},APPROVED:{ID:"approved",NAME:"Approved",CSSCLASS:"highlight"}};return o.getAll=function(){return r},o.getAllAsArray=function(){return t.values(r)},o.getStatusInfo=function(t){return r[e(t)]},o.getId=function(t){return r[e(t)].ID},o.getServerId=function(t){return n(t)},o.getName=function(t){return r[e(t)].NAME},o.getCSSClass=function(t){return r[e(t)].CSSCLASS},o}t.$inject=["_"],angular.module("app").factory("TransStatusService",t)}(),function(){"use strict";function t(t,e,n,o,r,i,a,s,c,u,l,d){function f(){S.selected||e.$apply(function(){a.emitEvent(a.EVENT.SELECT_TRANS_UNIT,{id:e.phrase.id,updateURL:!0,focus:!0},e)})}var S=this;S.selected=!1,S.focused=!1,S.focusedTranslationIndex=0,S.hasTranslationChanged=l.hasTranslationChanged,S.focusTranslation=function(){S.selected&&c("phrase-"+e.phrase.id+"-"+S.focusedTranslationIndex)},S.onTextAreaFocus=function(t,n){S.focused=!0,r.isUndefined(n)||(S.focusedTranslationIndex=n),S.selected||a.emitEvent(a.EVENT.SELECT_TRANS_UNIT,{id:t.id,updateURL:!0,focus:!0},e)},S.translationTextModified=function(t){a.emitEvent(a.EVENT.TRANSLATION_TEXT_MODIFIED,t)},S.getPhrase=function(){return e.phrase},S.init=function(){i.addController(e.phrase.id,S),o.id&&parseInt(o.id)===e.phrase.id&&a.emitEvent(a.EVENT.SELECT_TRANS_UNIT,{id:o.id,updateURL:!1,focus:o.selected})},S.copySource=function(t,n,o){t.stopPropagation(),a.emitEvent(a.EVENT.COPY_FROM_SOURCE,{phrase:n,sourceIndex:o},e)},S.undoEdit=function(t,n){t.stopPropagation(),a.emitEvent(a.EVENT.UNDO_EDIT,n,e)},S.cancelEdit=function(t,n){t.stopPropagation(),a.emitEvent(a.EVENT.CANCEL_EDIT,n,e)},S.saveAs=function(t,e,n){u.saveTranslationCallBack(t,e,n)},S.getLocaleName=function(t){return s.getName(t)},S.toggleSaveAsOptions=function(t){a.broadcastEvent(t?"openDropdown":"closeDropdown",{},e),t&&c(e.phrase.id+"-saveAsOption-0")};var g=d.SETTING.SHOW_SUGGESTIONS;return e.showSuggestions=d.subscribe(g,function(t){e.showSuggestions=t}),t.$on(a.EVENT.SUGGESTIONS_SEARCH_TOGGLE,function(t,e){S.suggestionsSearchIsActive=e}),S.toggleSuggestionPanel=function(){S.suggestionsSearchIsActive?a.emitEvent(a.EVENT.SUGGESTIONS_SEARCH_TOGGLE,!1):d.update(g,!e.showSuggestions)},e.suggestionCount=0,t.$on(a.EVENT.PHRASE_SUGGESTION_COUNT,function(t,n){n.id===e.phrase.id&&(e.suggestionCount=n.count)}),S.cancelSaveAsMode=function(){u.cancelSaveAsModeIfOn()},e.$on("$destroy",function(){n.unbind("click",f),n.unbind("focus",f)}),S.updateSaveButton=function(t){S.saveButtonStatus=l.getSaveButtonStatus(e.phrase),S.saveButtonOptions=i.getSaveButtonOptions(S.saveButtonStatus,e.phrase),S.saveButtonText=S.saveButtonStatus.NAME,S.saveButtonDisabled=!l.hasTranslationChanged(t),S.loadingClass="",S.savingStatus=""},S.phraseSaving=function(t){S.loadingClass="is-loading",S.saveButtonStatus=S.savingStatus=t.status,S.saveButtonOptions=i.getSaveButtonOptions(S.saveButtonStatus,t.phrase),S.saveButtonText="Saving…",S.saveButtonDisabled=!0},S.saveButtonOptionsAvailable=function(){return!r.isEmpty(S.saveButtonOptions)},S.selectTransUnit=function(t){S.selected||a.emitEvent(a.EVENT.SELECT_TRANS_UNIT,{id:t.id,updateURL:!0,focus:!0},e)},S}t.$inject=["$rootScope","$scope","$element","$stateParams","_","TransUnitService","EventService","LocaleService","focus","EditorShortcuts","PhraseUtil","SettingsService"],angular.module("app").controller("TransUnitCtrl",t)}(),function(){"use strict";function t(t,e,n,o,r,i,a,s,c,u,l,d,f){function S(t,e){var n=0;if(t.plural){var o=N[t.id];n=o.focusedTranslationIndex}t.newTranslations[n]=e,s.emitEvent(s.EVENT.TRANSLATION_TEXT_MODIFIED,t),s.emitEvent(s.EVENT.FOCUS_TRANSLATION,t)}function g(t,e){t.newTranslations=e.slice(),s.emitEvent(s.EVENT.TRANSLATION_TEXT_MODIFIED,t),s.emitEvent(s.EVENT.FOCUS_TRANSLATION,t)}function E(t,e){var n=N[e.id];n.updateSaveButton(e)}function p(t,e){var n=N[e.phrase.id];n.phraseSaving(e),s.emitEvent(s.EVENT.FOCUS_TRANSLATION,e.phrase)}function T(t,e){t.selected!==e&&(t.selected=e||!1)}function h(t,e){var n=N[e.id];n.focusTranslation()}function v(t,e){var n=[];return"untranslated"===t.ID?n:(n=i("filter")(c.getAllAsArray(),{ID:"!untranslated"}),e.plural&&(d.hasNoTranslation(e)?n=i("filter")(n,{ID:"!needswork"}):d.hasEmptyTranslation(e)&&(n=i("filter")(n,{ID:"!translated"}))),u&&(n=i("filter")(n,{ID:"!approved"})),i("filter")(n,{ID:"!"+t.ID}))}var I,m=this,N={};return m.addController=function(t,e){N[t]=e},m.getSaveButtonOptions=function(t,e){return v(t,e)},n.$on(s.EVENT.TOGGLE_SAVE_OPTIONS,function(t,e){var n=N[e.id];n&&n.toggleSaveAsOptions(e.open)}),n.$on(s.EVENT.SELECT_TRANS_UNIT,function(t,n){var i=N[n.id],u=N[I],f=n.updateURL;s.emitEvent(s.EVENT.REQUEST_PHRASE_SUGGESTIONS,{phrase:i.getPhrase()}),i?(l.selectedTUCtrl=i,I&&I!==n.id&&(T(u,!1),d.hasTranslationChanged(u.getPhrase())&&s.emitEvent(s.EVENT.SAVE_TRANSLATION,{phrase:u.getPhrase(),status:c.getStatusInfo("TRANSLATED"),locale:r.localeId,docId:r.docId})),E(t,i.getPhrase()),I=n.id,T(i,!0),s.emitEvent(s.EVENT.FOCUS_TRANSLATION,n),f&&("editor.selectedContext.tu"!==o.current.name?o.go("editor.selectedContext.tu",{id:n.id,selected:n.focus.toString()}):(e.search("id",n.id),e.search("selected",n.focus.toString())))):a.displayWarning("Trans-unit not found:"+n.id)}),n.$on(s.EVENT.COPY_FROM_SOURCE,function(e,n){var o=0;if(n.phrase.plural&&(o=n.sourceIndex,t.isUndefined(o))){var r=N[n.phrase.id];o=r.focusedTranslationIndex,n.phrase.sources.length1;if(s){var c=r.translations.length;if(a.lengthc&&(a=t.first(a,c)),g(r,a)}else S(r,a[0])}}),n.$on(s.EVENT.UNDO_EDIT,function(t,e){d.hasTranslationChanged(e)&&g(e,e.translations)}),n.$on(s.EVENT.CANCEL_EDIT,function(t,o){I&&(T(N[I],!1),I=!1,l.selectedTUCtrl=null),e.search("selected",null),o||e.search("id",null),o&&f(function(){return n.$broadcast("blurOn","phrase-"+o.id)})}),n.$on(s.EVENT.TRANSLATION_TEXT_MODIFIED,E),n.$on(s.EVENT.FOCUS_TRANSLATION,h),n.$on(s.EVENT.SAVE_INITIATED,p),n.$on(s.EVENT.SAVE_COMPLETED,E),m}t.$inject=["_","$location","$rootScope","$state","$stateParams","$filter","MessageHandler","EventService","TransStatusService","PRODUCTION","EditorShortcuts","PhraseUtil","$timeout"],angular.module("app").factory("TransUnitService",t)}(),function(){"use strict";function t(){return{restrict:"E",required:["phrase","editorContext"],scope:{phrase:"=",firstPhrase:"=",editorContext:"="},controller:"TransUnitCtrl as transUnitCtrl",templateUrl:"components/transUnit/trans-unit.html",link:function(t,e,n,o){o.init()}}}angular.module("app").directive("transUnit",t)}(),function(){"use strict";function t(){return{restrict:"E",required:["editor"],scope:{editor:"="},templateUrl:"components/transUnitFilter/trans-unit-filter.html"}}angular.module("app").directive("transUnitFilter",t)}(),function(){"use strict";function t(t,e){function n(n){var o=t(e.USER_INFO_URL,{},{query:{method:"GET",params:{username:n}}});return o.query().$promise}function o(){var n=t(e.MY_INFO_URL,{},{query:{method:"GET"}});return n.query().$promise}return{settings:{editor:{hideMainNav:!1}},getUserInfo:n,getMyInfo:o}}t.$inject=["$resource","UrlService"],angular.module("app").factory("UserService",t)}(),function(){"use strict";function t(t,e){function n(t,n,o){return t&&n&&o?e.filter(t,function(t){return i(t,n,o)}):t}function o(t){var e={},n=Object.keys(t).filter(function(t){return-1===t.indexOf("$")});return n.forEach(function(n){e[n]=t[n]}),e}function r(t){var e=[],n=Object.keys(t).filter(function(t){return-1===t.indexOf("$")});return n.forEach(function(n){e.push(t[n])}),e}function i(n,o,r){return n&&o&&r?e.any(o,function(o){return e.any(r,function(e){return t.equals(n[o],e,!0)})}):!1}return{filterResources:n,cleanResourceList:r,cleanResourceMap:o}}t.$inject=["StringUtil","_"],angular.module("app").factory("FilterUtil",t)}(),function(){"use strict";function t(t,e){function n(e){return r(e)?t.getStatusInfo("untranslated"):i(e)?t.getStatusInfo("needswork"):o(e)?t.getStatusInfo("translated"):e.status}function o(t){var n=e.every(t.translations,function(e,n){return a(e)===a(t.newTranslations[n])});return!n}function r(t){return e.isEmpty(e.compact(t.newTranslations))}function i(t){return e.compact(t.newTranslations).length!==t.newTranslations.length}function a(t){return t||""}return{getSaveButtonStatus:n,hasTranslationChanged:o,hasNoTranslation:r,hasEmptyTranslation:i}}t.$inject=["TransStatusService","_"],angular.module("app").factory("PhraseUtil",t)}(),function(){"use strict";function t(){return{getWordStatistic:function(t){return"WORD"===t[0].unit?t[0]:t[1]},getMsgStatistic:function(t){return"MESSAGE"===t[0].unit?t[0]:t[1]}}}angular.module("app").factory("StatisticUtil",t)}(),function(){"use strict";function t(){function t(t,e,n){return n&&t&&e&&(t=t.toUpperCase(),e=e.toUpperCase()),0===t.lastIndexOf(e,0)}function e(t,e,n){return n&&t&&e&&(t=t.toUpperCase(),e=e.toUpperCase()),-1!==t.indexOf(e,t.length-e.length)}function n(t,e,n){return n&&t&&e&&(t=t.toUpperCase(),e=e.toUpperCase()),t===e}return{startsWith:t,endsWith:e,equals:n}}angular.module("app").factory("StringUtil",t)}(),function(){"use strict";function t(t,e,n,o,r){function i(){return l+Array.prototype.join.call(arguments,"")}function a(t){return function(e){return t(e)}}location.origin||(location.origin=window.location.protocol+"//"+window.location.hostname+(window.location.port?":"+window.location.port:""));var s=this,c="http://www.gravatar.com/avatar",u="config.json",l="",d={},f=location.origin+location.pathname+"translations";return s.serverContextPath="",s.init=function(){return l?n.when(l):e.get(u).then(function(t){var e=t.data;if(e.baseUrl)l=e.baseUrl;else{var n=e.appPath.replace(/^\//g,""),o=location.href.indexOf(n);s.serverContextPath=location.origin+location.pathname,o>=0&&(s.serverContextPath=location.href.substring(0,o)),s.serverContextPath=s.serverContextPath.replace(/\/?$/,"/"),l=s.serverContextPath+"rest"}d=r.mapValues({project:"/project/:projectSlug",docs:"/project/:projectSlug/version/:versionSlug/docs",locales:"/project/:projectSlug/version/:versionSlug/locales",status:"/project/:projectSlug/version/:versionSlug/doc/:docId/status/:localeId",textFlows:"/source+trans/:localeId",docStats:"/stats/project/:projectSlug/version/:versionSlug/doc/:docId/locale/:localeId",myInfo:"/user",userInfo:"/user/:username",translation:"/trans/:localeId",allLocales:"/locales",suggestions:"/suggestions"},a(i)),s.PROJECT_URL=d.project,s.LOCALE_LIST_URL=d.locales,s.DOCUMENT_LIST_URL=d.docs,s.TRANSLATION_STATUS_URL=d.status,s.TEXT_FLOWS_URL=d.textFlows,s.DOC_STATISTIC_URL=d.docStats,s.MY_INFO_URL=d.myInfo,s.USER_INFO_URL=d.userInfo,s.TRANSLATION_URL=d.translation,s.ALL_LOCALE_URL=d.allLocales,s.SUGGESTIONS_URL=d.suggestions,s.PROJECT_PAGE=function(t,e){return s.serverContextPath+"iteration/view/"+t+"/"+e},s.DASHBOARD_PAGE=s.serverContextPath+"dashboard"})},s.readValue=function(e){return t.search()[e]},s.gravatarUrl=function(t,e){return c+"/"+t+"?d=mm&r=g&s="+e},s.uiTranslationURL=function(t){return f+"/"+t+".json"},s.uiTranslationListURL=f+"/locales",s}t.$inject=["$location","$http","$q","$stateParams","_"],angular.module("app").factory("UrlService",t)}(); -//# sourceMappingURL=../maps/app.js.map \ No newline at end of file +//# sourceMappingURL=../maps/app.js.map diff --git a/zanata-war/src/main/webapp/app/js/libs.js b/zanata-war/src/main/webapp/app/js/libs.js index 51f7bb3efc..46ea179a7d 100644 --- a/zanata-war/src/main/webapp/app/js/libs.js +++ b/zanata-war/src/main/webapp/app/js/libs.js @@ -10,4 +10,4 @@ r+=this.Patch_Margin,(n=t.substring(e.start2-r,e.start2))&&e.diffs.unshift([0,n] }),hljs.registerLanguage("vhdl",function(e){return{cI:!0,k:{keyword:"abs access after alias all and architecture array assert attribute begin block body buffer bus case component configuration constant context cover disconnect downto default else elsif end entity exit fairness file for force function generate generic group guarded if impure in inertial inout is label library linkage literal loop map mod nand new next nor not null of on open or others out package port postponed procedure process property protected pure range record register reject release rem report restrict restrict_guarantee return rol ror select sequence severity shared signal sla sll sra srl strong subtype then to transport type unaffected units until use variable vmode vprop vunit wait when while with xnor xor",typename:"boolean bit character severity_level integer time delay_length natural positive string bit_vector file_open_kind file_open_status std_ulogic std_ulogic_vector std_logic std_logic_vector unsigned signed boolean_vector integer_vector real_vector time_vector"},i:"{",c:[e.CBLCLM,{cN:"comment",b:"--",e:"$"},e.QSM,e.CNM,{cN:"literal",b:"'(U|X|0|1|Z|W|L|H|-)'",c:[e.BE]},{cN:"attribute",b:"'[A-Za-z](_?[A-Za-z0-9])*",c:[e.BE]}]}}),hljs.registerLanguage("parser3",function(e){return{sL:"xml",r:0,c:[{cN:"comment",b:"^#",e:"$"},{cN:"comment",b:"\\^rem{",e:"}",r:10,c:[{b:"{",e:"}",c:["self"]}]},{cN:"preprocessor",b:"^@(?:BASE|USE|CLASS|OPTIONS)$",r:10},{cN:"title",b:"@[\\w\\-]+\\[[\\w^;\\-]*\\](?:\\[[\\w^;\\-]*\\])?(?:.*)$"},{cN:"variable",b:"\\$\\{?[\\w\\-\\.\\:]+\\}?"},{cN:"keyword",b:"\\^[\\w\\-\\.\\:]+"},{cN:"number",b:"\\^#[0-9a-fA-F]+"},e.CNM]}}),hljs.registerLanguage("scala",function(e){var t={cN:"annotation",b:"@[A-Za-z]+"},n={cN:"string",b:'u?r?"""',e:'"""',r:10};return{k:"type yield lazy override def with val var false true sealed abstract private trait object null if for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws",c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",c:[{cN:"javadoctag",b:"@[A-Za-z]+"}],r:10},e.CLCM,e.CBLCLM,n,e.ASM,e.QSM,{cN:"class",b:"((case )?class |object |trait )",e:"({|$)",i:":",k:"case class trait object",c:[{bK:"extends with",r:10},e.UTM,{cN:"params",b:"\\(",e:"\\)",c:[e.ASM,e.QSM,n,t]}]},e.CNM,t]}}),hljs.registerLanguage("cpp",function(e){var t={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long throw volatile static protected bool template mutable if public friend do return goto auto void enum else break new extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue wchar_t inline delete alignof char16_t char32_t constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex _Complex _Imaginary",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf"};return{aliases:["c"],k:t,i:"",i:"\\n"},e.CLCM]},{cN:"stl_container",b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:t,r:10,c:["self"]}]}}),window.Modernizr=function(e,t,n){function r(e){v.cssText=e}function i(e,t){return typeof e===t}function a(e,t){return!!~(""+e).indexOf(t)}function o(e,t){for(var r in e){var i=e[r];if(!a(i,"-")&&v[i]!==n)return"pfx"==t?i:!0}return!1}function s(e,t,r){for(var a in e){var o=t[e[a]];if(o!==n)return r===!1?e[a]:i(o,"function")?o.bind(r||t):o}return!1}function l(e,t,n){var r=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+S.join(r+" ")+r).split(" ");return i(t,"string")||i(t,"undefined")?o(a,t):(a=(e+" "+C.join(r+" ")+r).split(" "),s(a,t,n))}var c,u,f,p="2.8.3",d={},h=!0,m=t.documentElement,g="modernizr",b=t.createElement(g),v=b.style,y=({}.toString," -webkit- -moz- -o- -ms- ".split(" ")),$="Webkit Moz O ms",S=$.split(" "),C=$.toLowerCase().split(" "),x={svg:"http://www.w3.org/2000/svg"},w={},D=[],E=D.slice,N=function(e,n,r,i){var a,o,s,l,c=t.createElement("div"),u=t.body,f=u||t.createElement("body");if(parseInt(r,10))for(;r--;)s=t.createElement("div"),s.id=i?i[r]:g+(r+1),c.appendChild(s);return a=["­",'"].join(""),c.id=g,(u?c:f).innerHTML+=a,f.appendChild(c),u||(f.style.background="",f.style.overflow="hidden",l=m.style.overflow,m.style.overflow="hidden",m.appendChild(f)),o=n(c,e),u?c.parentNode.removeChild(c):(f.parentNode.removeChild(f),m.style.overflow=l),!!o},M={}.hasOwnProperty;f=i(M,"undefined")||i(M.call,"undefined")?function(e,t){return t in e&&i(e.constructor.prototype[t],"undefined")}:function(e,t){return M.call(e,t)},Function.prototype.bind||(Function.prototype.bind=function(e){var t=this;if("function"!=typeof t)throw new TypeError;var n=E.call(arguments,1),r=function(){if(this instanceof r){var i=function(){};i.prototype=t.prototype;var a=new i,o=t.apply(a,n.concat(E.call(arguments)));return Object(o)===o?o:a}return t.apply(e,n.concat(E.call(arguments)))};return r}),w.touch=function(){var n;return"ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch?n=!0:N(["@media (",y.join("touch-enabled),("),g,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(e){n=9===e.offsetTop}),n},w.csstransforms=function(){return!!l("transform")},w.csstransforms3d=function(){var e=!!l("perspective");return e&&"webkitPerspective"in m.style&&N("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(t){e=9===t.offsetLeft&&3===t.offsetHeight}),e},w.svg=function(){return!!t.createElementNS&&!!t.createElementNS(x.svg,"svg").createSVGRect};for(var T in w)f(w,T)&&(u=T.toLowerCase(),d[u]=w[T](),D.push((d[u]?"":"no-")+u));return d.addTest=function(e,t){if("object"==typeof e)for(var r in e)f(e,r)&&d.addTest(r,e[r]);else{if(e=e.toLowerCase(),d[e]!==n)return d;t="function"==typeof t?t():t,"undefined"!=typeof h&&h&&(m.className+=" "+(t?"":"no-")+e),d[e]=t}return d},r(""),b=c=null,d._version=p,d._prefixes=y,d._domPrefixes=C,d._cssomPrefixes=S,d.testProp=function(e){return o([e])},d.testAllProps=l,d.testStyles=N,m.className=m.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(h?" js "+D.join(" "):""),d}(this,this.document),function(e,t){function n(e,t,n){return e.addEventListener?void e.addEventListener(t,n,!1):void e.attachEvent("on"+t,n)}function r(e){if("keypress"==e.type){var t=String.fromCharCode(e.which);return e.shiftKey||(t=t.toLowerCase()),t}return w[e.which]?w[e.which]:D[e.which]?D[e.which]:String.fromCharCode(e.which).toLowerCase()}function i(e,t){return e.sort().join(",")===t.sort().join(",")}function a(e){e=e||{};var t,n=!1;for(t in A)e[t]?n=!0:A[t]=0;n||(L=!1)}function o(e,t,n,r,a,o){var s,l,c=[],u=n.type;if(!M[e])return[];for("keyup"==u&&d(e)&&(t=[e]),s=0;s95&&112>e||w.hasOwnProperty(e)&&(C[w[e]]=e)}return C}function g(e,t,n){return n||(n=m()[e]?"keydown":"keypress"),"keypress"==n&&t.length&&(n="keydown"),n}function b(e,t,n,i){function o(t){return function(){L=t,++A[e],h()}}function s(t){u(n,t,e),"keyup"!==i&&(k=r(t)),setTimeout(a,10)}A[e]=0;for(var l=0;l1?void b(e,s,t,n):(a=y(e,n),M[a.key]=M[a.key]||[],o(a.key,a.modifiers,{type:a.action},r,e,i),void M[a.key][r?"unshift":"push"]({callback:t,modifiers:a.modifiers,action:a.action,seq:r,level:i,combo:e}))}function S(e,t,n){for(var r=0;r":".","?":"/","|":"\\"},N={option:"alt",command:"meta","return":"enter",escape:"esc",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},M={},T={},A={},k=!1,P=!1,L=!1,_=1;20>_;++_)w[111+_]="f"+_;for(_=0;9>=_;++_)w[_+96]=_;n(t,"keypress",p),n(t,"keydown",p),n(t,"keyup",p);var I={bind:function(e,t,n){return e=e instanceof Array?e:[e],S(e,t,n),this},unbind:function(e,t){return I.bind(e,function(){},t)},trigger:function(e,t){return T[e+":"+t]&&T[e+":"+t]({},e),this},reset:function(){return M={},T={},this},stopCallback:function(e,t){return(" "+t.className+" ").indexOf(" mousetrap ")>-1?!1:"INPUT"==t.tagName||"SELECT"==t.tagName||"TEXTAREA"==t.tagName||t.isContentEditable},handleKey:f};e.Mousetrap=I,"function"==typeof define&&define.amd&&define(I)}(window,document),function e(t,n,r){function i(o,s){if(!n[o]){if(!t[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(a)return a(o,!0);throw new Error("Cannot find module '"+o+"'")}var c=n[o]={exports:{}};t[o][0].call(c.exports,function(e){var n=t[o][1][e];return i(n?n:e)},c,c.exports,e,t,n,r)}return n[o].exports}for(var a="function"==typeof require&&require,o=0;o"+t+"");r(o.contents())(i);var s=a.contents(),p=o.contents();n.enter(p,a),n.leave(s)}var u=t(o.translateN),f=null;o.translateN&&i.$watch(o.translateN,s),i.$on("gettextLanguageChanged",s),s()}}}}}]),angular.module("gettext").filter("translate",["gettextCatalog",function(e){function t(t){return e.getString(t)}return t.$stateful=!0,t}]),angular.module("gettext").factory("gettextPlurals",function(){return function(e,t){switch(e){case"ay":case"bo":case"cgg":case"dz":case"fa":case"id":case"ja":case"jbo":case"ka":case"kk":case"km":case"ko":case"ky":case"lo":case"ms":case"my":case"sah":case"su":case"th":case"tt":case"ug":case"vi":case"wo":case"zh":return 0;case"is":return t%10!=1||t%100==11?1:0;case"jv":return 0!=t?1:0;case"mk":return 1==t||t%10==1?0:1;case"ach":case"ak":case"am":case"arn":case"br":case"fil":case"fr":case"gun":case"ln":case"mfe":case"mg":case"mi":case"oc":case"pt_BR":case"tg":case"ti":case"tr":case"uz":case"wa":case"zh":return t>1?1:0;case"lv":return t%10==1&&t%100!=11?0:0!=t?1:2;case"lt":return t%10==1&&t%100!=11?0:t%10>=2&&(10>t%100||t%100>=20)?1:2;case"be":case"bs":case"hr":case"ru":case"sr":case"uk":return t%10==1&&t%100!=11?0:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?1:2;case"mnk":return 0==t?0:1==t?1:2;case"ro":return 1==t?0:0==t||t%100>0&&20>t%100?1:2;case"pl":return 1==t?0:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?1:2;case"cs":case"sk":return 1==t?0:t>=2&&4>=t?1:2;case"sl":return t%100==1?1:t%100==2?2:t%100==3||t%100==4?3:0;case"mt":return 1==t?0:0==t||t%100>1&&11>t%100?1:t%100>10&&20>t%100?2:3;case"gd":return 1==t||11==t?0:2==t||12==t?1:t>2&&20>t?2:3;case"cy":return 1==t?0:2==t?1:8!=t&&11!=t?2:3;case"kw":return 1==t?0:2==t?1:3==t?2:3;case"ga":return 1==t?0:2==t?1:7>t?2:11>t?3:4;case"ar":return 0==t?0:1==t?1:2==t?2:t%100>=3&&10>=t%100?3:t%100>=11?4:5;default:return 1!=t?1:0}}}),function(){"use strict";angular.module("cfp.hotkeys",[]).provider("hotkeys",function(){this.includeCheatSheet=!0,this.templateTitle="Keyboard Shortcuts:",this.template='',this.cheatSheetHotkey="?",this.cheatSheetDescription="Show / hide this help menu",this.$get=["$rootElement","$rootScope","$compile","$window","$document",function(e,t,n,r,i){function a(e){var t={command:"⌘",shift:"⇧",left:"←",right:"→",up:"↑",down:"↓","return":"↩",backspace:"⌫"};e=e.split("+");for(var n=0;n=0?"command":"ctrl"),e[n]=t[e[n]]||e[n];return e.join(" + ")}function o(e,t,n,r,i,a){this.combo=e instanceof Array?e:[e],this.description=t,this.callback=n,this.action=r,this.allowIn=i,this.persistent=a}function s(){for(var e=h.hotkeys.length;e--;){var t=h.hotkeys[e];t&&!t.persistent&&u(t)}}function l(){h.helpVisible=!h.helpVisible,h.helpVisible?(y=f("esc"),u("esc"),c("esc",y.description,l)):(u("esc"),y!==!1&&c(y))}function c(e,t,n,r,i,a){var s,l=["INPUT","SELECT","TEXTAREA"],c=Object.prototype.toString.call(e);if("[object Object]"===c&&(t=e.description,n=e.callback,r=e.action,a=e.persistent,i=e.allowIn,e=e.combo),t instanceof Function?(r=n,n=t,t="$$undefined$$"):angular.isUndefined(t)&&(t="$$undefined$$"),void 0===a&&(a=!0),"function"==typeof n){s=n,i instanceof Array||(i=[]);for(var u,f=0;f-1)t=!0;else for(var i=0;i-1?(h.hotkeys[i].combo.length>1?h.hotkeys[i].combo.splice(h.hotkeys[i].combo.indexOf(t),1):h.hotkeys.splice(i,1),!0):!1}function f(e){for(var t,n=0;n-1)return t;return!1}function p(e){return e.$id in m||(m[e.$id]=[],e.$on("$destroy",function(){for(var t=m[e.$id].length;t--;)u(m[e.$id][t]),delete m[e.$id][t]})),{add:function(t){var n;return n=arguments.length>1?c.apply(this,arguments):c(t),m[e.$id].push(n),this}}}function d(e){return function(n,r){if(e instanceof Array){var i=e[0],a=e[1];e=function(){a.scope.$eval(i)}}t.$apply(function(){e(n,f(r))})}}Mousetrap.stopCallback=function(e,t){return(" "+t.className+" ").indexOf(" mousetrap ")>-1?!1:t.contentEditable&&"true"==t.contentEditable},o.prototype.format=function(){for(var e=this.combo[0],t=e.split(/[\s]/),n=0;n>>0,r=Number(arguments[2])||0;for(r=0>r?Math.ceil(r):Math.floor(r),0>r&&(r+=n);n>r;r++)if(r in e&&e[r]===t)return r;return-1}function l(e,t,n,r){var i,l=a(n,r),c={},u=[];for(var f in l)if(l[f].params&&(i=o(l[f].params),i.length))for(var p in i)s(u,i[p])>=0||(u.push(i[p]),c[i[p]]=e[i[p]]);return O({},c,t)}function c(e,t,n){if(!n){n=[];for(var r in e)n.push(r)}for(var i=0;i "));if(v[n]=r,_(e))g.push(n,[function(){return t.get(e)}],c);else{var i=t.annotate(e);R(i,function(e){e!==n&&l.hasOwnProperty(e)&&d(l[e],e)}),g.push(n,e,i)}b.pop(),v[n]=a}}function h(e){return I(e)&&e.then&&e.$$promises}if(!I(l))throw new Error("'invocables' must be an object");var m=o(l||{}),g=[],b=[],v={};return R(l,d),l=b=v=null,function(r,a,o){function s(){--$||(S||i(y,a.$$values),b.$$values=y,b.$$promises=b.$$promises||!0,delete b.$$inheritedValues,d.resolve(y))}function l(e){b.$$failure=e,d.reject(e)}function c(n,i,a){function c(e){f.reject(e),l(e)}function u(){if(!P(b.$$failure))try{f.resolve(t.invoke(i,o,y)),f.promise.then(function(e){y[n]=e,s()},c)}catch(e){c(e)}}var f=e.defer(),p=0;R(a,function(e){v.hasOwnProperty(e)&&!r.hasOwnProperty(e)&&(p++,v[e].then(function(t){y[e]=t,--p||u()},c))}),p||u(),v[n]=f.promise}if(h(r)&&o===n&&(o=a,a=r,r=null),r){if(!I(r))throw new Error("'locals' must be an object")}else r=u;if(a){if(!h(a))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else a=p;var d=e.defer(),b=d.promise,v=b.$$promises={},y=O({},r),$=1+g.length/3,S=!1;if(P(a.$$failure))return l(a.$$failure),b;a.$$inheritedValues&&i(y,f(a.$$inheritedValues,m)),O(v,a.$$promises),a.$$values?(S=i(y,f(a.$$values,m)),b.$$inheritedValues=f(a.$$values,m),s()):(a.$$inheritedValues&&(b.$$inheritedValues=f(a.$$inheritedValues,m)),a.then(s,l));for(var C=0,x=g.length;x>C;C+=3)r.hasOwnProperty(g[C])?s():c(g[C],g[C+1],g[C+2]);return b}},this.resolve=function(e,t,n,r){return this.study(e)(t,n,r)}}function m(e,t,n){this.fromConfig=function(e,t,n){return P(e.template)?this.fromString(e.template,t):P(e.templateUrl)?this.fromUrl(e.templateUrl,t):P(e.templateProvider)?this.fromProvider(e.templateProvider,t,n):null},this.fromString=function(e,t){return L(e)?e(t):e},this.fromUrl=function(n,r){return L(n)&&(n=n(r)),null==n?null:e.get(n,{cache:t,headers:{Accept:"text/html"}}).then(function(e){return e.data})},this.fromProvider=function(e,t,r){return n.invoke(e,null,r||{params:t})}}function g(e,t,i){function a(t,n,r,i){if(g.push(t),h[t])return h[t];if(!/^\w+(-+\w+)*(?:\[\])?$/.test(t))throw new Error("Invalid parameter name '"+t+"' in pattern '"+e+"'");if(m[t])throw new Error("Duplicate parameter name '"+t+"' in pattern '"+e+"'");return m[t]=new G.Param(t,n,r,i),m[t]}function o(e,t,n){var r=["",""],i=e.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!t)return i;switch(n){case!1:r=["(",")"];break;case!0:r=["?(",")?"];break;default:r=["("+n+"|",")?"]}return i+r[0]+t+r[1]}function s(n,i){var a,o,s,l,c;return a=n[2]||n[3],c=t.params[a],s=e.substring(p,n.index),o=i?n[4]:n[4]||("*"==n[1]?".*":null),l=G.type(o||"string")||r(G.type("string"),{pattern:new RegExp(o)}),{id:a,regexp:o,segment:s,type:l,cfg:c}}t=O({params:{}},I(t)?t:{});var l,c=/([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,u=/([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,f="^",p=0,d=this.segments=[],h=i?i.params:{},m=this.params=i?i.params.$$new():new G.ParamSet,g=[];this.source=e;for(var b,v,y;(l=c.exec(e))&&(b=s(l,!1),!(b.segment.indexOf("?")>=0));)v=a(b.id,b.type,b.cfg,"path"),f+=o(b.segment,v.type.pattern.source,v.squash),d.push(b.segment),p=c.lastIndex;y=e.substring(p);var $=y.indexOf("?");if($>=0){var S=this.sourceSearch=y.substring($);if(y=y.substring(0,$),this.sourcePath=e.substring(0,p+$),S.length>0)for(p=0;l=u.exec(S);)b=s(l,!0),v=a(b.id,b.type,b.cfg,"search"),p=c.lastIndex}else this.sourcePath=e,this.sourceSearch="";f+=o(y)+(t.strict===!1?"/?":"")+"$",d.push(y),this.regexp=new RegExp(f,t.caseInsensitive?"i":n),this.prefix=d[0],this.$$paramNames=g}function b(e){O(this,e)}function v(){function e(e){return null!=e?e.toString().replace(/\//g,"%2F"):e}function i(e){return null!=e?e.toString().replace(/%2F/g,"/"):e}function a(e){return this.pattern.test(e)}function l(){return{strict:y,caseInsensitive:m}}function c(e){return L(e)||F(e)&&L(e[e.length-1])}function u(){for(;x.length;){var e=x.shift();if(e.pattern)throw new Error("You cannot override a type's .pattern at runtime.");t.extend(S[e.name],h.invoke(e.def))}}function f(e){O(this,e||{})}G=this;var h,m=!1,y=!0,$=!1,S={},C=!0,x=[],w={string:{encode:e,decode:i,is:a,pattern:/[^/]*/},"int":{encode:e,decode:function(e){return parseInt(e,10)},is:function(e){return P(e)&&this.decode(e.toString())===e},pattern:/\d+/},bool:{encode:function(e){return e?1:0},decode:function(e){return 0!==parseInt(e,10)},is:function(e){return e===!0||e===!1},pattern:/0|1/},date:{encode:function(e){return this.is(e)?[e.getFullYear(),("0"+(e.getMonth()+1)).slice(-2),("0"+e.getDate()).slice(-2)].join("-"):n},decode:function(e){if(this.is(e))return e;var t=this.capture.exec(e);return t?new Date(t[1],t[2]-1,t[3]):n},is:function(e){return e instanceof Date&&!isNaN(e.valueOf())},equals:function(e,t){return this.is(e)&&this.is(t)&&e.toISOString()===t.toISOString()},pattern:/[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,capture:/([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/},json:{encode:t.toJson,decode:t.fromJson,is:t.isObject,equals:t.equals,pattern:/[^/]*/},any:{encode:t.identity,decode:t.identity,is:t.identity,equals:t.equals,pattern:/.*/}};v.$$getDefaultValue=function(e){if(!c(e.value))return e.value;if(!h)throw new Error("Injectable functions cannot be called at configuration time");return h.invoke(e.value)},this.caseInsensitive=function(e){return P(e)&&(m=e),m},this.strictMode=function(e){return P(e)&&(y=e),y},this.defaultSquashPolicy=function(e){if(!P(e))return $;if(e!==!0&&e!==!1&&!_(e))throw new Error("Invalid squash policy: "+e+". Valid policies: false, true, arbitrary-string");return $=e,e},this.compile=function(e,t){return new g(e,O(l(),t))},this.isMatcher=function(e){if(!I(e))return!1;var t=!0;return R(g.prototype,function(n,r){L(n)&&(t=t&&P(e[r])&&L(e[r]))}),t},this.type=function(e,t,n){if(!P(t))return S[e];if(S.hasOwnProperty(e))throw new Error("A type named '"+e+"' has already been defined.");return S[e]=new b(O({name:e},t)),n&&(x.push({name:e,def:n}),C||u()),this},R(w,function(e,t){S[t]=new b(O({name:t},e))}),S=r(S,{}),this.$get=["$injector",function(e){return h=e,C=!1,u(),R(w,function(e,t){S[t]||(S[t]=new b(e))}),this}],this.Param=function(e,t,r,i){function a(e){var t=I(e)?o(e):[],n=-1===s(t,"value")&&-1===s(t,"type")&&-1===s(t,"squash")&&-1===s(t,"array");return n&&(e={value:e}),e.$$fn=c(e.value)?e.value:function(){return e.value},e}function l(t,n,r){if(t.type&&n)throw new Error("Param '"+e+"' has two type configurations.");return n?n:t.type?t.type instanceof b?t.type:new b(t.type):"config"===r?S.any:S.string}function u(){var t={array:"search"===i?"auto":!1},n=e.match(/\[\]$/)?{array:!0}:{};return O(t,n,r).array}function f(e,t){var n=e.squash;if(!t||n===!1)return!1;if(!P(n)||null==n)return $;if(n===!0||_(n))return n;throw new Error("Invalid squash policy: '"+n+"'. Valid policies: false, true, or arbitrary string")}function m(e,t,r,i){var a,o,l=[{from:"",to:r||t?n:""},{from:null,to:r||t?n:""}];return a=F(e.replace)?e.replace:[],_(i)&&a.push({from:i,to:n}),o=d(a,function(e){return e.from}),p(l,function(e){return-1===s(o,e.from)}).concat(a)}function g(){if(!h)throw new Error("Injectable functions cannot be called at configuration time");return h.invoke(r.$$fn)}function v(e){function t(e){return function(t){return t.from===e}}function n(e){var n=d(p(C.replace,t(e)),function(e){return e.to});return n.length?n[0]:e}return e=n(e),P(e)?C.type.decode(e):g()}function y(){return"{Param:"+e+" "+t+" squash: '"+D+"' optional: "+w+"}"}var C=this;r=a(r),t=l(r,t,i);var x=u();t=x?t.$asArray(x,"search"===i):t,"string"!==t.name||x||"path"!==i||r.value!==n||(r.value="");var w=r.value!==n,D=f(r,w),E=m(r,x,w,D);O(this,{id:e,type:t,location:i,array:x,squash:D,replace:E,isOptional:w,value:v,dynamic:n,config:r,toString:y})},f.prototype={$$new:function(){return r(this,O(new f,{$$parent:this}))},$$keys:function(){for(var e=[],t=[],n=this,r=o(f.prototype);n;)t.push(n),n=n.$$parent;return t.reverse(),R(t,function(t){R(o(t),function(t){-1===s(e,t)&&-1===s(r,t)&&e.push(t)})}),e},$$values:function(e){var t={},n=this;return R(n.$$keys(),function(r){t[r]=n[r].value(e&&e[r])}),t},$$equals:function(e,t){var n=!0,r=this;return R(r.$$keys(),function(i){var a=e&&e[i],o=t&&t[i];r[i].type.equals(a,o)||(n=!1)}),n},$$validates:function(e){var t,n,r,i=!0,a=this;return R(this.$$keys(),function(o){r=a[o],n=e[o],t=!n&&r.isOptional,i=i&&(t||!!r.type.is(n))}),i},$$parent:n},this.ParamSet=f}function y(e,r){function i(e){var t=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(e.source);return null!=t?t[1].replace(/\\(.)/g,"$1"):""}function a(e,t){return e.replace(/\$(\$|\d{1,2})/,function(e,n){return t["$"===n?0:Number(n)]})}function o(e,t,n){if(!n)return!1;var r=e.invoke(t,t,{$match:n});return P(r)?r:!0}function s(r,i,a,o){function s(e,t,n){return"/"===m?e:t?m.slice(0,-1)+e:n?m.slice(1)+e:e}function p(e){function t(e){var t=e(a,r);return t?(_(t)&&r.replace().url(t),!0):!1}if(!e||!e.defaultPrevented){var i=h&&r.url()===h;if(h=n,i)return!0;var o,s=c.length;for(o=0;s>o;o++)if(t(c[o]))return;u&&t(u)}}function d(){return l=l||i.$on("$locationChangeSuccess",p)}var h,m=o.baseHref(),g=r.url();return f||d(),{sync:function(){p()},listen:function(){return d()},update:function(e){return e?void(g=r.url()):void(r.url()!==g&&(r.url(g),r.replace()))},push:function(e,t,i){r.url(e.format(t||{})),h=i&&i.$$avoidResync?r.url():n,i&&i.replace&&r.replace()},href:function(n,i,a){if(!n.validates(i))return null;var o=e.html5Mode();t.isObject(o)&&(o=o.enabled);var l=n.format(i);if(a=a||{},o||null===l||(l="#"+e.hashPrefix()+l),l=s(l,o,a.absolute),!a.absolute||!l)return l;var c=!o&&l?"/":"",u=r.port();return u=80===u||443===u?"":":"+u,[r.protocol(),"://",r.host(),u,c,l].join("")}}}var l,c=[],u=null,f=!1;this.rule=function(e){if(!L(e))throw new Error("'rule' must be a function"); return c.push(e),this},this.otherwise=function(e){if(_(e)){var t=e;e=function(){return t}}else if(!L(e))throw new Error("'rule' must be a function");return u=e,this},this.when=function(e,t){var n,s=_(t);if(_(e)&&(e=r.compile(e)),!s&&!L(t)&&!F(t))throw new Error("invalid 'handler' in when()");var l={matcher:function(e,t){return s&&(n=r.compile(t),t=["$match",function(e){return n.format(e)}]),O(function(n,r){return o(n,t,e.exec(r.path(),r.search()))},{prefix:_(e.prefix)?e.prefix:""})},regex:function(e,t){if(e.global||e.sticky)throw new Error("when() RegExp must not be global or sticky");return s&&(n=t,t=["$match",function(e){return a(n,e)}]),O(function(n,r){return o(n,t,e.exec(r.path()))},{prefix:i(e)})}},c={matcher:r.isMatcher(e),regex:e instanceof RegExp};for(var u in c)if(c[u])return this.rule(l[u](e,t));throw new Error("invalid 'what' in when()")},this.deferIntercept=function(e){e===n&&(e=!0),f=e},this.$get=s,s.$inject=["$location","$rootScope","$injector","$browser"]}function $(e,i){function a(e){return 0===e.indexOf(".")||0===e.indexOf("^")}function f(e,t){if(!e)return n;var r=_(e),i=r?e:e.name,o=a(i);if(o){if(!t)throw new Error("No reference point given for path '"+i+"'");t=f(t);for(var s=i.split("."),l=0,c=s.length,u=t;c>l;l++)if(""!==s[l]||0!==l){if("^"!==s[l])break;if(!u.parent)throw new Error("Path '"+i+"' not valid for state '"+t.name+"'");u=u.parent}else u=t;s=s.slice(l).join("."),i=u.name+(u.name&&s?".":"")+s}var p=w[i];return!p||!r&&(r||p!==e&&p.self!==e)?n:p}function p(e,t){D[e]||(D[e]=[]),D[e].push(t)}function h(e){for(var t=D[e]||[];t.length;)m(t.shift())}function m(t){t=r(t,{self:t,resolve:t.resolve||{},toString:function(){return this.name}});var n=t.name;if(!_(n)||n.indexOf("@")>=0)throw new Error("State must have a valid name");if(w.hasOwnProperty(n))throw new Error("State '"+n+"'' is already defined");var i=-1!==n.indexOf(".")?n.substring(0,n.lastIndexOf(".")):_(t.parent)?t.parent:I(t.parent)&&_(t.parent.name)?t.parent.name:"";if(i&&!w[i])return p(i,t.self);for(var a in N)L(N[a])&&(t[a]=N[a](t,N.$delegates[a]));return w[n]=t,!t[E]&&t.url&&e.when(t.url,["$match","$stateParams",function(e,n){x.$current.navigable==t&&c(e,n)||x.transitionTo(t,e,{inherit:!0,location:!1})}]),h(n),t}function g(e){return e.indexOf("*")>-1}function b(e){var t=e.split("."),n=x.$current.name.split(".");if("**"===t[0]&&(n=n.slice(s(n,t[1])),n.unshift("**")),"**"===t[t.length-1]&&(n.splice(s(n,t[t.length-2])+1,Number.MAX_VALUE),n.push("**")),t.length!=n.length)return!1;for(var r=0,i=t.length;i>r;r++)"*"===t[r]&&(n[r]="*");return n.join("")===t.join("")}function v(e,t){return _(e)&&!P(t)?N[e]:L(t)&&_(e)?(N[e]&&!N.$delegates[e]&&(N.$delegates[e]=N[e]),N[e]=t,this):this}function y(e,t){return I(e)?t=e:t.name=e,m(t),this}function $(e,i,a,s,p,h,m){function v(t,n,r,a){var o=e.$broadcast("$stateNotFound",t,n,r);if(o.defaultPrevented)return m.update(),N;if(!o.retry)return null;if(a.$retry)return m.update(),M;var s=x.transition=i.when(o.retry);return s.then(function(){return s!==x.transition?$:(t.options.$retry=!0,x.transitionTo(t.to,t.toParams,t.options))},function(){return N}),m.update(),s}function y(e,n,r,o,l,c){var f=r?n:u(e.params.$$keys(),n),d={$stateParams:f};l.resolve=p.resolve(e.resolve,d,l.resolve,e);var h=[l.resolve.then(function(e){l.globals=e})];return o&&h.push(o),R(e.views,function(n,r){var i=n.resolve&&n.resolve!==e.resolve?n.resolve:{};i.$template=[function(){return a.load(r,{view:n,locals:d,params:f,notify:c.notify})||""}],h.push(p.resolve(i,d,l.resolve,e).then(function(a){if(L(n.controllerProvider)||F(n.controllerProvider)){var o=t.extend({},i,d);a.$$controller=s.invoke(n.controllerProvider,null,o)}else a.$$controller=n.controller;a.$$state=e,a.$$controllerAs=n.controllerAs,l[r]=a}))}),i.all(h).then(function(){return l})}var $=i.reject(new Error("transition superseded")),D=i.reject(new Error("transition prevented")),N=i.reject(new Error("transition aborted")),M=i.reject(new Error("transition failed"));return C.locals={resolve:null,globals:{$stateParams:{}}},x={params:{},current:C.self,$current:C,transition:null},x.reload=function(){return x.transitionTo(x.current,h,{reload:!0,inherit:!1,notify:!0})},x.go=function(e,t,n){return x.transitionTo(e,t,O({inherit:!0,relative:x.$current},n))},x.transitionTo=function(t,n,a){n=n||{},a=O({location:!0,inherit:!1,relative:null,notify:!0,reload:!1,$retry:!1},a||{});var o,c=x.$current,p=x.params,d=c.path,g=f(t,a.relative);if(!P(g)){var b={to:t,toParams:n,options:a},w=v(b,c.self,p,a);if(w)return w;if(t=b.to,n=b.toParams,a=b.options,g=f(t,a.relative),!P(g)){if(!a.relative)throw new Error("No such state '"+t+"'");throw new Error("Could not resolve '"+t+"' from state '"+a.relative+"'")}}if(g[E])throw new Error("Cannot transition to abstract state '"+t+"'");if(a.inherit&&(n=l(h,n||{},x.$current,g)),!g.params.$$validates(n))return M;n=g.params.$$values(n),t=g;var N=t.path,T=0,A=N[T],k=C.locals,L=[];if(!a.reload)for(;A&&A===d[T]&&A.ownParams.$$equals(n,p);)k=L[T]=A.locals,T++,A=N[T];if(S(t,c,k,a))return t.self.reloadOnSearch!==!1&&m.update(),x.transition=null,i.when(x.current);if(n=u(t.params.$$keys(),n||{}),a.notify&&e.$broadcast("$stateChangeStart",t.self,n,c.self,p).defaultPrevented)return m.update(),D;for(var _=i.when(k),I=T;I=T;r--)o=d[r],o.self.onExit&&s.invoke(o.self.onExit,o.self,o.locals.globals),o.locals=null;for(r=T;r=0?i:i+"@"+(a?a.state.name:"")}function E(e,t){var n,r=e.match(/^\s*({[^}]*})\s*$/);if(r&&(e=t+"("+r[1]+")"),n=e.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/),!n||4!==n.length)throw new Error("Invalid state ref '"+e+"'");return{state:n[1],paramExpr:n[3]||null}}function N(e){var t=e.parent().inheritedData("$uiView");return t&&t.state&&t.state.name?t.state:void 0}function M(e,n){var r=["location","inherit","reload"];return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(i,a,o,s){var l=E(o.uiSref,e.current.name),c=null,u=N(a)||e.$current,f=null,p="A"===a.prop("tagName"),d="FORM"===a[0].nodeName,h=d?"action":"href",m=!0,g={relative:u,inherit:!0},b=i.$eval(o.uiSrefOpts)||{};t.forEach(r,function(e){e in b&&(g[e]=b[e])});var v=function(n){if(n&&(c=t.copy(n)),m){f=e.href(l.state,c,g);var r=s[1]||s[0];return r&&r.$$setStateInfo(l.state,c),null===f?(m=!1,!1):void o.$set(h,f)}};l.paramExpr&&(i.$watch(l.paramExpr,function(e){e!==c&&v(e)},!0),c=t.copy(i.$eval(l.paramExpr))),v(),d||a.bind("click",function(t){var r=t.which||t.button;if(!(r>1||t.ctrlKey||t.metaKey||t.shiftKey||a.attr("target"))){var i=n(function(){e.go(l.state,c,g)});t.preventDefault();var o=p&&!f?1:0;t.preventDefault=function(){o--<=0&&n.cancel(i)}}})}}}function T(e,t,n){return{restrict:"A",controller:["$scope","$element","$attrs",function(t,r,i){function a(){o()?r.addClass(c):r.removeClass(c)}function o(){return"undefined"!=typeof i.uiSrefActiveEq?s&&e.is(s.name,l):s&&e.includes(s.name,l)}var s,l,c;c=n(i.uiSrefActiveEq||i.uiSrefActive||"",!1)(t),this.$$setStateInfo=function(t,n){s=e.get(t,N(r)),l=n,a()},t.$on("$stateChangeSuccess",a)}]}}function A(e){var t=function(t){return e.is(t)};return t.$stateful=!0,t}function k(e){var t=function(t){return e.includes(t)};return t.$stateful=!0,t}var P=t.isDefined,L=t.isFunction,_=t.isString,I=t.isObject,F=t.isArray,R=t.forEach,O=t.extend,B=t.copy;t.module("ui.router.util",["ng"]),t.module("ui.router.router",["ui.router.util"]),t.module("ui.router.state",["ui.router.router","ui.router.util"]),t.module("ui.router",["ui.router.state"]),t.module("ui.router.compat",["ui.router"]),h.$inject=["$q","$injector"],t.module("ui.router.util").service("$resolve",h),m.$inject=["$http","$templateCache","$injector"],t.module("ui.router.util").service("$templateFactory",m);var G;g.prototype.concat=function(e,t){var n={caseInsensitive:G.caseInsensitive(),strict:G.strictMode(),squash:G.defaultSquashPolicy()};return new g(this.sourcePath+e+this.sourceSearch,O(n,t),this)},g.prototype.toString=function(){return this.source},g.prototype.exec=function(e,t){function n(e){function t(e){return e.split("").reverse().join("")}function n(e){return e.replace(/\\-/,"-")}var r=t(e).split(/-(?!\\)/),i=d(r,t);return d(i,n).reverse()}var r=this.regexp.exec(e);if(!r)return null;t=t||{};var i,a,o,s=this.parameters(),l=s.length,c=this.segments.length-1,u={};if(c!==r.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(i=0;c>i;i++){o=s[i];var f=this.params[o],p=r[i+1];for(a=0;ai;i++)o=s[i],u[o]=this.params[o].value(t[o]);return u},g.prototype.parameters=function(e){return P(e)?this.params[e]||null:this.$$paramNames},g.prototype.validates=function(e){return this.params.$$validates(e)},g.prototype.format=function(e){function t(e){return encodeURIComponent(e).replace(/-/g,function(e){return"%5C%"+e.charCodeAt(0).toString(16).toUpperCase()})}e=e||{};var n=this.segments,r=this.parameters(),i=this.params;if(!this.validates(e))return null;var a,o=!1,s=n.length-1,l=r.length,c=n[0];for(a=0;l>a;a++){var u=s>a,f=r[a],p=i[f],h=p.value(e[f]),m=p.isOptional&&p.type.equals(p.value(),h),g=m?p.squash:!1,b=p.type.encode(h);if(u){var v=n[a+1];if(g===!1)null!=b&&(c+=F(b)?d(b,t).join("-"):encodeURIComponent(b)),c+=v;else if(g===!0){var y=c.match(/\/$/)?/\/?(.*)/:/(.*)/;c+=v.match(y)[1]}else _(g)&&(c+=g+v)}else{if(null==b||m&&g!==!1)continue;F(b)||(b=[b]),b=d(b,encodeURIComponent).join("&"+f+"="),c+=(o?"&":"?")+(f+"="+b),o=!0}}return c},b.prototype.is=function(){return!0},b.prototype.encode=function(e){return e},b.prototype.decode=function(e){return e},b.prototype.equals=function(e,t){return e==t},b.prototype.$subPattern=function(){var e=this.pattern.toString();return e.substr(1,e.length-2)},b.prototype.pattern=/.*/,b.prototype.toString=function(){return"{Type:"+this.name+"}"},b.prototype.$asArray=function(e,t){function r(e,t){function r(e,t){return function(){return e[t].apply(e,arguments)}}function i(e){return F(e)?e:P(e)?[e]:[]}function a(e){switch(e.length){case 0:return n;case 1:return"auto"===t?e[0]:e;default:return e}}function o(e){return!e}function s(e,t){return function(n){n=i(n);var r=d(n,e);return t===!0?0===p(r,o).length:a(r)}}function l(e){return function(t,n){var r=i(t),a=i(n);if(r.length!==a.length)return!1;for(var o=0;o-1?0:-1:t?0:-1}function n(e){var t=this.cache,n=typeof e;if("boolean"==n||null==e)t[e]=!0;else{"number"!=n&&"string"!=n&&(n="object");var r="number"==n?e:y+e,i=t[n]||(t[n]={});"object"==n?(i[r]||(i[r]=[])).push(e):i[r]=!0}}function r(e){return e.charCodeAt(0)}function i(e,t){for(var n=e.criteria,r=t.criteria,i=-1,a=n.length;++is||"undefined"==typeof o)return 1;if(s>o||"undefined"==typeof s)return-1}}return e.index-t.index}function a(e){var t=-1,r=e.length,i=e[0],a=e[r/2|0],o=e[r-1];if(i&&"object"==typeof i&&a&&"object"==typeof a&&o&&"object"==typeof o)return!1;var s=l();s["false"]=s["null"]=s["true"]=s.undefined=!1;var c=l();for(c.array=e,c.cache=s,c.push=n;++ti?0:i);++r=$&&o===e,c=[];if(l){var u=a(r);u?(o=t,r=u):l=!1}for(;++i-1:void 0});return i.pop(),a.pop(),y&&(u(i),u(a)),o}function lt(e,t,n,r,i){(fi(t)?rn:Di)(t,function(t,a){var o,s,l=t,c=e[a];if(t&&((s=fi(t))||Ei(t))){for(var u=r.length;u--;)if(o=r[u]==t){c=i[u];break}if(!o){var f;n&&(l=n(c,t),(f="undefined"!=typeof l)&&(c=l)),f||(c=s?fi(c)?c:[]:Ei(c)?c:{}),r.push(t),i.push(c),f||lt(c,t,n,r,i)}}else n&&(l=n(c,t),"undefined"==typeof l&&(l=t)),"undefined"!=typeof l&&(c=l);e[a]=c})}function ct(e,t){return e+Ur(ai()*(t-e+1))}function ut(n,r,i){var o=-1,l=mt(),c=n?n.length:0,p=[],d=!r&&c>=$&&l===e,h=i||d?s():p;if(d){var m=a(h);l=t,h=m}for(;++o3&&"function"==typeof t[n-2])var r=nt(t[--n-1],t[n--],2);else n>2&&"function"==typeof t[n-1]&&(r=t[--n]);for(var i=p(arguments,1,n),a=-1,o=s(),l=s();++an?ni(0,a+n):n)||0,fi(e)?o=i(e,t,n)>-1:"number"==typeof a?o=(jt(e)?e.indexOf(t,n):i(e,t,n))>-1:Si(e,function(e){return++r>=n?!(o=e===t):void 0}),o}function Yt(e,t,n){var r=!0;if(t=m.createCallback(t,n,3),fi(e))for(var i=-1,a=e.length;++ia&&(a=l)}else t=null==t&&jt(e)?r:m.createCallback(t,n,3),Si(e,function(e,n,r){var o=t(e,n,r);o>i&&(i=o,a=e)});return a}function cn(e,t,n){var i=1/0,a=i;if("function"!=typeof t&&n&&n[t]===e&&(t=null),null==t&&fi(e))for(var o=-1,s=e.length;++ol&&(a=l)}else t=null==t&&jt(e)?r:m.createCallback(t,n,3),Si(e,function(e,n,r){var o=t(e,n,r);i>o&&(i=o,a=e)});return a}function un(e,t,n,r){var i=arguments.length<3;if(t=m.createCallback(t,r,4),fi(e)){var a=-1,o=e.length;for(i&&(n=e[++a]);++ar?ni(0,i+r):r||0}else if(r){var a=_n(t,n);return t[a]===n?a:-1}return e(t,n,r)}function En(e,t,n){var r=0,i=e?e.length:0;if("number"!=typeof t&&null!=t){var a=i;for(t=m.createCallback(t,n,3);a--&&t(e[a],a,e);)r++}else r=null==t||n?1:t||r;return p(e,0,ri(ni(0,i-r),i))}function Nn(){for(var n=[],r=-1,i=arguments.length,o=s(),l=mt(),c=l===e,p=s();++r=$&&a(r?n[r]:p)))}var h=n[0],m=-1,g=h?h.length:0,b=[];e:for(;++mn?ni(0,r+n):ri(n,r-1))+1);r--;)if(e[r]===t)return r;return-1}function An(e){for(var t=arguments,n=0,r=t.length,i=e?e.length:0;++ni;){var o=i+a>>>1;n(e[o])1?arguments:arguments[0],t=-1,n=e?ln(Ai(e,"length")):0,r=Sr(0>n?0:n);++t2?pt(e,17,p(arguments,2),null,t):pt(e,1,null,null,t)}function Vn(e){for(var t=arguments.length>1?ot(arguments,!0,!1,1):Nt(e),n=-1,r=t.length;++n2?pt(t,19,p(arguments,2),null,e):pt(t,3,null,null,e)}function Hn(){for(var e=arguments,t=e.length;t--;)if(!Ft(e[t]))throw new kr;return function(){for(var t=arguments,n=e.length;n--;)t=[e[n].apply(this,t)];return t[0]}}function zn(e,t){return t="number"==typeof t?t:+t||e.length,pt(e,4,null,null,null,t)}function Wn(e,t,n){var r,i,a,o,s,l,c,u=0,f=!1,p=!0;if(!Ft(e))throw new kr;if(t=ni(0,t)||0,n===!0){var d=!0;p=!1}else Rt(n)&&(d=n.leading,f="maxWait"in n&&(ni(t,n.maxWait)||0),p="trailing"in n?n.trailing:p);var m=function(){var n=t-(Pi()-o);if(0>=n){i&&Gr(i);var f=c;i=l=c=h,f&&(u=Pi(),a=e.apply(s,r),l||i||(r=s=null))}else l=Wr(m,n)},g=function(){l&&Gr(l),i=l=c=h,(p||f!==t)&&(u=Pi(),a=e.apply(s,r),l||i||(r=s=null))};return function(){if(r=arguments,o=Pi(),s=this,c=p&&(l||!d),f===!1)var n=d&&!l;else{i||d||(u=o);var h=f-(o-u),b=0>=h;b?(i&&(i=Gr(i)),u=o,a=e.apply(s,r)):i||(i=Wr(g,h))}return b&&l?l=Gr(l):l||t===f||(l=Wr(m,t)),n&&(b=!0,a=e.apply(s,r)),!b||l||i||(r=s=null),a}}function Qn(e){if(!Ft(e))throw new kr;var t=p(arguments,1);return Wr(function(){e.apply(h,t)},1)}function Kn(e,t){if(!Ft(e))throw new kr;var n=p(arguments,2);return Wr(function(){e.apply(h,n)},t)}function Zn(e,t){if(!Ft(e))throw new kr;var n=function(){var r=n.cache,i=t?t.apply(this,arguments):y+arguments[0];return qr.call(r,i)?r[i]:r[i]=e.apply(this,arguments)};return n.cache={},n}function Xn(e){var t,n;if(!Ft(e))throw new kr;return function(){return t?n:(t=!0,n=e.apply(this,arguments),e=null,n)}}function Jn(e){return pt(e,16,p(arguments,1))}function Yn(e){return pt(e,32,null,p(arguments,1))}function er(e,t,n){var r=!0,i=!0;if(!Ft(e))throw new kr;return n===!1?r=!1:Rt(n)&&(r="leading"in n?n.leading:r,i="trailing"in n?n.trailing:i),K.leading=r,K.maxWait=t,K.trailing=i,Wn(e,t,K)}function tr(e,t){return pt(t,16,[e])}function nr(e){return function(){return e}}function rr(e,t,n){var r=typeof e;if(null==e||"function"==r)return nt(e,t,n);if("object"!=r)return cr(e);var i=di(e),a=i[0],o=e[a];return 1!=i.length||o!==o||Rt(o)?function(t){for(var n=i.length,r=!1;n--&&(r=st(t[i[n]],e[i[n]],null,!0)););return r}:function(e){var t=e[a];return o===t&&(0!==o||1/o==1/t)}}function ir(e){return null==e?"":Ar(e).replace($i,ht)}function ar(e){return e}function or(e,t,n){var r=!0,i=t&&Nt(t);t&&(n||i.length)||(null==n&&(n=t),a=g,t=e,e=m,i=Nt(t)),n===!1?r=!1:Rt(n)&&"chain"in n&&(r=n.chain);var a=e,o=Ft(a);rn(i,function(n){var i=e[n]=t[n];o&&(a.prototype[n]=function(){var t=this.__chain__,n=this.__wrapped__,o=[n];Hr.apply(o,arguments);var s=i.apply(e,o);if(r||t){if(n===s&&Rt(s))return this;s=new a(s),s.__chain__=t}return s})})}function sr(){return n._=Fr,this}function lr(){}function cr(e){return function(t){return t[e]}}function ur(e,t,n){var r=null==e,i=null==t;if(null==n&&("boolean"==typeof e&&i?(n=e,e=1):i||"boolean"!=typeof t||(n=t,i=!0)),r&&i&&(t=1),e=+e||0,i?(t=e,e=0):t=+t||0,n||e%1||t%1){var a=ai();return ri(e+a*(t-e+parseFloat("1e-"+((a+"").length-1))),t)}return ct(e,t)}function fr(e,t){if(e){var n=e[t];return Ft(n)?e[t]():n}}function pr(e,t,n){var r=m.templateSettings;e=Ar(e||""),n=xi({},n,r);var i,a=xi({},n.imports,r.imports),s=di(a),l=Zt(a),c=0,u=n.interpolate||P,f="__p += '",p=Tr((n.escape||P).source+"|"+u.source+"|"+(u===T?E:P).source+"|"+(n.evaluate||P).source+"|$","g");e.replace(p,function(t,n,r,a,s,l){return r||(r=a),f+=e.slice(c,l).replace(_,o),n&&(f+="' +\n__e("+n+") +\n'"),s&&(i=!0,f+="';\n"+s+";\n__p += '"),r&&(f+="' +\n((__t = ("+r+")) == null ? '' : __t) +\n'"),c=l+t.length,t}),f+="';\n";var d=n.variable,g=d;g||(d="obj",f="with ("+d+") {\n"+f+"\n}\n"),f=(i?f.replace(x,""):f).replace(w,"$1").replace(D,"$1;"),f="function("+d+") {\n"+(g?"":d+" || ("+d+" = {});\n")+"var __t, __p = '', __e = _.escape"+(i?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+f+"return __p\n}";var b="\n/*\n//# sourceURL="+(n.sourceURL||"/lodash/template/source["+R++ +"]")+"\n*/";try{var v=Dr(s,"return "+f+b).apply(h,l)}catch(y){throw y.source=f,y}return t?v(t):(v.source=f,v)}function dr(e,t,n){e=(e=+e)>-1?e:0;var r=-1,i=Sr(e);for(t=nt(t,n,1);++r/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:T,variable:"",imports:{_:m}};var ci=function(e){var t="var index, iterable = "+e.firstArg+", result = "+e.init+";\nif (!iterable) return result;\n"+e.top+";";e.array?(t+="\nvar length = iterable.length; index = -1;\nif ("+e.array+") { ",li.unindexedChars&&(t+="\n if (isString(iterable)) {\n iterable = iterable.split('')\n } "),t+="\n while (++index < length) {\n "+e.loop+";\n }\n}\nelse { "):li.nonEnumArgs&&(t+="\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += '';\n "+e.loop+";\n }\n } else { "),li.enumPrototypes&&(t+="\n var skipProto = typeof iterable == 'function';\n "),li.enumErrorProps&&(t+="\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n ");var n=[];if(li.enumPrototypes&&n.push('!(skipProto && index == "prototype")'),li.enumErrorProps&&n.push('!(skipErrorProps && (index == "message" || index == "name"))'),e.useHas&&e.keys)t+="\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n",n.length&&(t+=" if ("+n.join(" && ")+") {\n "),t+=e.loop+"; ",n.length&&(t+="\n }"),t+="\n } ";else if(t+="\n for (index in iterable) {\n",e.useHas&&n.push("hasOwnProperty.call(iterable, index)"),n.length&&(t+=" if ("+n.join(" && ")+") {\n "),t+=e.loop+"; ",n.length&&(t+="\n }"),t+="\n } ",li.nonEnumShadows){for(t+="\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n ",k=0;k<7;k++)t+="\n index = '"+e.shadowedProps[k]+"';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))",e.useHas||(t+=" || (!nonEnum[index] && iterable[index] !== objectProto[index])"),t+=") {\n "+e.loop+";\n } ";t+="\n } "}return(e.array||li.nonEnumArgs)&&(t+="\n}"),t+=e.bottom+";\nreturn result"};Xr||(tt=function(){function e(){}return function(t){if(Rt(t)){e.prototype=t;var r=new e;e.prototype=null}return r||n.Object()}}());var ui=Zr?function(e,t){Z.value=t,Zr(e,"__bindData__",Z)}:lr;li.argsClass||(yt=function(e){return e&&"object"==typeof e&&"number"==typeof e.length&&qr.call(e,"callee")&&!zr.call(e,"callee")||!1});var fi=Jr||function(e){return e&&"object"==typeof e&&"number"==typeof e.length&&Rr.call(e)==B||!1},pi=dt({args:"object",init:"[]",top:"if (!(objectTypes[typeof object])) return result",loop:"result.push(index)"}),di=ti?function(e){return Rt(e)?li.enumPrototypes&&"function"==typeof e||li.nonEnumArgs&&e.length&&yt(e)?pi(e):ti(e):[]}:pi,hi={args:"collection, callback, thisArg",top:"callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)",array:"typeof length == 'number'",keys:di,loop:"if (callback(iterable[index], index, collection) === false) return result"},mi={args:"object, source, guard",top:"var args = arguments,\n argsIndex = 0,\n argsLength = typeof guard == 'number' ? 2 : args.length;\nwhile (++argsIndex < argsLength) {\n iterable = args[argsIndex];\n if (iterable && objectTypes[typeof iterable]) {",keys:di,loop:"if (typeof result[index] == 'undefined') result[index] = iterable[index]",bottom:" }\n}"},gi={top:"if (!objectTypes[typeof iterable]) return result;\n"+hi.top,array:!1},bi={"&":"&","<":"<",">":">",'"':""","'":"'"},vi=Tt(bi),yi=Tr("("+di(vi).join("|")+")","g"),$i=Tr("["+di(bi).join("")+"]","g"),Si=dt(hi),Ci=dt(mi,{top:mi.top.replace(";",";\nif (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n callback = args[--argsLength];\n}"),loop:"result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]"}),xi=dt(mi),wi=dt(hi,gi,{useHas:!1}),Di=dt(hi,gi);Ft(/x/)&&(Ft=function(e){return"function"==typeof e&&Rr.call(e)==V});var Ei=Vr?function(e){if(!e||Rr.call(e)!=H||!li.argsClass&&yt(e))return!1;var t=e.valueOf,n=gt(t)&&(n=Vr(t))&&Vr(n);return n?e==n||Vr(e)==n:bt(e)}:bt,Ni=ft(function(e,t,n){qr.call(e,n)?e[n]++:e[n]=1}),Mi=ft(function(e,t,n){(qr.call(e,n)?e[n]:e[n]=[]).push(t)}),Ti=ft(function(e,t,n){e[n]=t}),Ai=sn,ki=en,Pi=gt(Pi=xr.now)&&Pi||function(){return(new xr).getTime()},Li=8==ii(C+"08")?ii:function(e,t){return ii(jt(e)?e.replace(A,""):e,t||0)};return m.after=Un,m.assign=Ci,m.at=Xt,m.bind=jn,m.bindAll=Vn,m.bindKey=qn,m.chain=gr,m.compact=yn,m.compose=Hn,m.constant=nr,m.countBy=Ni,m.create=Ct,m.createCallback=rr,m.curry=zn,m.debounce=Wn,m.defaults=xi,m.defer=Qn,m.delay=Kn,m.difference=$n,m.filter=en,m.flatten=wn,m.forEach=rn,m.forEachRight=an,m.forIn=wi,m.forInRight=Dt,m.forOwn=Di,m.forOwnRight=Et,m.functions=Nt,m.groupBy=Mi,m.indexBy=Ti,m.initial=En,m.intersection=Nn,m.invert=Tt,m.invoke=on,m.keys=di,m.map=sn,m.mapValues=qt,m.max=ln,m.memoize=Zn,m.merge=Ht,m.min=cn,m.omit=zt,m.once=Xn,m.pairs=Wt,m.partial=Jn,m.partialRight=Yn,m.pick=Qt,m.pluck=Ai,m.property=cr,m.pull=An,m.range=kn,m.reject=pn,m.remove=Pn,m.rest=Ln,m.shuffle=hn,m.sortBy=bn,m.tap=br,m.throttle=er,m.times=dr,m.toArray=vn,m.transform=Kt,m.union=In,m.uniq=Fn,m.values=Zt,m.where=ki,m.without=Rn,m.wrap=tr,m.xor=On,m.zip=Bn,m.zipObject=Gn,m.collect=sn,m.drop=Ln,m.each=rn,m.eachRight=an,m.extend=Ci,m.methods=Nt,m.object=Gn,m.select=en,m.tail=Ln,m.unique=Fn,m.unzip=Bn,or(m),m.clone=$t,m.cloneDeep=St,m.contains=Jt,m.escape=ir,m.every=Yt,m.find=tn,m.findIndex=Sn,m.findKey=xt,m.findLast=nn,m.findLastIndex=Cn,m.findLastKey=wt,m.has=Mt,m.identity=ar,m.indexOf=Dn,m.isArguments=yt,m.isArray=fi,m.isBoolean=At,m.isDate=kt,m.isElement=Pt,m.isEmpty=Lt,m.isEqual=_t,m.isFinite=It,m.isFunction=Ft,m.isNaN=Ot,m.isNull=Bt,m.isNumber=Gt,m.isObject=Rt,m.isPlainObject=Ei,m.isRegExp=Ut,m.isString=jt,m.isUndefined=Vt,m.lastIndexOf=Tn,m.mixin=or,m.noConflict=sr,m.noop=lr,m.now=Pi,m.parseInt=Li,m.random=ur,m.reduce=un,m.reduceRight=fn,m.result=fr,m.runInContext=d,m.size=mn,m.some=gn,m.sortedIndex=_n,m.template=pr,m.unescape=hr,m.uniqueId=mr,m.all=Yt,m.any=gn,m.detect=tn,m.findWhere=tn,m.foldl=un,m.foldr=fn,m.include=Jt,m.inject=un,or(function(){var e={};return Di(m,function(t,n){m.prototype[n]||(e[n]=t)}),e}(),!1),m.first=xn,m.last=Mn,m.sample=dn,m.take=xn,m.head=xn,Di(m,function(e,t){var n="sample"!==t;m.prototype[t]||(m.prototype[t]=function(t,r){var i=this.__chain__,a=e(this.__wrapped__,t,r);return i||null!=t&&(!r||n&&"function"==typeof t)?new g(a,i):a})}),m.VERSION="2.4.1",m.prototype.chain=vr,m.prototype.toString=yr,m.prototype.value=$r,m.prototype.valueOf=$r,Si(["join","pop","shift"],function(e){var t=Pr[e];m.prototype[e]=function(){var e=this.__chain__,n=t.apply(this.__wrapped__,arguments);return e?new g(n,e):n}}),Si(["push","reverse","sort","unshift"],function(e){var t=Pr[e];m.prototype[e]=function(){return t.apply(this.__wrapped__,arguments),this}}),Si(["concat","slice","splice"],function(e){var t=Pr[e];m.prototype[e]=function(){return new g(t.apply(this.__wrapped__,arguments),this.__chain__)}}),li.spliceObjects||Si(["pop","shift","splice"],function(e){var t=Pr[e],n="splice"==e;m.prototype[e]=function(){var e=this.__chain__,r=this.__wrapped__,i=t.apply(r,arguments);return 0===r.length&&delete r[0],e||n?new g(i,e):i}}),m}var h,m=[],g=[],b=0,v={},y=+new Date+"",$=75,S=40,C=" \f \n\r\u2028\u2029 ᠎              ",x=/\b__p \+= '';/g,w=/\b(__p \+=) '' \+/g,D=/(__e\(.*?\)|\b__t\)) \+\n'';/g,E=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,N=/\w*$/,M=/^\s*function[ \n\r\t]+\w/,T=/<%=([\s\S]+?)%>/g,A=RegExp("^["+C+"]*0+(?=.$)"),P=/($^)/,L=/\bthis\b/,_=/['\n\r\t\u2028\u2029\\]/g,I=["Array","Boolean","Date","Error","Function","Math","Number","Object","RegExp","String","_","attachEvent","clearTimeout","isFinite","isNaN","parseInt","setTimeout"],F=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],R=0,O="[object Arguments]",B="[object Array]",G="[object Boolean]",U="[object Date]",j="[object Error]",V="[object Function]",q="[object Number]",H="[object Object]",z="[object RegExp]",W="[object String]",Q={};Q[V]=!1,Q[O]=Q[B]=Q[G]=Q[U]=Q[q]=Q[H]=Q[z]=Q[W]=!0;var K={leading:!1,maxWait:0,trailing:!1},Z={configurable:!1,enumerable:!1,value:null,writable:!1},X={args:"",array:null,bottom:"",firstArg:"",init:"",keys:null,loop:"",shadowedProps:null,support:null,top:"",useHas:!1},J={"boolean":!1,"function":!0,object:!0,number:!1,string:!1,undefined:!1},Y={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"},et=J[typeof window]&&window||this,tt=J[typeof exports]&&exports&&!exports.nodeType&&exports,nt=J[typeof module]&&module&&!module.nodeType&&module,rt=nt&&nt.exports===tt&&tt,it=J[typeof global]&&global;!it||it.global!==it&&it.window!==it||(et=it);var at=d();"function"==typeof define&&"object"==typeof define.amd&&define.amd?(et._=at,define(function(){return at})):tt&&nt?rt?(nt.exports=at)._=at:tt._=at:et._=at}.call(this),!function(e,t){"use strict";function n(e,t){var n,r,i=e.toLowerCase();for(t=[].concat(t),n=0;nt)return"";for(var n="";t>0;)1&t&&(n+=e),t>>=1,e+=e;return n},l=[].slice,c=function(e){return null==e?"\\s":e.source?e.source:"["+h.escapeRegExp(e)+"]"},u={lt:"<",gt:">",quot:'"',amp:"&",apos:"'"},f={};for(var p in u)f[u[p]]=p;f["'"]="#39";var d=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}var n=s,r=function(){return r.cache.hasOwnProperty(arguments[0])||(r.cache[arguments[0]]=r.parse(arguments[0])),r.format.call(null,r.cache[arguments[0]],arguments)};return r.format=function(r,i){var a,o,s,l,c,u,f,p=1,h=r.length,m="",g=[];for(o=0;h>o;o++)if(m=e(r[o]),"string"===m)g.push(r[o]);else if("array"===m){if(l=r[o],l[2])for(a=i[p],s=0;s=0?"+"+a:a,u=l[4]?"0"==l[4]?"0":l[4].charAt(1):" ",f=l[6]-t(a).length,c=l[6]?n(u,f):"",g.push(l[5]?a+c:c+a)}return g.join("")},r.cache={},r.parse=function(e){for(var t=e,n=[],r=[],i=0;t;){if(null!==(n=/^[^\x25]+/.exec(t)))r.push(n[0]);else if(null!==(n=/^\x25{2}/.exec(t)))r.push("%");else{if(null===(n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t)))throw new Error("[_.sprintf] huh?");if(n[2]){i|=1;var a=[],o=n[2],s=[];if(null===(s=/^([a-z_][a-z_\d]*)/i.exec(o)))throw new Error("[_.sprintf] huh?");for(a.push(s[1]);""!==(o=o.substring(s[0].length));)if(null!==(s=/^\.([a-z_][a-z_\d]*)/i.exec(o)))a.push(s[1]);else{if(null===(s=/^\[(\d+)\]/.exec(o)))throw new Error("[_.sprintf] huh?");a.push(s[1])}n[2]=a}else i|=2;if(3===i)throw new Error("[_.sprintf] mixing positional and named placeholders is not (yet) supported");r.push(n)}t=t.substring(n[0].length)}return r},r}(),h={VERSION:"2.4.0",isBlank:function(e){return null==e&&(e=""),/^\s*$/.test(e)},stripTags:function(e){return null==e?"":t(e).replace(/<\/?[^>]+>/g,"")},capitalize:function(e){return e=null==e?"":t(e),e.charAt(0).toUpperCase()+e.slice(1)},chop:function(e,n){return null==e?[]:(e=t(e),n=~~n,n>0?e.match(new RegExp(".{1,"+n+"}","g")):[e])},clean:function(e){return h.strip(e).replace(/\s+/g," ")},count:function(e,n){if(null==e||null==n)return 0;e=t(e),n=t(n);for(var r=0,i=0,a=n.length;;){if(i=e.indexOf(n,i),-1===i)break;r++,i+=a}return r},chars:function(e){return null==e?[]:t(e).split("")},swapCase:function(e){return null==e?"":t(e).replace(/\S/g,function(e){return e===e.toUpperCase()?e.toLowerCase():e.toUpperCase()})},escapeHTML:function(e){return null==e?"":t(e).replace(/[&<>"']/g,function(e){return"&"+f[e]+";"})},unescapeHTML:function(e){return null==e?"":t(e).replace(/\&([^;]+);/g,function(e,n){var r;return n in u?u[n]:(r=n.match(/^#x([\da-fA-F]+)$/))?t.fromCharCode(parseInt(r[1],16)):(r=n.match(/^#(\d+)$/))?t.fromCharCode(~~r[1]):e})},escapeRegExp:function(e){return null==e?"":t(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},splice:function(e,t,n,r){var i=h.chars(e);return i.splice(~~t,~~n,r),i.join("")},insert:function(e,t,n){return h.splice(e,t,0,n)},include:function(e,n){return""===n?!0:null==e?!1:-1!==t(e).indexOf(n)},join:function(){var e=l.call(arguments),t=e.shift();return null==t&&(t=""),e.join(t)},lines:function(e){return null==e?[]:t(e).split("\n")},reverse:function(e){return h.chars(e).reverse().join("")},startsWith:function(e,n){return""===n?!0:null==e||null==n?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(0,n.length)===n)},endsWith:function(e,n){return""===n?!0:null==e||null==n?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(e.length-n.length)===n)},succ:function(e){return null==e?"":(e=t(e),e.slice(0,-1)+t.fromCharCode(e.charCodeAt(e.length-1)+1))},titleize:function(e){return null==e?"":(e=t(e).toLowerCase(),e.replace(/(?:^|\s|-)\S/g,function(e){return e.toUpperCase()}))},camelize:function(e){return h.trim(e).replace(/[-_\s]+(.)?/g,function(e,t){return t?t.toUpperCase():""})},underscored:function(e){return h.trim(e).replace(/([a-z\d])([A-Z]+)/g,"$1_$2").replace(/[-\s]+/g,"_").toLowerCase()},dasherize:function(e){return h.trim(e).replace(/([A-Z])/g,"-$1").replace(/[-_\s]+/g,"-").toLowerCase()},classify:function(e){return h.capitalize(h.camelize(t(e).replace(/[\W_]/g," ")).replace(/\s/g,""))},humanize:function(e){return h.capitalize(h.underscored(e).replace(/_id$/,"").replace(/_/g," "))},trim:function(e,n){return null==e?"":!n&&r?r.call(e):(n=c(n),t(e).replace(new RegExp("^"+n+"+|"+n+"+$","g"),""))},ltrim:function(e,n){return null==e?"":!n&&a?a.call(e):(n=c(n),t(e).replace(new RegExp("^"+n+"+"),""))},rtrim:function(e,n){return null==e?"":!n&&i?i.call(e):(n=c(n),t(e).replace(new RegExp(n+"+$"),""))},truncate:function(e,n,r){return null==e?"":(e=t(e),r=r||"...",n=~~n,e.length>n?e.slice(0,n)+r:e)},prune:function(e,n,r){if(null==e)return"";if(e=t(e),n=~~n,r=null!=r?t(r):"...",e.length<=n)return e;var i=function(e){return e.toUpperCase()!==e.toLowerCase()?"A":" "},a=e.slice(0,n+1).replace(/.(?=\W*\w*$)/g,i);return a=a.slice(a.length-2).match(/\w\w/)?a.replace(/\s*\S+$/,""):h.rtrim(a.slice(0,a.length-1)),(a+r).length>e.length?e:e.slice(0,a.length)+r},words:function(e,t){return h.isBlank(e)?[]:h.trim(e,t).split(t||/\s+/)},pad:function(e,n,r,i){e=null==e?"":t(e),n=~~n;var a=0;switch(r?r.length>1&&(r=r.charAt(0)):r=" ",i){case"right":return a=n-e.length,e+s(r,a);case"both":return a=n-e.length,s(r,Math.ceil(a/2))+e+s(r,Math.floor(a/2));default:return a=n-e.length,s(r,a)+e}},lpad:function(e,t,n){return h.pad(e,t,n)},rpad:function(e,t,n){return h.pad(e,t,n,"right")},lrpad:function(e,t,n){return h.pad(e,t,n,"both")},sprintf:d,vsprintf:function(e,t){return t.unshift(e),d.apply(null,t)},toNumber:function(e,t){return e?(e=h.trim(e),e.match(/^-?\d+(?:\.\d+)?$/)?o(o(e).toFixed(~~t)):0/0):0},numberFormat:function(e,t,n,r){if(isNaN(e)||null==e)return"";e=e.toFixed(~~t),r="string"==typeof r?r:",";var i=e.split("."),a=i[0],o=i[1]?(n||".")+i[1]:"";return a.replace(/(\d)(?=(?:\d{3})+$)/g,"$1"+r)+o},strRight:function(e,n){if(null==e)return"";e=t(e),n=null!=n?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strRightBack:function(e,n){if(null==e)return"";e=t(e),n=null!=n?t(n):n;var r=n?e.lastIndexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strLeft:function(e,n){if(null==e)return"";e=t(e),n=null!=n?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(0,r):e},strLeftBack:function(e,t){if(null==e)return"";e+="",t=null!=t?""+t:t;var n=e.lastIndexOf(t);return~n?e.slice(0,n):e},toSentence:function(e,t,n,r){t=t||", ",n=n||" and ";var i=e.slice(),a=i.pop();return e.length>2&&r&&(n=h.rtrim(t)+n),i.length?i.join(t)+n+a:a},toSentenceSerial:function(){var e=l.call(arguments);return e[3]=!0,h.toSentence.apply(h,e)},slugify:function(e){if(null==e)return"";var n="ąàáäâãåæăćęèéëêìíïîłńòóöôõøśșțùúüûñçżź",r="aaaaaaaaaceeeeeiiiilnoooooosstuuuunczz",i=new RegExp(c(n),"g");return e=t(e).toLowerCase().replace(i,function(e){var t=n.indexOf(e);return r.charAt(t)||"-"}),h.dasherize(e.replace(/[^\w\s-]/g,""))},surround:function(e,t){return[t,e,t].join("")},quote:function(e,t){return h.surround(e,t||'"')},unquote:function(e,t){return t=t||'"',e[0]===t&&e[e.length-1]===t?e.slice(1,e.length-1):e},exports:function(){var e={};for(var t in this)this.hasOwnProperty(t)&&!t.match(/^(?:include|contains|reverse)$/)&&(e[t]=this[t]);return e},repeat:function(e,n,r){if(null==e)return"";if(n=~~n,null==r)return s(t(e),n);for(var i=[];n>0;i[--n]=e);return i.join(r)},naturalCmp:function(e,n){if(e==n)return 0;if(!e)return-1;if(!n)return 1;for(var r=/(\.\d+)|(\d+)|(\D+)/g,i=t(e).toLowerCase().match(r),a=t(n).toLowerCase().match(r),o=Math.min(i.length,a.length),s=0;o>s;s++){var l=i[s],c=a[s];if(l!==c){var u=parseInt(l,10);if(!isNaN(u)){var f=parseInt(c,10);if(!isNaN(f)&&u-f)return u-f}return c>l?-1:1}}return i.length===a.length?i.length-a.length:n>e?-1:1},levenshtein:function(e,n){if(null==e&&null==n)return 0;if(null==e)return t(n).length;if(null==n)return t(e).length;e=t(e),n=t(n);for(var r,i,a=[],o=0;o<=n.length;o++)for(var s=0;s<=e.length;s++)i=o&&s?e.charAt(s-1)===n.charAt(o-1)?r:Math.min(a[s],a[s-1],r)+1:o+s,r=a[s],a[s]=i;return a.pop()},toBoolean:function(e,t,r){return"number"==typeof e&&(e=""+e),"string"!=typeof e?!!e:(e=h.trim(e),n(e,t||["true","1"])?!0:n(e,r||["false","0"])?!1:void 0)}};h.strip=h.trim,h.lstrip=h.ltrim,h.rstrip=h.rtrim,h.center=h.lrpad,h.rjust=h.lpad,h.ljust=h.rpad,h.contains=h.include,h.q=h.quote,h.toBool=h.toBoolean,"undefined"!=typeof exports&&("undefined"!=typeof module&&module.exports&&(module.exports=h),exports._s=h),"function"==typeof define&&define.amd&&define("underscore.string",[],function(){return h}),e._=e._||{},e._.string=e._.str=h}(this,String); -//# sourceMappingURL=../maps/libs.js.map \ No newline at end of file +//# sourceMappingURL=../maps/libs.js.map diff --git a/zanata-war/src/main/webapp/app/js/templates.js b/zanata-war/src/main/webapp/app/js/templates.js index 6417c95742..acf37683bf 100644 --- a/zanata-war/src/main/webapp/app/js/templates.js +++ b/zanata-war/src/main/webapp/app/js/templates.js @@ -13,4 +13,4 @@ $templateCache.put("components/transUnitFilter/trans-unit-filter.html","
    \n
    \n \n
    \n\n"); $templateCache.put("components/transUnit/source/header.html","
    \n

    {{::editorContext.srcLocale.name}} {{::editorContext.srcLocale.localeId}}\n

    \n
      \n
    • \n
    • \n
    • \n \n
    • \n
    • \n \n
    • \n
    \n
    \n"); $templateCache.put("components/transUnit/translation/footer.html","
    \n
    \n
      \n
    • \n \n
    • \n
    • 0\">\n \n
    • \n
    \n
    \n
    \n \n Save as\n \n \n
    \n
    \n \n
    \n
    \n \n
    \n
    \n
      \n
    • \n \n
    • \n
    \n
    \n
    \n
    \n"); -$templateCache.put("components/transUnit/translation/header.html","
    \n

    {{transUnitCtrl.getLocaleName(editorContext.localeId)}} {{editorContext.localeId}}

    \n
      \n
    • \n \n
    • \n
    • \n \n
    • \n
    \n
    \n");}]); \ No newline at end of file +$templateCache.put("components/transUnit/translation/header.html","
    \n

    {{transUnitCtrl.getLocaleName(editorContext.localeId)}} {{editorContext.localeId}}

    \n
      \n
    • \n \n
    • \n
    • \n \n
    • \n
    \n
    \n");}]); diff --git a/zanata-war/src/test/java/org/zanata/MockResourceFactory.java b/zanata-war/src/test/java/org/zanata/MockResourceFactory.java index 231d20917d..ad6d4b3abe 100644 --- a/zanata-war/src/test/java/org/zanata/MockResourceFactory.java +++ b/zanata-war/src/test/java/org/zanata/MockResourceFactory.java @@ -5,6 +5,7 @@ import org.jboss.resteasy.spi.InjectorFactory; import org.jboss.resteasy.spi.PropertyInjector; import org.jboss.resteasy.spi.ResourceFactory; +import org.jboss.resteasy.spi.ResteasyProviderFactory; public class MockResourceFactory implements ResourceFactory { private PropertyInjector propertyInjector; @@ -16,7 +17,7 @@ public MockResourceFactory(Object obj) { @Override public Object createResource(HttpRequest request, HttpResponse response, - InjectorFactory factory) { + ResteasyProviderFactory factory) { propertyInjector.inject(request, response, obj); return obj; } @@ -27,9 +28,9 @@ public Class getScannableClass() { } @Override - public void registered(InjectorFactory factory) { + public void registered(ResteasyProviderFactory factory) { this.propertyInjector = - factory.createPropertyInjector(getScannableClass()); + factory.getInjectorFactory().createPropertyInjector(getScannableClass(), factory); } @Override diff --git a/zanata-war/src/test/java/org/zanata/ZanataRestTest.java b/zanata-war/src/test/java/org/zanata/ZanataRestTest.java index 912a47be14..778a42a410 100644 --- a/zanata-war/src/test/java/org/zanata/ZanataRestTest.java +++ b/zanata-war/src/test/java/org/zanata/ZanataRestTest.java @@ -70,7 +70,7 @@ public final void prepareRestEasyFramework() { // register Exception Mappers for (Class> mapper : exceptionMappers) { - dispatcher.getProviderFactory().addExceptionMapper(mapper); + dispatcher.getProviderFactory().registerProvider(mapper); } // register Providers diff --git a/zanata-war/src/test/java/org/zanata/arquillian/Deployments.java b/zanata-war/src/test/java/org/zanata/arquillian/Deployments.java index 7570d86596..8093ea38ed 100644 --- a/zanata-war/src/test/java/org/zanata/arquillian/Deployments.java +++ b/zanata-war/src/test/java/org/zanata/arquillian/Deployments.java @@ -33,6 +33,7 @@ import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.asset.FileAsset; +import org.jboss.shrinkwrap.api.exporter.ZipExporter; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.resolver.api.maven.Maven; import org.jboss.shrinkwrap.resolver.api.maven.strategy.RejectDependenciesStrategy; @@ -63,9 +64,13 @@ private static File[] runtimeAndTestDependenciesFromPom() { .resolve() .using( // JavaMelody's ServletFilter/Listener interfere with test - // deployments + // deployments. + // google-collections gets pulled in by arquillian and + // conflict with guava. new RejectDependenciesStrategy(false, - "net.bull.javamelody:javamelody-core")).asFile(); + "net.bull.javamelody:javamelody-core", + "com.google.collections:google-collections")) + .asFile(); } @Deployment(name = "zanata.war") @@ -89,6 +94,9 @@ public boolean include(ArchivePath object) { archive.addAsResource("pluralforms.properties"); archive.addAsResource(new ClassLoaderAsset("META-INF/orm.xml"), "META-INF/orm.xml"); + archive.addAsResource(new ClassLoaderAsset( + "META-INF/seam-deployment.properties"), + "META-INF/seam-deployment.properties"); archive.addAsResource( new FileAsset( new File( diff --git a/zanata-war/src/test/java/org/zanata/rest/RestLimitingSynchronousDispatcherTest.java b/zanata-war/src/test/java/org/zanata/rest/RestLimitingSynchronousDispatcherTest.java index 60146e56c8..c883b15704 100644 --- a/zanata-war/src/test/java/org/zanata/rest/RestLimitingSynchronousDispatcherTest.java +++ b/zanata-war/src/test/java/org/zanata/rest/RestLimitingSynchronousDispatcherTest.java @@ -6,9 +6,11 @@ import javax.ws.rs.core.MultivaluedMap; import org.jboss.resteasy.core.ResourceInvoker; +import org.jboss.resteasy.mock.MockHttpRequest; +import org.jboss.resteasy.mock.MockHttpResponse; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; -import org.jboss.seam.resteasy.SeamResteasyProviderFactory; +import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -20,6 +22,7 @@ import org.zanata.ZanataTest; import org.zanata.limits.RateLimitingProcessor; import org.zanata.model.HAccount; +import org.zanata.seam.resteasy.SeamResteasyProviderFactory; import org.zanata.util.HttpUtil; import static org.mockito.Mockito.*; @@ -40,8 +43,8 @@ public class RestLimitingSynchronousDispatcherTest extends ZanataTest { private HttpResponse response; @Mock private RateLimitingProcessor processor; - @Mock - private SeamResteasyProviderFactory providerFactory; + //@Mock + private ResteasyProviderFactory providerFactory = SeamResteasyProviderFactory.getInstance(); @Captor private ArgumentCaptor taskCaptor; @Mock @@ -59,6 +62,7 @@ public void beforeMethod() throws ServletException, IOException { MockitoAnnotations.initMocks(this); when(request.getHttpHeaders().getRequestHeaders()) .thenReturn(headers); + when(request.getMutableHeaders()).thenReturn(headers); when(request.getHttpMethod()).thenReturn("GET"); when(headers.getFirst(HttpUtil.X_AUTH_TOKEN_HEADER)).thenReturn( API_KEY); diff --git a/zanata-war/src/test/java/org/zanata/rest/editor/service/ProjectVersionServiceTest.java b/zanata-war/src/test/java/org/zanata/rest/editor/service/ProjectVersionServiceTest.java index 3db70aa331..662985b018 100644 --- a/zanata-war/src/test/java/org/zanata/rest/editor/service/ProjectVersionServiceTest.java +++ b/zanata-war/src/test/java/org/zanata/rest/editor/service/ProjectVersionServiceTest.java @@ -126,11 +126,8 @@ public void getLocalesWillReturnLocales() { Response response = service.getLocales("about-fedora", "master"); assertThat(response.getStatus()).isEqualTo(200); - assertThat(response.getEntity()).isInstanceOf( - GenericEntity.class); List iterationList = - ((GenericEntity>) response.getEntity()) - .getEntity(); + (List) response.getEntity(); assertThat(iterationList).extracting("localeId") .contains(LocaleId.DE, LocaleId.ES); } @@ -228,10 +225,8 @@ public void getDocumentWillReturnDocumentMetaList() { Response response = service.getDocuments("about-fedora", "master"); assertThat(response.getStatus()).isEqualTo(200); - assertThat(response.getEntity()).isInstanceOf(GenericEntity.class); List docList = - ((GenericEntity>) response.getEntity()) - .getEntity(); + (List) response.getEntity(); assertThat(docList).extracting("name").contains("pot/authors"); } diff --git a/zanata-war/src/test/java/org/zanata/rest/editor/service/StatisticsServiceTest.java b/zanata-war/src/test/java/org/zanata/rest/editor/service/StatisticsServiceTest.java index 8a9f671aab..07e34cdf41 100644 --- a/zanata-war/src/test/java/org/zanata/rest/editor/service/StatisticsServiceTest.java +++ b/zanata-war/src/test/java/org/zanata/rest/editor/service/StatisticsServiceTest.java @@ -56,6 +56,5 @@ public void getDocumentStatisticsWillReturnResult() { Response response = service.getDocumentStatistics("a", "1", "authors", "de"); assertThat(response.getStatus()).isEqualTo(200); - assertThat(response.getEntity()).isInstanceOf(GenericEntity.class); } }